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 */
30 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */
36 * Contains functions that deal with the device table and are not for
37 * consumption by the general user population.
40 * _opendevtab() Opens the device table for commands
41 * _setdevtab() Rewinds the open device table
42 * _enddevtab() Closes the open device table
43 * _getdevtabent() Gets the next entry in the device table
44 * _freedevtabent() Frees memory allocated to a device-table entry
45 * _getdevrec() Gets a specific record from the device table
46 * _devtabpath() Get the pathname of the device table file
47 * _validalias() Is a value a valid alias?
53 * <sys/sysmacros.h> System macro definitions
54 * <sys/types.h> System data types
55 * <sys/mkdev.h> Device Macros
56 * <unistd.h> System Symbolic Constants
57 * <stdio.h> Standard I/O definitions
58 * <string.h> String handling definitions
59 * <ctype.h> Character types and macros
60 * <errno.h> Error codes
61 * <sys/stat.h> File status information
62 * <devmgmt.h> Global Device Management definitions
63 * "devtab.h" Local Device Management definitions
66 #include <sys/sysmacros.h>
67 #include <sys/types.h>
69 #include <sys/mkdev.h>
82 * Static data definitions:
83 * dtabrecnum Record number of the current record (0 to n-1)
84 * leftoff Addr of char to begin next parse using
85 * getfld(), getattrval(), getquoted()
86 * recbufsz The size of the buffer used for reading records
87 * recbuf Addr of malloc() buffer for reading records
88 * xtndcnt Number of malloc()/realloc() calls on record buffer
91 static int xtndcnt
= 0;
92 static char *recbuf
= NULL
;
93 static int recbufsz
= 0;
95 static char *leftoff
= NULL
;
96 static int dtabrecnum
= 0;
102 * Compares pertinent information in a stat() structure
103 * to see if the two structures describe the same device.
104 * If the file modes are the same and they have the same
105 * file system and i-node (i.e. they're links) or they
106 * are block or character devices and have the same major
107 * and minor device numbers (i.e. "mknod"s for the same
108 * device), it's the same device.
111 * TRUE if the two structures describe the same device
116 samedev(struct stat64 x
, struct stat64 y
)
121 /* If the devices are of the same type ... */
122 if ((x
.st_mode
& 0170000) == (y
.st_mode
& 0170000)) {
125 * If they are described by the same inode on the same device,
126 * the two devices are the same. Otherwise, if the devices are
127 * character-special or block-special devices, try to match by
128 * device type and major and minor device numbers.
131 if ((x
.st_dev
== y
.st_dev
) && (x
.st_ino
== y
.st_ino
)) same
= TRUE
;
133 if (((x
.st_mode
& 0170000) == 0020000) ||
134 ((x
.st_mode
& 0170000) == 0060000)) {
135 if ((major(x
.st_rdev
) == major(y
.st_rdev
)) &&
136 (minor(x
.st_rdev
) == minor(y
.st_rdev
))) same
= TRUE
;
148 * This function rewinds the open device table so that the next
149 * _getdevtabent() returns the first record in the device table.
159 /* If the device table file is open, rewind the file */
160 if (oam_devtab
!= NULL
) {
169 * This function closes the open device table. It resets the
170 * open device table external variable to NULL.
180 /* If the device table file is open, close it */
181 if (oam_devtab
!= NULL
) {
182 (void) fclose(oam_devtab
);
189 * char *getfld(ptr, delims)
194 * - Can't use "strtok()" because of its use of static data. The caller
195 * may be using strtok() and we'll really mess them up.
196 * - The function returns NULL if it didn't find any token -- '\0' can't
197 * be a delimiter using this algorithm.
202 char *ptr
, /* String to parse */
203 char *delims
) /* List of delimiters */
205 int done
; /* TRUE if we're finished */
206 char *p
, *q
; /* Temp pointers */
209 * Figure out where to start.
210 * If given a pointer, use that.
211 * Otherwise, use where we left off.
214 p
= ptr
? ptr
: leftoff
;
218 * If there's anything to parse, search the string for the first
219 * occurrence of any of the delimiters. If one is found, change it
220 * to '\0' and remember the place to start for next time. If not
221 * found, forget the restart address and prepare to return NULL.
222 * Don't terminate on "escaped" characters.
225 if (p
) { /* Anything to do ?? */
226 q
= p
; /* Where to begin */
227 done
= FALSE
; /* We're not done yet */
228 while (*q
&& !done
) { /* Any more chars */
229 if (*q
== '\\') { /* Escaped ? */
230 if (*(++q
)) q
++; /* Skip escaped char */
231 } else /* Not escaped */
232 if (!strchr(delims
, *q
)) q
++; /* Skip non-delim */
233 else done
= TRUE
; /* Otherwise, done */
235 if (*q
) { /* Terminator found? */
236 *q
++ = '\0'; /* Null-terminate token */
237 leftoff
= q
; /* Remember restart pt. */
239 leftoff
= p
= NULL
; /* Nothin found or left */
243 return (p
); /* Return ptr to token */
247 * char *getquoted(ptr)
250 * This function extracts a quoted string from the string pointed
251 * to by <ptr>, or, if <ptr> is NULL, wherever we left off
255 * char *ptr Pointer to the character-string to parse, or
256 * (char *) NULL if we're to pick up where we
257 * [getquoted(), getfld(), and getattrval()] left off.
260 * The address of malloc()ed space that contains the possibly quoted
264 * - This code only works if it can assume that the last character in
265 * the string it's parsing is a '\n', something that is guarenteed
266 * by the getnextrec() function.
273 char *rtn
; /* Value to return */
274 char *p
, *q
; /* Temps */
276 /* Figure out where to start off */
277 p
= ptr
? ptr
: leftoff
;
279 /* If there's anything to parse and it's a quoted string ... */
280 if ((p
) && (*p
== '"') && (p
= getfld(p
+1, "\""))) {
282 /* Copy string for the caller */
283 if (rtn
= malloc(strlen(p
)+1)) { /* Malloc() space */
284 q
= rtn
; /* Set up temp ptr */
286 if (*p
== '\\') p
++; /* Skip escape */
287 *q
++ = *p
; /* Copy char */
288 } while (*p
++); /* While there's chars */
289 } else leftoff
= rtn
= NULL
;
290 } else leftoff
= rtn
= NULL
;
297 * struct attrval *getattrval(ptr)
300 * This function extracts the next attr=val pair from <ptr> or wherever
301 * getfld() left off...
304 * char *ptr The string to parse, or (char *) NULL if we're to
305 * begin wherever we left off last time.
307 * Returns: struct attrval *
308 * The address of a malloc()ed structure containing the attribute and the
309 * value of the attr=val pair extracted.
312 static struct attrval
*
313 getattrval(char *ptr
)
316 struct attrval
*rtn
; /* Ptr to struct to return */
317 char *p
, *q
; /* Temp pointers */
320 /* Use what's given to us or wherever we left off */
321 p
= ptr
? ptr
: leftoff
;
323 /* If there's anything to parse, extract the next attr=val pair */
326 /* Eat white space */
327 while (*p
&& isspace((unsigned char)*p
)) p
++;
329 /* Extract the attribute name, if any */
330 if (*p
&& getfld(p
, "=")) {
332 /* Allocate space for the structure we're building */
333 if (rtn
= malloc(sizeof (struct attrval
))) {
335 /* Allocate space for the attribute name */
336 if (rtn
->attr
= malloc(strlen(p
)+1)) {
338 /* Copy the attribute name into alloc'd space */
339 q
= rtn
->attr
; /* Set up temp ptr */
341 if (*p
== '\\') p
++; /* Skip escape */
342 *q
++ = *p
; /* Copy char */
343 } while (*p
++); /* While more */
345 /* Extract the value */
346 if (!(rtn
->val
= getquoted(NULL
))) {
347 /* Error getting value, free resources */
354 /* Error getting space for attribute, free resources */
361 /* No space for attr struct */
367 /* No attribute name */
373 /* Nothing to parse */
385 * This function gets the next record from the input stream "oam_devtab"
386 * and puts it in the device-table record buffer (whose address is in
387 * "recbuf"). If the buffer is not allocated or is too small to
388 * accommodate the record, the function allocates more space to the
394 * The address of the buffer containing the record.
396 * Static Data Referenced:
397 * recbuf Address of the buffer containing records read from the
399 * recbufsz Current size of the record buffer
400 * xtndcnt Number of times the record buffer has been extended
401 * oam_devtab Device table stream, expected to be open for (at
405 * - The string returned in the buffer <buf> ALWAYS end in a '\n' (newline)
406 * character followed by a '\0' (null).
413 char *recp
; /* Value to return */
414 char *p
; /* Temp pointer */
415 int done
; /* TRUE if we're finished */
416 int reclen
; /* Number of chars in record */
419 /* If there's no buffer for records, try to get one */
421 if (recbuf
= malloc(DTAB_BUFSIZ
)) {
422 recbufsz
= DTAB_BUFSIZ
;
424 } else return (NULL
);
428 /* Get the next record */
429 recp
= fgets(recbuf
, recbufsz
, oam_devtab
);
432 /* While we've something to return and we're not finished ... */
433 while (recp
&& !done
) {
435 /* If our return string isn't a null-string ... */
436 if ((reclen
= (int)strlen(recp
)) != 0) {
438 /* If we have a complete record, we're finished */
439 if ((*(recp
+reclen
-1) == '\n') &&
440 ((reclen
== 1) || (*(recp
+reclen
-2) != '\\'))) done
= TRUE
;
444 * Need to complete the record. A complete record is
445 * one which is terminated by an unescaped new-line
449 /* If the buffer is full, expand it and continue reading */
450 if (reclen
== recbufsz
-1) {
452 /* Have we reached our maximum extension count? */
453 if (xtndcnt
< XTND_MAXCNT
) {
455 /* Expand the record buffer */
456 if (p
= realloc(recbuf
,
457 (size_t)recbufsz
+DTAB_BUFINC
)) {
459 /* Update buffer information */
462 recbufsz
+= DTAB_BUFINC
;
466 /* Expansion failed */
473 /* Maximum extend count exceeded. Insane table */
480 /* Complete the record */
483 /* Read stuff into the expanded space */
484 if (fgets(recbuf
+reclen
, recbufsz
-reclen
, oam_devtab
)) {
485 reclen
= (int)strlen(recbuf
);
487 if ((*(recp
+reclen
-1) == '\n') &&
488 ((reclen
== 1) || (*(recp
+reclen
-2) != '\\')))
491 /* Read failed, corrupt record? */
497 } /* End incomplete record handling */
501 /* Read a null string? (corrupt table) */
506 } /* while (recp && !done) */
508 /* Return what we've got (if anything) */
513 * char *_devtabpath()
515 * Get the pathname of the device table
520 * Returns the pathname to the device table of NULL if
521 * there was a problem getting the memory needed to contain the
525 * 1. If OAM_DEVTAB is defined in the environment and is not
526 * defined as "", it returns the value of that environment
528 * 2. Otherwise, use the value of the environment variable DTAB_PATH.
538 char *path
; /* Ptr to path in environment */
540 char *rtnval
; /* Ptr to value to return */
544 * If compiled with -DDEBUG=1,
545 * look for the pathname in the environment
549 if (((path
= getenv(OAM_DEVTAB
)) != NULL
) && (*path
)) {
550 if (rtnval
= malloc(strlen(path
)+1))
551 (void) strcpy(rtnval
, path
);
555 * Use the standard device table.
558 if (rtnval
= malloc(strlen(DTAB_PATH
)+1))
559 (void) strcpy(rtnval
, DTAB_PATH
);
570 * int _opendevtab(mode)
573 * The _opendevtab() function opens a device table for a command.
576 * mode The open mode to use to open the file. (i.e. "r" for
577 * reading, "w" for writing. See FOPEN(BA_OS) in SVID.)
580 * TRUE if it successfully opens the device table file, FALSE otherwise
584 _opendevtab(char *mode
)
590 char *devtabname
; /* Ptr to the device table name */
591 int rtnval
; /* Value to return */
595 if (devtabname
= _devtabpath()) {
596 if (oam_devtab
) (void) fclose(oam_devtab
);
597 if (oam_devtab
= fopen(devtabname
, mode
))
598 dtabrecnum
= 0; /* :-) */
599 else rtnval
= FALSE
; /* :-( */
600 } else rtnval
= FALSE
; /* :-( */
605 * int _validalias(alias)
608 * Determine if <alias> is a valid alias. Returns TRUE if it is
609 * a valid alias, FALSE otherwise.
612 * alias Value to check out
615 * TRUE if <alias> is a valid alias, FALSE otherwise.
619 _validalias(char *alias
) /* Alias to validate */
622 char *p
; /* Temp pointer */
623 size_t len
; /* Length of <alias> */
624 int rtn
; /* Value to return */
627 /* Assume the worst */
631 * A valid alias contains 0 < i <= 14 characters. The first
632 * must be alphanumeric or "@$_." and the rest must be alphanumeric
637 if ((alias
!= NULL
) && ((len
= strlen(alias
)) > 0) && (len
<= 14)) {
639 /* Check the first character */
641 if (isalnum((unsigned char)*p
) || strchr("@$_.", *p
)) {
643 /* Check the rest of the characters */
644 for (p
++; *p
&& (isalnum((unsigned char)*p
) ||
645 strchr("@#$_-+.", *p
)); p
++)
647 if (!(*p
)) rtn
= TRUE
;
651 /* Return indicator... */
654 } /* int _validalias() */
657 * struct devtabent *_getdevtabent()
659 * This function returns the next entry in the device table.
660 * If no device table is open, it opens the standard device table
661 * and returns the first record in the table.
665 * Returns: struct devtabent *
666 * Pointer to the next record in the device table, or
667 * (struct devtabent *) NULL if it was unable to open the file or there
668 * are no more records to read. "errno" reflects the situation. If
669 * errno is not changed and the function returns NULL, there are no more
670 * records to read. If errno is set, it indicates the error.
673 * - The caller should set "errno" to 0 before calling this function.
680 struct devtabent
*ent
; /* Ptr to dev table entry structure */
681 struct attrval
*attr
; /* Ptr to struct for attr/val pair */
682 struct attrval
*t
; /* Tmp ptr to attr/val struct */
683 char *record
; /* Ptr to the record just read */
684 char *p
, *q
; /* Tmp char ptrs */
685 int done
; /* TRUE if we've built an entry */
688 /* Open the device table if it's not already open */
689 if (oam_devtab
== NULL
) {
690 if (!_opendevtab("r"))
694 /* Get space for the structure we're returning */
695 if (!(ent
= malloc(sizeof (struct devtabent
)))) {
700 while (!done
&& (record
= getnextrec())) {
702 /* Save record number in structure */
703 ent
->entryno
= dtabrecnum
++;
705 /* Comment record? If so, just save the value and we're through */
706 if (strchr("#\n", *record
) || isspace((unsigned char)*record
)) {
709 if (ent
->attrstr
= malloc(strlen(record
)+1)) {
724 /* Record is a data record. Parse it. */
725 ent
->comment
= FALSE
;
726 ent
->attrstr
= NULL
; /* For now */
728 /* Extract the device alias */
729 if (p
= getfld(record
, ":")) {
731 if (ent
->alias
= malloc(strlen(p
)+1)) {
738 } else ent
->alias
= NULL
;
740 /* Extract the character-device name */
741 if ((p
= getfld(NULL
, ":")) == NULL
) {
746 if (ent
->cdevice
= malloc(strlen(p
)+1)) {
753 } else ent
->cdevice
= NULL
;
755 /* Extract the block-device name */
756 if (!(p
= getfld(NULL
, ":"))) {
757 if (ent
->alias
) free(ent
->alias
);
758 if (ent
->cdevice
) free(ent
->cdevice
);
761 if (ent
->bdevice
= malloc(strlen(p
)+1)) {
771 /* Extract the pathname */
772 if ((p
= getfld(NULL
, ":\n")) == NULL
) {
773 if (ent
->alias
) free(ent
->alias
);
774 if (ent
->cdevice
) free(ent
->cdevice
);
775 if (ent
->bdevice
) free(ent
->bdevice
);
778 if (ent
->pathname
= malloc(strlen(p
)+1)) {
786 ent
->pathname
= NULL
;
788 /* Found a valid record */
792 * Extract attributes, build a linked list of
795 if (attr
= getattrval(NULL
)) {
796 ent
->attrlist
= attr
;
798 while (attr
= getattrval(NULL
)) {
804 ent
->attrlist
= NULL
;
806 } /* pathname extracted */
807 } /* bdevice extracted */
808 } /* cdevice extracted */
809 } /* alias extracted */
811 } /* !done && record read */
813 /* If no entry was read, free space allocated to the structure */
823 * void _freedevtabent(devtabent)
824 * struct devtabent *devtabent;
826 * This function frees space allocated to a device table entry.
829 * struct devtabent *devtabent The structure whose space is to be
836 _freedevtabent(struct devtabent
*ent
)
842 struct attrval
*p
; /* Structure being freed */
843 struct attrval
*q
; /* Next structure to free */
848 * Free the attribute list. For each item in the attribute
850 * 1. Free the attribute name (always defined),
851 * 2. Free the value (if any -- it's not defined if we're
852 * changing an existing attribute),
853 * 3. Free the space allocated to the structure.
862 if (p
->val
) free(p
->val
);
866 /* Free the standard fields (alias, cdevice, bdevice, pathname) */
867 if (ent
->alias
) free(ent
->alias
);
868 if (ent
->cdevice
) free(ent
->cdevice
);
869 if (ent
->bdevice
) free(ent
->bdevice
);
870 if (ent
->pathname
) free(ent
->pathname
);
873 /* Free the attribute string */
874 if (ent
->attrstr
) free(ent
->attrstr
);
876 /* Free the space allocated to the structure */
881 * struct devtabent *_getdevrec(device)
884 * Thie _getdevrec() function returns a pointer to a structure that
885 * contains the information in the device-table entry that describes
886 * the device <device>.
888 * The device <device> can be a device alias, a pathname contained in
889 * the entry as the "cdevice", "bdevice", or "pathname" attribute,
890 * or a pathname to a device described using the "cdevice", "bdevice",
891 * or "pathname" attribute (depending on whether the pathname references
892 * a character-special file, block-special file, or something else,
896 * char *device A character-string describing the device whose record
897 * is to be retrieved from the device table.
899 * Returns: struct devtabent *
900 * A pointer to a structure describing the device.
903 * - Someday, add a cache so that repeated requests for the same record
904 * don't require going to the filesystem. (Maybe -- this might belong
909 _getdevrec(char *device
) /* The device to search for */
915 struct stat64 devstatbuf
; /* Stat struct, <device> */
916 struct stat64 tblstatbuf
; /* Stat struct, tbl entry */
917 struct devtabent
*devrec
; /* Pointer to current record */
918 int found
; /* TRUE if record found */
919 int olderrno
; /* Old value of errno */
923 * Search the device table looking for the requested device
929 if ((device
!= NULL
) && !_validalias(device
)) {
930 while (!found
&& (devrec
= _getdevtabent())) {
931 if (!devrec
->comment
) {
933 if (strcmp(device
, devrec
->cdevice
) == 0) found
= TRUE
;
935 if (strcmp(device
, devrec
->bdevice
) == 0) found
= TRUE
;
936 if (devrec
->pathname
)
937 if (strcmp(device
, devrec
->pathname
) == 0) found
= TRUE
;
938 } else _freedevtabent(devrec
);
942 * If the device <device> wasn't named explicitly in the device
943 * table, compare it against like entries by comparing file-
944 * system, major device number, and minor device number
950 /* Status the file <device>. If fails, invalid device */
951 if (stat64(device
, &devstatbuf
) != 0) errno
= ENODEV
;
955 * If <device> is a block-special device. See if it is
956 * in the table by matching its file-system indicator
957 * and major/minor device numbers against the
958 * file-system and major/minor device numbers of the
962 if ((devstatbuf
.st_mode
& 0170000) == 0020000) {
963 while (!found
&& (devrec
= _getdevtabent())) {
964 if (!devrec
->comment
&&
965 (devrec
->cdevice
!= NULL
))
966 if (stat64(devrec
->cdevice
, &tblstatbuf
) == 0) {
967 if (samedev(tblstatbuf
, devstatbuf
))
970 /* Ignore stat() errs */
973 if (!found
) _freedevtabent(devrec
);
978 * If <device> is a block-special device. See if it is
979 * in the table by matching its file-system indicator
980 * and major/minor device numbers against the
981 * file-system and major/minor device numbers of the
985 else if ((devstatbuf
.st_mode
& 0170000) == 0060000) {
986 while (!found
&& (devrec
= _getdevtabent())) {
987 if (!devrec
->comment
&&
988 (devrec
->bdevice
!= NULL
))
989 if (stat64(devrec
->bdevice
, &tblstatbuf
) == 0) {
990 if (samedev(tblstatbuf
, devstatbuf
))
993 /* Ignore stat() errs */
996 if (!found
) _freedevtabent(devrec
);
1001 * If <device> is neither a block-special or character-
1002 * special device. See if it is in the table by
1003 * matching its file-system indicator and major/minor
1004 * device numbers against the file-system and
1005 * major/minor device numbers of the "pathname" entries.
1009 while (!found
&& (devrec
= _getdevtabent())) {
1010 if (!devrec
->comment
&&
1011 (devrec
->pathname
!= NULL
))
1012 if (stat64(devrec
->pathname
,
1013 &tblstatbuf
) == 0) {
1014 if (samedev(tblstatbuf
, devstatbuf
))
1017 /* Ignore stat() errs */
1020 if (!found
) _freedevtabent(devrec
);
1029 } /* End case where stat() on the <device> succeeded */
1031 } /* End case handling pathname not explicitly in device table */
1033 } /* End case handling <device> as a fully-qualified pathname */
1037 * Otherwise the device <device> is an alias.
1038 * Search the table for a record that has as the "alias" attribute
1039 * the value <device>.
1043 while (!found
&& (devrec
= _getdevtabent())) {
1044 if (!devrec
->comment
&& (device
!= NULL
) &&
1045 strcmp(device
, devrec
->alias
) == 0)
1047 else _freedevtabent(devrec
);