4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 1996-1998 by Sun Microsystems, Inc.
24 * All Rights reserved.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
35 * Contains functions that deal with the device table and are not for
36 * consumption by the general user population.
39 * _opendevtab() Opens the device table for commands
40 * _setdevtab() Rewinds the open device table
41 * _enddevtab() Closes the open device table
42 * _getdevtabent() Gets the next entry in the device table
43 * _freedevtabent() Frees memory allocated to a device-table entry
44 * _getdevrec() Gets a specific record from the device table
45 * _devtabpath() Get the pathname of the device table file
46 * _validalias() Is a value a valid alias?
52 * <sys/sysmacros.h> System macro definitions
53 * <sys/types.h> System data types
54 * <sys/mkdev.h> Device Macros
55 * <unistd.h> System Symbolic Constants
56 * <stdio.h> Standard I/O definitions
57 * <string.h> String handling definitions
58 * <ctype.h> Character types and macros
59 * <errno.h> Error codes
60 * <sys/stat.h> File status information
61 * <devmgmt.h> Global Device Management definitions
62 * "devtab.h" Local Device Management definitions
65 #include <sys/sysmacros.h>
66 #include <sys/types.h>
68 #include <sys/mkdev.h>
81 * Static data definitions:
82 * dtabrecnum Record number of the current record (0 to n-1)
83 * leftoff Addr of char to begin next parse using
84 * getfld(), getattrval(), getquoted()
85 * recbufsz The size of the buffer used for reading records
86 * recbuf Addr of malloc() buffer for reading records
87 * xtndcnt Number of malloc()/realloc() calls on record buffer
90 static int xtndcnt
= 0;
91 static char *recbuf
= NULL
;
92 static int recbufsz
= 0;
94 static char *leftoff
= NULL
;
95 static int dtabrecnum
= 0;
101 * Compares pertinent information in a stat() structure
102 * to see if the two structures describe the same device.
103 * If the file modes are the same and they have the same
104 * file system and i-node (i.e. they're links) or they
105 * are block or character devices and have the same major
106 * and minor device numbers (i.e. "mknod"s for the same
107 * device), it's the same device.
110 * TRUE if the two structures describe the same device
115 samedev(struct stat64 x
, struct stat64 y
)
120 /* If the devices are of the same type ... */
121 if ((x
.st_mode
& 0170000) == (y
.st_mode
& 0170000)) {
124 * If they are described by the same inode on the same device,
125 * the two devices are the same. Otherwise, if the devices are
126 * character-special or block-special devices, try to match by
127 * device type and major and minor device numbers.
130 if ((x
.st_dev
== y
.st_dev
) && (x
.st_ino
== y
.st_ino
)) same
= TRUE
;
132 if (((x
.st_mode
& 0170000) == 0020000) ||
133 ((x
.st_mode
& 0170000) == 0060000)) {
134 if ((major(x
.st_rdev
) == major(y
.st_rdev
)) &&
135 (minor(x
.st_rdev
) == minor(y
.st_rdev
))) same
= TRUE
;
147 * This function rewinds the open device table so that the next
148 * _getdevtabent() returns the first record in the device table.
158 /* If the device table file is open, rewind the file */
159 if (oam_devtab
!= NULL
) {
168 * This function closes the open device table. It resets the
169 * open device table external variable to NULL.
179 /* If the device table file is open, close it */
180 if (oam_devtab
!= NULL
) {
181 (void) fclose(oam_devtab
);
188 * char *getfld(ptr, delims)
193 * - Can't use "strtok()" because of its use of static data. The caller
194 * may be using strtok() and we'll really mess them up.
195 * - The function returns NULL if it didn't find any token -- '\0' can't
196 * be a delimiter using this algorithm.
201 char *ptr
, /* String to parse */
202 char *delims
) /* List of delimiters */
204 int done
; /* TRUE if we're finished */
205 char *p
, *q
; /* Temp pointers */
208 * Figure out where to start.
209 * If given a pointer, use that.
210 * Otherwise, use where we left off.
213 p
= ptr
? ptr
: leftoff
;
217 * If there's anything to parse, search the string for the first
218 * occurrence of any of the delimiters. If one is found, change it
219 * to '\0' and remember the place to start for next time. If not
220 * found, forget the restart address and prepare to return NULL.
221 * Don't terminate on "escaped" characters.
224 if (p
) { /* Anything to do ?? */
225 q
= p
; /* Where to begin */
226 done
= FALSE
; /* We're not done yet */
227 while (*q
&& !done
) { /* Any more chars */
228 if (*q
== '\\') { /* Escaped ? */
229 if (*(++q
)) q
++; /* Skip escaped char */
230 } else /* Not escaped */
231 if (!strchr(delims
, *q
)) q
++; /* Skip non-delim */
232 else done
= TRUE
; /* Otherwise, done */
234 if (*q
) { /* Terminator found? */
235 *q
++ = '\0'; /* Null-terminate token */
236 leftoff
= q
; /* Remember restart pt. */
238 leftoff
= p
= NULL
; /* Nothin found or left */
242 return (p
); /* Return ptr to token */
246 * char *getquoted(ptr)
249 * This function extracts a quoted string from the string pointed
250 * to by <ptr>, or, if <ptr> is NULL, wherever we left off
254 * char *ptr Pointer to the character-string to parse, or
255 * (char *) NULL if we're to pick up where we
256 * [getquoted(), getfld(), and getattrval()] left off.
259 * The address of malloc()ed space that contains the possibly quoted
263 * - This code only works if it can assume that the last character in
264 * the string it's parsing is a '\n', something that is guarenteed
265 * by the getnextrec() function.
272 char *rtn
; /* Value to return */
273 char *p
, *q
; /* Temps */
275 /* Figure out where to start off */
276 p
= ptr
? ptr
: leftoff
;
278 /* If there's anything to parse and it's a quoted string ... */
279 if ((p
) && (*p
== '"') && (p
= getfld(p
+1, "\""))) {
281 /* Copy string for the caller */
282 if (rtn
= malloc(strlen(p
)+1)) { /* Malloc() space */
283 q
= rtn
; /* Set up temp ptr */
285 if (*p
== '\\') p
++; /* Skip escape */
286 *q
++ = *p
; /* Copy char */
287 } while (*p
++); /* While there's chars */
288 } else leftoff
= rtn
= NULL
;
289 } else leftoff
= rtn
= NULL
;
296 * struct attrval *getattrval(ptr)
299 * This function extracts the next attr=val pair from <ptr> or wherever
300 * getfld() left off...
303 * char *ptr The string to parse, or (char *) NULL if we're to
304 * begin wherever we left off last time.
306 * Returns: struct attrval *
307 * The address of a malloc()ed structure containing the attribute and the
308 * value of the attr=val pair extracted.
311 static struct attrval
*
312 getattrval(char *ptr
)
315 struct attrval
*rtn
; /* Ptr to struct to return */
316 char *p
, *q
; /* Temp pointers */
319 /* Use what's given to us or wherever we left off */
320 p
= ptr
? ptr
: leftoff
;
322 /* If there's anything to parse, extract the next attr=val pair */
325 /* Eat white space */
326 while (*p
&& isspace((unsigned char)*p
)) p
++;
328 /* Extract the attribute name, if any */
329 if (*p
&& getfld(p
, "=")) {
331 /* Allocate space for the structure we're building */
332 if (rtn
= malloc(sizeof (struct attrval
))) {
334 /* Allocate space for the attribute name */
335 if (rtn
->attr
= malloc(strlen(p
)+1)) {
337 /* Copy the attribute name into alloc'd space */
338 q
= rtn
->attr
; /* Set up temp ptr */
340 if (*p
== '\\') p
++; /* Skip escape */
341 *q
++ = *p
; /* Copy char */
342 } while (*p
++); /* While more */
344 /* Extract the value */
345 if (!(rtn
->val
= getquoted(NULL
))) {
346 /* Error getting value, free resources */
353 /* Error getting space for attribute, free resources */
360 /* No space for attr struct */
366 /* No attribute name */
372 /* Nothing to parse */
384 * This function gets the next record from the input stream "oam_devtab"
385 * and puts it in the device-table record buffer (whose address is in
386 * "recbuf"). If the buffer is not allocated or is too small to
387 * accommodate the record, the function allocates more space to the
393 * The address of the buffer containing the record.
395 * Static Data Referenced:
396 * recbuf Address of the buffer containing records read from the
398 * recbufsz Current size of the record buffer
399 * xtndcnt Number of times the record buffer has been extended
400 * oam_devtab Device table stream, expected to be open for (at
404 * - The string returned in the buffer <buf> ALWAYS end in a '\n' (newline)
405 * character followed by a '\0' (null).
412 char *recp
; /* Value to return */
413 char *p
; /* Temp pointer */
414 int done
; /* TRUE if we're finished */
415 int reclen
; /* Number of chars in record */
418 /* If there's no buffer for records, try to get one */
420 if (recbuf
= malloc(DTAB_BUFSIZ
)) {
421 recbufsz
= DTAB_BUFSIZ
;
423 } else return (NULL
);
427 /* Get the next record */
428 recp
= fgets(recbuf
, recbufsz
, oam_devtab
);
431 /* While we've something to return and we're not finished ... */
432 while (recp
&& !done
) {
434 /* If our return string isn't a null-string ... */
435 if ((reclen
= (int)strlen(recp
)) != 0) {
437 /* If we have a complete record, we're finished */
438 if ((*(recp
+reclen
-1) == '\n') &&
439 ((reclen
== 1) || (*(recp
+reclen
-2) != '\\'))) done
= TRUE
;
443 * Need to complete the record. A complete record is
444 * one which is terminated by an unescaped new-line
448 /* If the buffer is full, expand it and continue reading */
449 if (reclen
== recbufsz
-1) {
451 /* Have we reached our maximum extension count? */
452 if (xtndcnt
< XTND_MAXCNT
) {
454 /* Expand the record buffer */
455 if (p
= realloc(recbuf
,
456 (size_t)recbufsz
+DTAB_BUFINC
)) {
458 /* Update buffer information */
461 recbufsz
+= DTAB_BUFINC
;
465 /* Expansion failed */
472 /* Maximum extend count exceeded. Insane table */
479 /* Complete the record */
482 /* Read stuff into the expanded space */
483 if (fgets(recbuf
+reclen
, recbufsz
-reclen
, oam_devtab
)) {
484 reclen
= (int)strlen(recbuf
);
486 if ((*(recp
+reclen
-1) == '\n') &&
487 ((reclen
== 1) || (*(recp
+reclen
-2) != '\\')))
490 /* Read failed, corrupt record? */
496 } /* End incomplete record handling */
500 /* Read a null string? (corrupt table) */
505 } /* while (recp && !done) */
507 /* Return what we've got (if anything) */
512 * char *_devtabpath()
514 * Get the pathname of the device table
519 * Returns the pathname to the device table of NULL if
520 * there was a problem getting the memory needed to contain the
524 * 1. If OAM_DEVTAB is defined in the environment and is not
525 * defined as "", it returns the value of that environment
527 * 2. Otherwise, use the value of the environment variable DTAB_PATH.
537 char *path
; /* Ptr to path in environment */
539 char *rtnval
; /* Ptr to value to return */
543 * If compiled with -DDEBUG=1,
544 * look for the pathname in the environment
548 if (((path
= getenv(OAM_DEVTAB
)) != NULL
) && (*path
)) {
549 if (rtnval
= malloc(strlen(path
)+1))
550 (void) strcpy(rtnval
, path
);
554 * Use the standard device table.
557 if (rtnval
= malloc(strlen(DTAB_PATH
)+1))
558 (void) strcpy(rtnval
, DTAB_PATH
);
569 * int _opendevtab(mode)
572 * The _opendevtab() function opens a device table for a command.
575 * mode The open mode to use to open the file. (i.e. "r" for
576 * reading, "w" for writing. See FOPEN(BA_OS) in SVID.)
579 * TRUE if it successfully opens the device table file, FALSE otherwise
583 _opendevtab(char *mode
)
589 char *devtabname
; /* Ptr to the device table name */
590 int rtnval
; /* Value to return */
594 if (devtabname
= _devtabpath()) {
595 if (oam_devtab
) (void) fclose(oam_devtab
);
596 if (oam_devtab
= fopen(devtabname
, mode
))
597 dtabrecnum
= 0; /* :-) */
598 else rtnval
= FALSE
; /* :-( */
599 } else rtnval
= FALSE
; /* :-( */
604 * int _validalias(alias)
607 * Determine if <alias> is a valid alias. Returns TRUE if it is
608 * a valid alias, FALSE otherwise.
611 * alias Value to check out
614 * TRUE if <alias> is a valid alias, FALSE otherwise.
618 _validalias(char *alias
) /* Alias to validate */
621 char *p
; /* Temp pointer */
622 size_t len
; /* Length of <alias> */
623 int rtn
; /* Value to return */
626 /* Assume the worst */
630 * A valid alias contains 0 < i <= 14 characters. The first
631 * must be alphanumeric or "@$_." and the rest must be alphanumeric
636 if ((alias
!= NULL
) && ((len
= strlen(alias
)) > 0) && (len
<= 14)) {
638 /* Check the first character */
640 if (isalnum((unsigned char)*p
) || strchr("@$_.", *p
)) {
642 /* Check the rest of the characters */
643 for (p
++; *p
&& (isalnum((unsigned char)*p
) ||
644 strchr("@#$_-+.", *p
)); p
++)
646 if (!(*p
)) rtn
= TRUE
;
650 /* Return indicator... */
653 } /* int _validalias() */
656 * struct devtabent *_getdevtabent()
658 * This function returns the next entry in the device table.
659 * If no device table is open, it opens the standard device table
660 * and returns the first record in the table.
664 * Returns: struct devtabent *
665 * Pointer to the next record in the device table, or
666 * NULL if it was unable to open the file or there
667 * are no more records to read. "errno" reflects the situation. If
668 * errno is not changed and the function returns NULL, there are no more
669 * records to read. If errno is set, it indicates the error.
672 * - The caller should set "errno" to 0 before calling this function.
679 struct devtabent
*ent
; /* Ptr to dev table entry structure */
680 struct attrval
*attr
; /* Ptr to struct for attr/val pair */
681 struct attrval
*t
; /* Tmp ptr to attr/val struct */
682 char *record
; /* Ptr to the record just read */
683 char *p
, *q
; /* Tmp char ptrs */
684 int done
; /* TRUE if we've built an entry */
687 /* Open the device table if it's not already open */
688 if (oam_devtab
== NULL
) {
689 if (!_opendevtab("r"))
693 /* Get space for the structure we're returning */
694 if (!(ent
= malloc(sizeof (struct devtabent
)))) {
699 while (!done
&& (record
= getnextrec())) {
701 /* Save record number in structure */
702 ent
->entryno
= dtabrecnum
++;
704 /* Comment record? If so, just save the value and we're through */
705 if (strchr("#\n", *record
) || isspace((unsigned char)*record
)) {
708 if (ent
->attrstr
= malloc(strlen(record
)+1)) {
723 /* Record is a data record. Parse it. */
724 ent
->comment
= FALSE
;
725 ent
->attrstr
= NULL
; /* For now */
727 /* Extract the device alias */
728 if (p
= getfld(record
, ":")) {
730 if (ent
->alias
= malloc(strlen(p
)+1)) {
737 } else ent
->alias
= NULL
;
739 /* Extract the character-device name */
740 if ((p
= getfld(NULL
, ":")) == NULL
) {
744 if (ent
->cdevice
= malloc(strlen(p
)+1)) {
751 } else ent
->cdevice
= NULL
;
753 /* Extract the block-device name */
754 if (!(p
= getfld(NULL
, ":"))) {
759 if (ent
->bdevice
= malloc(strlen(p
)+1)) {
769 /* Extract the pathname */
770 if ((p
= getfld(NULL
, ":\n")) == NULL
) {
776 if (ent
->pathname
= malloc(strlen(p
)+1)) {
784 ent
->pathname
= NULL
;
786 /* Found a valid record */
790 * Extract attributes, build a linked list of
793 if (attr
= getattrval(NULL
)) {
794 ent
->attrlist
= attr
;
796 while (attr
= getattrval(NULL
)) {
802 ent
->attrlist
= NULL
;
804 } /* pathname extracted */
805 } /* bdevice extracted */
806 } /* cdevice extracted */
807 } /* alias extracted */
809 } /* !done && record read */
811 /* If no entry was read, free space allocated to the structure */
821 * void _freedevtabent(devtabent)
822 * struct devtabent *devtabent;
824 * This function frees space allocated to a device table entry.
827 * struct devtabent *devtabent The structure whose space is to be
834 _freedevtabent(struct devtabent
*ent
)
840 struct attrval
*p
; /* Structure being freed */
841 struct attrval
*q
; /* Next structure to free */
846 * Free the attribute list. For each item in the attribute
848 * 1. Free the attribute name (always defined),
849 * 2. Free the value (if any -- it's not defined if we're
850 * changing an existing attribute),
851 * 3. Free the space allocated to the structure.
864 /* Free the standard fields (alias, cdevice, bdevice, pathname) */
871 /* Free the attribute string */
874 /* Free the space allocated to the structure */
879 * struct devtabent *_getdevrec(device)
882 * Thie _getdevrec() function returns a pointer to a structure that
883 * contains the information in the device-table entry that describes
884 * the device <device>.
886 * The device <device> can be a device alias, a pathname contained in
887 * the entry as the "cdevice", "bdevice", or "pathname" attribute,
888 * or a pathname to a device described using the "cdevice", "bdevice",
889 * or "pathname" attribute (depending on whether the pathname references
890 * a character-special file, block-special file, or something else,
894 * char *device A character-string describing the device whose record
895 * is to be retrieved from the device table.
897 * Returns: struct devtabent *
898 * A pointer to a structure describing the device.
901 * - Someday, add a cache so that repeated requests for the same record
902 * don't require going to the filesystem. (Maybe -- this might belong
907 _getdevrec(char *device
) /* The device to search for */
913 struct stat64 devstatbuf
; /* Stat struct, <device> */
914 struct stat64 tblstatbuf
; /* Stat struct, tbl entry */
915 struct devtabent
*devrec
; /* Pointer to current record */
916 int found
; /* TRUE if record found */
917 int olderrno
; /* Old value of errno */
921 * Search the device table looking for the requested device
927 if ((device
!= NULL
) && !_validalias(device
)) {
928 while (!found
&& (devrec
= _getdevtabent())) {
929 if (!devrec
->comment
) {
931 if (strcmp(device
, devrec
->cdevice
) == 0) found
= TRUE
;
933 if (strcmp(device
, devrec
->bdevice
) == 0) found
= TRUE
;
934 if (devrec
->pathname
)
935 if (strcmp(device
, devrec
->pathname
) == 0) found
= TRUE
;
936 } else _freedevtabent(devrec
);
940 * If the device <device> wasn't named explicitly in the device
941 * table, compare it against like entries by comparing file-
942 * system, major device number, and minor device number
948 /* Status the file <device>. If fails, invalid device */
949 if (stat64(device
, &devstatbuf
) != 0) errno
= ENODEV
;
953 * If <device> is a block-special device. See if it is
954 * in the table by matching its file-system indicator
955 * and major/minor device numbers against the
956 * file-system and major/minor device numbers of the
960 if ((devstatbuf
.st_mode
& 0170000) == 0020000) {
961 while (!found
&& (devrec
= _getdevtabent())) {
962 if (!devrec
->comment
&&
963 (devrec
->cdevice
!= NULL
))
964 if (stat64(devrec
->cdevice
, &tblstatbuf
) == 0) {
965 if (samedev(tblstatbuf
, devstatbuf
))
968 /* Ignore stat() errs */
971 if (!found
) _freedevtabent(devrec
);
976 * If <device> is a block-special device. See if it is
977 * in the table by matching its file-system indicator
978 * and major/minor device numbers against the
979 * file-system and major/minor device numbers of the
983 else if ((devstatbuf
.st_mode
& 0170000) == 0060000) {
984 while (!found
&& (devrec
= _getdevtabent())) {
985 if (!devrec
->comment
&&
986 (devrec
->bdevice
!= NULL
))
987 if (stat64(devrec
->bdevice
, &tblstatbuf
) == 0) {
988 if (samedev(tblstatbuf
, devstatbuf
))
991 /* Ignore stat() errs */
994 if (!found
) _freedevtabent(devrec
);
999 * If <device> is neither a block-special or character-
1000 * special device. See if it is in the table by
1001 * matching its file-system indicator and major/minor
1002 * device numbers against the file-system and
1003 * major/minor device numbers of the "pathname" entries.
1007 while (!found
&& (devrec
= _getdevtabent())) {
1008 if (!devrec
->comment
&&
1009 (devrec
->pathname
!= NULL
))
1010 if (stat64(devrec
->pathname
,
1011 &tblstatbuf
) == 0) {
1012 if (samedev(tblstatbuf
, devstatbuf
))
1015 /* Ignore stat() errs */
1018 if (!found
) _freedevtabent(devrec
);
1027 } /* End case where stat() on the <device> succeeded */
1029 } /* End case handling pathname not explicitly in device table */
1031 } /* End case handling <device> as a fully-qualified pathname */
1035 * Otherwise the device <device> is an alias.
1036 * Search the table for a record that has as the "alias" attribute
1037 * the value <device>.
1041 while (!found
&& (devrec
= _getdevtabent())) {
1042 if (!devrec
->comment
&& (device
!= NULL
) &&
1043 strcmp(device
, devrec
->alias
) == 0)
1045 else _freedevtabent(devrec
);