4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
28 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/vfstab.h>
40 #include "libdevinfo.h"
41 #include "device_info.h"
44 #define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f')
45 #define isnamechar(ch) (isalpha(ch) || isdigit(ch) || (ch) == '_' ||\
47 #define MAX_TOKEN_SIZE 1024
49 #define STRVAL(s) ((s) ? (s) : "NULL")
51 #define SCSI_VHCI_CONF "/kernel/drv/scsi_vhci.conf"
52 #define QLC_CONF "/kernel/drv/qlc.conf"
53 #define FP_CONF "/kernel/drv/fp.conf"
54 #define DRIVER_CLASSES "/etc/driver_classes"
56 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl"
57 #define SLASH_DEVICES "/devices"
58 #define SLASH_DEVICES_SLASH "/devices/"
59 #define SLASH_FP_AT "/fp@"
60 #define SLASH_SCSI_VHCI "/scsi_vhci"
61 #define META_DEV "/dev/md/dsk/"
62 #define SLASH_DEV_SLASH "/dev/"
65 * Macros to produce a quoted string containing the value of a
66 * preprocessor macro. For example, if SIZE is defined to be 256,
67 * VAL2STR(SIZE) is "256". This is used to construct format
68 * strings for scanf-family functions below.
71 #define VAL2STR(x) QUOTE(x)
99 begin
, parent
, drvname
, drvclass
, prop
,
100 parent_equals
, name_equals
, drvclass_equals
,
101 parent_equals_string
, name_equals_string
,
102 drvclass_equals_string
,
103 prop_equals
, prop_equals_string
, prop_equals_integer
,
104 prop_equals_string_comma
, prop_equals_integer_comma
107 /* structure to hold entries with mpxio-disable property in driver.conf file */
115 struct conf_entry
*next
;
124 static char *tok_err
= "Unexpected token '%s'\n";
131 int devfsmap_debug
= 0;
132 /* /var/run is not mounted at install time. Therefore use /tmp */
133 char *devfsmap_logfile
= "/tmp/devfsmap.log";
135 #define logdmsg(args) log_debug_msg args
136 static void vlog_debug_msg(char *, va_list);
137 static void log_debug_msg(char *, ...);
139 static void log_confent_list(char *, struct conf_entry
*, int);
140 static void log_pathlist(char **);
144 #define logdmsg(args) /* nothing */
149 * Leave NEWLINE as the next character.
156 while ((ch
= getc(fp
)) != EOF
) {
158 (void) ungetc(ch
, fp
);
164 /* ignore parsing errors */
167 file_err(struct conf_file
*filep
, char *fmt
, ...)
173 log_debug_msg("WARNING: %s line # %d: ",
174 filep
->filename
, filep
->linenum
);
175 vlog_debug_msg(fmt
, ap
);
180 /* return the next token from the given driver.conf file, or -1 on error */
182 lex(struct conf_file
*filep
, char *val
, size_t size
)
185 int ch
, oval
, badquote
;
188 FILE *fp
= filep
->fp
;
194 while ((ch
= getc(fp
)) == ' ' || ch
== '\t')
230 while ((ch
= getc(fp
)) == ' ' ||
231 ch
== '\t' || ch
== '\f') {
238 (void) ungetc(ch
, fp
);
239 token
= T_WHITE_SPACE
;
249 while (!badquote
&& (ch
= getc(fp
)) != '"') {
253 file_err(filep
, "Missing \"\n");
258 /* since we consumed the newline/EOF */
259 (void) ungetc(ch
, fp
);
269 /* escape the character */
274 while (ch
>= '0' && ch
<= '7') {
276 oval
= (oval
<< 3) + ch
;
279 (void) ungetc(ch
, fp
);
280 /* check for character overflow? */
284 "overflow detected.\n");
306 * detect a lone '-' (including at the end of a line), and
307 * identify it as a 'name'
314 *cp
++ = (char)(ch
= getc(fp
));
315 if (ch
== ' ' || ch
== '\t' || ch
== '\n') {
316 (void) ungetc(ch
, fp
);
322 } else if (ch
== '~' || ch
== '-') {
327 *cp
++ = (char)(ch
= getc(fp
));
333 if ((ch
= getc(fp
)) == 'x') {
340 while (isxdigit(ch
)) {
348 (void) ungetc(ch
, fp
);
356 while (isdigit(ch
)) {
364 (void) ungetc(ch
, fp
);
367 } else if (isalpha(ch
) || ch
== '\\') {
372 * if the character was a backslash,
373 * back up so we can overwrite it with
374 * the next (i.e. escaped) character.
379 while (isnamechar(ch
) || ch
== '\\') {
389 (void) ungetc(ch
, fp
);
404 devlink_callback(di_devlink_t devlink
, void *argp
)
408 if ((link
= di_devlink_path(devlink
)) != NULL
)
409 (void) strlcpy((char *)argp
, link
, MAXPATHLEN
);
411 return (DI_WALK_CONTINUE
);
415 * Get the /dev name in the install environment corresponding to physpath.
417 * physpath /devices path in the install environment without the /devices
419 * buf caller supplied buffer where the /dev name is placed on return
420 * bufsz length of the buffer
422 * Returns strlen of the /dev name on success, -1 on failure.
425 get_install_devlink(char *physpath
, char *buf
, size_t bufsz
)
427 di_devlink_handle_t devlink_hdl
;
428 char devname
[MAXPATHLEN
];
430 int sleeptime
= 2; /* number of seconds to sleep between retries */
431 int maxtries
= 10; /* maximum number of tries */
433 logdmsg(("get_install_devlink: physpath = %s\n", physpath
));
436 * devlink_db sync happens after MINOR_FINI_TIMEOUT_DEFAULT secs
437 * after dev link creation. So wait for minimum that amout of time.
441 (void) sleep(sleeptime
);
443 if ((devlink_hdl
= di_devlink_init(NULL
, 0)) == NULL
) {
444 logdmsg(("get_install_devlink: di_devlink_init() failed: %s\n",
450 if (di_devlink_walk(devlink_hdl
, NULL
, physpath
, DI_PRIMARY_LINK
,
451 devname
, devlink_callback
) == 0) {
452 if (devname
[0] == '\0' && tries
< maxtries
) {
454 (void) di_devlink_fini(&devlink_hdl
);
456 } else if (devname
[0] == '\0') {
457 logdmsg(("get_install_devlink: di_devlink_walk"
458 " failed: %s\n", strerror(errno
)));
459 (void) di_devlink_fini(&devlink_hdl
);
463 logdmsg(("get_install_devlink: di_devlink_walk failed: %s\n",
465 (void) di_devlink_fini(&devlink_hdl
);
469 (void) di_devlink_fini(&devlink_hdl
);
471 logdmsg(("get_install_devlink: devlink = %s\n", devname
));
472 return (strlcpy(buf
, devname
, bufsz
));
476 * Get the /dev name in the target environment corresponding to physpath.
478 * rootdir root directory of the target environment
479 * physpath /devices path in the target environment without the /devices
481 * buf caller supplied buffer where the /dev name is placed on return
482 * bufsz length of the buffer
484 * Returns strlen of the /dev name on success, -1 on failure.
487 get_target_devlink(char *rootdir
, char *physpath
, char *buf
, size_t bufsz
)
492 struct dirent
*direntry
;
493 char dirpath
[MAXPATHLEN
];
494 char devname
[MAXPATHLEN
];
495 char physdev
[MAXPATHLEN
];
497 logdmsg(("get_target_devlink: rootdir = %s, physpath = %s\n",
500 if ((p
= strrchr(physpath
, '/')) == NULL
)
503 if (strstr(p
, ",raw") != NULL
) {
504 (void) snprintf(dirpath
, MAXPATHLEN
, "%s/dev/rdsk", rootdir
);
506 (void) snprintf(dirpath
, MAXPATHLEN
, "%s/dev/dsk", rootdir
);
509 if ((dirp
= opendir(dirpath
)) == NULL
)
512 while ((direntry
= readdir(dirp
)) != NULL
) {
513 if (strcmp(direntry
->d_name
, ".") == 0 ||
514 strcmp(direntry
->d_name
, "..") == 0)
517 (void) snprintf(devname
, MAXPATHLEN
, "%s/%s",
518 dirpath
, direntry
->d_name
);
520 if ((linksize
= readlink(devname
, physdev
, MAXPATHLEN
)) > 0 &&
521 linksize
< (MAXPATHLEN
- 1)) {
522 physdev
[linksize
] = '\0';
523 if ((p
= strstr(physdev
, SLASH_DEVICES_SLASH
)) !=
524 NULL
&& strcmp(p
+ sizeof (SLASH_DEVICES
) - 1,
526 (void) closedir(dirp
);
527 logdmsg(("get_target_devlink: devlink = %s\n",
528 devname
+ strlen(rootdir
)));
529 return (strlcpy(buf
, devname
+ strlen(rootdir
),
535 (void) closedir(dirp
);
540 * Convert device name to physpath.
542 * rootdir root directory
543 * devname a /dev name or /devices name under rootdir
544 * physpath caller supplied buffer where the /devices path will be placed
545 * on return (without the /devices prefix).
546 * physpathlen length of the physpath buffer
548 * Returns 0 on success, -1 on failure.
551 devname2physpath(char *rootdir
, char *devname
, char *physpath
, int physpathlen
)
555 char devlink
[MAXPATHLEN
];
556 char tmpphyspath
[MAXPATHLEN
];
558 logdmsg(("devname2physpath: rootdir = %s, devname = %s\n",
561 if (strncmp(devname
, SLASH_DEVICES_SLASH
,
562 sizeof (SLASH_DEVICES_SLASH
) - 1) != 0) {
563 if (*rootdir
== '\0')
564 linksize
= readlink(devname
, tmpphyspath
, MAXPATHLEN
);
566 (void) snprintf(devlink
, MAXPATHLEN
, "%s%s",
568 linksize
= readlink(devlink
, tmpphyspath
, MAXPATHLEN
);
570 if (linksize
> 0 && linksize
< (MAXPATHLEN
- 1)) {
571 tmpphyspath
[linksize
] = '\0';
572 if ((p
= strstr(tmpphyspath
, SLASH_DEVICES_SLASH
))
580 (void) strlcpy(physpath
, p
+ sizeof (SLASH_DEVICES
) - 1, physpathlen
);
581 logdmsg(("devname2physpath: physpath = %s\n", physpath
));
586 * Map a device name (devname) from the target environment to the
587 * install environment.
589 * rootdir root directory of the target environment
590 * devname /dev or /devices name under the target environment
591 * buf caller supplied buffer where the mapped /dev name is placed
593 * bufsz length of the buffer
595 * Returns strlen of the mapped /dev name on success, -1 on failure.
598 devfs_target2install(const char *rootdir
, const char *devname
, char *buf
,
601 char physpath
[MAXPATHLEN
];
603 logdmsg(("devfs_target2install: rootdir = %s, devname = %s\n",
604 STRVAL(rootdir
), STRVAL(devname
)));
606 if (rootdir
== NULL
|| devname
== NULL
|| buf
== NULL
|| bufsz
== 0)
609 if (strcmp(rootdir
, "/") == 0)
612 if (devname2physpath((char *)rootdir
, (char *)devname
, physpath
,
617 return (get_install_devlink(physpath
, buf
, bufsz
));
621 * Map a device name (devname) from the install environment to the target
624 * rootdir root directory of the target environment
625 * devname /dev or /devices name under the install environment
626 * buf caller supplied buffer where the mapped /dev name is placed
628 * bufsz length of the buffer
630 * Returns strlen of the mapped /dev name on success, -1 on failure.
633 devfs_install2target(const char *rootdir
, const char *devname
, char *buf
,
636 char physpath
[MAXPATHLEN
];
638 logdmsg(("devfs_install2target: rootdir = %s, devname = %s\n",
639 STRVAL(rootdir
), STRVAL(devname
)));
641 if (rootdir
== NULL
|| devname
== NULL
|| buf
== NULL
|| bufsz
== 0)
644 if (strcmp(rootdir
, "/") == 0)
647 if (devname2physpath("", (char *)devname
, physpath
, MAXPATHLEN
) != 0)
651 return (get_target_devlink((char *)rootdir
, physpath
, buf
, bufsz
));
655 * A parser for /etc/path_to_inst.
656 * The user-supplied callback is called once for each entry in the file.
657 * Returns 0 on success, ENOMEM/ENOENT/EINVAL on error.
658 * Callback may return DI_WALK_TERMINATE to terminate the walk,
659 * otherwise DI_WALK_CONTINUE.
662 devfs_parse_binding_file(const char *binding_file
,
663 int (*callback
)(void *, const char *, int,
664 const char *), void *cb_arg
)
667 struct conf_file file
;
668 char tokval
[MAX_TOKEN_SIZE
];
669 enum { STATE_RESET
, STATE_DEVPATH
, STATE_INSTVAL
} state
;
675 if ((devpath
= calloc(1, MAXPATHLEN
)) == NULL
)
677 if ((bindname
= calloc(1, MAX_TOKEN_SIZE
)) == NULL
) {
682 if ((file
.fp
= fopen(binding_file
, "r")) == NULL
) {
688 file
.filename
= (char *)binding_file
;
692 while ((token
= lex(&file
, tokval
, MAX_TOKEN_SIZE
)) != T_EOF
) {
704 if (strlcpy(devpath
, tokval
,
705 MAXPATHLEN
) >= MAXPATHLEN
)
707 state
= STATE_DEVPATH
;
710 if (strlcpy(bindname
, tokval
,
711 MAX_TOKEN_SIZE
) >= MAX_TOKEN_SIZE
)
713 rv
= callback(cb_arg
,
714 devpath
, instval
, bindname
);
715 if (rv
== DI_WALK_TERMINATE
)
717 if (rv
!= DI_WALK_CONTINUE
)
722 file_err(&file
, tok_err
, tokval
);
731 instval
= (int)strtol(tokval
, NULL
, 0);
732 state
= STATE_INSTVAL
;
735 file_err(&file
, tok_err
, tokval
);
745 file_err(&file
, tok_err
, tokval
);
752 (void) fclose(file
.fp
);
758 (void) fclose(file
.fp
);
765 * Walk the minor nodes of all children below the specified device
766 * by calling the provided callback with the path to each minor.
769 devfs_walk_children_minors(const char *device_path
, struct stat
*st
,
770 int (*callback
)(void *, const char *), void *cb_arg
, int *terminate
)
774 char *minor_path
= NULL
;
778 if ((minor_path
= calloc(1, MAXPATHLEN
)) == NULL
)
781 if ((dir
= opendir(device_path
)) == NULL
) {
787 while ((dp
= readdir(dir
)) != NULL
) {
788 if ((strcmp(dp
->d_name
, ".") == 0) ||
789 (strcmp(dp
->d_name
, "..") == 0))
791 (void) snprintf(minor_path
, MAXPATHLEN
,
792 "%s/%s", device_path
, dp
->d_name
);
793 if (stat(minor_path
, st
) == -1)
795 if (S_ISDIR(st
->st_mode
)) {
796 rv
= devfs_walk_children_minors(
797 (const char *)minor_path
, st
,
798 callback
, cb_arg
, terminate
);
804 rv
= callback(cb_arg
, minor_path
);
805 if (rv
== DI_WALK_TERMINATE
) {
809 if (rv
!= DI_WALK_CONTINUE
) {
819 (void) closedir(dir
);
825 * Return the path to each minor node for a device by
826 * calling the provided callback.
829 devfs_walk_device_minors(const char *device_path
, struct stat
*st
,
830 int (*callback
)(void *, const char *), void *cb_arg
, int *terminate
)
836 int need_regfree
= 0;
843 minor_path
= calloc(1, MAXPATHLEN
);
844 devpath
= calloc(1, MAXPATHLEN
);
845 expr
= calloc(1, MAXNAMELEN
);
846 if (devpath
== NULL
|| expr
== NULL
|| minor_path
== NULL
) {
852 if (strlcpy(devpath
, device_path
, MAXPATHLEN
) >= MAXPATHLEN
)
854 if ((p
= strrchr(devpath
, '/')) == NULL
)
859 if (snprintf(expr
, MAXNAMELEN
, "%s:.*", p
) >= MAXNAMELEN
)
861 if (regcomp(®ex
, expr
, REG_EXTENDED
) != 0)
865 if ((dir
= opendir(devpath
)) == NULL
) {
871 while ((dp
= readdir(dir
)) != NULL
) {
872 if ((strcmp(dp
->d_name
, ".") == 0) ||
873 (strcmp(dp
->d_name
, "..") == 0))
875 (void) snprintf(minor_path
, MAXPATHLEN
,
876 "%s/%s", devpath
, dp
->d_name
);
877 if (stat(minor_path
, st
) == -1)
879 if ((S_ISBLK(st
->st_mode
) || S_ISCHR(st
->st_mode
)) &&
880 regexec(®ex
, dp
->d_name
, 0, NULL
, 0) == 0) {
881 rv
= callback(cb_arg
, minor_path
);
882 if (rv
== DI_WALK_TERMINATE
) {
886 if (rv
!= DI_WALK_CONTINUE
) {
896 (void) closedir(dir
);
906 * Perform a walk of all minor nodes for the specified device,
907 * and minor nodes below the device.
910 devfs_walk_minor_nodes(const char *device_path
,
911 int (*callback
)(void *, const char *), void *cb_arg
)
917 rv
= devfs_walk_device_minors(device_path
,
918 &stbuf
, callback
, cb_arg
, &terminate
);
919 if (rv
== 0 && terminate
== 0) {
920 rv
= devfs_walk_children_minors(device_path
,
921 &stbuf
, callback
, cb_arg
, &terminate
);
929 vlog_debug_msg(char *fmt
, va_list ap
)
938 if (*devfsmap_logfile
!= '\0') {
939 logfp
= fopen(devfsmap_logfile
, "a");
941 (void) fprintf(logfp
, "\nNew Log:\n");
949 (void) localtime_r(&clock
, &t
);
950 (void) fprintf(logfp
, "%02d:%02d:%02d ", t
.tm_hour
, t
.tm_min
,
952 (void) vfprintf(logfp
, fmt
, ap
);
953 (void) fflush(logfp
);
957 log_debug_msg(char *fmt
, ...)
962 vlog_debug_msg(fmt
, ap
);
969 mpxio_disable_string(int mpxio_disable
)
971 if (mpxio_disable
== 0)
973 else if (mpxio_disable
== 1)
976 return ("not specified");
980 log_confent_list(char *filename
, struct conf_entry
*confent_list
,
981 int global_mpxio_disable
)
983 struct conf_entry
*confent
;
985 log_debug_msg("log_confent_list: filename = %s:\n", filename
);
986 if (global_mpxio_disable
!= -1)
987 log_debug_msg("\tdriver global mpxio_disable = \"%s\"\n\n",
988 mpxio_disable_string(global_mpxio_disable
));
990 for (confent
= confent_list
; confent
!= NULL
; confent
= confent
->next
) {
992 log_debug_msg("\tname = %s\n", confent
->name
);
994 log_debug_msg("\tparent = %s\n", confent
->parent
);
996 log_debug_msg("\tclass = %s\n", confent
->class);
997 if (confent
->unit_address
)
998 log_debug_msg("\tunit_address = %s\n",
999 confent
->unit_address
);
1000 if (confent
->port
!= -1)
1001 log_debug_msg("\tport = %d\n", confent
->port
);
1002 log_debug_msg("\tmpxio_disable = \"%s\"\n\n",
1003 mpxio_disable_string(confent
->mpxio_disable
));
1008 log_pathlist(char **pathlist
)
1012 for (p
= pathlist
; *p
!= NULL
; p
++)
1013 log_debug_msg("\t%s\n", *p
);
1016 #endif /* __sparc */