6602 lofi should support labeled devices
[unleashed.git] / usr / src / cmd / devfsadm / devfsadm.c
blob7d7904e9c090462c0e42cf11e975949e9e0bd8f1
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2016 Toomas Soome <tsoome@me.com>
24 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Devfsadm replaces drvconfig, audlinks, disks, tapes, ports, devlinks
29 * as a general purpose device administrative utility. It creates
30 * devices special files in /devices and logical links in /dev, and
31 * coordinates updates to /etc/path_to_instance with the kernel. It
32 * operates in both command line mode to handle user or script invoked
33 * reconfiguration updates, and operates in daemon mode to handle dynamic
34 * reconfiguration for hotplugging support.
37 #include <string.h>
38 #include <deflt.h>
39 #include <tsol/label.h>
40 #include <bsm/devices.h>
41 #include <bsm/devalloc.h>
42 #include <utime.h>
43 #include <sys/param.h>
44 #include <bsm/libbsm.h>
45 #include <zone.h>
46 #include "devfsadm_impl.h"
48 /* externs from devalloc.c */
49 extern void _reset_devalloc(int);
50 extern void _update_devalloc_db(devlist_t *, int, int, char *, char *);
51 extern int _da_check_for_usb(char *, char *);
53 /* create or remove nodes or links. unset with -n */
54 static int file_mods = TRUE;
56 /* cleanup mode. Set with -C */
57 static int cleanup = FALSE;
59 /* devlinks -d compatibility */
60 static int devlinks_debug = FALSE;
62 /* flag to check if system is labeled */
63 int system_labeled = FALSE;
65 /* flag to enable/disable device allocation with -e/-d */
66 static int devalloc_flag = 0;
68 /* flag that indicates if device allocation is on or not */
69 static int devalloc_is_on = 0;
71 /* flag to update device allocation database for this device type */
72 static int update_devdb = 0;
75 * devices to be deallocated with -d :
76 * audio, floppy, cd, floppy, tape, rmdisk.
78 static char *devalloc_list[10] = {DDI_NT_AUDIO, DDI_NT_CD, DDI_NT_CD_CHAN,
79 DDI_NT_FD, DDI_NT_TAPE, DDI_NT_BLOCK_CHAN,
80 DDI_NT_UGEN, DDI_NT_USB_ATTACHMENT_POINT,
81 DDI_NT_SCSI_NEXUS, NULL};
83 /* list of allocatable devices */
84 static devlist_t devlist;
86 /* load a single driver only. set with -i */
87 static int single_drv = FALSE;
88 static char *driver = NULL;
90 /* attempt to load drivers or defer attach nodes */
91 static int load_attach_drv = TRUE;
93 /* reload all driver.conf files */
94 static int update_all_drivers = FALSE;
96 /* set if invoked via /usr/lib/devfsadm/devfsadmd */
97 static int daemon_mode = FALSE;
99 /* set if event_handler triggered */
100 int event_driven = FALSE;
102 /* output directed to syslog during daemon mode if set */
103 static int logflag = FALSE;
105 /* build links in /dev. -x to turn off */
106 static int build_dev = TRUE;
108 /* build nodes in /devices. -y to turn off */
109 static int build_devices = TRUE;
111 /* -z to turn off */
112 static int flush_path_to_inst_enable = TRUE;
114 /* variables used for path_to_inst flushing */
115 static int inst_count = 0;
116 static mutex_t count_lock;
117 static cond_t cv;
119 /* variables for minor_fini thread */
120 static mutex_t minor_fini_mutex;
121 static int minor_fini_canceled = TRUE;
122 static int minor_fini_delayed = FALSE;
123 static cond_t minor_fini_cv;
124 static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
126 /* single-threads /dev modification */
127 static sema_t dev_sema;
129 /* the program we were invoked as; ie argv[0] */
130 static char *prog;
132 /* pointers to create/remove link lists */
133 static create_list_t *create_head = NULL;
134 static remove_list_t *remove_head = NULL;
136 /* supports the class -c option */
137 static char **classes = NULL;
138 static int num_classes = 0;
140 /* used with verbose option -v or -V */
141 static int num_verbose = 0;
142 static char **verbose = NULL;
144 static struct mperm *minor_perms = NULL;
145 static driver_alias_t *driver_aliases = NULL;
147 /* set if -r alternate root given */
148 static char *root_dir = "";
150 /* /devices or <rootdir>/devices */
151 static char *devices_dir = DEVICES;
153 /* /dev or <rootdir>/dev */
154 static char *dev_dir = DEV;
156 /* /etc/dev or <rootdir>/etc/dev */
157 static char *etc_dev_dir = ETCDEV;
160 * writable root (for lock files and doors during install).
161 * This is also root dir for /dev attr dir during install.
163 static char *attr_root = NULL;
165 /* /etc/path_to_inst unless -p used */
166 static char *inst_file = INSTANCE_FILE;
168 /* /usr/lib/devfsadm/linkmods unless -l used */
169 static char *module_dirs = MODULE_DIRS;
171 /* default uid/gid used if /etc/minor_perm entry not found */
172 static uid_t root_uid;
173 static gid_t sys_gid;
175 /* /etc/devlink.tab unless devlinks -t used */
176 static char *devlinktab_file = NULL;
178 /* File and data structure to reserve enumerate IDs */
179 static char *enumerate_file = ENUMERATE_RESERVED;
180 static enumerate_file_t *enumerate_reserved = NULL;
182 /* set if /dev link is new. speeds up rm_stale_links */
183 static int linknew = TRUE;
185 /* variables for devlink.tab compat processing */
186 static devlinktab_list_t *devlinktab_list = NULL;
187 static unsigned int devlinktab_line = 0;
189 /* cache head for devfsadm_enumerate*() functions */
190 static numeral_set_t *head_numeral_set = NULL;
192 /* list list of devfsadm modules */
193 static module_t *module_head = NULL;
195 /* name_to_major list used in utility function */
196 static n2m_t *n2m_list = NULL;
198 /* cache of some links used for performance */
199 static linkhead_t *headlinkhead = NULL;
201 /* locking variables to prevent multiples writes to /dev */
202 static int hold_dev_lock = FALSE;
203 static int hold_daemon_lock = FALSE;
204 static int dev_lock_fd;
205 static int daemon_lock_fd;
206 static char dev_lockfile[PATH_MAX + 1];
207 static char daemon_lockfile[PATH_MAX + 1];
209 /* last devinfo node/minor processed. used for performance */
210 static di_node_t lnode;
211 static di_minor_t lminor;
212 static char lphy_path[PATH_MAX + 1] = {""};
214 /* Globals used by the link database */
215 static di_devlink_handle_t devlink_cache;
216 static int update_database = FALSE;
218 /* Globals used to set logindev perms */
219 static struct login_dev *login_dev_cache = NULL;
220 static int login_dev_enable = FALSE;
222 /* Global to use devinfo snapshot cache */
223 static int use_snapshot_cache = FALSE;
225 /* Global for no-further-processing hash */
226 static item_t **nfp_hash;
227 static mutex_t nfp_mutex = DEFAULTMUTEX;
230 * Directories not removed even when empty. They are packaged, or may
231 * be referred to from a non-global zone. The dirs must be listed in
232 * canonical form i.e. without leading "/dev/"
234 static char *sticky_dirs[] =
235 {"dsk", "rdsk", "term", "lofi", "rlofi", NULL};
237 /* Devname globals */
238 static int lookup_door_fd = -1;
239 static char *lookup_door_path;
241 static void load_dev_acl(void);
242 static void update_drvconf(major_t, int);
243 static void check_reconfig_state(void);
244 static int s_stat(const char *, struct stat *);
246 static int is_blank(char *);
248 /* sysevent queue related globals */
249 static mutex_t syseventq_mutex = DEFAULTMUTEX;
250 static syseventq_t *syseventq_front;
251 static syseventq_t *syseventq_back;
252 static void process_syseventq();
254 static di_node_t devi_root_node = DI_NODE_NIL;
257 main(int argc, char *argv[])
259 struct passwd *pw;
260 struct group *gp;
261 pid_t pid;
263 (void) setlocale(LC_ALL, "");
264 (void) textdomain(TEXT_DOMAIN);
266 if ((prog = strrchr(argv[0], '/')) == NULL) {
267 prog = argv[0];
268 } else {
269 prog++;
272 if (getuid() != 0) {
273 err_print(MUST_BE_ROOT);
274 devfsadm_exit(1);
275 /*NOTREACHED*/
278 if (getzoneid() != GLOBAL_ZONEID) {
279 err_print(MUST_BE_GLOBAL_ZONE);
280 devfsadm_exit(1);
284 * Close all files except stdin/stdout/stderr
286 closefrom(3);
288 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
289 root_uid = pw->pw_uid;
290 } else {
291 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
292 root_uid = (uid_t)0; /* assume 0 is root */
295 /* the default group is sys */
297 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
298 sys_gid = gp->gr_gid;
299 } else {
300 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
301 sys_gid = (gid_t)3; /* assume 3 is sys */
304 (void) umask(0);
306 system_labeled = is_system_labeled();
307 if (system_labeled == FALSE) {
309 * is_system_labeled() will return false in case we are
310 * starting before the first reboot after Trusted Extensions
311 * is enabled. Check the setting in /etc/system to see if
312 * TX is enabled (even if not yet booted).
314 if (defopen("/etc/system") == 0) {
315 if (defread("set sys_labeling=1") != NULL)
316 system_labeled = TRUE;
318 /* close defaults file */
319 (void) defopen(NULL);
323 * Check if device allocation is enabled.
325 devalloc_is_on = (da_is_on() == 1) ? 1 : 0;
327 #ifdef DEBUG
328 if (system_labeled == FALSE) {
329 struct stat tx_stat;
331 /* test hook: see also mkdevalloc.c and allocate.c */
332 system_labeled = is_system_labeled_debug(&tx_stat);
334 #endif
336 parse_args(argc, argv);
338 (void) sema_init(&dev_sema, 1, USYNC_THREAD, NULL);
340 /* Initialize device allocation list */
341 devlist.audio = devlist.cd = devlist.floppy = devlist.tape =
342 devlist.rmdisk = NULL;
344 if (daemon_mode == TRUE) {
346 * Build /dev and /devices before daemonizing if
347 * reconfig booting and daemon invoked with alternate
348 * root. This is to support install.
350 if (getenv(RECONFIG_BOOT) != NULL && root_dir[0] != '\0') {
351 vprint(INFO_MID, CONFIGURING);
352 load_dev_acl();
353 update_drvconf((major_t)-1, 0);
354 process_devinfo_tree();
355 (void) modctl(MODSETMINIROOT);
359 * fork before detaching from tty in order to print error
360 * message if unable to acquire file lock. locks not preserved
361 * across forks. Even under debug we want to fork so that
362 * when executed at boot we don't hang.
364 if (fork() != 0) {
365 devfsadm_exit(0);
366 /*NOTREACHED*/
369 /* set directory to / so it coredumps there */
370 if (chdir("/") == -1) {
371 err_print(CHROOT_FAILED, strerror(errno));
374 /* only one daemon can run at a time */
375 if ((pid = enter_daemon_lock()) == getpid()) {
376 detachfromtty();
377 (void) cond_init(&cv, USYNC_THREAD, 0);
378 (void) mutex_init(&count_lock, USYNC_THREAD, 0);
379 if (thr_create(NULL, NULL,
380 (void *(*)(void *))instance_flush_thread,
381 NULL, THR_DETACHED, NULL) != 0) {
382 err_print(CANT_CREATE_THREAD, "daemon",
383 strerror(errno));
384 devfsadm_exit(1);
385 /*NOTREACHED*/
388 /* start the minor_fini_thread */
389 (void) mutex_init(&minor_fini_mutex, USYNC_THREAD, 0);
390 (void) cond_init(&minor_fini_cv, USYNC_THREAD, 0);
391 if (thr_create(NULL, NULL,
392 (void *(*)(void *))minor_fini_thread,
393 NULL, THR_DETACHED, NULL)) {
394 err_print(CANT_CREATE_THREAD, "minor_fini",
395 strerror(errno));
396 devfsadm_exit(1);
397 /*NOTREACHED*/
402 * logindevperms need only be set
403 * in daemon mode and when root dir is "/".
405 if (root_dir[0] == '\0')
406 login_dev_enable = TRUE;
407 daemon_update();
408 devfsadm_exit(0);
409 /*NOTREACHED*/
410 } else {
411 err_print(DAEMON_RUNNING, pid);
412 devfsadm_exit(1);
413 /*NOTREACHED*/
415 } else {
416 /* not a daemon, so just build /dev and /devices */
419 * If turning off device allocation, load the
420 * minor_perm file because process_devinfo_tree() will
421 * need this in order to reset the permissions of the
422 * device files.
424 if (devalloc_flag == DA_OFF) {
425 read_minor_perm_file();
428 process_devinfo_tree();
429 if (devalloc_flag != 0)
430 /* Enable/disable device allocation */
431 _reset_devalloc(devalloc_flag);
433 return (0);
436 static void
437 update_drvconf(major_t major, int flags)
439 if (modctl(MODLOADDRVCONF, major, flags) != 0)
440 err_print(gettext("update_drvconf failed for major %d\n"),
441 major);
444 static void
445 load_dev_acl()
447 if (load_devpolicy() != 0)
448 err_print(gettext("device policy load failed\n"));
449 load_minor_perm_file();
453 * As devfsadm is run early in boot to provide the kernel with
454 * minor_perm info, we might as well check for reconfig at the
455 * same time to avoid running devfsadm twice. This gets invoked
456 * earlier than the env variable RECONFIG_BOOT is set up.
458 static void
459 check_reconfig_state()
461 struct stat sb;
463 if (s_stat("/reconfigure", &sb) == 0) {
464 (void) modctl(MODDEVNAME, MODDEVNAME_RECONFIG, 0);
468 static void
469 modctl_sysavail()
472 * Inform /dev that system is available, that
473 * implicit reconfig can now be performed.
475 (void) modctl(MODDEVNAME, MODDEVNAME_SYSAVAIL, 0);
478 static void
479 set_lock_root(void)
481 struct stat sb;
482 char *lock_root;
483 size_t len;
485 lock_root = attr_root ? attr_root : root_dir;
487 len = strlen(lock_root) + strlen(ETCDEV) + 1;
488 etc_dev_dir = s_malloc(len);
489 (void) snprintf(etc_dev_dir, len, "%s%s", lock_root, ETCDEV);
491 if (s_stat(etc_dev_dir, &sb) != 0) {
492 s_mkdirp(etc_dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
493 } else if (!S_ISDIR(sb.st_mode)) {
494 err_print(NOT_DIR, etc_dev_dir);
495 devfsadm_exit(1);
496 /*NOTREACHED*/
502 * Parse arguments for all 6 programs handled from devfsadm.
504 static void
505 parse_args(int argc, char *argv[])
507 char opt;
508 char get_linkcompat_opts = FALSE;
509 char *compat_class;
510 int num_aliases = 0;
511 int len;
512 int retval;
513 int config = TRUE;
514 int bind = FALSE;
515 int force_flag = FALSE;
516 struct aliases *ap = NULL;
517 struct aliases *a_head = NULL;
518 struct aliases *a_tail = NULL;
519 struct modconfig mc;
521 (void) bzero(&mc, sizeof (mc));
523 if (strcmp(prog, DISKS) == 0) {
524 compat_class = "disk";
525 get_linkcompat_opts = TRUE;
527 } else if (strcmp(prog, TAPES) == 0) {
528 compat_class = "tape";
529 get_linkcompat_opts = TRUE;
531 } else if (strcmp(prog, PORTS) == 0) {
532 compat_class = "port";
533 get_linkcompat_opts = TRUE;
535 } else if (strcmp(prog, AUDLINKS) == 0) {
536 compat_class = "audio";
537 get_linkcompat_opts = TRUE;
539 } else if (strcmp(prog, DEVLINKS) == 0) {
540 devlinktab_file = DEVLINKTAB_FILE;
542 build_devices = FALSE;
543 load_attach_drv = FALSE;
545 while ((opt = getopt(argc, argv, "dnr:st:vV:")) != EOF) {
546 switch (opt) {
547 case 'd':
548 file_mods = FALSE;
549 flush_path_to_inst_enable = FALSE;
550 devlinks_debug = TRUE;
551 break;
552 case 'n':
553 /* prevent driver loading and deferred attach */
554 load_attach_drv = FALSE;
555 break;
556 case 'r':
557 set_root_devices_dev_dir(optarg);
558 if (zone_pathcheck(root_dir) !=
559 DEVFSADM_SUCCESS)
560 devfsadm_exit(1);
561 /*NOTREACHED*/
562 break;
563 case 's':
565 * suppress. don't create/remove links/nodes
566 * useful with -v or -V
568 file_mods = FALSE;
569 flush_path_to_inst_enable = FALSE;
570 break;
571 case 't':
572 /* supply a non-default table file */
573 devlinktab_file = optarg;
574 break;
575 case 'v':
576 /* documented verbose flag */
577 add_verbose_id(VERBOSE_MID);
578 break;
579 case 'V':
580 /* undocumented for extra verbose levels */
581 add_verbose_id(optarg);
582 break;
583 default:
584 usage();
585 break;
589 if (optind < argc) {
590 usage();
593 } else if (strcmp(prog, DRVCONFIG) == 0) {
594 int update_only = 0;
595 build_dev = FALSE;
597 while ((opt =
598 getopt(argc, argv, "a:bc:dfi:m:np:R:r:suvV:x")) != EOF) {
599 switch (opt) {
600 case 'a':
601 ap = calloc(sizeof (struct aliases), 1);
602 ap->a_name = dequote(optarg);
603 len = strlen(ap->a_name) + 1;
604 if (len > MAXMODCONFNAME) {
605 err_print(ALIAS_TOO_LONG,
606 MAXMODCONFNAME, ap->a_name);
607 devfsadm_exit(1);
608 /*NOTREACHED*/
610 ap->a_len = len;
611 if (a_tail == NULL) {
612 a_head = ap;
613 } else {
614 a_tail->a_next = ap;
616 a_tail = ap;
617 num_aliases++;
618 bind = TRUE;
619 break;
620 case 'b':
621 bind = TRUE;
622 break;
623 case 'c':
624 (void) strcpy(mc.drvclass, optarg);
625 break;
626 case 'd':
628 * need to keep for compatibility, but
629 * do nothing.
631 break;
632 case 'f':
633 force_flag = TRUE;
634 break;
635 case 'i':
636 single_drv = TRUE;
637 (void) strcpy(mc.drvname, optarg);
638 driver = s_strdup(optarg);
639 break;
640 case 'm':
641 mc.major = atoi(optarg);
642 break;
643 case 'n':
644 /* prevent driver loading and deferred attach */
645 load_attach_drv = FALSE;
646 break;
647 case 'p':
648 /* specify alternate path_to_inst file */
649 inst_file = s_strdup(optarg);
650 break;
651 case 'R':
653 * Private flag for suninstall to populate
654 * device information on the installed root.
656 root_dir = s_strdup(optarg);
657 if (zone_pathcheck(root_dir) !=
658 DEVFSADM_SUCCESS)
659 devfsadm_exit(devfsadm_copy());
660 /*NOTREACHED*/
661 break;
662 case 'r':
663 devices_dir = s_strdup(optarg);
664 if (zone_pathcheck(devices_dir) !=
665 DEVFSADM_SUCCESS)
666 devfsadm_exit(1);
667 /*NOTREACHED*/
668 break;
669 case 's':
671 * suppress. don't create nodes
672 * useful with -v or -V
674 file_mods = FALSE;
675 flush_path_to_inst_enable = FALSE;
676 break;
677 case 'u':
679 * Invoked via update_drv(1m) to update
680 * the kernel's driver/alias binding
681 * when removing one or more aliases.
683 config = FALSE;
684 break;
685 case 'v':
686 /* documented verbose flag */
687 add_verbose_id(VERBOSE_MID);
688 break;
689 case 'V':
690 /* undocumented for extra verbose levels */
691 add_verbose_id(optarg);
692 break;
693 case 'x':
694 update_only = 1;
695 break;
696 default:
697 usage();
701 if (optind < argc) {
702 usage();
705 if (bind == TRUE) {
706 if ((mc.major == -1) || (mc.drvname[0] == NULL)) {
707 err_print(MAJOR_AND_B_FLAG);
708 devfsadm_exit(1);
709 /*NOTREACHED*/
711 mc.flags = 0;
712 if (force_flag)
713 mc.flags |= MOD_UNBIND_OVERRIDE;
714 if (update_only)
715 mc.flags |= MOD_ADDMAJBIND_UPDATE;
716 mc.num_aliases = num_aliases;
717 mc.ap = a_head;
718 retval = modctl((config == TRUE) ? MODADDMAJBIND :
719 MODREMDRVALIAS, NULL, (caddr_t)&mc);
720 if (retval < 0) {
721 err_print((config == TRUE) ? MODCTL_ADDMAJBIND :
722 MODCTL_REMMAJBIND);
724 devfsadm_exit(retval);
725 /*NOTREACHED*/
728 } else if ((strcmp(prog, DEVFSADM) == 0) ||
729 (strcmp(prog, DEVFSADMD) == 0)) {
730 char *zonename = NULL;
731 int init_drvconf = 0;
732 int init_perm = 0;
733 int public_mode = 0;
734 int init_sysavail = 0;
736 if (strcmp(prog, DEVFSADMD) == 0) {
737 daemon_mode = TRUE;
740 devlinktab_file = DEVLINKTAB_FILE;
742 while ((opt = getopt(argc, argv,
743 "a:Cc:deIi:l:np:PR:r:sSt:uvV:x:")) != EOF) {
744 if (opt == 'I' || opt == 'P' || opt == 'S') {
745 if (public_mode)
746 usage();
747 } else {
748 if (init_perm || init_drvconf || init_sysavail)
749 usage();
750 public_mode = 1;
752 switch (opt) {
753 case 'a':
754 attr_root = s_strdup(optarg);
755 break;
756 case 'C':
757 cleanup = TRUE;
758 break;
759 case 'c':
760 num_classes++;
761 classes = s_realloc(classes,
762 num_classes * sizeof (char *));
763 classes[num_classes - 1] = optarg;
764 break;
765 case 'd':
766 if (daemon_mode == FALSE) {
768 * Device allocation to be disabled.
770 devalloc_flag = DA_OFF;
771 build_dev = FALSE;
773 break;
774 case 'e':
775 if (daemon_mode == FALSE) {
777 * Device allocation to be enabled.
779 devalloc_flag = DA_ON;
780 build_dev = FALSE;
782 break;
783 case 'I': /* update kernel driver.conf cache */
784 if (daemon_mode == TRUE)
785 usage();
786 init_drvconf = 1;
787 break;
788 case 'i':
789 single_drv = TRUE;
790 driver = s_strdup(optarg);
791 break;
792 case 'l':
793 /* specify an alternate module load path */
794 module_dirs = s_strdup(optarg);
795 break;
796 case 'n':
797 /* prevent driver loading and deferred attach */
798 load_attach_drv = FALSE;
799 break;
800 case 'p':
801 /* specify alternate path_to_inst file */
802 inst_file = s_strdup(optarg);
803 break;
804 case 'P':
805 if (daemon_mode == TRUE)
806 usage();
807 /* load minor_perm and device_policy */
808 init_perm = 1;
809 break;
810 case 'R':
812 * Private flag for suninstall to populate
813 * device information on the installed root.
815 root_dir = s_strdup(optarg);
816 devfsadm_exit(devfsadm_copy());
817 /*NOTREACHED*/
818 break;
819 case 'r':
820 set_root_devices_dev_dir(optarg);
821 break;
822 case 's':
824 * suppress. don't create/remove links/nodes
825 * useful with -v or -V
827 file_mods = FALSE;
828 flush_path_to_inst_enable = FALSE;
829 break;
830 case 'S':
831 if (daemon_mode == TRUE)
832 usage();
833 init_sysavail = 1;
834 break;
835 case 't':
836 devlinktab_file = optarg;
837 break;
838 case 'u': /* complete configuration after */
839 /* adding a driver update-only */
840 if (daemon_mode == TRUE)
841 usage();
842 update_all_drivers = TRUE;
843 break;
844 case 'v':
845 /* documented verbose flag */
846 add_verbose_id(VERBOSE_MID);
847 break;
848 case 'V':
849 /* undocumented: specify verbose lvl */
850 add_verbose_id(optarg);
851 break;
852 case 'x':
854 * x is the "private switch" option. The
855 * goal is to not suck up all the other
856 * option letters.
858 if (strcmp(optarg, "update_devlinksdb") == 0) {
859 update_database = TRUE;
860 } else if (strcmp(optarg, "no_dev") == 0) {
861 /* don't build /dev */
862 build_dev = FALSE;
863 } else if (strcmp(optarg, "no_devices") == 0) {
864 /* don't build /devices */
865 build_devices = FALSE;
866 } else if (strcmp(optarg, "no_p2i") == 0) {
867 /* don't flush path_to_inst */
868 flush_path_to_inst_enable = FALSE;
869 } else if (strcmp(optarg, "use_dicache") == 0) {
870 use_snapshot_cache = TRUE;
871 } else {
872 usage();
874 break;
875 default:
876 usage();
877 break;
880 if (optind < argc) {
881 usage();
885 * We're not in zone mode; Check to see if the rootpath
886 * collides with any zonepaths.
888 if (zonename == NULL) {
889 if (zone_pathcheck(root_dir) != DEVFSADM_SUCCESS)
890 devfsadm_exit(1);
891 /*NOTREACHED*/
894 if (init_drvconf || init_perm || init_sysavail) {
896 * Load minor perm before force-loading drivers
897 * so the correct permissions are picked up.
899 if (init_perm) {
900 check_reconfig_state();
901 load_dev_acl();
903 if (init_drvconf)
904 update_drvconf((major_t)-1, 0);
905 if (init_sysavail)
906 modctl_sysavail();
907 devfsadm_exit(0);
908 /*NOTREACHED*/
913 if (get_linkcompat_opts == TRUE) {
915 build_devices = FALSE;
916 load_attach_drv = FALSE;
917 num_classes++;
918 classes = s_realloc(classes, num_classes *
919 sizeof (char *));
920 classes[num_classes - 1] = compat_class;
922 while ((opt = getopt(argc, argv, "Cnr:svV:")) != EOF) {
923 switch (opt) {
924 case 'C':
925 cleanup = TRUE;
926 break;
927 case 'n':
928 /* prevent driver loading or deferred attach */
929 load_attach_drv = FALSE;
930 break;
931 case 'r':
932 set_root_devices_dev_dir(optarg);
933 if (zone_pathcheck(root_dir) !=
934 DEVFSADM_SUCCESS)
935 devfsadm_exit(1);
936 /*NOTREACHED*/
937 break;
938 case 's':
939 /* suppress. don't create/remove links/nodes */
940 /* useful with -v or -V */
941 file_mods = FALSE;
942 flush_path_to_inst_enable = FALSE;
943 break;
944 case 'v':
945 /* documented verbose flag */
946 add_verbose_id(VERBOSE_MID);
947 break;
948 case 'V':
949 /* undocumented for extra verbose levels */
950 add_verbose_id(optarg);
951 break;
952 default:
953 usage();
956 if (optind < argc) {
957 usage();
960 set_lock_root();
963 void
964 usage(void)
966 if (strcmp(prog, DEVLINKS) == 0) {
967 err_print(DEVLINKS_USAGE);
968 } else if (strcmp(prog, DRVCONFIG) == 0) {
969 err_print(DRVCONFIG_USAGE);
970 } else if ((strcmp(prog, DEVFSADM) == 0) ||
971 (strcmp(prog, DEVFSADMD) == 0)) {
972 err_print(DEVFSADM_USAGE);
973 } else {
974 err_print(COMPAT_LINK_USAGE);
977 devfsadm_exit(1);
978 /*NOTREACHED*/
981 static void
982 devi_tree_walk(struct dca_impl *dcip, int flags, char *ev_subclass)
984 char *msg, *name;
985 struct mlist mlist = {0};
986 di_node_t node;
988 vprint(CHATTY_MID, "devi_tree_walk: root=%s, minor=%s, driver=%s,"
989 " error=%d, flags=%u\n", dcip->dci_root,
990 dcip->dci_minor ? dcip->dci_minor : "<NULL>",
991 dcip->dci_driver ? dcip->dci_driver : "<NULL>", dcip->dci_error,
992 dcip->dci_flags);
994 assert(dcip->dci_root);
996 if (dcip->dci_flags & DCA_LOAD_DRV) {
997 node = di_init_driver(dcip->dci_driver, flags);
998 msg = DRIVER_FAILURE;
999 name = dcip->dci_driver;
1000 } else {
1001 node = di_init(dcip->dci_root, flags);
1002 msg = DI_INIT_FAILED;
1003 name = dcip->dci_root;
1006 if (node == DI_NODE_NIL) {
1007 dcip->dci_error = errno;
1009 * Rapid hotplugging (commonly seen during USB testing),
1010 * may remove a device before the create event for it
1011 * has been processed. To prevent alarming users with
1012 * a superfluous message, we suppress error messages
1013 * for ENXIO and hotplug.
1015 if (!(errno == ENXIO && (dcip->dci_flags & DCA_HOT_PLUG)))
1016 err_print(msg, name, strerror(dcip->dci_error));
1017 return;
1020 if (dcip->dci_flags & DCA_FLUSH_PATHINST)
1021 flush_path_to_inst();
1023 dcip->dci_arg = &mlist;
1024 devi_root_node = node; /* protected by lock_dev() */
1026 vprint(CHATTY_MID, "walking device tree\n");
1028 (void) di_walk_minor(node, NULL, DI_CHECK_ALIAS, dcip,
1029 check_minor_type);
1031 process_deferred_links(dcip, DCA_CREATE_LINK);
1033 dcip->dci_arg = NULL;
1036 * Finished creating devfs files and dev links.
1037 * Log sysevent.
1039 if (ev_subclass)
1040 build_and_enq_event(EC_DEV_ADD, ev_subclass, dcip->dci_root,
1041 node, dcip->dci_minor);
1043 /* Add new device to device allocation database */
1044 if (system_labeled && update_devdb) {
1045 _update_devalloc_db(&devlist, 0, DA_ADD, NULL, root_dir);
1046 update_devdb = 0;
1049 devi_root_node = DI_NODE_NIL; /* protected by lock_dev() */
1050 di_fini(node);
1053 static void
1054 process_deferred_links(struct dca_impl *dcip, int flags)
1056 struct mlist *dep;
1057 struct minor *mp, *smp;
1059 vprint(CHATTY_MID, "processing deferred links\n");
1061 dep = dcip->dci_arg;
1064 * The list head is not used during the deferred create phase
1066 dcip->dci_arg = NULL;
1068 assert(dep);
1069 assert((dep->head == NULL) ^ (dep->tail != NULL));
1070 assert(flags == DCA_FREE_LIST || flags == DCA_CREATE_LINK);
1072 for (smp = NULL, mp = dep->head; mp; mp = mp->next) {
1073 if (flags == DCA_CREATE_LINK)
1074 (void) check_minor_type(mp->node, mp->minor, dcip);
1075 free(smp);
1076 smp = mp;
1079 free(smp);
1083 * Called in non-daemon mode to take a snap shot of the devinfo tree.
1084 * Then it calls the appropriate functions to build /devices and /dev.
1085 * It also flushes path_to_inst.
1086 * Except in the devfsadm -i (single driver case), the flags used by devfsadm
1087 * needs to match DI_CACHE_SNAPSHOT_FLAGS. That will make DINFOCACHE snapshot
1088 * updated.
1090 void
1091 process_devinfo_tree()
1093 uint_t flags;
1094 struct dca_impl dci;
1095 char name[MAXNAMELEN];
1096 char *fcn = "process_devinfo_tree: ";
1098 vprint(CHATTY_MID, "%senter\n", fcn);
1100 dca_impl_init("/", NULL, &dci);
1102 lock_dev();
1105 * Update kernel driver.conf cache when devfsadm/drvconfig
1106 * is invoked to build /devices and /dev.
1108 if (update_all_drivers || load_attach_drv) {
1109 update_drvconf((major_t)-1,
1110 update_all_drivers ? MOD_LOADDRVCONF_RECONF : 0);
1113 if (single_drv == TRUE) {
1115 * load a single driver, but walk the entire devinfo tree
1117 if (load_attach_drv == FALSE)
1118 err_print(DRV_LOAD_REQD);
1120 vprint(CHATTY_MID, "%sattaching driver (%s)\n", fcn, driver);
1122 dci.dci_flags |= DCA_LOAD_DRV;
1123 (void) snprintf(name, sizeof (name), "%s", driver);
1124 dci.dci_driver = name;
1125 flags = DINFOCPYALL | DINFOPATH;
1127 } else if (load_attach_drv == TRUE) {
1129 * Load and attach all drivers, then walk the entire tree.
1130 * If the cache flag is set, use DINFOCACHE to get cached
1131 * data.
1133 if (use_snapshot_cache == TRUE) {
1134 flags = DINFOCACHE;
1135 vprint(CHATTY_MID, "%susing snapshot cache\n", fcn);
1136 } else {
1137 vprint(CHATTY_MID, "%sattaching all drivers\n", fcn);
1138 flags = DI_CACHE_SNAPSHOT_FLAGS;
1139 if (cleanup) {
1141 * remove dangling entries from /etc/devices
1142 * files.
1144 flags |= DINFOCLEANUP;
1147 } else {
1149 * For devlinks, disks, ports, tapes and devfsadm -n,
1150 * just need to take a snapshot with active devices.
1152 vprint(CHATTY_MID, "%staking snapshot of active devices\n",
1153 fcn);
1154 flags = DINFOCPYALL;
1157 if (((load_attach_drv == TRUE) || (single_drv == TRUE)) &&
1158 (build_devices == TRUE)) {
1159 dci.dci_flags |= DCA_FLUSH_PATHINST;
1162 /* handle pre-cleanup operations desired by the modules. */
1163 pre_and_post_cleanup(RM_PRE);
1165 devi_tree_walk(&dci, flags, NULL);
1167 if (dci.dci_error) {
1168 devfsadm_exit(1);
1169 /*NOTREACHED*/
1172 /* handle post-cleanup operations desired by the modules. */
1173 pre_and_post_cleanup(RM_POST);
1175 unlock_dev(SYNC_STATE);
1178 /*ARGSUSED*/
1179 static void
1180 print_cache_signal(int signo)
1182 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1183 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1184 devfsadm_exit(1);
1185 /*NOTREACHED*/
1189 static void
1190 revoke_lookup_door(void)
1192 if (lookup_door_fd != -1) {
1193 if (door_revoke(lookup_door_fd) == -1) {
1194 err_print("door_revoke of %s failed - %s\n",
1195 lookup_door_path, strerror(errno));
1200 /*ARGSUSED*/
1201 static void
1202 catch_exit(int signo)
1204 revoke_lookup_door();
1208 * Register with eventd for messages. Create doors for synchronous
1209 * link creation.
1211 static void
1212 daemon_update(void)
1214 int fd;
1215 char *fcn = "daemon_update: ";
1216 char door_file[MAXPATHLEN];
1217 const char *subclass_list;
1218 sysevent_handle_t *sysevent_hp;
1219 vprint(CHATTY_MID, "%senter\n", fcn);
1221 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1222 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1223 devfsadm_exit(1);
1224 /*NOTREACHED*/
1226 if (signal(SIGTERM, catch_exit) == SIG_ERR) {
1227 err_print("signal SIGTERM failed: %s\n", strerror(errno));
1228 devfsadm_exit(1);
1229 /*NOTREACHED*/
1232 if (snprintf(door_file, sizeof (door_file),
1233 "%s%s", attr_root ? attr_root : root_dir, DEVFSADM_SERVICE_DOOR)
1234 >= sizeof (door_file)) {
1235 err_print("update_daemon failed to open sysevent service "
1236 "door\n");
1237 devfsadm_exit(1);
1238 /*NOTREACHED*/
1240 if ((sysevent_hp = sysevent_open_channel_alt(
1241 door_file)) == NULL) {
1242 err_print(CANT_CREATE_DOOR,
1243 door_file, strerror(errno));
1244 devfsadm_exit(1);
1245 /*NOTREACHED*/
1247 if (sysevent_bind_subscriber(sysevent_hp, event_handler) != 0) {
1248 err_print(CANT_CREATE_DOOR,
1249 door_file, strerror(errno));
1250 (void) sysevent_close_channel(sysevent_hp);
1251 devfsadm_exit(1);
1252 /*NOTREACHED*/
1254 subclass_list = EC_SUB_ALL;
1255 if (sysevent_register_event(sysevent_hp, EC_ALL, &subclass_list, 1)
1256 != 0) {
1257 err_print(CANT_CREATE_DOOR,
1258 door_file, strerror(errno));
1259 (void) sysevent_unbind_subscriber(sysevent_hp);
1260 (void) sysevent_close_channel(sysevent_hp);
1261 devfsadm_exit(1);
1262 /*NOTREACHED*/
1264 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1265 etc_dev_dir, DEVFSADM_SYNCH_DOOR) >= sizeof (door_file)) {
1266 err_print(CANT_CREATE_DOOR, DEVFSADM_SYNCH_DOOR,
1267 strerror(ENAMETOOLONG));
1268 devfsadm_exit(1);
1269 /*NOTREACHED*/
1272 (void) s_unlink(door_file);
1273 if ((fd = open(door_file, O_RDWR | O_CREAT, SYNCH_DOOR_PERMS)) == -1) {
1274 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1275 devfsadm_exit(1);
1276 /*NOTREACHED*/
1278 (void) close(fd);
1280 if ((fd = door_create(sync_handler, NULL,
1281 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1282 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1283 (void) s_unlink(door_file);
1284 devfsadm_exit(1);
1285 /*NOTREACHED*/
1288 if (fattach(fd, door_file) == -1) {
1289 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1290 (void) s_unlink(door_file);
1291 devfsadm_exit(1);
1292 /*NOTREACHED*/
1296 * devname_lookup_door
1298 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1299 etc_dev_dir, DEVNAME_LOOKUP_DOOR) >= sizeof (door_file)) {
1300 err_print(CANT_CREATE_DOOR, DEVNAME_LOOKUP_DOOR,
1301 strerror(ENAMETOOLONG));
1302 devfsadm_exit(1);
1303 /*NOTREACHED*/
1306 (void) s_unlink(door_file);
1307 if ((fd = open(door_file, O_RDWR | O_CREAT, S_IRUSR|S_IWUSR)) == -1) {
1308 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1309 devfsadm_exit(1);
1310 /*NOTREACHED*/
1312 (void) close(fd);
1314 if ((fd = door_create(devname_lookup_handler, NULL,
1315 DOOR_REFUSE_DESC)) == -1) {
1316 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1317 (void) s_unlink(door_file);
1318 devfsadm_exit(1);
1319 /*NOTREACHED*/
1322 (void) fdetach(door_file);
1323 lookup_door_path = s_strdup(door_file);
1324 retry:
1325 if (fattach(fd, door_file) == -1) {
1326 if (errno == EBUSY)
1327 goto retry;
1328 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1329 (void) s_unlink(door_file);
1330 devfsadm_exit(1);
1331 /*NOTREACHED*/
1333 lookup_door_fd = fd;
1335 /* pass down the door name to kernel for door_ki_open */
1336 if (devname_kcall(MODDEVNAME_LOOKUPDOOR, (void *)door_file) != 0)
1337 err_print(DEVNAME_CONTACT_FAILED, strerror(errno));
1339 vprint(CHATTY_MID, "%spausing\n", fcn);
1340 for (;;) {
1341 (void) pause();
1345 /*ARGSUSED*/
1346 static void
1347 sync_handler(void *cookie, char *ap, size_t asize,
1348 door_desc_t *dp, uint_t ndesc)
1350 door_cred_t dcred;
1351 struct dca_off *dcp, rdca;
1352 struct dca_impl dci;
1355 * Must be root to make this call
1356 * If caller is not root, don't touch its data.
1358 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
1359 dcp = &rdca;
1360 dcp->dca_error = EPERM;
1361 goto out;
1364 assert(ap);
1365 assert(asize == sizeof (*dcp));
1367 dcp = (void *)ap;
1370 * Root is always present and is the first component of "name" member
1372 assert(dcp->dca_root == 0);
1375 * The structure passed in by the door_client uses offsets
1376 * instead of pointers to work across address space boundaries.
1377 * Now copy the data into a structure (dca_impl) which uses
1378 * pointers.
1380 dci.dci_root = &dcp->dca_name[dcp->dca_root];
1381 dci.dci_minor = dcp->dca_minor ? &dcp->dca_name[dcp->dca_minor] : NULL;
1382 dci.dci_driver =
1383 dcp->dca_driver ? &dcp->dca_name[dcp->dca_driver] : NULL;
1384 dci.dci_error = 0;
1385 dci.dci_flags = dcp->dca_flags | (dci.dci_driver ? DCA_LOAD_DRV : 0);
1386 dci.dci_arg = NULL;
1388 lock_dev();
1389 devi_tree_walk(&dci, DINFOCPYALL, NULL);
1390 dcp->dca_error = dci.dci_error;
1392 if (dcp->dca_flags & DCA_DEVLINK_SYNC)
1393 unlock_dev(SYNC_STATE);
1394 else
1395 unlock_dev(CACHE_STATE);
1397 out: (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0);
1400 static void
1401 lock_dev(void)
1403 vprint(CHATTY_MID, "lock_dev(): entered\n");
1405 if (build_dev == FALSE)
1406 return;
1408 /* lockout other threads from /dev */
1409 while (sema_wait(&dev_sema) != 0)
1413 * Lock out other devfsadm processes from /dev.
1414 * If this wasn't the last process to run,
1415 * clear caches
1417 if (enter_dev_lock() != getpid()) {
1418 invalidate_enumerate_cache();
1419 rm_all_links_from_cache();
1420 (void) di_devlink_close(&devlink_cache, DI_LINK_ERROR);
1422 /* send any sysevents that were queued up. */
1423 process_syseventq();
1427 * (re)load the reverse links database if not
1428 * already cached.
1430 if (devlink_cache == NULL)
1431 devlink_cache = di_devlink_open(root_dir, 0);
1434 * If modules were unloaded, reload them. Also use module status
1435 * as an indication that we should check to see if other binding
1436 * files need to be reloaded.
1438 if (module_head == NULL) {
1439 load_modules();
1440 read_minor_perm_file();
1441 read_driver_aliases_file();
1442 read_devlinktab_file();
1443 read_logindevperm_file();
1444 read_enumerate_file();
1447 if (module_head != NULL)
1448 return;
1450 if (strcmp(prog, DEVLINKS) == 0) {
1451 if (devlinktab_list == NULL) {
1452 err_print(NO_LINKTAB, devlinktab_file);
1453 err_print(NO_MODULES, module_dirs);
1454 err_print(ABORTING);
1455 devfsadm_exit(1);
1456 /*NOTREACHED*/
1458 } else {
1459 err_print(NO_MODULES, module_dirs);
1460 if (strcmp(prog, DEVFSADM) == 0) {
1461 err_print(MODIFY_PATH);
1467 * Unlock the device. If we are processing a CACHE_STATE call, we signal a
1468 * minor_fini_thread delayed SYNC_STATE at the end of the call. If we are
1469 * processing a SYNC_STATE call, we cancel any minor_fini_thread SYNC_STATE
1470 * at both the start and end of the call since we will be doing the SYNC_STATE.
1472 static void
1473 unlock_dev(int flag)
1475 assert(flag == SYNC_STATE || flag == CACHE_STATE);
1477 vprint(CHATTY_MID, "unlock_dev(): entered\n");
1479 /* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */
1480 if (flag == SYNC_STATE) {
1481 (void) mutex_lock(&minor_fini_mutex);
1482 minor_fini_canceled = TRUE;
1483 minor_fini_delayed = FALSE;
1484 (void) mutex_unlock(&minor_fini_mutex);
1487 if (build_dev == FALSE)
1488 return;
1490 if (devlink_cache == NULL) {
1491 err_print(NO_DEVLINK_CACHE);
1493 assert(devlink_cache);
1495 if (flag == SYNC_STATE) {
1496 unload_modules();
1497 if (update_database)
1498 (void) di_devlink_update(devlink_cache);
1499 (void) di_devlink_close(&devlink_cache, 0);
1502 * now that the devlinks db cache has been flushed, it is safe
1503 * to send any sysevents that were queued up.
1505 process_syseventq();
1508 exit_dev_lock(0);
1510 (void) mutex_lock(&minor_fini_mutex);
1511 if (flag == SYNC_STATE) {
1512 /* We did a SYNC_STATE, cancel minor_fini_thread SYNC */
1513 minor_fini_canceled = TRUE;
1514 minor_fini_delayed = FALSE;
1515 } else {
1516 /* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */
1517 minor_fini_canceled = FALSE;
1518 minor_fini_delayed = TRUE;
1519 (void) cond_signal(&minor_fini_cv);
1521 (void) mutex_unlock(&minor_fini_mutex);
1523 (void) sema_post(&dev_sema);
1527 * Check that if -r is set, it is not any part of a zone--- that is, that
1528 * the zonepath is not a substring of the root path.
1530 static int
1531 zone_pathcheck(char *checkpath)
1533 void *dlhdl = NULL;
1534 char *name;
1535 char root[MAXPATHLEN]; /* resolved devfsadm root path */
1536 char zroot[MAXPATHLEN]; /* zone root path */
1537 char rzroot[MAXPATHLEN]; /* resolved zone root path */
1538 char tmp[MAXPATHLEN];
1539 FILE *cookie;
1540 int err = DEVFSADM_SUCCESS;
1542 if (checkpath[0] == '\0')
1543 return (DEVFSADM_SUCCESS);
1546 * Check if zones is available on this system.
1548 if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
1549 return (DEVFSADM_SUCCESS);
1552 bzero(root, sizeof (root));
1553 if (resolvepath(checkpath, root, sizeof (root) - 1) == -1) {
1555 * In this case the user has done "devfsadm -r" on some path
1556 * which does not yet exist, or we got some other misc. error.
1557 * We punt and don't resolve the path in this case.
1559 (void) strlcpy(root, checkpath, sizeof (root));
1562 if (strlen(root) > 0 && (root[strlen(root) - 1] != '/')) {
1563 (void) snprintf(tmp, sizeof (tmp), "%s/", root);
1564 (void) strlcpy(root, tmp, sizeof (root));
1567 cookie = setzoneent();
1568 while ((name = getzoneent(cookie)) != NULL) {
1569 /* Skip the global zone */
1570 if (strcmp(name, GLOBAL_ZONENAME) == 0) {
1571 free(name);
1572 continue;
1575 if (zone_get_zonepath(name, zroot, sizeof (zroot)) != Z_OK) {
1576 free(name);
1577 continue;
1580 bzero(rzroot, sizeof (rzroot));
1581 if (resolvepath(zroot, rzroot, sizeof (rzroot) - 1) == -1) {
1583 * Zone path doesn't exist, or other misc error,
1584 * so we try using the non-resolved pathname.
1586 (void) strlcpy(rzroot, zroot, sizeof (rzroot));
1588 if (strlen(rzroot) > 0 && (rzroot[strlen(rzroot) - 1] != '/')) {
1589 (void) snprintf(tmp, sizeof (tmp), "%s/", rzroot);
1590 (void) strlcpy(rzroot, tmp, sizeof (rzroot));
1594 * Finally, the comparison. If the zone root path is a
1595 * leading substring of the root path, fail.
1597 if (strncmp(rzroot, root, strlen(rzroot)) == 0) {
1598 err_print(ZONE_PATHCHECK, root, name);
1599 err = DEVFSADM_FAILURE;
1600 free(name);
1601 break;
1603 free(name);
1605 endzoneent(cookie);
1606 (void) dlclose(dlhdl);
1607 return (err);
1611 * Called by the daemon when it receives an event from the devfsadm SLM
1612 * to syseventd.
1614 * The devfsadm SLM uses a private event channel for communication to
1615 * devfsadmd set-up via private libsysevent interfaces. This handler is
1616 * used to bind to the devfsadmd channel for event delivery.
1617 * The devfsadmd SLM insures single calls to this routine as well as
1618 * synchronized event delivery.
1621 static void
1622 event_handler(sysevent_t *ev)
1624 char *path;
1625 char *minor;
1626 char *subclass;
1627 char *dev_ev_subclass;
1628 char *driver_name;
1629 nvlist_t *attr_list = NULL;
1630 int err = 0;
1631 int instance;
1632 int branch_event = 0;
1635 * If this is event-driven, then we cannot trust the static devlist
1636 * to be correct.
1639 event_driven = TRUE;
1640 subclass = sysevent_get_subclass_name(ev);
1641 vprint(EVENT_MID, "event_handler: %s id:0X%llx\n",
1642 subclass, sysevent_get_seq(ev));
1644 if (strcmp(subclass, ESC_DEVFS_START) == 0) {
1645 return;
1648 /* Check if event is an instance modification */
1649 if (strcmp(subclass, ESC_DEVFS_INSTANCE_MOD) == 0) {
1650 devfs_instance_mod();
1651 return;
1653 if (sysevent_get_attr_list(ev, &attr_list) != 0) {
1654 vprint(EVENT_MID, "event_handler: can not get attr list\n");
1655 return;
1658 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0 ||
1659 strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0 ||
1660 strcmp(subclass, ESC_DEVFS_MINOR_CREATE) == 0 ||
1661 strcmp(subclass, ESC_DEVFS_MINOR_REMOVE) == 0) {
1662 if ((err = nvlist_lookup_string(attr_list, DEVFS_PATHNAME,
1663 &path)) != 0)
1664 goto out;
1666 if (nvlist_lookup_string(attr_list, DEVFS_DEVI_CLASS,
1667 &dev_ev_subclass) != 0)
1668 dev_ev_subclass = NULL;
1670 if (nvlist_lookup_string(attr_list, DEVFS_DRIVER_NAME,
1671 &driver_name) != 0)
1672 driver_name = NULL;
1674 if (nvlist_lookup_int32(attr_list, DEVFS_INSTANCE,
1675 &instance) != 0)
1676 instance = -1;
1678 if (nvlist_lookup_int32(attr_list, DEVFS_BRANCH_EVENT,
1679 &branch_event) != 0)
1680 branch_event = 0;
1682 if (nvlist_lookup_string(attr_list, DEVFS_MINOR_NAME,
1683 &minor) != 0)
1684 minor = NULL;
1686 lock_dev();
1688 if (strcmp(ESC_DEVFS_DEVI_ADD, subclass) == 0) {
1689 add_minor_pathname(path, NULL, dev_ev_subclass);
1690 if (branch_event) {
1691 build_and_enq_event(EC_DEV_BRANCH,
1692 ESC_DEV_BRANCH_ADD, path, DI_NODE_NIL,
1693 NULL);
1696 } else if (strcmp(ESC_DEVFS_MINOR_CREATE, subclass) == 0) {
1697 add_minor_pathname(path, minor, dev_ev_subclass);
1699 } else if (strcmp(ESC_DEVFS_MINOR_REMOVE, subclass) == 0) {
1700 hot_cleanup(path, minor, dev_ev_subclass, driver_name,
1701 instance);
1703 } else { /* ESC_DEVFS_DEVI_REMOVE */
1704 hot_cleanup(path, NULL, dev_ev_subclass,
1705 driver_name, instance);
1706 if (branch_event) {
1707 build_and_enq_event(EC_DEV_BRANCH,
1708 ESC_DEV_BRANCH_REMOVE, path, DI_NODE_NIL,
1709 NULL);
1713 unlock_dev(CACHE_STATE);
1715 } else if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0 ||
1716 strcmp(subclass, ESC_DEVFS_BRANCH_REMOVE) == 0) {
1717 if ((err = nvlist_lookup_string(attr_list,
1718 DEVFS_PATHNAME, &path)) != 0)
1719 goto out;
1721 /* just log ESC_DEV_BRANCH... event */
1722 if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0)
1723 dev_ev_subclass = ESC_DEV_BRANCH_ADD;
1724 else
1725 dev_ev_subclass = ESC_DEV_BRANCH_REMOVE;
1727 lock_dev();
1728 build_and_enq_event(EC_DEV_BRANCH, dev_ev_subclass, path,
1729 DI_NODE_NIL, NULL);
1730 unlock_dev(CACHE_STATE);
1731 } else
1732 err_print(UNKNOWN_EVENT, subclass);
1734 out:
1735 if (err)
1736 err_print(EVENT_ATTR_LOOKUP_FAILED, strerror(err));
1737 nvlist_free(attr_list);
1740 static void
1741 dca_impl_init(char *root, char *minor, struct dca_impl *dcip)
1743 assert(root);
1745 dcip->dci_root = root;
1746 dcip->dci_minor = minor;
1747 dcip->dci_driver = NULL;
1748 dcip->dci_error = 0;
1749 dcip->dci_flags = 0;
1750 dcip->dci_arg = NULL;
1754 * Kernel logs a message when a devinfo node is attached. Try to create
1755 * /dev and /devices for each minor node. minorname can be NULL.
1757 void
1758 add_minor_pathname(char *node, char *minor, char *ev_subclass)
1760 struct dca_impl dci;
1762 vprint(CHATTY_MID, "add_minor_pathname: node_path=%s minor=%s\n",
1763 node, minor ? minor : "NULL");
1765 dca_impl_init(node, minor, &dci);
1768 * Restrict hotplug link creation if daemon
1769 * started with -i option.
1771 if (single_drv == TRUE) {
1772 dci.dci_driver = driver;
1776 * We are being invoked in response to a hotplug event.
1778 dci.dci_flags = DCA_HOT_PLUG | DCA_CHECK_TYPE;
1780 devi_tree_walk(&dci, DINFOPROP|DINFOMINOR, ev_subclass);
1783 static di_node_t
1784 find_clone_node()
1786 static di_node_t clone_node = DI_NODE_NIL;
1788 if (clone_node == DI_NODE_NIL)
1789 clone_node = di_init("/pseudo/clone@0", DINFOPROP);
1790 return (clone_node);
1793 static int
1794 is_descendent_of(di_node_t node, char *driver)
1796 while (node != DI_NODE_NIL) {
1797 char *drv = di_driver_name(node);
1798 if (strcmp(drv, driver) == 0)
1799 return (1);
1800 node = di_parent_node(node);
1802 return (0);
1806 * Checks the minor type. If it is an alias node, then lookup
1807 * the real node/minor first, then call minor_process() to
1808 * do the real work.
1810 static int
1811 check_minor_type(di_node_t node, di_minor_t minor, void *arg)
1813 ddi_minor_type minor_type;
1814 di_node_t clone_node;
1815 char *mn;
1816 char *nt;
1817 struct mlist *dep;
1818 struct dca_impl *dcip = arg;
1820 assert(dcip);
1822 dep = dcip->dci_arg;
1824 mn = di_minor_name(minor);
1827 * We match driver here instead of in minor_process
1828 * as we want the actual driver name. This check is
1829 * unnecessary during deferred processing.
1831 if (dep &&
1832 ((dcip->dci_driver && !is_descendent_of(node, dcip->dci_driver)) ||
1833 (dcip->dci_minor && strcmp(mn, dcip->dci_minor)))) {
1834 return (DI_WALK_CONTINUE);
1837 if ((dcip->dci_flags & DCA_CHECK_TYPE) &&
1838 (nt = di_minor_nodetype(minor)) &&
1839 (strcmp(nt, DDI_NT_NET) == 0)) {
1840 dcip->dci_flags &= ~DCA_CHECK_TYPE;
1843 minor_type = di_minor_type(minor);
1845 if (minor_type == DDM_MINOR) {
1846 minor_process(node, minor, dep);
1848 } else if (minor_type == DDM_ALIAS) {
1849 struct mlist *cdep, clone_del = {0};
1851 clone_node = find_clone_node();
1852 if (clone_node == DI_NODE_NIL) {
1853 err_print(DI_INIT_FAILED, "clone", strerror(errno));
1854 return (DI_WALK_CONTINUE);
1857 cdep = dep ? &clone_del : NULL;
1859 minor_process(clone_node, minor, cdep);
1862 * cache "alias" minor node and free "clone" minor
1864 if (cdep != NULL && cdep->head != NULL) {
1865 assert(cdep->tail != NULL);
1866 cache_deferred_minor(dep, node, minor);
1867 dcip->dci_arg = cdep;
1868 process_deferred_links(dcip, DCA_FREE_LIST);
1869 dcip->dci_arg = dep;
1873 return (DI_WALK_CONTINUE);
1878 * This is the entry point for each minor node, whether walking
1879 * the entire tree via di_walk_minor() or processing a hotplug event
1880 * for a single devinfo node (via hotplug ndi_devi_online()).
1882 /*ARGSUSED*/
1883 static void
1884 minor_process(di_node_t node, di_minor_t minor, struct mlist *dep)
1886 create_list_t *create;
1887 int defer;
1889 vprint(CHATTY_MID, "minor_process: node=%s, minor=%s\n",
1890 di_node_name(node), di_minor_name(minor));
1892 if (dep != NULL) {
1895 * Reset /devices node to minor_perm perm/ownership
1896 * if we are here to deactivate device allocation
1898 if (build_devices == TRUE) {
1899 reset_node_permissions(node, minor);
1902 if (build_dev == FALSE) {
1903 return;
1907 * This function will create any nodes for /etc/devlink.tab.
1908 * If devlink.tab handles link creation, we don't call any
1909 * devfsadm modules since that could cause duplicate caching
1910 * in the enumerate functions if different re strings are
1911 * passed that are logically identical. I'm still not
1912 * convinced this would cause any harm, but better to be safe.
1914 * Deferred processing is available only for devlinks
1915 * created through devfsadm modules.
1917 if (process_devlink_compat(minor, node) == TRUE) {
1918 return;
1920 } else {
1921 vprint(CHATTY_MID, "minor_process: deferred processing\n");
1925 * look for relevant link create rules in the modules, and
1926 * invoke the link create callback function to build a link
1927 * if there is a match.
1929 defer = 0;
1930 for (create = create_head; create != NULL; create = create->next) {
1931 if ((minor_matches_rule(node, minor, create) == TRUE) &&
1932 class_ok(create->create->device_class) ==
1933 DEVFSADM_SUCCESS) {
1934 if (call_minor_init(create->modptr) ==
1935 DEVFSADM_FAILURE) {
1936 continue;
1940 * If NOT doing the deferred creates (i.e. 1st pass) and
1941 * rule requests deferred processing cache the minor
1942 * data.
1944 * If deferred processing (2nd pass), create links
1945 * ONLY if rule requests deferred processing.
1947 if (dep && ((create->create->flags & CREATE_MASK) ==
1948 CREATE_DEFER)) {
1949 defer = 1;
1950 continue;
1951 } else if (dep == NULL &&
1952 ((create->create->flags & CREATE_MASK) !=
1953 CREATE_DEFER)) {
1954 continue;
1957 if ((*(create->create->callback_fcn))
1958 (minor, node) == DEVFSADM_TERMINATE) {
1959 break;
1964 if (defer)
1965 cache_deferred_minor(dep, node, minor);
1970 * Cache node and minor in defer list.
1972 static void
1973 cache_deferred_minor(
1974 struct mlist *dep,
1975 di_node_t node,
1976 di_minor_t minor)
1978 struct minor *mp;
1979 const char *fcn = "cache_deferred_minor";
1981 vprint(CHATTY_MID, "%s node=%s, minor=%s\n", fcn,
1982 di_node_name(node), di_minor_name(minor));
1984 if (dep == NULL) {
1985 vprint(CHATTY_MID, "%s: cannot cache during "
1986 "deferred processing. Ignoring minor\n", fcn);
1987 return;
1990 mp = (struct minor *)s_zalloc(sizeof (struct minor));
1991 mp->node = node;
1992 mp->minor = minor;
1993 mp->next = NULL;
1995 assert(dep->head == NULL || dep->tail != NULL);
1996 if (dep->head == NULL) {
1997 dep->head = mp;
1998 } else {
1999 dep->tail->next = mp;
2001 dep->tail = mp;
2005 * Check to see if "create" link creation rule matches this node/minor.
2006 * If it does, return TRUE.
2008 static int
2009 minor_matches_rule(di_node_t node, di_minor_t minor, create_list_t *create)
2011 char *m_nodetype, *m_drvname;
2013 if (create->create->node_type != NULL) {
2015 m_nodetype = di_minor_nodetype(minor);
2016 assert(m_nodetype != NULL);
2018 switch (create->create->flags & TYPE_MASK) {
2019 case TYPE_EXACT:
2020 if (strcmp(create->create->node_type, m_nodetype) !=
2021 0) {
2022 return (FALSE);
2024 break;
2025 case TYPE_PARTIAL:
2026 if (strncmp(create->create->node_type, m_nodetype,
2027 strlen(create->create->node_type)) != 0) {
2028 return (FALSE);
2030 break;
2031 case TYPE_RE:
2032 if (regexec(&(create->node_type_comp), m_nodetype,
2033 0, NULL, 0) != 0) {
2034 return (FALSE);
2036 break;
2040 if (create->create->drv_name != NULL) {
2041 m_drvname = di_driver_name(node);
2042 switch (create->create->flags & DRV_MASK) {
2043 case DRV_EXACT:
2044 if (strcmp(create->create->drv_name, m_drvname) != 0) {
2045 return (FALSE);
2047 break;
2048 case DRV_RE:
2049 if (regexec(&(create->drv_name_comp), m_drvname,
2050 0, NULL, 0) != 0) {
2051 return (FALSE);
2053 break;
2057 return (TRUE);
2061 * If no classes were given on the command line, then return DEVFSADM_SUCCESS.
2062 * Otherwise, return DEVFSADM_SUCCESS if the device "class" from the module
2063 * matches one of the device classes given on the command line,
2064 * otherwise, return DEVFSADM_FAILURE.
2066 static int
2067 class_ok(char *class)
2069 int i;
2071 if (num_classes == 0) {
2072 return (DEVFSADM_SUCCESS);
2075 for (i = 0; i < num_classes; i++) {
2076 if (strcmp(class, classes[i]) == 0) {
2077 return (DEVFSADM_SUCCESS);
2080 return (DEVFSADM_FAILURE);
2084 * call minor_fini on active modules, then unload ALL modules
2086 static void
2087 unload_modules(void)
2089 module_t *module_free;
2090 create_list_t *create_free;
2091 remove_list_t *remove_free;
2093 while (create_head != NULL) {
2094 create_free = create_head;
2095 create_head = create_head->next;
2097 if ((create_free->create->flags & TYPE_RE) == TYPE_RE) {
2098 regfree(&(create_free->node_type_comp));
2100 if ((create_free->create->flags & DRV_RE) == DRV_RE) {
2101 regfree(&(create_free->drv_name_comp));
2103 free(create_free);
2106 while (remove_head != NULL) {
2107 remove_free = remove_head;
2108 remove_head = remove_head->next;
2109 free(remove_free);
2112 while (module_head != NULL) {
2114 if ((module_head->minor_fini != NULL) &&
2115 ((module_head->flags & MODULE_ACTIVE) == MODULE_ACTIVE)) {
2116 (void) (*(module_head->minor_fini))();
2119 vprint(MODLOAD_MID, "unloading module %s\n", module_head->name);
2120 free(module_head->name);
2121 (void) dlclose(module_head->dlhandle);
2123 module_free = module_head;
2124 module_head = module_head->next;
2125 free(module_free);
2130 * Load devfsadm logical link processing modules.
2132 static void
2133 load_modules(void)
2135 DIR *mod_dir;
2136 struct dirent *entp;
2137 char cdir[PATH_MAX + 1];
2138 char *last;
2139 char *mdir = module_dirs;
2140 char *fcn = "load_modules: ";
2142 while (*mdir != '\0') {
2144 while (*mdir == ':') {
2145 mdir++;
2148 if (*mdir == '\0') {
2149 continue;
2152 last = strchr(mdir, ':');
2154 if (last == NULL) {
2155 last = mdir + strlen(mdir);
2158 (void) strncpy(cdir, mdir, last - mdir);
2159 cdir[last - mdir] = '\0';
2160 mdir += strlen(cdir);
2162 if ((mod_dir = opendir(cdir)) == NULL) {
2163 vprint(MODLOAD_MID, "%sopendir(%s): %s\n",
2164 fcn, cdir, strerror(errno));
2165 continue;
2168 while ((entp = readdir(mod_dir)) != NULL) {
2170 if ((strcmp(entp->d_name, ".") == 0) ||
2171 (strcmp(entp->d_name, "..") == 0)) {
2172 continue;
2175 load_module(entp->d_name, cdir);
2177 s_closedir(mod_dir);
2181 static void
2182 load_module(char *mname, char *cdir)
2184 _devfsadm_create_reg_t *create_reg;
2185 _devfsadm_remove_reg_V1_t *remove_reg;
2186 create_list_t *create_list_element;
2187 create_list_t **create_list_next;
2188 remove_list_t *remove_list_element;
2189 remove_list_t **remove_list_next;
2190 char epath[PATH_MAX + 1], *end;
2191 char *fcn = "load_module: ";
2192 char *dlerrstr;
2193 void *dlhandle;
2194 module_t *module;
2195 int flags;
2196 int n;
2197 int i;
2199 /* ignore any file which does not end in '.so' */
2200 if ((end = strstr(mname, MODULE_SUFFIX)) != NULL) {
2201 if (end[strlen(MODULE_SUFFIX)] != '\0') {
2202 return;
2204 } else {
2205 return;
2208 (void) snprintf(epath, sizeof (epath), "%s/%s", cdir, mname);
2210 if ((dlhandle = dlopen(epath, RTLD_LAZY)) == NULL) {
2211 dlerrstr = dlerror();
2212 err_print(DLOPEN_FAILED, epath,
2213 dlerrstr ? dlerrstr : "unknown error");
2214 return;
2217 /* dlsym the _devfsadm_create_reg structure */
2218 if (NULL == (create_reg = (_devfsadm_create_reg_t *)
2219 dlsym(dlhandle, _DEVFSADM_CREATE_REG))) {
2220 vprint(MODLOAD_MID, "dlsym(%s, %s): symbol not found\n", epath,
2221 _DEVFSADM_CREATE_REG);
2222 } else {
2223 vprint(MODLOAD_MID, "%sdlsym(%s, %s) succeeded\n",
2224 fcn, epath, _DEVFSADM_CREATE_REG);
2227 /* dlsym the _devfsadm_remove_reg structure */
2228 if (NULL == (remove_reg = (_devfsadm_remove_reg_V1_t *)
2229 dlsym(dlhandle, _DEVFSADM_REMOVE_REG))) {
2230 vprint(MODLOAD_MID, "dlsym(%s,\n\t%s): symbol not found\n",
2231 epath, _DEVFSADM_REMOVE_REG);
2232 } else {
2233 vprint(MODLOAD_MID, "dlsym(%s, %s): succeeded\n",
2234 epath, _DEVFSADM_REMOVE_REG);
2237 vprint(MODLOAD_MID, "module %s loaded\n", epath);
2239 module = (module_t *)s_malloc(sizeof (module_t));
2240 module->name = s_strdup(epath);
2241 module->dlhandle = dlhandle;
2243 /* dlsym other module functions, to be called later */
2244 module->minor_fini = (int (*)())dlsym(dlhandle, MINOR_FINI);
2245 module->minor_init = (int (*)())dlsym(dlhandle, MINOR_INIT);
2246 module->flags = 0;
2249 * put a ptr to each struct devfsadm_create on "create_head"
2250 * list sorted in interpose_lvl.
2252 if (create_reg != NULL) {
2253 for (i = 0; i < create_reg->count; i++) {
2254 int flags = create_reg->tblp[i].flags;
2256 create_list_element = (create_list_t *)
2257 s_malloc(sizeof (create_list_t));
2259 create_list_element->create = &(create_reg->tblp[i]);
2260 create_list_element->modptr = module;
2262 if (((flags & CREATE_MASK) != 0) &&
2263 ((flags & CREATE_MASK) != CREATE_DEFER)) {
2264 free(create_list_element);
2265 err_print("illegal flag combination in "
2266 "module create\n");
2267 err_print(IGNORING_ENTRY, i, epath);
2268 continue;
2271 if (((flags & TYPE_MASK) == 0) ^
2272 (create_reg->tblp[i].node_type == NULL)) {
2273 free(create_list_element);
2274 err_print("flags value incompatible with "
2275 "node_type value in module create\n");
2276 err_print(IGNORING_ENTRY, i, epath);
2277 continue;
2280 if (((flags & TYPE_MASK) != 0) &&
2281 ((flags & TYPE_MASK) != TYPE_EXACT) &&
2282 ((flags & TYPE_MASK) != TYPE_RE) &&
2283 ((flags & TYPE_MASK) != TYPE_PARTIAL)) {
2284 free(create_list_element);
2285 err_print("illegal TYPE_* flag combination in "
2286 "module create\n");
2287 err_print(IGNORING_ENTRY, i, epath);
2288 continue;
2291 /* precompile regular expression for efficiency */
2292 if ((flags & TYPE_RE) == TYPE_RE) {
2293 if ((n = regcomp(&(create_list_element->
2294 node_type_comp),
2295 create_reg->tblp[i].node_type,
2296 REG_EXTENDED)) != 0) {
2297 free(create_list_element);
2298 err_print(REGCOMP_FAILED,
2299 create_reg->tblp[i].node_type, n);
2300 err_print(IGNORING_ENTRY, i, epath);
2301 continue;
2305 if (((flags & DRV_MASK) == 0) ^
2306 (create_reg->tblp[i].drv_name == NULL)) {
2307 if ((flags & TYPE_RE) == TYPE_RE) {
2308 regfree(&(create_list_element->
2309 node_type_comp));
2311 free(create_list_element);
2312 err_print("flags value incompatible with "
2313 "drv_name value in module create\n");
2314 err_print(IGNORING_ENTRY, i, epath);
2315 continue;
2318 if (((flags & DRV_MASK) != 0) &&
2319 ((flags & DRV_MASK) != DRV_EXACT) &&
2320 ((flags & DRV_MASK) != DRV_RE)) {
2321 if ((flags & TYPE_RE) == TYPE_RE) {
2322 regfree(&(create_list_element->
2323 node_type_comp));
2325 free(create_list_element);
2326 err_print("illegal DRV_* flag combination in "
2327 "module create\n");
2328 err_print(IGNORING_ENTRY, i, epath);
2329 continue;
2332 /* precompile regular expression for efficiency */
2333 if ((create_reg->tblp[i].flags & DRV_RE) == DRV_RE) {
2334 if ((n = regcomp(&(create_list_element->
2335 drv_name_comp),
2336 create_reg->tblp[i].drv_name,
2337 REG_EXTENDED)) != 0) {
2338 if ((flags & TYPE_RE) == TYPE_RE) {
2339 regfree(&(create_list_element->
2340 node_type_comp));
2342 free(create_list_element);
2343 err_print(REGCOMP_FAILED,
2344 create_reg->tblp[i].drv_name, n);
2345 err_print(IGNORING_ENTRY, i, epath);
2346 continue;
2351 /* add to list sorted by interpose level */
2352 for (create_list_next = &(create_head);
2353 (*create_list_next != NULL) &&
2354 (*create_list_next)->create->interpose_lvl >=
2355 create_list_element->create->interpose_lvl;
2356 create_list_next = &((*create_list_next)->next))
2358 create_list_element->next = *create_list_next;
2359 *create_list_next = create_list_element;
2364 * put a ptr to each struct devfsadm_remove on "remove_head"
2365 * list sorted by interpose_lvl.
2367 flags = 0;
2368 if (remove_reg != NULL) {
2369 if (remove_reg->version < DEVFSADM_V1)
2370 flags |= RM_NOINTERPOSE;
2371 for (i = 0; i < remove_reg->count; i++) {
2373 remove_list_element = (remove_list_t *)
2374 s_malloc(sizeof (remove_list_t));
2376 remove_list_element->remove = &(remove_reg->tblp[i]);
2377 remove_list_element->remove->flags |= flags;
2378 remove_list_element->modptr = module;
2380 for (remove_list_next = &(remove_head);
2381 (*remove_list_next != NULL) &&
2382 (*remove_list_next)->remove->interpose_lvl >=
2383 remove_list_element->remove->interpose_lvl;
2384 remove_list_next = &((*remove_list_next)->next))
2386 remove_list_element->next = *remove_list_next;
2387 *remove_list_next = remove_list_element;
2391 module->next = module_head;
2392 module_head = module;
2396 * After we have completed a CACHE_STATE, if a SYNC_STATE does not occur
2397 * within 'timeout' secs the minor_fini_thread needs to do a SYNC_STATE
2398 * so that we still call the minor_fini routines.
2400 /*ARGSUSED*/
2401 static void
2402 minor_fini_thread(void *arg)
2404 timestruc_t abstime;
2406 vprint(INITFINI_MID, "minor_fini_thread starting\n");
2408 (void) mutex_lock(&minor_fini_mutex);
2409 for (;;) {
2410 /* wait the gather period, or until signaled */
2411 abstime.tv_sec = time(NULL) + minor_fini_timeout;
2412 abstime.tv_nsec = 0;
2413 (void) cond_timedwait(&minor_fini_cv,
2414 &minor_fini_mutex, &abstime);
2416 /* if minor_fini was canceled, go wait again */
2417 if (minor_fini_canceled == TRUE)
2418 continue;
2420 /* if minor_fini was delayed, go wait again */
2421 if (minor_fini_delayed == TRUE) {
2422 minor_fini_delayed = FALSE;
2423 continue;
2426 /* done with cancellations and delays, do the SYNC_STATE */
2427 (void) mutex_unlock(&minor_fini_mutex);
2429 lock_dev();
2430 unlock_dev(SYNC_STATE);
2431 vprint(INITFINI_MID, "minor_fini sync done\n");
2433 (void) mutex_lock(&minor_fini_mutex);
2439 * Attempt to initialize module, if a minor_init routine exists. Set
2440 * the active flag if the routine exists and succeeds. If it doesn't
2441 * exist, just set the active flag.
2443 static int
2444 call_minor_init(module_t *module)
2446 char *fcn = "call_minor_init: ";
2448 if ((module->flags & MODULE_ACTIVE) == MODULE_ACTIVE) {
2449 return (DEVFSADM_SUCCESS);
2452 vprint(INITFINI_MID, "%smodule %s. current state: inactive\n",
2453 fcn, module->name);
2455 if (module->minor_init == NULL) {
2456 module->flags |= MODULE_ACTIVE;
2457 vprint(INITFINI_MID, "minor_init not defined\n");
2458 return (DEVFSADM_SUCCESS);
2461 if ((*(module->minor_init))() == DEVFSADM_FAILURE) {
2462 err_print(FAILED_FOR_MODULE, MINOR_INIT, module->name);
2463 return (DEVFSADM_FAILURE);
2466 vprint(INITFINI_MID, "minor_init() returns DEVFSADM_SUCCESS. "
2467 "new state: active\n");
2469 module->flags |= MODULE_ACTIVE;
2470 return (DEVFSADM_SUCCESS);
2474 * Creates a symlink 'link' to the physical path of node:minor.
2475 * Construct link contents, then call create_link_common().
2477 /*ARGSUSED*/
2479 devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags)
2481 char rcontents[PATH_MAX];
2482 char devlink[PATH_MAX];
2483 char phy_path[PATH_MAX];
2484 char *acontents;
2485 char *dev_path;
2486 int numslashes;
2487 int rv;
2488 int i, link_exists;
2489 int last_was_slash = FALSE;
2492 * try to use devices path
2494 if ((node == lnode) && (minor == lminor)) {
2495 acontents = lphy_path;
2496 } else if (di_minor_type(minor) == DDM_ALIAS) {
2497 /* use /pseudo/clone@0:<driver> as the phys path */
2498 (void) snprintf(phy_path, sizeof (phy_path),
2499 "/pseudo/clone@0:%s",
2500 di_driver_name(di_minor_devinfo(minor)));
2501 acontents = phy_path;
2502 } else {
2503 if ((dev_path = di_devfs_path(node)) == NULL) {
2504 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2505 devfsadm_exit(1);
2506 /*NOTREACHED*/
2508 (void) snprintf(phy_path, sizeof (phy_path), "%s:%s",
2509 dev_path, di_minor_name(minor));
2510 di_devfs_path_free(dev_path);
2511 acontents = phy_path;
2514 /* prepend link with dev_dir contents */
2515 (void) strlcpy(devlink, dev_dir, sizeof (devlink));
2516 (void) strlcat(devlink, "/", sizeof (devlink));
2517 (void) strlcat(devlink, link, sizeof (devlink));
2520 * Calculate # of ../ to add. Account for double '//' in path.
2521 * Ignore all leading slashes.
2523 for (i = 0; link[i] == '/'; i++)
2525 for (numslashes = 0; link[i] != '\0'; i++) {
2526 if (link[i] == '/') {
2527 if (last_was_slash == FALSE) {
2528 numslashes++;
2529 last_was_slash = TRUE;
2531 } else {
2532 last_was_slash = FALSE;
2535 /* Don't count any trailing '/' */
2536 if (link[i-1] == '/') {
2537 numslashes--;
2540 rcontents[0] = '\0';
2541 do {
2542 (void) strlcat(rcontents, "../", sizeof (rcontents));
2543 } while (numslashes-- != 0);
2545 (void) strlcat(rcontents, "devices", sizeof (rcontents));
2546 (void) strlcat(rcontents, acontents, sizeof (rcontents));
2548 if (devlinks_debug == TRUE) {
2549 vprint(INFO_MID, "adding link %s ==> %s\n", devlink, rcontents);
2552 if ((rv = create_link_common(devlink, rcontents, &link_exists))
2553 == DEVFSADM_SUCCESS) {
2554 linknew = TRUE;
2555 add_link_to_cache(link, acontents);
2556 } else {
2557 linknew = FALSE;
2560 if (link_exists == TRUE) {
2561 /* Link exists or was just created */
2562 (void) di_devlink_add_link(devlink_cache, link, rcontents,
2563 DI_PRIMARY_LINK);
2565 if (system_labeled && (flags & DA_ADD)) {
2567 * Add this to the list of allocatable devices. If this
2568 * is a hotplugged, removable disk, add it as rmdisk.
2570 int instance = di_instance(node);
2572 if ((flags & DA_CD) &&
2573 (_da_check_for_usb(devlink, root_dir) == 1)) {
2574 (void) da_add_list(&devlist, devlink, instance,
2575 DA_ADD|DA_RMDISK);
2576 update_devdb = DA_RMDISK;
2577 } else if (linknew == TRUE) {
2578 (void) da_add_list(&devlist, devlink, instance,
2579 flags);
2580 update_devdb = flags;
2585 return (rv);
2589 * Creates a symlink link to primary_link. Calculates relative
2590 * directory offsets, then calls link_common().
2592 /*ARGSUSED*/
2594 devfsadm_secondary_link(char *link, char *primary_link, int flags)
2596 char contents[PATH_MAX + 1];
2597 char devlink[PATH_MAX + 1];
2598 int rv, link_exists;
2599 char *fpath;
2600 char *tpath;
2601 char *op;
2603 /* prepend link with dev_dir contents */
2604 (void) strcpy(devlink, dev_dir);
2605 (void) strcat(devlink, "/");
2606 (void) strcat(devlink, link);
2608 * building extra link, so use first link as link contents, but first
2609 * make it relative.
2611 fpath = link;
2612 tpath = primary_link;
2613 op = contents;
2615 while (*fpath == *tpath && *fpath != '\0') {
2616 fpath++, tpath++;
2619 /* Count directories to go up, if any, and add "../" */
2620 while (*fpath != '\0') {
2621 if (*fpath == '/') {
2622 (void) strcpy(op, "../");
2623 op += 3;
2625 fpath++;
2629 * Back up to the start of the current path component, in
2630 * case in the middle
2632 while (tpath != primary_link && *(tpath-1) != '/') {
2633 tpath--;
2635 (void) strcpy(op, tpath);
2637 if (devlinks_debug == TRUE) {
2638 vprint(INFO_MID, "adding extra link %s ==> %s\n",
2639 devlink, contents);
2642 if ((rv = create_link_common(devlink, contents, &link_exists))
2643 == DEVFSADM_SUCCESS) {
2645 * we need to save the ultimate /devices contents, and not the
2646 * secondary link, since hotcleanup only looks at /devices path.
2647 * Since we don't have devices path here, we can try to get it
2648 * by readlink'ing the secondary link. This assumes the primary
2649 * link was created first.
2651 add_link_to_cache(link, lphy_path);
2652 linknew = TRUE;
2653 if (system_labeled &&
2654 ((flags & DA_AUDIO) && (flags & DA_ADD))) {
2656 * Add this device to the list of allocatable devices.
2658 int instance = 0;
2660 op = strrchr(contents, '/');
2661 op++;
2662 (void) sscanf(op, "%d", &instance);
2663 (void) da_add_list(&devlist, devlink, instance, flags);
2664 update_devdb = flags;
2666 } else {
2667 linknew = FALSE;
2671 * If link exists or was just created, add it to the database
2673 if (link_exists == TRUE) {
2674 (void) di_devlink_add_link(devlink_cache, link, contents,
2675 DI_SECONDARY_LINK);
2678 return (rv);
2681 /* returns pointer to the devices directory */
2682 char *
2683 devfsadm_get_devices_dir()
2685 return (devices_dir);
2689 * Does the actual link creation. VERBOSE_MID only used if there is
2690 * a change. CHATTY_MID used otherwise.
2692 static int
2693 create_link_common(char *devlink, char *contents, int *exists)
2695 int try;
2696 int linksize;
2697 int max_tries = 0;
2698 static int prev_link_existed = TRUE;
2699 char checkcontents[PATH_MAX + 1];
2700 char *hide;
2702 *exists = FALSE;
2704 /* Database is not updated when file_mods == FALSE */
2705 if (file_mods == FALSE) {
2706 /* we want *actual* link contents so no alias redirection */
2707 linksize = readlink(devlink, checkcontents, PATH_MAX);
2708 if (linksize > 0) {
2709 checkcontents[linksize] = '\0';
2710 if (strcmp(checkcontents, contents) != 0) {
2711 vprint(CHATTY_MID, REMOVING_LINK,
2712 devlink, checkcontents);
2713 return (DEVFSADM_SUCCESS);
2714 } else {
2715 vprint(CHATTY_MID, "link exists and is correct:"
2716 " %s -> %s\n", devlink, contents);
2717 /* failure only in that the link existed */
2718 return (DEVFSADM_FAILURE);
2720 } else {
2721 vprint(VERBOSE_MID, CREATING_LINK, devlink, contents);
2722 return (DEVFSADM_SUCCESS);
2727 * systems calls are expensive, so predict whether to readlink
2728 * or symlink first, based on previous attempt
2730 if (prev_link_existed == FALSE) {
2731 try = CREATE_LINK;
2732 } else {
2733 try = READ_LINK;
2736 while (++max_tries <= 3) {
2738 switch (try) {
2739 case CREATE_LINK:
2741 if (symlink(contents, devlink) == 0) {
2742 vprint(VERBOSE_MID, CREATING_LINK, devlink,
2743 contents);
2744 prev_link_existed = FALSE;
2745 /* link successfully created */
2746 *exists = TRUE;
2747 set_logindev_perms(devlink);
2748 return (DEVFSADM_SUCCESS);
2749 } else {
2750 switch (errno) {
2752 case ENOENT:
2753 /* dirpath to node doesn't exist */
2754 hide = strrchr(devlink, '/');
2755 *hide = '\0';
2756 s_mkdirp(devlink, S_IRWXU|S_IRGRP|
2757 S_IXGRP|S_IROTH|S_IXOTH);
2758 *hide = '/';
2759 break;
2760 case EEXIST:
2761 try = READ_LINK;
2762 break;
2763 default:
2764 err_print(SYMLINK_FAILED, devlink,
2765 contents, strerror(errno));
2766 return (DEVFSADM_FAILURE);
2769 break;
2771 case READ_LINK:
2774 * If there is redirection, new phys path
2775 * and old phys path will not match and the
2776 * link will be created with new phys path
2777 * which is what we want. So we want real
2778 * contents.
2780 linksize = readlink(devlink, checkcontents, PATH_MAX);
2781 if (linksize >= 0) {
2782 checkcontents[linksize] = '\0';
2783 if (strcmp(checkcontents, contents) != 0) {
2784 s_unlink(devlink);
2785 vprint(VERBOSE_MID, REMOVING_LINK,
2786 devlink, checkcontents);
2787 try = CREATE_LINK;
2788 } else {
2789 prev_link_existed = TRUE;
2790 vprint(CHATTY_MID,
2791 "link exists and is correct:"
2792 " %s -> %s\n", devlink, contents);
2793 *exists = TRUE;
2794 /* failure in that the link existed */
2795 return (DEVFSADM_FAILURE);
2797 } else {
2798 switch (errno) {
2799 case EINVAL:
2800 /* not a symlink, remove and create */
2801 s_unlink(devlink);
2802 default:
2803 /* maybe it didn't exist at all */
2804 try = CREATE_LINK;
2805 break;
2808 break;
2811 err_print(MAX_ATTEMPTS, devlink, contents);
2812 return (DEVFSADM_FAILURE);
2815 static void
2816 set_logindev_perms(char *devlink)
2818 struct login_dev *newdev;
2819 struct passwd pwd, *resp;
2820 char pwd_buf[PATH_MAX];
2821 int rv;
2822 struct stat sb;
2823 char *devfs_path = NULL;
2826 * We only want logindev perms to be set when a device is
2827 * hotplugged or an application requests synchronous creates.
2828 * So we enable this only in daemon mode. In addition,
2829 * login(1) only fixes the std. /dev dir. So we don't
2830 * change perms if alternate root is set.
2831 * login_dev_enable is TRUE only in these cases.
2833 if (login_dev_enable != TRUE)
2834 return;
2837 * Normally, /etc/logindevperm has few (8 - 10 entries) which
2838 * may be regular expressions (globs were converted to RE).
2839 * So just do a linear search through the list.
2841 for (newdev = login_dev_cache; newdev; newdev = newdev->ldev_next) {
2842 vprint(FILES_MID, "matching %s with %s\n", devlink,
2843 newdev->ldev_device);
2845 if (regexec(&newdev->ldev_device_regex, devlink, 0,
2846 NULL, 0) == 0) {
2847 vprint(FILES_MID, "matched %s with %s\n", devlink,
2848 newdev->ldev_device);
2849 break;
2853 if (newdev == NULL)
2854 return;
2857 * we have a match, now find the driver associated with this
2858 * minor node using a snapshot on the physical path
2860 (void) resolve_link(devlink, NULL, NULL, &devfs_path, 0);
2862 * We dont need redirection here - the actual link contents
2863 * whether "alias" or "current" are fine
2865 if (devfs_path) {
2866 di_node_t node;
2867 char *drv;
2868 struct driver_list *list;
2869 char *p;
2871 /* truncate on : so we can take a snapshot */
2872 (void) strcpy(pwd_buf, devfs_path);
2873 p = strrchr(pwd_buf, ':');
2874 if (p == NULL) {
2875 free(devfs_path);
2876 return;
2878 *p = '\0';
2880 vprint(FILES_MID, "link=%s->physpath=%s\n",
2881 devlink, pwd_buf);
2883 node = di_init(pwd_buf, DINFOMINOR);
2885 drv = NULL;
2886 if (node) {
2887 drv = di_driver_name(node);
2889 if (drv) {
2890 vprint(FILES_MID, "%s: driver is %s\n",
2891 devlink, drv);
2894 /* search thru the driver list specified in logindevperm */
2895 list = newdev->ldev_driver_list;
2896 if ((drv != NULL) && (list != NULL)) {
2897 while (list) {
2898 if (strcmp(list->driver_name,
2899 drv) == 0) {
2900 vprint(FILES_MID,
2901 "driver %s match!\n", drv);
2902 break;
2904 list = list->next;
2906 if (list == NULL) {
2907 vprint(FILES_MID, "no driver match!\n");
2908 free(devfs_path);
2909 return;
2912 free(devfs_path);
2913 di_fini(node);
2914 } else {
2915 return;
2918 vprint(FILES_MID, "changing permissions of %s\n", devlink);
2921 * We have a match. We now attempt to determine the
2922 * owner and group of the console user.
2924 * stat() the console device newdev->ldev_console
2925 * which will always exist - it will have the right owner but
2926 * not the right group. Use getpwuid_r() to determine group for this
2927 * uid.
2928 * Note, it is safe to use name service here since if name services
2929 * are not available (during boot or in single-user mode), then
2930 * console owner will be root and its gid can be found in
2931 * local files.
2933 if (stat(newdev->ldev_console, &sb) == -1) {
2934 vprint(VERBOSE_MID, STAT_FAILED, newdev->ldev_console,
2935 strerror(errno));
2936 return;
2939 resp = NULL;
2940 rv = getpwuid_r(sb.st_uid, &pwd, pwd_buf, sizeof (pwd_buf), &resp);
2941 if (rv || resp == NULL) {
2942 rv = rv ? rv : EINVAL;
2943 vprint(VERBOSE_MID, GID_FAILED, sb.st_uid,
2944 strerror(rv));
2945 return;
2948 assert(&pwd == resp);
2950 sb.st_gid = resp->pw_gid;
2952 if (chmod(devlink, newdev->ldev_perms) == -1) {
2953 vprint(VERBOSE_MID, CHMOD_FAILED, devlink,
2954 strerror(errno));
2955 return;
2958 if (chown(devlink, sb.st_uid, sb.st_gid) == -1) {
2959 vprint(VERBOSE_MID, CHOWN_FAILED, devlink,
2960 strerror(errno));
2965 * Reset /devices node with appropriate permissions and
2966 * ownership as specified in /etc/minor_perm.
2968 static void
2969 reset_node_permissions(di_node_t node, di_minor_t minor)
2971 int spectype;
2972 char phy_path[PATH_MAX + 1];
2973 mode_t mode;
2974 dev_t dev;
2975 uid_t uid;
2976 gid_t gid;
2977 struct stat sb;
2978 char *dev_path, *aminor = NULL;
2980 /* lphy_path starts with / */
2981 if ((dev_path = di_devfs_path(node)) == NULL) {
2982 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2983 devfsadm_exit(1);
2984 /*NOTREACHED*/
2986 (void) strcpy(lphy_path, dev_path);
2987 di_devfs_path_free(dev_path);
2989 (void) strcat(lphy_path, ":");
2990 if (di_minor_type(minor) == DDM_ALIAS) {
2991 char *driver;
2992 aminor = di_minor_name(minor);
2993 driver = di_driver_name(di_minor_devinfo(minor));
2994 (void) strcat(lphy_path, driver);
2995 } else
2996 (void) strcat(lphy_path, di_minor_name(minor));
2998 (void) strcpy(phy_path, devices_dir);
2999 (void) strcat(phy_path, lphy_path);
3001 lnode = node;
3002 lminor = minor;
3004 vprint(CHATTY_MID, "reset_node_permissions: phy_path=%s lphy_path=%s\n",
3005 phy_path, lphy_path);
3007 dev = di_minor_devt(minor);
3008 spectype = di_minor_spectype(minor); /* block or char */
3010 getattr(phy_path, aminor, spectype, dev, &mode, &uid, &gid);
3013 * compare and set permissions and ownership
3015 * Under devfs, a quick insertion and removal of USB devices
3016 * would cause stat of physical path to fail. In this case,
3017 * we emit a verbose message, but don't print errors.
3019 if ((stat(phy_path, &sb) == -1) || (sb.st_rdev != dev)) {
3020 vprint(VERBOSE_MID, NO_DEVFS_NODE, phy_path);
3021 return;
3025 * If we are here for a new device
3026 * If device allocation is on
3027 * then
3028 * set ownership to root:other and permissions to 0000
3029 * else
3030 * set ownership and permissions as specified in minor_perm
3031 * If we are here for an existing device
3032 * If device allocation is to be turned on
3033 * then
3034 * reset ownership to root:other and permissions to 0000
3035 * else if device allocation is to be turned off
3036 * reset ownership and permissions to those specified in
3037 * minor_perm
3038 * else
3039 * preserve existing/user-modified ownership and
3040 * permissions
3042 * devfs indicates a new device by faking access time to be zero.
3044 if (sb.st_atime != 0) {
3045 int i;
3046 char *nt;
3048 if ((devalloc_flag == 0) && (devalloc_is_on != 1))
3050 * Leave existing devices as they are if we are not
3051 * turning device allocation on/off.
3053 return;
3055 nt = di_minor_nodetype(minor);
3057 if (nt == NULL)
3058 return;
3060 for (i = 0; devalloc_list[i]; i++) {
3061 if (strcmp(nt, devalloc_list[i]) == 0)
3063 * One of the types recognized by devalloc,
3064 * reset attrs.
3066 break;
3068 if (devalloc_list[i] == NULL)
3069 return;
3072 if (file_mods == FALSE) {
3073 /* Nothing more to do if simulating */
3074 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3075 return;
3078 if ((devalloc_flag == DA_ON) ||
3079 ((devalloc_is_on == 1) && (devalloc_flag != DA_OFF))) {
3081 * we are here either to turn device allocation on or
3082 * to add a new device while device allocation is on
3083 * (and we've confirmed that we're not turning it
3084 * off).
3086 mode = DEALLOC_MODE;
3087 uid = DA_UID;
3088 gid = DA_GID;
3091 if ((devalloc_is_on == 1) || (devalloc_flag == DA_ON) ||
3092 (sb.st_mode != mode)) {
3093 if (chmod(phy_path, mode) == -1)
3094 vprint(VERBOSE_MID, CHMOD_FAILED,
3095 phy_path, strerror(errno));
3097 if ((devalloc_is_on == 1) || (devalloc_flag == DA_ON) ||
3098 (sb.st_uid != uid || sb.st_gid != gid)) {
3099 if (chown(phy_path, uid, gid) == -1)
3100 vprint(VERBOSE_MID, CHOWN_FAILED,
3101 phy_path, strerror(errno));
3104 /* Report that we actually did something */
3105 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3109 * Removes logical link and the minor node it refers to. If file is a
3110 * link, we recurse and try to remove the minor node (or link if path is
3111 * a double link) that file's link contents refer to.
3113 static void
3114 devfsadm_rm_work(char *file, int recurse, int file_type)
3116 char *fcn = "devfsadm_rm_work: ";
3117 int linksize;
3118 char contents[PATH_MAX + 1];
3119 char nextfile[PATH_MAX + 1];
3120 char newfile[PATH_MAX + 1];
3121 char *ptr;
3123 vprint(REMOVE_MID, "%s%s\n", fcn, file);
3126 * Note: we don't remove /devices (non-links) entries because they are
3127 * covered by devfs.
3129 if (file_type != TYPE_LINK) {
3130 return;
3133 /* split into multiple if's due to excessive indentations */
3134 (void) strcpy(newfile, dev_dir);
3135 (void) strcat(newfile, "/");
3136 (void) strcat(newfile, file);
3139 * we dont care about the content of the symlink, so
3140 * redirection is not needed.
3142 if ((recurse == TRUE) &&
3143 ((linksize = readlink(newfile, contents, PATH_MAX)) > 0)) {
3144 contents[linksize] = '\0';
3147 * recurse if link points to another link
3149 if (is_minor_node(contents, &ptr) != DEVFSADM_TRUE) {
3150 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
3151 devfsadm_rm_work(&contents[strlen(DEV) + 1],
3152 TRUE, TYPE_LINK);
3153 } else {
3154 if ((ptr = strrchr(file, '/')) != NULL) {
3155 *ptr = '\0';
3156 (void) strcpy(nextfile, file);
3157 *ptr = '/';
3158 (void) strcat(nextfile, "/");
3159 } else {
3160 (void) strcpy(nextfile, "");
3162 (void) strcat(nextfile, contents);
3163 devfsadm_rm_work(nextfile, TRUE, TYPE_LINK);
3168 vprint(VERBOSE_MID, DEVFSADM_UNLINK, newfile);
3169 if (file_mods == TRUE) {
3170 rm_link_from_cache(file);
3171 s_unlink(newfile);
3172 rm_parent_dir_if_empty(newfile);
3173 invalidate_enumerate_cache();
3174 (void) di_devlink_rm_link(devlink_cache, file);
3178 void
3179 devfsadm_rm_link(char *file)
3181 devfsadm_rm_work(file, FALSE, TYPE_LINK);
3184 void
3185 devfsadm_rm_all(char *file)
3187 devfsadm_rm_work(file, TRUE, TYPE_LINK);
3190 static int
3191 s_rmdir(char *path)
3193 int i;
3194 char *rpath, *dir;
3195 const char *fcn = "s_rmdir";
3198 * Certain directories are created at install time by packages.
3199 * Some of them (listed in sticky_dirs[]) are required by apps
3200 * and need to be present even when empty.
3202 vprint(REMOVE_MID, "%s: checking if %s is sticky\n", fcn, path);
3204 rpath = path + strlen(dev_dir) + 1;
3206 for (i = 0; (dir = sticky_dirs[i]) != NULL; i++) {
3207 if (*rpath == *dir) {
3208 if (strcmp(rpath, dir) == 0) {
3209 vprint(REMOVE_MID, "%s: skipping sticky dir: "
3210 "%s\n", fcn, path);
3211 errno = EEXIST;
3212 return (-1);
3217 return (rmdir(path));
3221 * Try to remove any empty directories up the tree. It is assumed that
3222 * pathname is a file that was removed, so start with its parent, and
3223 * work up the tree.
3225 static void
3226 rm_parent_dir_if_empty(char *pathname)
3228 char *ptr, path[PATH_MAX + 1];
3229 char *fcn = "rm_parent_dir_if_empty: ";
3231 vprint(REMOVE_MID, "%schecking %s if empty\n", fcn, pathname);
3233 (void) strcpy(path, pathname);
3236 * ascend up the dir tree, deleting all empty dirs.
3237 * Return immediately if a dir is not empty.
3239 for (;;) {
3241 if ((ptr = strrchr(path, '/')) == NULL) {
3242 return;
3245 *ptr = '\0';
3247 if (finddev_emptydir(path)) {
3248 /* directory is empty */
3249 if (s_rmdir(path) == 0) {
3250 vprint(REMOVE_MID,
3251 "%sremoving empty dir %s\n", fcn, path);
3252 } else if (errno == EEXIST) {
3253 vprint(REMOVE_MID,
3254 "%sfailed to remove dir: %s\n", fcn, path);
3255 return;
3257 } else {
3258 /* some other file is here, so return */
3259 vprint(REMOVE_MID, "%sdir not empty: %s\n", fcn, path);
3260 return;
3266 * This function and all the functions it calls below were added to
3267 * handle the unique problem with world wide names (WWN). The problem is
3268 * that if a WWN device is moved to another address on the same controller
3269 * its logical link will change, while the physical node remains the same.
3270 * The result is that two logical links will point to the same physical path
3271 * in /devices, the valid link and a stale link. This function will
3272 * find all the stale nodes, though at a significant performance cost.
3274 * Caching is used to increase performance.
3275 * A cache will be built from disk if the cache tag doesn't already exist.
3276 * The cache tag is a regular expression "dir_re", which selects a
3277 * subset of disks to search from typically something like
3278 * "dev/cXt[0-9]+d[0-9]+s[0-9]+". After the cache is built, consistency must
3279 * be maintained, so entries are added as new links are created, and removed
3280 * as old links are deleted. The whole cache is flushed if we are a daemon,
3281 * and another devfsadm process ran in between.
3283 * Once the cache is built, this function finds the cache which matches
3284 * dir_re, and then it searches all links in that cache looking for
3285 * any link whose contents match "valid_link_contents" with a corresponding link
3286 * which does not match "valid_link". Any such matches are stale and removed.
3288 * This happens outside the context of a "reparenting" so we dont need
3289 * redirection.
3291 void
3292 devfsadm_rm_stale_links(char *dir_re, char *valid_link, di_node_t node,
3293 di_minor_t minor)
3295 link_t *link;
3296 linkhead_t *head;
3297 char phy_path[PATH_MAX + 1];
3298 char *valid_link_contents;
3299 char *dev_path;
3300 char rmlink[PATH_MAX + 1];
3303 * try to use devices path
3305 if ((node == lnode) && (minor == lminor)) {
3306 valid_link_contents = lphy_path;
3307 } else {
3308 if ((dev_path = di_devfs_path(node)) == NULL) {
3309 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
3310 devfsadm_exit(1);
3311 /*NOTREACHED*/
3313 (void) strcpy(phy_path, dev_path);
3314 di_devfs_path_free(dev_path);
3316 (void) strcat(phy_path, ":");
3317 (void) strcat(phy_path, di_minor_name(minor));
3318 valid_link_contents = phy_path;
3322 * As an optimization, check to make sure the corresponding
3323 * devlink was just created before continuing.
3326 if (linknew == FALSE) {
3327 return;
3330 head = get_cached_links(dir_re);
3332 assert(head->nextlink == NULL);
3334 for (link = head->link; link != NULL; link = head->nextlink) {
3336 * See hot_cleanup() for why we do this
3338 head->nextlink = link->next;
3339 if ((strcmp(link->contents, valid_link_contents) == 0) &&
3340 (strcmp(link->devlink, valid_link) != 0)) {
3341 vprint(CHATTY_MID, "removing %s -> %s\n"
3342 "valid link is: %s -> %s\n",
3343 link->devlink, link->contents,
3344 valid_link, valid_link_contents);
3346 * Use a copy of the cached link name as the
3347 * cache entry will go away during link removal
3349 (void) snprintf(rmlink, sizeof (rmlink), "%s",
3350 link->devlink);
3351 devfsadm_rm_link(rmlink);
3357 * Return previously created cache, or create cache.
3359 static linkhead_t *
3360 get_cached_links(char *dir_re)
3362 recurse_dev_t rd;
3363 linkhead_t *linkhead;
3364 int n;
3366 vprint(BUILDCACHE_MID, "get_cached_links: %s\n", dir_re);
3368 for (linkhead = headlinkhead; linkhead != NULL;
3369 linkhead = linkhead->nexthead) {
3370 if (strcmp(linkhead->dir_re, dir_re) == 0) {
3371 return (linkhead);
3376 * This tag is not in cache, so add it, along with all its
3377 * matching /dev entries. This is the only time we go to disk.
3379 linkhead = s_malloc(sizeof (linkhead_t));
3380 linkhead->nexthead = headlinkhead;
3381 headlinkhead = linkhead;
3382 linkhead->dir_re = s_strdup(dir_re);
3384 if ((n = regcomp(&(linkhead->dir_re_compiled), dir_re,
3385 REG_EXTENDED)) != 0) {
3386 err_print(REGCOMP_FAILED, dir_re, n);
3389 linkhead->nextlink = NULL;
3390 linkhead->link = NULL;
3392 rd.fcn = build_devlink_list;
3393 rd.data = (void *)linkhead;
3395 vprint(BUILDCACHE_MID, "get_cached_links: calling recurse_dev_re\n");
3397 /* call build_devlink_list for each directory in the dir_re RE */
3398 if (dir_re[0] == '/') {
3399 recurse_dev_re("/", &dir_re[1], &rd);
3400 } else {
3401 recurse_dev_re(dev_dir, dir_re, &rd);
3404 return (linkhead);
3407 static void
3408 build_devlink_list(char *devlink, void *data)
3410 char *fcn = "build_devlink_list: ";
3411 char *ptr;
3412 char *r_contents;
3413 char *r_devlink;
3414 char contents[PATH_MAX + 1];
3415 char newlink[PATH_MAX + 1];
3416 char stage_link[PATH_MAX + 1];
3417 int linksize;
3418 linkhead_t *linkhead = (linkhead_t *)data;
3419 link_t *link;
3420 int i = 0;
3422 vprint(BUILDCACHE_MID, "%scheck_link: %s\n", fcn, devlink);
3424 (void) strcpy(newlink, devlink);
3426 do {
3428 * None of the consumers of this function need redirection
3429 * so this readlink gets the "current" contents
3431 linksize = readlink(newlink, contents, PATH_MAX);
3432 if (linksize <= 0) {
3434 * The first pass through the do loop we may readlink()
3435 * non-symlink files(EINVAL) from false regexec matches.
3436 * Suppress error messages in those cases or if the link
3437 * content is the empty string.
3439 if (linksize < 0 && (i || errno != EINVAL))
3440 err_print(READLINK_FAILED, "build_devlink_list",
3441 newlink, strerror(errno));
3442 return;
3444 contents[linksize] = '\0';
3445 i = 1;
3447 if (is_minor_node(contents, &r_contents) == DEVFSADM_FALSE) {
3449 * assume that link contents is really a pointer to
3450 * another link, so recurse and read its link contents.
3452 * some link contents are absolute:
3453 * /dev/audio -> /dev/sound/0
3455 if (strncmp(contents, DEV "/",
3456 strlen(DEV) + strlen("/")) != 0) {
3458 if ((ptr = strrchr(newlink, '/')) == NULL) {
3459 vprint(REMOVE_MID, "%s%s -> %s invalid "
3460 "link. missing '/'\n", fcn,
3461 newlink, contents);
3462 return;
3464 *ptr = '\0';
3465 (void) strcpy(stage_link, newlink);
3466 *ptr = '/';
3467 (void) strcat(stage_link, "/");
3468 (void) strcat(stage_link, contents);
3469 (void) strcpy(newlink, stage_link);
3470 } else {
3471 (void) strcpy(newlink, dev_dir);
3472 (void) strcat(newlink, "/");
3473 (void) strcat(newlink,
3474 &contents[strlen(DEV) + strlen("/")]);
3477 } else {
3478 newlink[0] = '\0';
3480 } while (newlink[0] != '\0');
3482 if (strncmp(devlink, dev_dir, strlen(dev_dir)) != 0) {
3483 vprint(BUILDCACHE_MID, "%sinvalid link: %s\n", fcn, devlink);
3484 return;
3487 r_devlink = devlink + strlen(dev_dir);
3489 if (r_devlink[0] != '/')
3490 return;
3492 link = s_malloc(sizeof (link_t));
3494 /* don't store the '/' after rootdir/dev */
3495 r_devlink += 1;
3497 vprint(BUILDCACHE_MID, "%scaching link: %s\n", fcn, r_devlink);
3498 link->devlink = s_strdup(r_devlink);
3500 link->contents = s_strdup(r_contents);
3502 link->next = linkhead->link;
3503 linkhead->link = link;
3507 * to be consistent, devlink must not begin with / and must be
3508 * relative to /dev/, whereas physpath must contain / and be
3509 * relative to /devices.
3511 static void
3512 add_link_to_cache(char *devlink, char *physpath)
3514 linkhead_t *linkhead;
3515 link_t *link;
3516 int added = 0;
3518 if (file_mods == FALSE) {
3519 return;
3522 vprint(CACHE_MID, "add_link_to_cache: %s -> %s ",
3523 devlink, physpath);
3525 for (linkhead = headlinkhead; linkhead != NULL;
3526 linkhead = linkhead->nexthead) {
3527 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3528 == 0) {
3529 added++;
3530 link = s_malloc(sizeof (link_t));
3531 link->devlink = s_strdup(devlink);
3532 link->contents = s_strdup(physpath);
3533 link->next = linkhead->link;
3534 linkhead->link = link;
3538 vprint(CACHE_MID,
3539 " %d %s\n", added, added == 0 ? "NOT ADDED" : "ADDED");
3543 * Remove devlink from cache. Devlink must be relative to /dev/ and not start
3544 * with /.
3546 static void
3547 rm_link_from_cache(char *devlink)
3549 linkhead_t *linkhead;
3550 link_t **linkp;
3551 link_t *save;
3553 vprint(CACHE_MID, "rm_link_from_cache enter: %s\n", devlink);
3555 for (linkhead = headlinkhead; linkhead != NULL;
3556 linkhead = linkhead->nexthead) {
3557 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3558 == 0) {
3560 for (linkp = &(linkhead->link); *linkp != NULL; ) {
3561 if ((strcmp((*linkp)->devlink, devlink) == 0)) {
3562 save = *linkp;
3563 *linkp = (*linkp)->next;
3565 * We are removing our caller's
3566 * "next" link. Update the nextlink
3567 * field in the head so that our
3568 * callers accesses the next valid
3569 * link
3571 if (linkhead->nextlink == save)
3572 linkhead->nextlink = *linkp;
3573 free(save->devlink);
3574 free(save->contents);
3575 free(save);
3576 vprint(CACHE_MID, " %s FREED FROM "
3577 "CACHE\n", devlink);
3578 } else {
3579 linkp = &((*linkp)->next);
3586 static void
3587 rm_all_links_from_cache()
3589 linkhead_t *linkhead;
3590 linkhead_t *nextlinkhead;
3591 link_t *link;
3592 link_t *nextlink;
3594 vprint(CACHE_MID, "rm_all_links_from_cache\n");
3596 for (linkhead = headlinkhead; linkhead != NULL;
3597 linkhead = nextlinkhead) {
3599 nextlinkhead = linkhead->nexthead;
3600 assert(linkhead->nextlink == NULL);
3601 for (link = linkhead->link; link != NULL; link = nextlink) {
3602 nextlink = link->next;
3603 free(link->devlink);
3604 free(link->contents);
3605 free(link);
3607 regfree(&(linkhead->dir_re_compiled));
3608 free(linkhead->dir_re);
3609 free(linkhead);
3611 headlinkhead = NULL;
3615 * Called when the kernel has modified the incore path_to_inst data. This
3616 * function will schedule a flush of the data to the filesystem.
3618 static void
3619 devfs_instance_mod(void)
3621 char *fcn = "devfs_instance_mod: ";
3622 vprint(PATH2INST_MID, "%senter\n", fcn);
3624 /* signal instance thread */
3625 (void) mutex_lock(&count_lock);
3626 inst_count++;
3627 (void) cond_signal(&cv);
3628 (void) mutex_unlock(&count_lock);
3631 static void
3632 instance_flush_thread(void)
3634 int i;
3635 int idle;
3637 for (;;) {
3639 (void) mutex_lock(&count_lock);
3640 while (inst_count == 0) {
3641 (void) cond_wait(&cv, &count_lock);
3643 inst_count = 0;
3645 vprint(PATH2INST_MID, "signaled to flush path_to_inst."
3646 " Enter delay loop\n");
3648 * Wait MAX_IDLE_DELAY seconds after getting the last flush
3649 * path_to_inst event before invoking a flush, but never wait
3650 * more than MAX_DELAY seconds after getting the first event.
3652 for (idle = 0, i = 0; i < MAX_DELAY; i++) {
3654 (void) mutex_unlock(&count_lock);
3655 (void) sleep(1);
3656 (void) mutex_lock(&count_lock);
3658 /* shorten the delay if we are idle */
3659 if (inst_count == 0) {
3660 idle++;
3661 if (idle > MAX_IDLE_DELAY) {
3662 break;
3664 } else {
3665 inst_count = idle = 0;
3669 (void) mutex_unlock(&count_lock);
3671 flush_path_to_inst();
3676 * Helper function for flush_path_to_inst() below; this routine calls the
3677 * inst_sync syscall to flush the path_to_inst database to the given file.
3679 static int
3680 do_inst_sync(char *filename, char *instfilename)
3682 void (*sigsaved)(int);
3683 int err = 0, flags = INST_SYNC_IF_REQUIRED;
3684 struct stat sb;
3686 if (stat(instfilename, &sb) == -1 && errno == ENOENT)
3687 flags = INST_SYNC_ALWAYS;
3689 vprint(INSTSYNC_MID, "do_inst_sync: about to flush %s\n", filename);
3690 sigsaved = sigset(SIGSYS, SIG_IGN);
3691 if (inst_sync(filename, flags) == -1)
3692 err = errno;
3693 (void) sigset(SIGSYS, sigsaved);
3695 switch (err) {
3696 case 0:
3697 return (DEVFSADM_SUCCESS);
3698 case EALREADY: /* no-op, path_to_inst already up to date */
3699 return (EALREADY);
3700 case ENOSYS:
3701 err_print(CANT_LOAD_SYSCALL);
3702 break;
3703 case EPERM:
3704 err_print(SUPER_TO_SYNC);
3705 break;
3706 default:
3707 err_print(INSTSYNC_FAILED, filename, strerror(err));
3708 break;
3710 return (DEVFSADM_FAILURE);
3714 * Flush the kernel's path_to_inst database to /etc/path_to_inst. To do so
3715 * safely, the database is flushed to a temporary file, then moved into place.
3717 * The following files are used during this process:
3718 * /etc/path_to_inst: The path_to_inst file
3719 * /etc/path_to_inst.<pid>: Contains data flushed from the kernel
3720 * /etc/path_to_inst.old: The backup file
3721 * /etc/path_to_inst.old.<pid>: Temp file for creating backup
3724 static void
3725 flush_path_to_inst(void)
3727 char *new_inst_file = NULL;
3728 char *old_inst_file = NULL;
3729 char *old_inst_file_npid = NULL;
3730 FILE *inst_file_fp = NULL;
3731 FILE *old_inst_file_fp = NULL;
3732 struct stat sb;
3733 int err = 0;
3734 int c;
3735 int inst_strlen;
3737 vprint(PATH2INST_MID, "flush_path_to_inst: %s\n",
3738 (flush_path_to_inst_enable == TRUE) ? "ENABLED" : "DISABLED");
3740 if (flush_path_to_inst_enable == FALSE) {
3741 return;
3744 inst_strlen = strlen(inst_file);
3745 new_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 2);
3746 old_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 6);
3747 old_inst_file_npid = s_malloc(inst_strlen +
3748 sizeof (INSTANCE_FILE_SUFFIX));
3750 (void) snprintf(new_inst_file, inst_strlen + PID_STR_LEN + 2,
3751 "%s.%ld", inst_file, getpid());
3753 if (stat(new_inst_file, &sb) == 0) {
3754 s_unlink(new_inst_file);
3757 err = do_inst_sync(new_inst_file, inst_file);
3758 if (err != DEVFSADM_SUCCESS) {
3759 goto out;
3760 /*NOTREACHED*/
3764 * Now we deal with the somewhat tricky updating and renaming
3765 * of this critical piece of kernel state.
3769 * Copy the current instance file into a temporary file.
3770 * Then rename the temporary file into the backup (.old)
3771 * file and rename the newly flushed kernel data into
3772 * the instance file.
3773 * Of course if 'inst_file' doesn't exist, there's much
3774 * less for us to do .. tee hee.
3776 if ((inst_file_fp = fopen(inst_file, "r")) == NULL) {
3778 * No such file. Rename the new onto the old
3780 if ((err = rename(new_inst_file, inst_file)) != 0)
3781 err_print(RENAME_FAILED, inst_file, strerror(errno));
3782 goto out;
3783 /*NOTREACHED*/
3786 (void) snprintf(old_inst_file, inst_strlen + PID_STR_LEN + 6,
3787 "%s.old.%ld", inst_file, getpid());
3789 if (stat(old_inst_file, &sb) == 0) {
3790 s_unlink(old_inst_file);
3793 if ((old_inst_file_fp = fopen(old_inst_file, "w")) == NULL) {
3795 * Can't open the 'old_inst_file' file for writing.
3796 * This is somewhat strange given that the syscall
3797 * just succeeded to write a file out.. hmm.. maybe
3798 * the fs just filled up or something nasty.
3800 * Anyway, abort what we've done so far.
3802 err_print(CANT_UPDATE, old_inst_file);
3803 err = DEVFSADM_FAILURE;
3804 goto out;
3805 /*NOTREACHED*/
3809 * Copy current instance file into the temporary file
3811 err = 0;
3812 while ((c = getc(inst_file_fp)) != EOF) {
3813 if ((err = putc(c, old_inst_file_fp)) == EOF) {
3814 break;
3818 if (fclose(old_inst_file_fp) == EOF || err == EOF) {
3819 vprint(INFO_MID, CANT_UPDATE, old_inst_file);
3820 err = DEVFSADM_FAILURE;
3821 goto out;
3822 /* NOTREACHED */
3826 * Set permissions to be the same on the backup as
3827 * /etc/path_to_inst.
3829 (void) chmod(old_inst_file, 0444);
3832 * So far, everything we've done is more or less reversible.
3833 * But now we're going to commit ourselves.
3836 (void) snprintf(old_inst_file_npid,
3837 inst_strlen + sizeof (INSTANCE_FILE_SUFFIX),
3838 "%s%s", inst_file, INSTANCE_FILE_SUFFIX);
3840 if ((err = rename(old_inst_file, old_inst_file_npid)) != 0) {
3841 err_print(RENAME_FAILED, old_inst_file_npid,
3842 strerror(errno));
3843 } else if ((err = rename(new_inst_file, inst_file)) != 0) {
3844 err_print(RENAME_FAILED, inst_file, strerror(errno));
3847 out:
3848 if (inst_file_fp != NULL) {
3849 if (fclose(inst_file_fp) == EOF) {
3850 err_print(FCLOSE_FAILED, inst_file, strerror(errno));
3854 if (stat(new_inst_file, &sb) == 0) {
3855 s_unlink(new_inst_file);
3857 free(new_inst_file);
3859 if (stat(old_inst_file, &sb) == 0) {
3860 s_unlink(old_inst_file);
3862 free(old_inst_file);
3864 free(old_inst_file_npid);
3866 if (err != 0 && err != EALREADY) {
3867 err_print(FAILED_TO_UPDATE, inst_file);
3872 * detach from tty. For daemon mode.
3874 void
3875 detachfromtty()
3877 (void) setsid();
3878 if (DEVFSADM_DEBUG_ON == TRUE) {
3879 return;
3882 (void) close(0);
3883 (void) close(1);
3884 (void) close(2);
3885 (void) open("/dev/null", O_RDWR, 0);
3886 (void) dup(0);
3887 (void) dup(0);
3888 openlog(DEVFSADMD, LOG_PID, LOG_DAEMON);
3889 (void) setlogmask(LOG_UPTO(LOG_INFO));
3890 logflag = TRUE;
3894 * Use an advisory lock to synchronize updates to /dev. If the lock is
3895 * held by another process, block in the fcntl() system call until that
3896 * process drops the lock or exits. The lock file itself is
3897 * DEV_LOCK_FILE. The process id of the current and last process owning
3898 * the lock is kept in the lock file. After acquiring the lock, read the
3899 * process id and return it. It is the process ID which last owned the
3900 * lock, and will be used to determine if caches need to be flushed.
3902 * NOTE: if the devlink database is held open by the caller, it may
3903 * be closed by this routine. This is to enforce the following lock ordering:
3904 * 1) /dev lock 2) database open
3906 pid_t
3907 enter_dev_lock()
3909 struct flock lock;
3910 int n;
3911 pid_t pid;
3912 pid_t last_owner_pid;
3914 if (file_mods == FALSE) {
3915 return (0);
3918 (void) snprintf(dev_lockfile, sizeof (dev_lockfile),
3919 "%s/%s", etc_dev_dir, DEV_LOCK_FILE);
3921 vprint(LOCK_MID, "enter_dev_lock: lock file %s\n", dev_lockfile);
3923 dev_lock_fd = open(dev_lockfile, O_CREAT|O_RDWR, 0644);
3924 if (dev_lock_fd < 0) {
3925 err_print(OPEN_FAILED, dev_lockfile, strerror(errno));
3926 devfsadm_exit(1);
3927 /*NOTREACHED*/
3930 lock.l_type = F_WRLCK;
3931 lock.l_whence = SEEK_SET;
3932 lock.l_start = 0;
3933 lock.l_len = 0;
3935 /* try for the lock, but don't wait */
3936 if (fcntl(dev_lock_fd, F_SETLK, &lock) == -1) {
3937 if ((errno == EACCES) || (errno == EAGAIN)) {
3938 pid = 0;
3939 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3940 vprint(LOCK_MID, "waiting for PID %d to complete\n",
3941 (int)pid);
3942 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3943 err_print(LSEEK_FAILED, dev_lockfile,
3944 strerror(errno));
3945 devfsadm_exit(1);
3946 /*NOTREACHED*/
3949 * wait for the dev lock. If we have the database open,
3950 * close it first - the order of lock acquisition should
3951 * always be: 1) dev_lock 2) database
3952 * This is to prevent deadlocks with any locks the
3953 * database code may hold.
3955 (void) di_devlink_close(&devlink_cache, 0);
3957 /* send any sysevents that were queued up. */
3958 process_syseventq();
3960 if (fcntl(dev_lock_fd, F_SETLKW, &lock) == -1) {
3961 err_print(LOCK_FAILED, dev_lockfile,
3962 strerror(errno));
3963 devfsadm_exit(1);
3964 /*NOTREACHED*/
3969 hold_dev_lock = TRUE;
3970 pid = 0;
3971 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3972 if (n == sizeof (pid_t) && pid == getpid()) {
3973 return (pid);
3976 last_owner_pid = pid;
3978 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3979 err_print(LSEEK_FAILED, dev_lockfile, strerror(errno));
3980 devfsadm_exit(1);
3981 /*NOTREACHED*/
3983 pid = getpid();
3984 n = write(dev_lock_fd, &pid, sizeof (pid_t));
3985 if (n != sizeof (pid_t)) {
3986 err_print(WRITE_FAILED, dev_lockfile, strerror(errno));
3987 devfsadm_exit(1);
3988 /*NOTREACHED*/
3991 return (last_owner_pid);
3995 * Drop the advisory /dev lock, close lock file. Close and re-open the
3996 * file every time so to ensure a resync if for some reason the lock file
3997 * gets removed.
3999 void
4000 exit_dev_lock(int exiting)
4002 struct flock unlock;
4004 if (hold_dev_lock == FALSE) {
4005 return;
4008 vprint(LOCK_MID, "exit_dev_lock: lock file %s, exiting = %d\n",
4009 dev_lockfile, exiting);
4011 unlock.l_type = F_UNLCK;
4012 unlock.l_whence = SEEK_SET;
4013 unlock.l_start = 0;
4014 unlock.l_len = 0;
4016 if (fcntl(dev_lock_fd, F_SETLK, &unlock) == -1) {
4017 err_print(UNLOCK_FAILED, dev_lockfile, strerror(errno));
4020 hold_dev_lock = FALSE;
4022 if (close(dev_lock_fd) == -1) {
4023 err_print(CLOSE_FAILED, dev_lockfile, strerror(errno));
4024 if (!exiting)
4025 devfsadm_exit(1);
4026 /*NOTREACHED*/
4032 * Use an advisory lock to ensure that only one daemon process is active
4033 * in the system at any point in time. If the lock is held by another
4034 * process, do not block but return the pid owner of the lock to the
4035 * caller immediately. The lock is cleared if the holding daemon process
4036 * exits for any reason even if the lock file remains, so the daemon can
4037 * be restarted if necessary. The lock file is DAEMON_LOCK_FILE.
4039 pid_t
4040 enter_daemon_lock(void)
4042 struct flock lock;
4044 (void) snprintf(daemon_lockfile, sizeof (daemon_lockfile),
4045 "%s/%s", etc_dev_dir, DAEMON_LOCK_FILE);
4047 vprint(LOCK_MID, "enter_daemon_lock: lock file %s\n", daemon_lockfile);
4049 daemon_lock_fd = open(daemon_lockfile, O_CREAT|O_RDWR, 0644);
4050 if (daemon_lock_fd < 0) {
4051 err_print(OPEN_FAILED, daemon_lockfile, strerror(errno));
4052 devfsadm_exit(1);
4053 /*NOTREACHED*/
4056 lock.l_type = F_WRLCK;
4057 lock.l_whence = SEEK_SET;
4058 lock.l_start = 0;
4059 lock.l_len = 0;
4061 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
4063 if (errno == EAGAIN || errno == EDEADLK) {
4064 if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) {
4065 err_print(LOCK_FAILED, daemon_lockfile,
4066 strerror(errno));
4067 devfsadm_exit(1);
4068 /*NOTREACHED*/
4070 return (lock.l_pid);
4073 hold_daemon_lock = TRUE;
4074 return (getpid());
4078 * Drop the advisory daemon lock, close lock file
4080 void
4081 exit_daemon_lock(int exiting)
4083 struct flock lock;
4085 if (hold_daemon_lock == FALSE) {
4086 return;
4089 vprint(LOCK_MID, "exit_daemon_lock: lock file %s, exiting = %d\n",
4090 daemon_lockfile, exiting);
4092 lock.l_type = F_UNLCK;
4093 lock.l_whence = SEEK_SET;
4094 lock.l_start = 0;
4095 lock.l_len = 0;
4097 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
4098 err_print(UNLOCK_FAILED, daemon_lockfile, strerror(errno));
4101 if (close(daemon_lock_fd) == -1) {
4102 err_print(CLOSE_FAILED, daemon_lockfile, strerror(errno));
4103 if (!exiting)
4104 devfsadm_exit(1);
4105 /*NOTREACHED*/
4110 * Called to removed danging nodes in two different modes: RM_PRE, RM_POST.
4111 * RM_PRE mode is called before processing the entire devinfo tree, and RM_POST
4112 * is called after processing the entire devinfo tree.
4114 static void
4115 pre_and_post_cleanup(int flags)
4117 remove_list_t *rm;
4118 recurse_dev_t rd;
4119 cleanup_data_t cleanup_data;
4120 char *fcn = "pre_and_post_cleanup: ";
4122 if (build_dev == FALSE)
4123 return;
4125 vprint(CHATTY_MID, "attempting %s-cleanup\n",
4126 flags == RM_PRE ? "pre" : "post");
4127 vprint(REMOVE_MID, "%sflags = %d\n", fcn, flags);
4130 * the generic function recurse_dev_re is shared among different
4131 * functions, so set the method and data that it should use for
4132 * matches.
4134 rd.fcn = matching_dev;
4135 rd.data = (void *)&cleanup_data;
4136 cleanup_data.flags = flags;
4138 (void) mutex_lock(&nfp_mutex);
4139 nfphash_create();
4141 for (rm = remove_head; rm != NULL; rm = rm->next) {
4142 if ((flags & rm->remove->flags) == flags) {
4143 cleanup_data.rm = rm;
4145 * If reached this point, RM_PRE or RM_POST cleanup is
4146 * desired. clean_ok() decides whether to clean
4147 * under the given circumstances.
4149 vprint(REMOVE_MID, "%scleanup: PRE or POST\n", fcn);
4150 if (clean_ok(rm->remove) == DEVFSADM_SUCCESS) {
4151 vprint(REMOVE_MID, "cleanup: cleanup OK\n");
4152 recurse_dev_re(dev_dir,
4153 rm->remove->dev_dirs_re, &rd);
4157 nfphash_destroy();
4158 (void) mutex_unlock(&nfp_mutex);
4162 * clean_ok() determines whether cleanup should be done according
4163 * to the following matrix:
4165 * command line arguments RM_PRE RM_POST RM_PRE && RM_POST &&
4166 * RM_ALWAYS RM_ALWAYS
4167 * ---------------------- ------ ----- --------- ----------
4169 * <neither -c nor -C> - - pre-clean post-clean
4171 * -C pre-clean post-clean pre-clean post-clean
4173 * -C -c class pre-clean post-clean pre-clean post-clean
4174 * if class if class if class if class
4175 * matches matches matches matches
4177 * -c class - - pre-clean post-clean
4178 * if class if class
4179 * matches matches
4182 static int
4183 clean_ok(devfsadm_remove_V1_t *remove)
4185 int i;
4187 if (single_drv == TRUE) {
4188 /* no cleanup at all when using -i option */
4189 return (DEVFSADM_FAILURE);
4193 * no cleanup if drivers are not loaded. We make an exception
4194 * for the "disks" program however, since disks has a public
4195 * cleanup flag (-C) and disk drivers are usually never
4196 * unloaded.
4198 if (load_attach_drv == FALSE && strcmp(prog, DISKS) != 0) {
4199 return (DEVFSADM_FAILURE);
4202 /* if the cleanup flag was not specified, return false */
4203 if ((cleanup == FALSE) && ((remove->flags & RM_ALWAYS) == 0)) {
4204 return (DEVFSADM_FAILURE);
4207 if (num_classes == 0) {
4208 return (DEVFSADM_SUCCESS);
4212 * if reached this point, check to see if the class in the given
4213 * remove structure matches a class given on the command line
4216 for (i = 0; i < num_classes; i++) {
4217 if (strcmp(remove->device_class, classes[i]) == 0) {
4218 return (DEVFSADM_SUCCESS);
4222 return (DEVFSADM_FAILURE);
4226 * Called to remove dangling nodes after receiving a hotplug event
4227 * containing the physical node pathname to be removed.
4229 void
4230 hot_cleanup(char *node_path, char *minor_name, char *ev_subclass,
4231 char *driver_name, int instance)
4233 link_t *link;
4234 linkhead_t *head;
4235 remove_list_t *rm;
4236 char *fcn = "hot_cleanup: ";
4237 char path[PATH_MAX + 1];
4238 int path_len;
4239 char rmlink[PATH_MAX + 1];
4240 nvlist_t *nvl = NULL;
4241 int skip;
4242 int ret;
4245 * dev links can go away as part of hot cleanup.
4246 * So first build event attributes in order capture dev links.
4248 if (ev_subclass != NULL)
4249 nvl = build_event_attributes(EC_DEV_REMOVE, ev_subclass,
4250 node_path, DI_NODE_NIL, driver_name, instance, minor_name);
4252 (void) strcpy(path, node_path);
4253 (void) strcat(path, ":");
4254 (void) strcat(path, minor_name == NULL ? "" : minor_name);
4256 path_len = strlen(path);
4258 vprint(REMOVE_MID, "%spath=%s\n", fcn, path);
4260 (void) mutex_lock(&nfp_mutex);
4261 nfphash_create();
4263 for (rm = remove_head; rm != NULL; rm = rm->next) {
4264 if ((RM_HOT & rm->remove->flags) == RM_HOT) {
4265 head = get_cached_links(rm->remove->dev_dirs_re);
4266 assert(head->nextlink == NULL);
4267 for (link = head->link;
4268 link != NULL; link = head->nextlink) {
4270 * The remove callback below may remove
4271 * the current and/or any or all of the
4272 * subsequent links in the list.
4273 * Save the next link in the head. If
4274 * the callback removes the next link
4275 * the saved pointer in the head will be
4276 * updated by the callback to point at
4277 * the next valid link.
4279 head->nextlink = link->next;
4282 * if devlink is in no-further-process hash,
4283 * skip its remove
4285 if (nfphash_lookup(link->devlink) != NULL)
4286 continue;
4288 if (minor_name)
4289 skip = strcmp(link->contents, path);
4290 else
4291 skip = strncmp(link->contents, path,
4292 path_len);
4293 if (skip ||
4294 (call_minor_init(rm->modptr) ==
4295 DEVFSADM_FAILURE))
4296 continue;
4298 vprint(REMOVE_MID,
4299 "%sremoving %s -> %s\n", fcn,
4300 link->devlink, link->contents);
4302 * Use a copy of the cached link name
4303 * as the cache entry will go away
4304 * during link removal
4306 (void) snprintf(rmlink, sizeof (rmlink),
4307 "%s", link->devlink);
4308 if (rm->remove->flags & RM_NOINTERPOSE) {
4309 ((void (*)(char *))
4310 (rm->remove->callback_fcn))(rmlink);
4311 } else {
4312 ret = ((int (*)(char *))
4313 (rm->remove->callback_fcn))(rmlink);
4314 if (ret == DEVFSADM_TERMINATE)
4315 nfphash_insert(rmlink);
4321 nfphash_destroy();
4322 (void) mutex_unlock(&nfp_mutex);
4324 /* update device allocation database */
4325 if (system_labeled) {
4326 int devtype = 0;
4328 if (strstr(path, DA_SOUND_NAME))
4329 devtype = DA_AUDIO;
4330 else if (strstr(path, "storage"))
4331 devtype = DA_RMDISK;
4332 else if (strstr(path, "disk"))
4333 devtype = DA_RMDISK;
4334 else if (strstr(path, "floppy"))
4335 /* TODO: detect usb cds and floppies at insert time */
4336 devtype = DA_RMDISK;
4337 else
4338 goto out;
4340 (void) _update_devalloc_db(&devlist, devtype, DA_REMOVE,
4341 node_path, root_dir);
4344 out:
4345 /* now log an event */
4346 if (nvl) {
4347 log_event(EC_DEV_REMOVE, ev_subclass, nvl);
4348 free(nvl);
4353 * Open the dir current_dir. For every file which matches the first dir
4354 * component of path_re, recurse. If there are no more *dir* path
4355 * components left in path_re (ie no more /), then call function rd->fcn.
4357 static void
4358 recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
4360 regex_t re1;
4361 char *slash;
4362 char new_path[PATH_MAX + 1];
4363 char *anchored_path_re;
4364 size_t len;
4365 finddevhdl_t fhandle;
4366 const char *fp;
4368 vprint(RECURSEDEV_MID, "recurse_dev_re: curr = %s path=%s\n",
4369 current_dir, path_re);
4371 if (finddev_readdir(current_dir, &fhandle) != 0)
4372 return;
4374 len = strlen(path_re);
4375 if ((slash = strchr(path_re, '/')) != NULL) {
4376 len = (slash - path_re);
4379 anchored_path_re = s_malloc(len + 3);
4380 (void) sprintf(anchored_path_re, "^%.*s$", len, path_re);
4382 if (regcomp(&re1, anchored_path_re, REG_EXTENDED) != 0) {
4383 free(anchored_path_re);
4384 goto out;
4387 free(anchored_path_re);
4389 while ((fp = finddev_next(fhandle)) != NULL) {
4391 if (regexec(&re1, fp, 0, NULL, 0) == 0) {
4392 /* match */
4393 (void) strcpy(new_path, current_dir);
4394 (void) strcat(new_path, "/");
4395 (void) strcat(new_path, fp);
4397 vprint(RECURSEDEV_MID, "recurse_dev_re: match, new "
4398 "path = %s\n", new_path);
4400 if (slash != NULL) {
4401 recurse_dev_re(new_path, slash + 1, rd);
4402 } else {
4403 /* reached the leaf component of path_re */
4404 vprint(RECURSEDEV_MID,
4405 "recurse_dev_re: calling fcn\n");
4406 (*(rd->fcn))(new_path, rd->data);
4411 regfree(&re1);
4413 out:
4414 finddev_close(fhandle);
4418 * Found a devpath which matches a RE in the remove structure.
4419 * Now check to see if it is dangling.
4421 static void
4422 matching_dev(char *devpath, void *data)
4424 cleanup_data_t *cleanup_data = data;
4425 int norm_len = strlen(dev_dir) + strlen("/");
4426 int ret;
4427 char *fcn = "matching_dev: ";
4429 vprint(RECURSEDEV_MID, "%sexamining devpath = '%s'\n", fcn,
4430 devpath);
4433 * If the link is in the no-further-process hash
4434 * don't do any remove operation on it.
4436 if (nfphash_lookup(devpath + norm_len) != NULL)
4437 return;
4440 * Dangling check will work whether "alias" or "current"
4441 * so no need to redirect.
4443 if (resolve_link(devpath, NULL, NULL, NULL, 1) == TRUE) {
4444 if (call_minor_init(cleanup_data->rm->modptr) ==
4445 DEVFSADM_FAILURE) {
4446 return;
4449 devpath += norm_len;
4451 vprint(RECURSEDEV_MID, "%scalling callback %s\n", fcn, devpath);
4452 if (cleanup_data->rm->remove->flags & RM_NOINTERPOSE)
4453 ((void (*)(char *))
4454 (cleanup_data->rm->remove->callback_fcn))(devpath);
4455 else {
4456 ret = ((int (*)(char *))
4457 (cleanup_data->rm->remove->callback_fcn))(devpath);
4458 if (ret == DEVFSADM_TERMINATE) {
4460 * We want no further remove processing for
4461 * this link. Add it to the nfp_hash;
4463 nfphash_insert(devpath);
4470 devfsadm_read_link(di_node_t anynode, char *link, char **devfs_path)
4472 char devlink[PATH_MAX];
4473 char *path;
4475 *devfs_path = NULL;
4477 /* prepend link with dev_dir contents */
4478 (void) strcpy(devlink, dev_dir);
4479 (void) strcat(devlink, "/");
4480 (void) strcat(devlink, link);
4482 /* We *don't* want a stat of the /devices node */
4483 path = NULL;
4484 (void) resolve_link(devlink, NULL, NULL, &path, 0);
4485 if (path != NULL) {
4486 /* redirect if alias to current */
4487 *devfs_path = di_alias2curr(anynode, path);
4488 free(path);
4490 return (*devfs_path ? DEVFSADM_SUCCESS : DEVFSADM_FAILURE);
4494 devfsadm_link_valid(di_node_t anynode, char *link)
4496 struct stat sb;
4497 char devlink[PATH_MAX + 1], *contents, *raw_contents;
4498 int rv, type;
4499 int instance = 0;
4501 /* prepend link with dev_dir contents */
4502 (void) strcpy(devlink, dev_dir);
4503 (void) strcat(devlink, "/");
4504 (void) strcat(devlink, link);
4506 if (!device_exists(devlink) || lstat(devlink, &sb) != 0) {
4507 return (DEVFSADM_FALSE);
4510 raw_contents = NULL;
4511 type = 0;
4512 if (resolve_link(devlink, &raw_contents, &type, NULL, 1) == TRUE) {
4513 rv = DEVFSADM_FALSE;
4514 } else {
4515 rv = DEVFSADM_TRUE;
4519 * resolve alias paths for primary links
4521 contents = raw_contents;
4522 if (type == DI_PRIMARY_LINK) {
4523 contents = di_alias2curr(anynode, raw_contents);
4524 free(raw_contents);
4528 * The link exists. Add it to the database
4530 (void) di_devlink_add_link(devlink_cache, link, contents, type);
4531 if (system_labeled && (rv == DEVFSADM_TRUE) &&
4532 strstr(devlink, DA_AUDIO_NAME) && contents) {
4533 (void) sscanf(contents, "%*[a-z]%d", &instance);
4534 (void) da_add_list(&devlist, devlink, instance,
4535 DA_ADD|DA_AUDIO);
4536 _update_devalloc_db(&devlist, 0, DA_ADD, NULL, root_dir);
4538 free(contents);
4540 return (rv);
4544 * devpath: Absolute path to /dev link
4545 * content_p: Returns malloced string (link content)
4546 * type_p: Returns link type: primary or secondary
4547 * devfs_path: Returns malloced string: /devices path w/out "/devices"
4548 * dangle: if set, check if link is dangling
4549 * Returns:
4550 * TRUE if dangling
4551 * FALSE if not or if caller doesn't care
4552 * Caller is assumed to have initialized pointer contents to NULL
4555 static int
4556 resolve_link(char *devpath, char **content_p, int *type_p, char **devfs_path,
4557 int dangle)
4559 char contents[PATH_MAX + 1];
4560 char stage_link[PATH_MAX + 1];
4561 char *fcn = "resolve_link: ";
4562 char *ptr;
4563 int linksize;
4564 int rv = TRUE;
4565 struct stat sb;
4568 * This routine will return the "raw" contents. It is upto the
4569 * the caller to redirect "alias" to "current" (or vice versa)
4571 linksize = readlink(devpath, contents, PATH_MAX);
4573 if (linksize <= 0) {
4574 return (FALSE);
4575 } else {
4576 contents[linksize] = '\0';
4578 vprint(REMOVE_MID, "%s %s -> %s\n", fcn, devpath, contents);
4580 if (content_p) {
4581 *content_p = s_strdup(contents);
4585 * Check to see if this is a link pointing to another link in /dev. The
4586 * cheap way to do this is to look for a lack of ../devices/.
4589 if (is_minor_node(contents, &ptr) == DEVFSADM_FALSE) {
4591 if (type_p) {
4592 *type_p = DI_SECONDARY_LINK;
4596 * assume that linkcontents is really a pointer to another
4597 * link, and if so recurse and read its link contents.
4599 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
4600 (void) strcpy(stage_link, dev_dir);
4601 (void) strcat(stage_link, "/");
4602 (void) strcpy(stage_link,
4603 &contents[strlen(DEV) + strlen("/")]);
4604 } else {
4605 if ((ptr = strrchr(devpath, '/')) == NULL) {
4606 vprint(REMOVE_MID, "%s%s -> %s invalid link. "
4607 "missing '/'\n", fcn, devpath, contents);
4608 return (TRUE);
4610 *ptr = '\0';
4611 (void) strcpy(stage_link, devpath);
4612 *ptr = '/';
4613 (void) strcat(stage_link, "/");
4614 (void) strcat(stage_link, contents);
4616 return (resolve_link(stage_link, NULL, NULL, devfs_path,
4617 dangle));
4620 /* Current link points at a /devices minor node */
4621 if (type_p) {
4622 *type_p = DI_PRIMARY_LINK;
4625 if (devfs_path)
4626 *devfs_path = s_strdup(ptr);
4628 rv = FALSE;
4629 if (dangle)
4630 rv = (stat(ptr - strlen(DEVICES), &sb) == -1);
4632 vprint(REMOVE_MID, "%slink=%s, returning %s\n", fcn,
4633 devpath, ((rv == TRUE) ? "TRUE" : "FALSE"));
4635 return (rv);
4639 * Returns the substring of interest, given a path.
4641 static char *
4642 alloc_cmp_str(const char *path, devfsadm_enumerate_t *dep)
4644 uint_t match;
4645 char *np, *ap, *mp;
4646 char *cmp_str = NULL;
4647 char at[] = "@";
4648 char *fcn = "alloc_cmp_str";
4650 np = ap = mp = NULL;
4653 * extract match flags from the flags argument.
4655 match = (dep->flags & MATCH_MASK);
4657 vprint(ENUM_MID, "%s: enumeration match type: 0x%x"
4658 " path: %s\n", fcn, match, path);
4661 * MATCH_CALLBACK and MATCH_ALL are the only flags
4662 * which may be used if "path" is a /dev path
4664 if (match == MATCH_CALLBACK) {
4665 if (dep->sel_fcn == NULL) {
4666 vprint(ENUM_MID, "%s: invalid enumerate"
4667 " callback: path: %s\n", fcn, path);
4668 return (NULL);
4670 cmp_str = dep->sel_fcn(path, dep->cb_arg);
4671 return (cmp_str);
4674 cmp_str = s_strdup(path);
4676 if (match == MATCH_ALL) {
4677 return (cmp_str);
4681 * The remaining flags make sense only for /devices
4682 * paths
4684 if ((mp = strrchr(cmp_str, ':')) == NULL) {
4685 vprint(ENUM_MID, "%s: invalid path: %s\n",
4686 fcn, path);
4687 goto err;
4690 if (match == MATCH_MINOR) {
4691 /* A NULL "match_arg" values implies entire minor */
4692 if (get_component(mp + 1, dep->match_arg) == NULL) {
4693 vprint(ENUM_MID, "%s: invalid minor component:"
4694 " path: %s\n", fcn, path);
4695 goto err;
4697 return (cmp_str);
4700 if ((np = strrchr(cmp_str, '/')) == NULL) {
4701 vprint(ENUM_MID, "%s: invalid path: %s\n", fcn, path);
4702 goto err;
4705 if (match == MATCH_PARENT) {
4706 if (strcmp(cmp_str, "/") == 0) {
4707 vprint(ENUM_MID, "%s: invalid path: %s\n",
4708 fcn, path);
4709 goto err;
4712 if (np == cmp_str) {
4713 *(np + 1) = '\0';
4714 } else {
4715 *np = '\0';
4717 return (cmp_str);
4720 /* ap can be NULL - Leaf address may not exist or be empty string */
4721 ap = strchr(np+1, '@');
4723 /* minor is no longer of interest */
4724 *mp = '\0';
4726 if (match == MATCH_NODE) {
4727 if (ap)
4728 *ap = '\0';
4729 return (cmp_str);
4730 } else if (match == MATCH_ADDR) {
4732 * The empty string is a valid address. The only MATCH_ADDR
4733 * allowed in this case is against the whole address or
4734 * the first component of the address (match_arg=NULL/"0"/"1")
4735 * Note that in this case, the path won't have an "@"
4736 * As a result ap will be NULL. We fake up an ap = @'\0'
4737 * so that get_component() will work correctly.
4739 if (ap == NULL) {
4740 ap = at;
4743 if (get_component(ap + 1, dep->match_arg) == NULL) {
4744 vprint(ENUM_MID, "%s: invalid leaf addr. component:"
4745 " path: %s\n", fcn, path);
4746 goto err;
4748 return (cmp_str);
4751 vprint(ENUM_MID, "%s: invalid enumeration flags: 0x%x"
4752 " path: %s\n", fcn, dep->flags, path);
4754 /*FALLTHRU*/
4755 err:
4756 free(cmp_str);
4757 return (NULL);
4762 * "str" is expected to be a string with components separated by ','
4763 * The terminating null char is considered a separator.
4764 * get_component() will remove the portion of the string beyond
4765 * the component indicated.
4766 * If comp_str is NULL, the entire "str" is returned.
4768 static char *
4769 get_component(char *str, const char *comp_str)
4771 long comp;
4772 char *cp;
4774 if (str == NULL) {
4775 return (NULL);
4778 if (comp_str == NULL) {
4779 return (str);
4782 errno = 0;
4783 comp = strtol(comp_str, &cp, 10);
4784 if (errno != 0 || *cp != '\0' || comp < 0) {
4785 return (NULL);
4788 if (comp == 0)
4789 return (str);
4791 for (cp = str; ; cp++) {
4792 if (*cp == ',' || *cp == '\0')
4793 comp--;
4794 if (*cp == '\0' || comp <= 0) {
4795 break;
4799 if (comp == 0) {
4800 *cp = '\0';
4801 } else {
4802 str = NULL;
4805 return (str);
4810 * Enumerate serves as a generic counter as well as a means to determine
4811 * logical unit/controller numbers for such items as disk and tape
4812 * drives.
4814 * rules[] is an array of devfsadm_enumerate_t structures which defines
4815 * the enumeration rules to be used for a specified set of links in /dev.
4816 * The set of links is specified through regular expressions (of the flavor
4817 * described in regex(5)). These regular expressions are used to determine
4818 * the set of links in /dev to examine. The last path component in these
4819 * regular expressions MUST contain a parenthesized subexpression surrounding
4820 * the RE which is to be considered the enumerating component. The subexp
4821 * member in a rule is the subexpression number of the enumerating
4822 * component. Subexpressions in the last path component are numbered starting
4823 * from 1.
4825 * A cache of current id assignments is built up from existing symlinks and
4826 * new assignments use the lowest unused id. Assignments are based on a
4827 * match of a specified substring of a symlink's contents. If the specified
4828 * component for the devfs_path argument matches the corresponding substring
4829 * for a existing symlink's contents, the cached id is returned. Else, a new
4830 * id is created and returned in *buf. *buf must be freed by the caller.
4832 * An id assignment may be governed by a combination of rules, each rule
4833 * applicable to a different subset of links in /dev. For example, controller
4834 * numbers may be determined by a combination of disk symlinks in /dev/[r]dsk
4835 * and controller symlinks in /dev/cfg, with the two sets requiring different
4836 * rules to derive the "substring of interest". In such cases, the rules
4837 * array will have more than one element.
4840 devfsadm_enumerate_int(char *devfs_path, int index, char **buf,
4841 devfsadm_enumerate_t rules[], int nrules)
4843 return (find_enum_id(rules, nrules,
4844 devfs_path, index, "0", INTEGER, buf, 0));
4848 disk_enumerate_int(char *devfs_path, int index, char **buf,
4849 devfsadm_enumerate_t rules[], int nrules)
4851 return (find_enum_id(rules, nrules,
4852 devfs_path, index, "0", INTEGER, buf, 1));
4856 * Same as above, but allows a starting value to be specified.
4857 * Private to devfsadm.... used by devlinks.
4859 static int
4860 devfsadm_enumerate_int_start(char *devfs_path, int index, char **buf,
4861 devfsadm_enumerate_t rules[], int nrules, char *start)
4863 return (find_enum_id(rules, nrules,
4864 devfs_path, index, start, INTEGER, buf, 0));
4868 * devfsadm_enumerate_char serves as a generic counter returning
4869 * a single letter.
4872 devfsadm_enumerate_char(char *devfs_path, int index, char **buf,
4873 devfsadm_enumerate_t rules[], int nrules)
4875 return (find_enum_id(rules, nrules,
4876 devfs_path, index, "a", LETTER, buf, 0));
4880 * Same as above, but allows a starting char to be specified.
4881 * Private to devfsadm - used by ports module (port_link.c)
4884 devfsadm_enumerate_char_start(char *devfs_path, int index, char **buf,
4885 devfsadm_enumerate_t rules[], int nrules, char *start)
4887 return (find_enum_id(rules, nrules,
4888 devfs_path, index, start, LETTER, buf, 0));
4893 * For a given numeral_set (see get_cached_set for desc of numeral_set),
4894 * search all cached entries looking for matches on a specified substring
4895 * of devfs_path. The substring is derived from devfs_path based on the
4896 * rule specified by "index". If a match is found on a cached entry,
4897 * return the enumerated id in buf. Otherwise, create a new id by calling
4898 * new_id, then cache and return that entry.
4900 static int
4901 find_enum_id(devfsadm_enumerate_t rules[], int nrules,
4902 char *devfs_path, int index, char *min, int type, char **buf,
4903 int multiple)
4905 numeral_t *matchnp;
4906 numeral_t *numeral;
4907 int matchcount = 0;
4908 char *cmp_str;
4909 char *fcn = "find_enum_id";
4910 numeral_set_t *set;
4912 if (rules == NULL) {
4913 vprint(ENUM_MID, "%s: no rules. path: %s\n",
4914 fcn, devfs_path ? devfs_path : "<NULL path>");
4915 return (DEVFSADM_FAILURE);
4918 if (devfs_path == NULL) {
4919 vprint(ENUM_MID, "%s: NULL path\n", fcn);
4920 return (DEVFSADM_FAILURE);
4923 if (nrules <= 0 || index < 0 || index >= nrules || buf == NULL) {
4924 vprint(ENUM_MID, "%s: invalid arguments. path: %s\n",
4925 fcn, devfs_path);
4926 return (DEVFSADM_FAILURE);
4929 *buf = NULL;
4932 cmp_str = alloc_cmp_str(devfs_path, &rules[index]);
4933 if (cmp_str == NULL) {
4934 return (DEVFSADM_FAILURE);
4937 if ((set = get_enum_cache(rules, nrules)) == NULL) {
4938 free(cmp_str);
4939 return (DEVFSADM_FAILURE);
4942 assert(nrules == set->re_count);
4945 * Check and see if a matching entry is already cached.
4947 matchcount = lookup_enum_cache(set, cmp_str, rules, index,
4948 &matchnp);
4950 if (matchcount < 0 || matchcount > 1) {
4951 free(cmp_str);
4952 if (multiple && matchcount > 1)
4953 return (DEVFSADM_MULTIPLE);
4954 else
4955 return (DEVFSADM_FAILURE);
4958 /* if matching entry already cached, return it */
4959 if (matchcount == 1) {
4960 /* should never create a link with a reserved ID */
4961 vprint(ENUM_MID, "%s: 1 match w/ ID: %s\n", fcn, matchnp->id);
4962 assert(matchnp->flags == 0);
4963 *buf = s_strdup(matchnp->id);
4964 free(cmp_str);
4965 return (DEVFSADM_SUCCESS);
4969 * no cached entry, initialize a numeral struct
4970 * by calling new_id() and cache onto the numeral_set
4972 numeral = s_malloc(sizeof (numeral_t));
4973 numeral->id = new_id(set->headnumeral, type, min);
4974 numeral->full_path = s_strdup(devfs_path);
4975 numeral->rule_index = index;
4976 numeral->cmp_str = cmp_str;
4977 cmp_str = NULL;
4978 numeral->flags = 0;
4979 vprint(RSRV_MID, "%s: alloc new_id: %s numeral flags = %d\n",
4980 fcn, numeral->id, numeral->flags);
4983 /* insert to head of list for fast lookups */
4984 numeral->next = set->headnumeral;
4985 set->headnumeral = numeral;
4987 *buf = s_strdup(numeral->id);
4988 return (DEVFSADM_SUCCESS);
4993 * Looks up the specified cache for a match with a specified string
4994 * Returns:
4995 * -1 : on error.
4996 * 0/1/2 : Number of matches.
4997 * Returns the matching element only if there is a single match.
4998 * If the "uncached" flag is set, derives the "cmp_str" afresh
4999 * for the match instead of using cached values.
5001 static int
5002 lookup_enum_cache(numeral_set_t *set, char *cmp_str,
5003 devfsadm_enumerate_t rules[], int index, numeral_t **matchnpp)
5005 int matchcount = 0, rv = -1;
5006 int uncached;
5007 numeral_t *np;
5008 char *fcn = "lookup_enum_cache";
5009 char *cp;
5011 *matchnpp = NULL;
5013 assert(index < set->re_count);
5015 if (cmp_str == NULL) {
5016 return (-1);
5019 uncached = 0;
5020 if ((rules[index].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
5021 uncached = 1;
5025 * Check and see if a matching entry is already cached.
5027 for (np = set->headnumeral; np != NULL; np = np->next) {
5030 * Skip reserved IDs
5032 if (np->flags & NUMERAL_RESERVED) {
5033 vprint(RSRV_MID, "lookup_enum_cache: "
5034 "Cannot Match with reserved ID (%s), "
5035 "skipping\n", np->id);
5036 assert(np->flags == NUMERAL_RESERVED);
5037 continue;
5038 } else {
5039 vprint(RSRV_MID, "lookup_enum_cache: "
5040 "Attempting match with numeral ID: %s"
5041 " numeral flags = %d\n", np->id, np->flags);
5042 assert(np->flags == 0);
5045 if (np->cmp_str == NULL) {
5046 vprint(ENUM_MID, "%s: invalid entry in enumerate"
5047 " cache. path: %s\n", fcn, np->full_path);
5048 return (-1);
5051 if (uncached) {
5052 vprint(CHATTY_MID, "%s: bypassing enumerate cache."
5053 " path: %s\n", fcn, cmp_str);
5054 cp = alloc_cmp_str(np->full_path,
5055 &rules[np->rule_index]);
5056 if (cp == NULL)
5057 return (-1);
5058 rv = strcmp(cmp_str, cp);
5059 free(cp);
5060 } else {
5061 rv = strcmp(cmp_str, np->cmp_str);
5064 if (rv == 0) {
5065 if (matchcount++ != 0) {
5066 break; /* more than 1 match. */
5068 *matchnpp = np;
5072 return (matchcount);
5075 #ifdef DEBUG
5076 static void
5077 dump_enum_cache(numeral_set_t *setp)
5079 int i;
5080 numeral_t *np;
5081 char *fcn = "dump_enum_cache";
5083 vprint(ENUM_MID, "%s: re_count = %d\n", fcn, setp->re_count);
5084 for (i = 0; i < setp->re_count; i++) {
5085 vprint(ENUM_MID, "%s: re[%d] = %s\n", fcn, i, setp->re[i]);
5088 for (np = setp->headnumeral; np != NULL; np = np->next) {
5089 vprint(ENUM_MID, "%s: id: %s\n", fcn, np->id);
5090 vprint(ENUM_MID, "%s: full_path: %s\n", fcn, np->full_path);
5091 vprint(ENUM_MID, "%s: rule_index: %d\n", fcn, np->rule_index);
5092 vprint(ENUM_MID, "%s: cmp_str: %s\n", fcn, np->cmp_str);
5093 vprint(ENUM_MID, "%s: flags: %d\n", fcn, np->flags);
5096 #endif
5099 * For a given set of regular expressions in rules[], this function returns
5100 * either a previously cached struct numeral_set or it will create and
5101 * cache a new struct numeral_set. There is only one struct numeral_set
5102 * for the combination of REs present in rules[]. Each numeral_set contains
5103 * the regular expressions in rules[] used for cache selection AND a linked
5104 * list of struct numerals, ONE FOR EACH *UNIQUE* numeral or character ID
5105 * selected by the grouping parenthesized subexpression found in the last
5106 * path component of each rules[].re. For example, the RE: "rmt/([0-9]+)"
5107 * selects all the logical nodes of the correct form in dev/rmt/.
5108 * Each rmt/X will store a *single* struct numeral... ie 0, 1, 2 each get a
5109 * single struct numeral. There is no need to store more than a single logical
5110 * node matching X since the information desired in the devfspath would be
5111 * identical for the portion of the devfspath of interest. (the part up to,
5112 * but not including the minor name in this example.)
5114 * If the given numeral_set is not yet cached, call enumerate_recurse to
5115 * create it.
5117 static numeral_set_t *
5118 get_enum_cache(devfsadm_enumerate_t rules[], int nrules)
5120 /* linked list of numeral sets */
5121 numeral_set_t *setp;
5122 int i;
5123 int ret;
5124 char *path_left;
5125 enumerate_file_t *entry;
5126 char *fcn = "get_enum_cache";
5129 * See if we've already cached this numeral set.
5131 for (setp = head_numeral_set; setp != NULL; setp = setp->next) {
5133 * check all regexp's passed in function against
5134 * those in cached set.
5136 if (nrules != setp->re_count) {
5137 continue;
5140 for (i = 0; i < nrules; i++) {
5141 if (strcmp(setp->re[i], rules[i].re) != 0) {
5142 break;
5146 if (i == nrules) {
5147 return (setp);
5152 * If the MATCH_UNCACHED flag is set, we should not be here.
5154 for (i = 0; i < nrules; i++) {
5155 if ((rules[i].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
5156 vprint(ENUM_MID, "%s: invalid enumeration flags: "
5157 "0x%x\n", fcn, rules[i].flags);
5158 return (NULL);
5163 * Since we made it here, we have not yet cached the given set of
5164 * logical nodes matching the passed re. Create a cached entry
5165 * struct numeral_set and populate it with a minimal set of
5166 * logical nodes from /dev.
5169 setp = s_malloc(sizeof (numeral_set_t));
5170 setp->re = s_malloc(sizeof (char *) * nrules);
5171 for (i = 0; i < nrules; i++) {
5172 setp->re[i] = s_strdup(rules[i].re);
5174 setp->re_count = nrules;
5175 setp->headnumeral = NULL;
5177 /* put this new cached set on the cached set list */
5178 setp->next = head_numeral_set;
5179 head_numeral_set = setp;
5182 * For each RE, search the "reserved" list to create numeral IDs that
5183 * are reserved.
5185 for (entry = enumerate_reserved; entry; entry = entry->er_next) {
5187 vprint(RSRV_MID, "parsing rstring: %s\n", entry->er_file);
5189 for (i = 0; i < nrules; i++) {
5190 path_left = s_strdup(setp->re[i]);
5191 vprint(RSRV_MID, "parsing rule RE: %s\n", path_left);
5192 ret = enumerate_parse(entry->er_file, path_left,
5193 setp, rules, i);
5194 free(path_left);
5195 if (ret == 1) {
5197 * We found the reserved ID for this entry.
5198 * We still keep the entry since it is needed
5199 * by the new link bypass code in disks
5201 vprint(RSRV_MID, "found rsv ID: rstring: %s "
5202 "rule RE: %s\n", entry->er_file, path_left);
5203 break;
5209 * For each RE, search disk and cache any matches on the
5210 * numeral list.
5212 for (i = 0; i < nrules; i++) {
5213 path_left = s_strdup(setp->re[i]);
5214 enumerate_recurse(dev_dir, path_left, setp, rules, i);
5215 free(path_left);
5218 #ifdef DEBUG
5219 dump_enum_cache(setp);
5220 #endif
5222 return (setp);
5227 * This function stats the pathname namebuf. If this is a directory
5228 * entry, we recurse down dname/fname until we find the first symbolic
5229 * link, and then stat and return it. This is valid for the same reason
5230 * that we only need to read a single pathname for multiple matching
5231 * logical ID's... ie, all the logical nodes should contain identical
5232 * physical paths for the parts we are interested.
5235 get_stat_info(char *namebuf, struct stat *sb)
5237 char *cp;
5238 finddevhdl_t fhandle;
5239 const char *fp;
5241 if (lstat(namebuf, sb) < 0) {
5242 (void) err_print(LSTAT_FAILED, namebuf, strerror(errno));
5243 return (DEVFSADM_FAILURE);
5246 if ((sb->st_mode & S_IFMT) == S_IFLNK) {
5247 return (DEVFSADM_SUCCESS);
5251 * If it is a dir, recurse down until we find a link and
5252 * then use the link.
5254 if ((sb->st_mode & S_IFMT) == S_IFDIR) {
5256 if (finddev_readdir(namebuf, &fhandle) != 0) {
5257 return (DEVFSADM_FAILURE);
5261 * Search each dir entry looking for a symlink. Return
5262 * the first symlink found in namebuf. Recurse dirs.
5264 while ((fp = finddev_next(fhandle)) != NULL) {
5265 cp = namebuf + strlen(namebuf);
5266 if ((strlcat(namebuf, "/", PATH_MAX) >= PATH_MAX) ||
5267 (strlcat(namebuf, fp, PATH_MAX) >= PATH_MAX)) {
5268 *cp = '\0';
5269 finddev_close(fhandle);
5270 return (DEVFSADM_FAILURE);
5272 if (get_stat_info(namebuf, sb) == DEVFSADM_SUCCESS) {
5273 finddev_close(fhandle);
5274 return (DEVFSADM_SUCCESS);
5276 *cp = '\0';
5278 finddev_close(fhandle);
5281 /* no symlink found, so return error */
5282 return (DEVFSADM_FAILURE);
5286 * An existing matching ID was not found, so this function is called to
5287 * create the next lowest ID. In the INTEGER case, return the next
5288 * lowest unused integer. In the case of LETTER, return the next lowest
5289 * unused letter. Return empty string if all 26 are used.
5290 * Only IDs >= min will be returned.
5292 char *
5293 new_id(numeral_t *numeral, int type, char *min)
5295 int imin;
5296 temp_t *temp;
5297 temp_t *ptr;
5298 temp_t **previous;
5299 temp_t *head = NULL;
5300 char *retval;
5301 static char tempbuff[8];
5302 numeral_t *np;
5304 if (type == LETTER) {
5306 char letter[26], i;
5308 if (numeral == NULL) {
5309 return (s_strdup(min));
5312 for (i = 0; i < 26; i++) {
5313 letter[i] = 0;
5316 for (np = numeral; np != NULL; np = np->next) {
5317 assert(np->flags == 0 ||
5318 np->flags == NUMERAL_RESERVED);
5319 letter[*np->id - 'a']++;
5322 imin = *min - 'a';
5324 for (i = imin; i < 26; i++) {
5325 if (letter[i] == 0) {
5326 retval = s_malloc(2);
5327 retval[0] = 'a' + i;
5328 retval[1] = '\0';
5329 return (retval);
5333 return (s_strdup(""));
5336 if (type == INTEGER) {
5338 if (numeral == NULL) {
5339 return (s_strdup(min));
5342 imin = atoi(min);
5344 /* sort list */
5345 for (np = numeral; np != NULL; np = np->next) {
5346 assert(np->flags == 0 ||
5347 np->flags == NUMERAL_RESERVED);
5348 temp = s_malloc(sizeof (temp_t));
5349 temp->integer = atoi(np->id);
5350 temp->next = NULL;
5352 previous = &head;
5353 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5354 if (temp->integer < ptr->integer) {
5355 temp->next = ptr;
5356 *previous = temp;
5357 break;
5359 previous = &(ptr->next);
5361 if (ptr == NULL) {
5362 *previous = temp;
5366 /* now search sorted list for first hole >= imin */
5367 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5368 if (imin == ptr->integer) {
5369 imin++;
5370 } else {
5371 if (imin < ptr->integer) {
5372 break;
5378 /* free temp list */
5379 for (ptr = head; ptr != NULL; ) {
5380 temp = ptr;
5381 ptr = ptr->next;
5382 free(temp);
5385 (void) sprintf(tempbuff, "%d", imin);
5386 return (s_strdup(tempbuff));
5389 return (s_strdup(""));
5392 static int
5393 enumerate_parse(char *rsvstr, char *path_left, numeral_set_t *setp,
5394 devfsadm_enumerate_t rules[], int index)
5396 char *slash1 = NULL;
5397 char *slash2 = NULL;
5398 char *numeral_id;
5399 char *path_left_save;
5400 char *rsvstr_save;
5401 int ret = 0;
5402 static int warned = 0;
5404 rsvstr_save = rsvstr;
5405 path_left_save = path_left;
5407 if (rsvstr == NULL || rsvstr[0] == '\0' || rsvstr[0] == '/') {
5408 if (!warned) {
5409 err_print("invalid reserved filepath: %s\n",
5410 rsvstr ? rsvstr : "<NULL>");
5411 warned = 1;
5413 return (0);
5416 vprint(RSRV_MID, "processing rule: %s, rstring: %s\n",
5417 path_left, rsvstr);
5420 for (;;) {
5421 /* get rid of any extra '/' in the reserve string */
5422 while (*rsvstr == '/') {
5423 rsvstr++;
5426 /* get rid of any extra '/' in the RE */
5427 while (*path_left == '/') {
5428 path_left++;
5431 if (slash1 = strchr(path_left, '/')) {
5432 *slash1 = '\0';
5434 if (slash2 = strchr(rsvstr, '/')) {
5435 *slash2 = '\0';
5438 if ((slash1 != NULL) ^ (slash2 != NULL)) {
5439 ret = 0;
5440 vprint(RSRV_MID, "mismatch in # of path components\n");
5441 goto out;
5445 * Returns true if path_left matches the list entry.
5446 * If it is the last path component, pass subexp
5447 * so that it will return the corresponding ID in
5448 * numeral_id.
5450 numeral_id = NULL;
5451 if (match_path_component(path_left, rsvstr, &numeral_id,
5452 slash1 ? 0 : rules[index].subexp)) {
5454 /* We have a match. */
5455 if (slash1 == NULL) {
5456 /* Is last path component */
5457 vprint(RSRV_MID, "match and last component\n");
5458 create_reserved_numeral(setp, numeral_id);
5459 if (numeral_id != NULL) {
5460 free(numeral_id);
5462 ret = 1;
5463 goto out;
5464 } else {
5465 /* Not last path component. Continue parsing */
5466 *slash1 = '/';
5467 *slash2 = '/';
5468 path_left = slash1 + 1;
5469 rsvstr = slash2 + 1;
5470 vprint(RSRV_MID,
5471 "match and NOT last component\n");
5472 continue;
5474 } else {
5475 /* No match */
5476 ret = 0;
5477 vprint(RSRV_MID, "No match: rule RE = %s, "
5478 "rstring = %s\n", path_left, rsvstr);
5479 goto out;
5483 out:
5484 if (slash1)
5485 *slash1 = '/';
5486 if (slash2)
5487 *slash2 = '/';
5489 if (ret == 1) {
5490 vprint(RSRV_MID, "match: rule RE: %s, rstring: %s\n",
5491 path_left_save, rsvstr_save);
5492 } else {
5493 vprint(RSRV_MID, "NO match: rule RE: %s, rstring: %s\n",
5494 path_left_save, rsvstr_save);
5497 return (ret);
5501 * Search current_dir for all files which match the first path component
5502 * of path_left, which is an RE. If a match is found, but there are more
5503 * components of path_left, then recurse, otherwise, if we have reached
5504 * the last component of path_left, call create_cached_numerals for each
5505 * file. At some point, recurse_dev_re() should be rewritten so that this
5506 * function can be eliminated.
5508 static void
5509 enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
5510 devfsadm_enumerate_t rules[], int index)
5512 char *slash;
5513 char *new_path;
5514 char *numeral_id;
5515 finddevhdl_t fhandle;
5516 const char *fp;
5518 if (finddev_readdir(current_dir, &fhandle) != 0) {
5519 return;
5522 /* get rid of any extra '/' */
5523 while (*path_left == '/') {
5524 path_left++;
5527 if (slash = strchr(path_left, '/')) {
5528 *slash = '\0';
5531 while ((fp = finddev_next(fhandle)) != NULL) {
5534 * Returns true if path_left matches the list entry.
5535 * If it is the last path component, pass subexp
5536 * so that it will return the corresponding ID in
5537 * numeral_id.
5539 numeral_id = NULL;
5540 if (match_path_component(path_left, (char *)fp, &numeral_id,
5541 slash ? 0 : rules[index].subexp)) {
5543 new_path = s_malloc(strlen(current_dir) +
5544 strlen(fp) + 2);
5546 (void) strcpy(new_path, current_dir);
5547 (void) strcat(new_path, "/");
5548 (void) strcat(new_path, fp);
5550 if (slash != NULL) {
5551 enumerate_recurse(new_path, slash + 1,
5552 setp, rules, index);
5553 } else {
5554 create_cached_numeral(new_path, setp,
5555 numeral_id, rules, index);
5556 if (numeral_id != NULL) {
5557 free(numeral_id);
5560 free(new_path);
5564 if (slash != NULL) {
5565 *slash = '/';
5567 finddev_close(fhandle);
5572 * Returns true if file matches file_re. If subexp is non-zero, it means
5573 * we are searching the last path component and need to return the
5574 * parenthesized subexpression subexp in id.
5577 static int
5578 match_path_component(char *file_re, char *file, char **id, int subexp)
5580 regex_t re1;
5581 int match = 0;
5582 int nelements;
5583 regmatch_t *pmatch;
5585 if (subexp != 0) {
5586 nelements = subexp + 1;
5587 pmatch =
5588 (regmatch_t *)s_malloc(sizeof (regmatch_t) * nelements);
5589 } else {
5590 pmatch = NULL;
5591 nelements = 0;
5594 if (regcomp(&re1, file_re, REG_EXTENDED) != 0) {
5595 if (pmatch != NULL) {
5596 free(pmatch);
5598 return (0);
5601 if (regexec(&re1, file, nelements, pmatch, 0) == 0) {
5602 match = 1;
5605 if ((match != 0) && (subexp != 0)) {
5606 int size = pmatch[subexp].rm_eo - pmatch[subexp].rm_so;
5607 *id = s_malloc(size + 1);
5608 (void) strncpy(*id, &file[pmatch[subexp].rm_so], size);
5609 (*id)[size] = '\0';
5612 if (pmatch != NULL) {
5613 free(pmatch);
5615 regfree(&re1);
5616 return (match);
5619 static void
5620 create_reserved_numeral(numeral_set_t *setp, char *numeral_id)
5622 numeral_t *np;
5624 vprint(RSRV_MID, "Attempting to create reserved numeral: %s\n",
5625 numeral_id);
5628 * We found a numeral_id from an entry in the enumerate_reserved file
5629 * which matched the re passed in from devfsadm_enumerate. We only
5630 * need to make sure ONE copy of numeral_id exists on the numeral list.
5631 * We only need to store /dev/dsk/cNtod0s0 and no other entries
5632 * hanging off of controller N.
5634 for (np = setp->headnumeral; np != NULL; np = np->next) {
5635 if (strcmp(numeral_id, np->id) == 0) {
5636 vprint(RSRV_MID, "ID: %s, already reserved\n", np->id);
5637 assert(np->flags == NUMERAL_RESERVED);
5638 return;
5639 } else {
5640 assert(np->flags == 0 ||
5641 np->flags == NUMERAL_RESERVED);
5645 /* NOT on list, so add it */
5646 np = s_malloc(sizeof (numeral_t));
5647 np->id = s_strdup(numeral_id);
5648 np->full_path = NULL;
5649 np->rule_index = 0;
5650 np->cmp_str = NULL;
5651 np->flags = NUMERAL_RESERVED;
5652 np->next = setp->headnumeral;
5653 setp->headnumeral = np;
5655 vprint(RSRV_MID, "Reserved numeral ID: %s\n", np->id);
5659 * This function is called for every file which matched the leaf
5660 * component of the RE. If the "numeral_id" is not already on the
5661 * numeral set's numeral list, add it and its physical path.
5663 static void
5664 create_cached_numeral(char *path, numeral_set_t *setp, char *numeral_id,
5665 devfsadm_enumerate_t rules[], int index)
5667 char linkbuf[PATH_MAX + 1];
5668 char lpath[PATH_MAX + 1];
5669 char *linkptr, *cmp_str;
5670 numeral_t *np;
5671 int linksize;
5672 struct stat sb;
5673 char *contents;
5674 const char *fcn = "create_cached_numeral";
5676 assert(index >= 0 && index < setp->re_count);
5677 assert(strcmp(rules[index].re, setp->re[index]) == 0);
5680 * We found a numeral_id from an entry in /dev which matched
5681 * the re passed in from devfsadm_enumerate. We only need to make sure
5682 * ONE copy of numeral_id exists on the numeral list. We only need
5683 * to store /dev/dsk/cNtod0s0 and no other entries hanging off
5684 * of controller N.
5686 for (np = setp->headnumeral; np != NULL; np = np->next) {
5687 assert(np->flags == 0 || np->flags == NUMERAL_RESERVED);
5688 if (strcmp(numeral_id, np->id) == 0) {
5690 * Note that we can't assert that the flags field
5691 * of the numeral is 0, since both reserved and
5692 * unreserved links in /dev come here
5694 if (np->flags == NUMERAL_RESERVED) {
5695 vprint(RSRV_MID, "ID derived from /dev link is"
5696 " reserved: %s\n", np->id);
5697 } else {
5698 vprint(RSRV_MID, "ID derived from /dev link is"
5699 " NOT reserved: %s\n", np->id);
5701 return;
5705 /* NOT on list, so add it */
5707 (void) strcpy(lpath, path);
5709 * If path is a dir, it is changed to the first symbolic link it find
5710 * if it finds one.
5712 if (get_stat_info(lpath, &sb) == DEVFSADM_FAILURE) {
5713 return;
5716 /* If we get here, we found a symlink */
5717 linksize = readlink(lpath, linkbuf, PATH_MAX);
5719 if (linksize <= 0) {
5720 err_print(READLINK_FAILED, fcn, lpath, strerror(errno));
5721 return;
5724 linkbuf[linksize] = '\0';
5727 * redirect alias path to current path
5728 * devi_root_node is protected by lock_dev()
5730 contents = di_alias2curr(devi_root_node, linkbuf);
5733 * the following just points linkptr to the root of the /devices
5734 * node if it is a minor node, otherwise, to the first char of
5735 * linkbuf if it is a link.
5737 (void) is_minor_node(contents, &linkptr);
5739 cmp_str = alloc_cmp_str(linkptr, &rules[index]);
5740 if (cmp_str == NULL) {
5741 free(contents);
5742 return;
5745 np = s_malloc(sizeof (numeral_t));
5747 np->id = s_strdup(numeral_id);
5748 np->full_path = s_strdup(linkptr);
5749 np->rule_index = index;
5750 np->cmp_str = cmp_str;
5751 np->flags = 0;
5753 np->next = setp->headnumeral;
5754 setp->headnumeral = np;
5756 free(contents);
5761 * This should be called either before or after granting access to a
5762 * command line version of devfsadm running, since it may have changed
5763 * the state of /dev. It forces future enumerate calls to re-build
5764 * cached information from /dev.
5766 void
5767 invalidate_enumerate_cache(void)
5769 numeral_set_t *setp;
5770 numeral_set_t *savedsetp;
5771 numeral_t *savednumset;
5772 numeral_t *numset;
5773 int i;
5775 for (setp = head_numeral_set; setp != NULL; ) {
5777 * check all regexp's passed in function against
5778 * those in cached set.
5781 savedsetp = setp;
5782 setp = setp->next;
5784 for (i = 0; i < savedsetp->re_count; i++) {
5785 free(savedsetp->re[i]);
5787 free(savedsetp->re);
5789 for (numset = savedsetp->headnumeral; numset != NULL; ) {
5790 savednumset = numset;
5791 numset = numset->next;
5792 assert(savednumset->rule_index < savedsetp->re_count);
5793 free(savednumset->id);
5794 free(savednumset->full_path);
5795 free(savednumset->cmp_str);
5796 free(savednumset);
5798 free(savedsetp);
5800 head_numeral_set = NULL;
5804 * Copies over links from /dev to <root>/dev and device special files in
5805 * /devices to <root>/devices, preserving the existing file modes. If
5806 * the link or special file already exists on <root>, skip the copy. (it
5807 * would exist only if a package hard coded it there, so assume package
5808 * knows best?). Use /etc/name_to_major and <root>/etc/name_to_major to
5809 * make translations for major numbers on device special files. No need to
5810 * make a translation on minor_perm since if the file was created in the
5811 * miniroot then it would presumably have the same minor_perm entry in
5812 * <root>/etc/minor_perm. To be used only by install.
5815 devfsadm_copy(void)
5817 char filename[PATH_MAX + 1];
5819 /* load the installed root's name_to_major for translations */
5820 (void) snprintf(filename, sizeof (filename), "%s%s", root_dir,
5821 NAME_TO_MAJOR);
5822 if (load_n2m_table(filename) == DEVFSADM_FAILURE) {
5823 return (DEVFSADM_FAILURE);
5826 /* Copy /dev to target disk. No need to copy /devices with devfs */
5827 (void) nftw(DEV, devfsadm_copy_file, 20, FTW_PHYS);
5829 /* Let install handle copying over path_to_inst */
5831 return (DEVFSADM_SUCCESS);
5835 * This function copies links, dirs, and device special files.
5836 * Note that it always returns DEVFSADM_SUCCESS, so that nftw doesn't
5837 * abort.
5839 /*ARGSUSED*/
5840 static int
5841 devfsadm_copy_file(const char *file, const struct stat *stat,
5842 int flags, struct FTW *ftw)
5844 struct stat sp;
5845 dev_t newdev;
5846 char newfile[PATH_MAX + 1];
5847 char linkcontents[PATH_MAX + 1];
5848 int bytes;
5849 const char *fcn = "devfsadm_copy_file";
5851 (void) strcpy(newfile, root_dir);
5852 (void) strcat(newfile, "/");
5853 (void) strcat(newfile, file);
5855 if (lstat(newfile, &sp) == 0) {
5856 /* newfile already exists, so no need to continue */
5857 return (DEVFSADM_SUCCESS);
5860 if (((stat->st_mode & S_IFMT) == S_IFBLK) ||
5861 ((stat->st_mode & S_IFMT) == S_IFCHR)) {
5862 if (translate_major(stat->st_rdev, &newdev) ==
5863 DEVFSADM_FAILURE) {
5864 return (DEVFSADM_SUCCESS);
5866 if (mknod(newfile, stat->st_mode, newdev) == -1) {
5867 err_print(MKNOD_FAILED, newfile, strerror(errno));
5868 return (DEVFSADM_SUCCESS);
5870 } else if ((stat->st_mode & S_IFMT) == S_IFDIR) {
5871 if (mknod(newfile, stat->st_mode, 0) == -1) {
5872 err_print(MKNOD_FAILED, newfile, strerror(errno));
5873 return (DEVFSADM_SUCCESS);
5875 } else if ((stat->st_mode & S_IFMT) == S_IFLNK) {
5877 * No need to redirect alias paths. We want a
5878 * true copy. The system on first boot after install
5879 * will redirect paths
5881 if ((bytes = readlink(file, linkcontents, PATH_MAX)) == -1) {
5882 err_print(READLINK_FAILED, fcn, file, strerror(errno));
5883 return (DEVFSADM_SUCCESS);
5885 linkcontents[bytes] = '\0';
5886 if (symlink(linkcontents, newfile) == -1) {
5887 err_print(SYMLINK_FAILED, newfile, newfile,
5888 strerror(errno));
5889 return (DEVFSADM_SUCCESS);
5893 (void) lchown(newfile, stat->st_uid, stat->st_gid);
5894 return (DEVFSADM_SUCCESS);
5898 * Given a dev_t from the running kernel, return the new_dev_t
5899 * by translating to the major number found on the installed
5900 * target's root name_to_major file.
5902 static int
5903 translate_major(dev_t old_dev, dev_t *new_dev)
5905 major_t oldmajor;
5906 major_t newmajor;
5907 minor_t oldminor;
5908 minor_t newminor;
5909 char cdriver[FILENAME_MAX + 1];
5910 char driver[FILENAME_MAX + 1];
5911 char *fcn = "translate_major: ";
5913 oldmajor = major(old_dev);
5914 if (modctl(MODGETNAME, driver, sizeof (driver), &oldmajor) != 0) {
5915 return (DEVFSADM_FAILURE);
5918 if (strcmp(driver, "clone") != 0) {
5919 /* non-clone case */
5921 /* look up major number is target's name2major */
5922 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5923 return (DEVFSADM_FAILURE);
5926 *new_dev = makedev(newmajor, minor(old_dev));
5927 if (old_dev != *new_dev) {
5928 vprint(CHATTY_MID, "%sdriver: %s old: %lu,%lu "
5929 "new: %lu,%lu\n", fcn, driver, major(old_dev),
5930 minor(old_dev), major(*new_dev), minor(*new_dev));
5932 return (DEVFSADM_SUCCESS);
5933 } else {
5935 * The clone is a special case. Look at its minor
5936 * number since it is the major number of the real driver.
5938 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5939 return (DEVFSADM_FAILURE);
5942 oldminor = minor(old_dev);
5943 if (modctl(MODGETNAME, cdriver, sizeof (cdriver),
5944 &oldminor) != 0) {
5945 err_print(MODGETNAME_FAILED, oldminor);
5946 return (DEVFSADM_FAILURE);
5949 if (get_major_no(cdriver, &newminor) == DEVFSADM_FAILURE) {
5950 return (DEVFSADM_FAILURE);
5953 *new_dev = makedev(newmajor, newminor);
5954 if (old_dev != *new_dev) {
5955 vprint(CHATTY_MID, "%sdriver: %s old: "
5956 "%lu,%lu new: %lu,%lu\n", fcn, driver,
5957 major(old_dev), minor(old_dev),
5958 major(*new_dev), minor(*new_dev));
5960 return (DEVFSADM_SUCCESS);
5966 * Find the major number for driver, searching the n2m_list that was
5967 * built in load_n2m_table().
5969 static int
5970 get_major_no(char *driver, major_t *major)
5972 n2m_t *ptr;
5974 for (ptr = n2m_list; ptr != NULL; ptr = ptr->next) {
5975 if (strcmp(ptr->driver, driver) == 0) {
5976 *major = ptr->major;
5977 return (DEVFSADM_SUCCESS);
5980 err_print(FIND_MAJOR_FAILED, driver);
5981 return (DEVFSADM_FAILURE);
5985 * Loads a name_to_major table into memory. Used only for suninstall's
5986 * private -R option to devfsadm, to translate major numbers from the
5987 * running to the installed target disk.
5989 static int
5990 load_n2m_table(char *file)
5992 FILE *fp;
5993 char line[1024], *cp;
5994 char driver[PATH_MAX + 1];
5995 major_t major;
5996 n2m_t *ptr;
5997 int ln = 0;
5999 if ((fp = fopen(file, "r")) == NULL) {
6000 err_print(FOPEN_FAILED, file, strerror(errno));
6001 return (DEVFSADM_FAILURE);
6004 while (fgets(line, sizeof (line), fp) != NULL) {
6005 ln++;
6006 /* cut off comments starting with '#' */
6007 if ((cp = strchr(line, '#')) != NULL)
6008 *cp = '\0';
6009 /* ignore comment or blank lines */
6010 if (is_blank(line))
6011 continue;
6012 /* sanity-check */
6013 if (sscanf(line, "%1024s%lu", driver, &major) != 2) {
6014 err_print(IGNORING_LINE_IN, ln, file);
6015 continue;
6017 ptr = (n2m_t *)s_malloc(sizeof (n2m_t));
6018 ptr->major = major;
6019 ptr->driver = s_strdup(driver);
6020 ptr->next = n2m_list;
6021 n2m_list = ptr;
6023 if (fclose(fp) == EOF) {
6024 err_print(FCLOSE_FAILED, file, strerror(errno));
6026 return (DEVFSADM_SUCCESS);
6030 * Called at devfsadm startup to read the file /etc/dev/enumerate_reserved
6031 * Creates a linked list of devlinks from which reserved IDs can be derived
6033 static void
6034 read_enumerate_file(void)
6036 FILE *fp;
6037 int linenum;
6038 char line[PATH_MAX+1];
6039 enumerate_file_t *entry;
6040 struct stat current_sb;
6041 static struct stat cached_sb;
6042 static int cached = FALSE;
6044 assert(enumerate_file);
6046 if (stat(enumerate_file, &current_sb) == -1) {
6047 vprint(RSRV_MID, "No reserved file: %s\n", enumerate_file);
6048 cached = FALSE;
6049 if (enumerate_reserved != NULL) {
6050 vprint(RSRV_MID, "invalidating %s cache\n",
6051 enumerate_file);
6053 while (enumerate_reserved != NULL) {
6054 entry = enumerate_reserved;
6055 enumerate_reserved = entry->er_next;
6056 free(entry->er_file);
6057 free(entry->er_id);
6058 free(entry);
6060 return;
6063 /* if already cached, check to see if it is still valid */
6064 if (cached == TRUE) {
6066 if (current_sb.st_mtime == cached_sb.st_mtime) {
6067 vprint(RSRV_MID, "%s cache valid\n", enumerate_file);
6068 vprint(FILES_MID, "%s cache valid\n", enumerate_file);
6069 return;
6072 vprint(RSRV_MID, "invalidating %s cache\n", enumerate_file);
6073 vprint(FILES_MID, "invalidating %s cache\n", enumerate_file);
6075 while (enumerate_reserved != NULL) {
6076 entry = enumerate_reserved;
6077 enumerate_reserved = entry->er_next;
6078 free(entry->er_file);
6079 free(entry->er_id);
6080 free(entry);
6082 vprint(RSRV_MID, "Recaching file: %s\n", enumerate_file);
6083 } else {
6084 vprint(RSRV_MID, "Caching file (first time): %s\n",
6085 enumerate_file);
6086 cached = TRUE;
6089 (void) stat(enumerate_file, &cached_sb);
6091 if ((fp = fopen(enumerate_file, "r")) == NULL) {
6092 err_print(FOPEN_FAILED, enumerate_file, strerror(errno));
6093 return;
6096 vprint(RSRV_MID, "Reading reserve file: %s\n", enumerate_file);
6097 linenum = 0;
6098 while (fgets(line, sizeof (line), fp) != NULL) {
6099 char *cp, *ncp;
6101 linenum++;
6103 /* remove newline */
6104 cp = strchr(line, '\n');
6105 if (cp)
6106 *cp = '\0';
6108 vprint(RSRV_MID, "Reserve file: line %d: %s\n", linenum, line);
6110 /* skip over space and tab */
6111 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
6114 if (*cp == '\0' || *cp == '#') {
6115 vprint(RSRV_MID, "Skipping line: '%s'\n", line);
6116 continue; /* blank line or comment line */
6119 ncp = cp;
6121 /* delete trailing blanks */
6122 for (; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
6124 *cp = '\0';
6126 entry = s_zalloc(sizeof (enumerate_file_t));
6127 entry->er_file = s_strdup(ncp);
6128 entry->er_id = NULL;
6129 entry->er_next = enumerate_reserved;
6130 enumerate_reserved = entry;
6133 if (fclose(fp) == EOF) {
6134 err_print(FCLOSE_FAILED, enumerate_file, strerror(errno));
6139 * Called at devfsadm startup to read in the devlink.tab file. Creates
6140 * a linked list of devlinktab_list structures which will be
6141 * searched for every minor node.
6143 static void
6144 read_devlinktab_file(void)
6146 devlinktab_list_t *headp = NULL;
6147 devlinktab_list_t *entryp;
6148 devlinktab_list_t **previous;
6149 devlinktab_list_t *save;
6150 char line[MAX_DEVLINK_LINE], *cp;
6151 char *selector;
6152 char *p_link;
6153 char *s_link;
6154 FILE *fp;
6155 int i;
6156 static struct stat cached_sb;
6157 struct stat current_sb;
6158 static int cached = FALSE;
6160 if (devlinktab_file == NULL) {
6161 return;
6164 (void) stat(devlinktab_file, &current_sb);
6166 /* if already cached, check to see if it is still valid */
6167 if (cached == TRUE) {
6169 if (current_sb.st_mtime == cached_sb.st_mtime) {
6170 vprint(FILES_MID, "%s cache valid\n", devlinktab_file);
6171 return;
6174 vprint(FILES_MID, "invalidating %s cache\n", devlinktab_file);
6176 while (devlinktab_list != NULL) {
6177 free_link_list(devlinktab_list->p_link);
6178 free_link_list(devlinktab_list->s_link);
6179 free_selector_list(devlinktab_list->selector);
6180 free(devlinktab_list->selector_pattern);
6181 free(devlinktab_list->p_link_pattern);
6182 if (devlinktab_list->s_link_pattern != NULL) {
6183 free(devlinktab_list->s_link_pattern);
6185 save = devlinktab_list;
6186 devlinktab_list = devlinktab_list->next;
6187 free(save);
6189 } else {
6190 cached = TRUE;
6193 (void) stat(devlinktab_file, &cached_sb);
6195 if ((fp = fopen(devlinktab_file, "r")) == NULL) {
6196 err_print(FOPEN_FAILED, devlinktab_file, strerror(errno));
6197 return;
6200 previous = &headp;
6202 while (fgets(line, sizeof (line), fp) != NULL) {
6203 devlinktab_line++;
6204 i = strlen(line);
6205 if (line[i-1] == NEWLINE) {
6206 line[i-1] = '\0';
6207 } else if (i == sizeof (line-1)) {
6208 err_print(LINE_TOO_LONG, devlinktab_line,
6209 devlinktab_file, sizeof (line)-1);
6210 while (((i = getc(fp)) != '\n') && (i != EOF))
6212 continue;
6215 /* cut off comments starting with '#' */
6216 if ((cp = strchr(line, '#')) != NULL)
6217 *cp = '\0';
6218 /* ignore comment or blank lines */
6219 if (is_blank(line))
6220 continue;
6222 vprint(DEVLINK_MID, "table: %s line %d: '%s'\n",
6223 devlinktab_file, devlinktab_line, line);
6225 /* break each entry into fields. s_link may be NULL */
6226 if (split_devlinktab_entry(line, &selector, &p_link,
6227 &s_link) == DEVFSADM_FAILURE) {
6228 vprint(DEVLINK_MID, "split_entry returns failure\n");
6229 continue;
6230 } else {
6231 vprint(DEVLINK_MID, "split_entry selector='%s' "
6232 "p_link='%s' s_link='%s'\n\n", selector,
6233 p_link, (s_link == NULL) ? "" : s_link);
6236 entryp =
6237 (devlinktab_list_t *)s_malloc(sizeof (devlinktab_list_t));
6239 entryp->line_number = devlinktab_line;
6241 if ((entryp->selector = create_selector_list(selector))
6242 == NULL) {
6243 free(entryp);
6244 continue;
6246 entryp->selector_pattern = s_strdup(selector);
6248 if ((entryp->p_link = create_link_list(p_link)) == NULL) {
6249 free_selector_list(entryp->selector);
6250 free(entryp->selector_pattern);
6251 free(entryp);
6252 continue;
6255 entryp->p_link_pattern = s_strdup(p_link);
6257 if (s_link != NULL) {
6258 if ((entryp->s_link =
6259 create_link_list(s_link)) == NULL) {
6260 free_selector_list(entryp->selector);
6261 free_link_list(entryp->p_link);
6262 free(entryp->selector_pattern);
6263 free(entryp->p_link_pattern);
6264 free(entryp);
6265 continue;
6267 entryp->s_link_pattern = s_strdup(s_link);
6268 } else {
6269 entryp->s_link = NULL;
6270 entryp->s_link_pattern = NULL;
6274 /* append to end of list */
6276 entryp->next = NULL;
6277 *previous = entryp;
6278 previous = &(entryp->next);
6280 if (fclose(fp) == EOF) {
6281 err_print(FCLOSE_FAILED, devlinktab_file, strerror(errno));
6283 devlinktab_list = headp;
6288 * For a single line entry in devlink.tab, split the line into fields
6289 * selector, p_link, and an optionally s_link. If s_link field is not
6290 * present, then return NULL in s_link (not NULL string).
6292 static int
6293 split_devlinktab_entry(char *entry, char **selector, char **p_link,
6294 char **s_link)
6296 char *tab;
6298 *selector = entry;
6300 if ((tab = strchr(entry, TAB)) != NULL) {
6301 *tab = '\0';
6302 *p_link = ++tab;
6303 } else {
6304 err_print(MISSING_TAB, devlinktab_line, devlinktab_file);
6305 return (DEVFSADM_FAILURE);
6308 if (*p_link == '\0') {
6309 err_print(MISSING_DEVNAME, devlinktab_line, devlinktab_file);
6310 return (DEVFSADM_FAILURE);
6313 if ((tab = strchr(*p_link, TAB)) != NULL) {
6314 *tab = '\0';
6315 *s_link = ++tab;
6316 if (strchr(*s_link, TAB) != NULL) {
6317 err_print(TOO_MANY_FIELDS, devlinktab_line,
6318 devlinktab_file);
6319 return (DEVFSADM_FAILURE);
6321 } else {
6322 *s_link = NULL;
6325 return (DEVFSADM_SUCCESS);
6329 * For a given devfs_spec field, for each element in the field, add it to
6330 * a linked list of devfs_spec structures. Return the linked list in
6331 * devfs_spec_list.
6333 static selector_list_t *
6334 create_selector_list(char *selector)
6336 char *key;
6337 char *val;
6338 int error = FALSE;
6339 selector_list_t *head_selector_list = NULL;
6340 selector_list_t *selector_list;
6342 /* parse_devfs_spec splits the next field into keyword & value */
6343 while ((*selector != NULL) && (error == FALSE)) {
6344 if (parse_selector(&selector, &key, &val) == DEVFSADM_FAILURE) {
6345 error = TRUE;
6346 break;
6347 } else {
6348 selector_list = (selector_list_t *)
6349 s_malloc(sizeof (selector_list_t));
6350 if (strcmp(NAME_S, key) == 0) {
6351 selector_list->key = NAME;
6352 } else if (strcmp(TYPE_S, key) == 0) {
6353 selector_list->key = TYPE;
6354 } else if (strncmp(ADDR_S, key, ADDR_S_LEN) == 0) {
6355 selector_list->key = ADDR;
6356 if (key[ADDR_S_LEN] == '\0') {
6357 selector_list->arg = 0;
6358 } else if (isdigit(key[ADDR_S_LEN]) != FALSE) {
6359 selector_list->arg =
6360 atoi(&key[ADDR_S_LEN]);
6361 } else {
6362 error = TRUE;
6363 free(selector_list);
6364 err_print(BADKEYWORD, key,
6365 devlinktab_line, devlinktab_file);
6366 break;
6368 } else if (strncmp(MINOR_S, key, MINOR_S_LEN) == 0) {
6369 selector_list->key = MINOR;
6370 if (key[MINOR_S_LEN] == '\0') {
6371 selector_list->arg = 0;
6372 } else if (isdigit(key[MINOR_S_LEN]) != FALSE) {
6373 selector_list->arg =
6374 atoi(&key[MINOR_S_LEN]);
6375 } else {
6376 error = TRUE;
6377 free(selector_list);
6378 err_print(BADKEYWORD, key,
6379 devlinktab_line, devlinktab_file);
6380 break;
6382 vprint(DEVLINK_MID, "MINOR = %s\n", val);
6383 } else {
6384 err_print(UNRECOGNIZED_KEY, key,
6385 devlinktab_line, devlinktab_file);
6386 error = TRUE;
6387 free(selector_list);
6388 break;
6390 selector_list->val = s_strdup(val);
6391 selector_list->next = head_selector_list;
6392 head_selector_list = selector_list;
6393 vprint(DEVLINK_MID, "key='%s' val='%s' arg=%d\n",
6394 key, val, selector_list->arg);
6398 if ((error == FALSE) && (head_selector_list != NULL)) {
6399 return (head_selector_list);
6400 } else {
6401 /* parse failed. Free any allocated structs */
6402 free_selector_list(head_selector_list);
6403 return (NULL);
6408 * Takes a semicolon separated list of selector elements and breaks up
6409 * into a keyword-value pair. semicolon and equal characters are
6410 * replaced with NULL's. On success, selector is updated to point to the
6411 * terminating NULL character terminating the keyword-value pair, and the
6412 * function returns DEVFSADM_SUCCESS. If there is a syntax error,
6413 * devfs_spec is not modified and function returns DEVFSADM_FAILURE.
6415 static int
6416 parse_selector(char **selector, char **key, char **val)
6418 char *equal;
6419 char *semi_colon;
6421 *key = *selector;
6423 if ((equal = strchr(*key, '=')) != NULL) {
6424 *equal = '\0';
6425 } else {
6426 err_print(MISSING_EQUAL, devlinktab_line, devlinktab_file);
6427 return (DEVFSADM_FAILURE);
6430 *val = ++equal;
6431 if ((semi_colon = strchr(equal, ';')) != NULL) {
6432 *semi_colon = '\0';
6433 *selector = semi_colon + 1;
6434 } else {
6435 *selector = equal + strlen(equal);
6437 return (DEVFSADM_SUCCESS);
6441 * link is either the second or third field of devlink.tab. Parse link
6442 * into a linked list of devlink structures and return ptr to list. Each
6443 * list element is either a constant string, or one of the following
6444 * escape sequences: \M, \A, \N, or \D. The first three escape sequences
6445 * take a numerical argument.
6447 static link_list_t *
6448 create_link_list(char *link)
6450 int x = 0;
6451 int error = FALSE;
6452 int counter_found = FALSE;
6453 link_list_t *head = NULL;
6454 link_list_t **ptr;
6455 link_list_t *link_list;
6456 char constant[MAX_DEVLINK_LINE];
6457 char *error_str;
6459 if (link == NULL) {
6460 return (NULL);
6463 while ((*link != '\0') && (error == FALSE)) {
6464 link_list = (link_list_t *)s_malloc(sizeof (link_list_t));
6465 link_list->next = NULL;
6467 while ((*link != '\0') && (*link != '\\')) {
6468 /* a non-escaped string */
6469 constant[x++] = *(link++);
6471 if (x != 0) {
6472 constant[x] = '\0';
6473 link_list->type = CONSTANT;
6474 link_list->constant = s_strdup(constant);
6475 x = 0;
6476 vprint(DEVLINK_MID, "CONSTANT FOUND %s\n", constant);
6477 } else {
6478 switch (*(++link)) {
6479 case 'M':
6480 link_list->type = MINOR;
6481 break;
6482 case 'A':
6483 link_list->type = ADDR;
6484 break;
6485 case 'N':
6486 if (counter_found == TRUE) {
6487 error = TRUE;
6488 error_str =
6489 "multiple counters not permitted";
6490 free(link_list);
6491 } else {
6492 counter_found = TRUE;
6493 link_list->type = COUNTER;
6495 break;
6496 case 'D':
6497 link_list->type = NAME;
6498 break;
6499 default:
6500 error = TRUE;
6501 free(link_list);
6502 error_str = "unrecognized escape sequence";
6503 break;
6505 if (*(link++) != 'D') {
6506 if (isdigit(*link) == FALSE) {
6507 error_str = "escape sequence must be "
6508 "followed by a digit\n";
6509 error = TRUE;
6510 free(link_list);
6511 } else {
6512 link_list->arg =
6513 (int)strtoul(link, &link, 10);
6514 vprint(DEVLINK_MID, "link_list->arg = "
6515 "%d\n", link_list->arg);
6519 /* append link_list struct to end of list */
6520 if (error == FALSE) {
6521 for (ptr = &head; *ptr != NULL; ptr = &((*ptr)->next))
6523 *ptr = link_list;
6527 if (error == FALSE) {
6528 return (head);
6529 } else {
6530 err_print(CONFIG_INCORRECT, devlinktab_line, devlinktab_file,
6531 error_str);
6532 free_link_list(head);
6533 return (NULL);
6538 * Called for each minor node devfsadm processes; for each minor node,
6539 * look for matches in the devlinktab_list list which was created on
6540 * startup read_devlinktab_file(). If there is a match, call build_links()
6541 * to build a logical devlink and a possible extra devlink.
6543 static int
6544 process_devlink_compat(di_minor_t minor, di_node_t node)
6546 int link_built = FALSE;
6547 devlinktab_list_t *entry;
6548 char *nodetype;
6549 char *dev_path;
6551 if (devlinks_debug == TRUE) {
6552 nodetype = di_minor_nodetype(minor);
6553 assert(nodetype != NULL);
6554 if ((dev_path = di_devfs_path(node)) != NULL) {
6555 vprint(INFO_MID, "'%s' entry: %s:%s\n",
6556 nodetype, dev_path,
6557 di_minor_name(minor) ? di_minor_name(minor) : "");
6558 di_devfs_path_free(dev_path);
6564 /* don't process devlink.tab if devfsadm invoked with -c <class> */
6565 if (num_classes > 0) {
6566 return (FALSE);
6569 for (entry = devlinktab_list; entry != NULL; entry = entry->next) {
6570 if (devlink_matches(entry, minor, node) == DEVFSADM_SUCCESS) {
6571 link_built = TRUE;
6572 (void) build_links(entry, minor, node);
6575 return (link_built);
6579 * For a given devlink.tab devlinktab_list entry, see if the selector
6580 * field matches this minor node. If it does, return DEVFSADM_SUCCESS,
6581 * otherwise DEVFSADM_FAILURE.
6583 static int
6584 devlink_matches(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6586 selector_list_t *selector = entry->selector;
6587 char *addr;
6588 char *minor_name;
6589 char *node_type;
6591 for (; selector != NULL; selector = selector->next) {
6592 switch (selector->key) {
6593 case NAME:
6594 if (strcmp(di_node_name(node), selector->val) != 0) {
6595 return (DEVFSADM_FAILURE);
6597 break;
6598 case TYPE:
6599 node_type = di_minor_nodetype(minor);
6600 assert(node_type != NULL);
6601 if (strcmp(node_type, selector->val) != 0) {
6602 return (DEVFSADM_FAILURE);
6604 break;
6605 case ADDR:
6606 if ((addr = di_bus_addr(node)) == NULL) {
6607 return (DEVFSADM_FAILURE);
6609 if (selector->arg == 0) {
6610 if (strcmp(addr, selector->val) != 0) {
6611 return (DEVFSADM_FAILURE);
6613 } else {
6614 if (compare_field(addr, selector->val,
6615 selector->arg) == DEVFSADM_FAILURE) {
6616 return (DEVFSADM_FAILURE);
6619 break;
6620 case MINOR:
6621 if ((minor_name = di_minor_name(minor)) == NULL) {
6622 return (DEVFSADM_FAILURE);
6624 if (selector->arg == 0) {
6625 if (strcmp(minor_name, selector->val) != 0) {
6626 return (DEVFSADM_FAILURE);
6628 } else {
6629 if (compare_field(minor_name, selector->val,
6630 selector->arg) == DEVFSADM_FAILURE) {
6631 return (DEVFSADM_FAILURE);
6634 break;
6635 default:
6636 return (DEVFSADM_FAILURE);
6640 return (DEVFSADM_SUCCESS);
6644 * For the given minor node and devlinktab_list entry from devlink.tab,
6645 * build a logical dev link and a possible extra devlink.
6646 * Return DEVFSADM_SUCCESS if link is created, otherwise DEVFSADM_FAILURE.
6648 static int
6649 build_links(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6651 char secondary_link[PATH_MAX + 1];
6652 char primary_link[PATH_MAX + 1];
6653 char contents[PATH_MAX + 1];
6654 char *dev_path;
6656 if ((dev_path = di_devfs_path(node)) == NULL) {
6657 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
6658 devfsadm_exit(1);
6659 /*NOTREACHED*/
6661 (void) strcpy(contents, dev_path);
6662 di_devfs_path_free(dev_path);
6664 (void) strcat(contents, ":");
6665 (void) strcat(contents, di_minor_name(minor));
6667 if (construct_devlink(primary_link, entry->p_link, contents,
6668 minor, node, entry->p_link_pattern) == DEVFSADM_FAILURE) {
6669 return (DEVFSADM_FAILURE);
6671 (void) devfsadm_mklink(primary_link, node, minor, 0);
6673 if (entry->s_link == NULL) {
6674 return (DEVFSADM_SUCCESS);
6677 if (construct_devlink(secondary_link, entry->s_link, primary_link,
6678 minor, node, entry->s_link_pattern) == DEVFSADM_FAILURE) {
6679 return (DEVFSADM_FAILURE);
6682 (void) devfsadm_secondary_link(secondary_link, primary_link, 0);
6684 return (DEVFSADM_SUCCESS);
6688 * The counter rule for devlink.tab entries is implemented via
6689 * devfsadm_enumerate_int_start(). One of the arguments to this function
6690 * is a path, where each path component is treated as a regular expression.
6691 * For devlink.tab entries, this path regular expression is derived from
6692 * the devlink spec. get_anchored_re() accepts path regular expressions derived
6693 * from devlink.tab entries and inserts the anchors '^' and '$' at the beginning
6694 * and end respectively of each path component. This is done to prevent
6695 * false matches. For example, without anchors, "a/([0-9]+)" will match "ab/c9"
6696 * and incorrect links will be generated.
6698 static int
6699 get_anchored_re(char *link, char *anchored_re, char *pattern)
6701 if (*link == '/' || *link == '\0') {
6702 err_print(INVALID_DEVLINK_SPEC, pattern);
6703 return (DEVFSADM_FAILURE);
6706 *anchored_re++ = '^';
6707 for (; *link != '\0'; ) {
6708 if (*link == '/') {
6709 while (*link == '/')
6710 link++;
6711 *anchored_re++ = '$';
6712 *anchored_re++ = '/';
6713 if (*link != '\0') {
6714 *anchored_re++ = '^';
6716 } else {
6717 *anchored_re++ = *link++;
6718 if (*link == '\0') {
6719 *anchored_re++ = '$';
6723 *anchored_re = '\0';
6725 return (DEVFSADM_SUCCESS);
6728 static int
6729 construct_devlink(char *link, link_list_t *link_build, char *contents,
6730 di_minor_t minor, di_node_t node, char *pattern)
6732 int counter_offset = -1;
6733 devfsadm_enumerate_t rules[1] = {NULL};
6734 char templink[PATH_MAX + 1];
6735 char *buff;
6736 char start[10];
6737 char *node_path;
6738 char anchored_re[PATH_MAX + 1];
6740 link[0] = '\0';
6742 for (; link_build != NULL; link_build = link_build->next) {
6743 switch (link_build->type) {
6744 case NAME:
6745 (void) strcat(link, di_node_name(node));
6746 break;
6747 case CONSTANT:
6748 (void) strcat(link, link_build->constant);
6749 break;
6750 case ADDR:
6751 if (component_cat(link, di_bus_addr(node),
6752 link_build->arg) == DEVFSADM_FAILURE) {
6753 node_path = di_devfs_path(node);
6754 err_print(CANNOT_BE_USED, pattern, node_path,
6755 di_minor_name(minor));
6756 di_devfs_path_free(node_path);
6757 return (DEVFSADM_FAILURE);
6759 break;
6760 case MINOR:
6761 if (component_cat(link, di_minor_name(minor),
6762 link_build->arg) == DEVFSADM_FAILURE) {
6763 node_path = di_devfs_path(node);
6764 err_print(CANNOT_BE_USED, pattern, node_path,
6765 di_minor_name(minor));
6766 di_devfs_path_free(node_path);
6767 return (DEVFSADM_FAILURE);
6769 break;
6770 case COUNTER:
6771 counter_offset = strlen(link);
6772 (void) strcat(link, "([0-9]+)");
6773 (void) sprintf(start, "%d", link_build->arg);
6774 break;
6775 default:
6776 return (DEVFSADM_FAILURE);
6780 if (counter_offset != -1) {
6782 * copy anything appended after "([0-9]+)" into
6783 * templink
6786 (void) strcpy(templink,
6787 &link[counter_offset + strlen("([0-9]+)")]);
6788 if (get_anchored_re(link, anchored_re, pattern)
6789 != DEVFSADM_SUCCESS) {
6790 return (DEVFSADM_FAILURE);
6792 rules[0].re = anchored_re;
6793 rules[0].subexp = 1;
6794 rules[0].flags = MATCH_ALL;
6795 if (devfsadm_enumerate_int_start(contents, 0, &buff,
6796 rules, 1, start) == DEVFSADM_FAILURE) {
6797 return (DEVFSADM_FAILURE);
6799 (void) strcpy(&link[counter_offset], buff);
6800 free(buff);
6801 (void) strcat(link, templink);
6802 vprint(DEVLINK_MID, "COUNTER is %s\n", link);
6804 return (DEVFSADM_SUCCESS);
6808 * Compares "field" number of the comma separated list "full_name" with
6809 * field_item. Returns DEVFSADM_SUCCESS for match,
6810 * DEVFSADM_FAILURE for no match.
6812 static int
6813 compare_field(char *full_name, char *field_item, int field)
6815 --field;
6816 while ((*full_name != '\0') && (field != 0)) {
6817 if (*(full_name++) == ',') {
6818 field--;
6822 if (field != 0) {
6823 return (DEVFSADM_FAILURE);
6826 while ((*full_name != '\0') && (*field_item != '\0') &&
6827 (*full_name != ',')) {
6828 if (*(full_name++) != *(field_item++)) {
6829 return (DEVFSADM_FAILURE);
6833 if (*field_item != '\0') {
6834 return (DEVFSADM_FAILURE);
6837 if ((*full_name == '\0') || (*full_name == ','))
6838 return (DEVFSADM_SUCCESS);
6840 return (DEVFSADM_FAILURE);
6844 * strcat() field # "field" of comma separated list "name" to "link".
6845 * Field 0 is the entire name.
6846 * Return DEVFSADM_SUCCESS or DEVFSADM_FAILURE.
6848 static int
6849 component_cat(char *link, char *name, int field)
6852 if (name == NULL) {
6853 return (DEVFSADM_FAILURE);
6856 if (field == 0) {
6857 (void) strcat(link, name);
6858 return (DEVFSADM_SUCCESS);
6861 while (*link != '\0') {
6862 link++;
6865 --field;
6866 while ((*name != '\0') && (field != 0)) {
6867 if (*(name++) == ',') {
6868 --field;
6872 if (field != 0) {
6873 return (DEVFSADM_FAILURE);
6876 while ((*name != '\0') && (*name != ',')) {
6877 *(link++) = *(name++);
6880 *link = '\0';
6881 return (DEVFSADM_SUCCESS);
6884 static void
6885 free_selector_list(selector_list_t *head)
6887 selector_list_t *temp;
6889 while (head != NULL) {
6890 temp = head;
6891 head = head->next;
6892 free(temp->val);
6893 free(temp);
6897 static void
6898 free_link_list(link_list_t *head)
6900 link_list_t *temp;
6902 while (head != NULL) {
6903 temp = head;
6904 head = head->next;
6905 if (temp->type == CONSTANT) {
6906 free(temp->constant);
6908 free(temp);
6913 * Prints only if level matches one of the debug levels
6914 * given on command line. INFO_MID is always printed.
6916 * See devfsadm.h for a listing of globally defined levels and
6917 * meanings. Modules should prefix the level with their
6918 * module name to prevent collisions.
6920 /*PRINTFLIKE2*/
6921 void
6922 devfsadm_print(char *msgid, char *message, ...)
6924 va_list ap;
6925 static int newline = TRUE;
6926 int x;
6928 if (msgid != NULL) {
6929 for (x = 0; x < num_verbose; x++) {
6930 if (strcmp(verbose[x], msgid) == 0) {
6931 break;
6933 if (strcmp(verbose[x], ALL_MID) == 0) {
6934 break;
6937 if (x == num_verbose) {
6938 return;
6942 va_start(ap, message);
6944 if (msgid == NULL) {
6945 if (logflag == TRUE) {
6946 (void) vsyslog(LOG_NOTICE, message, ap);
6947 } else {
6948 (void) vfprintf(stdout, message, ap);
6951 } else {
6952 if (logflag == TRUE) {
6953 (void) syslog(LOG_DEBUG, "%s[%ld]: %s: ",
6954 prog, getpid(), msgid);
6955 (void) vsyslog(LOG_DEBUG, message, ap);
6956 } else {
6957 if (newline == TRUE) {
6958 (void) fprintf(stdout, "%s[%ld]: %s: ",
6959 prog, getpid(), msgid);
6961 (void) vfprintf(stdout, message, ap);
6965 if (message[strlen(message) - 1] == '\n') {
6966 newline = TRUE;
6967 } else {
6968 newline = FALSE;
6970 va_end(ap);
6974 * print error messages to the terminal or to syslog
6976 /*PRINTFLIKE1*/
6977 void
6978 devfsadm_errprint(char *message, ...)
6980 va_list ap;
6982 va_start(ap, message);
6984 if (logflag == TRUE) {
6985 (void) vsyslog(LOG_ERR, message, ap);
6986 } else {
6987 (void) fprintf(stderr, "%s: ", prog);
6988 (void) vfprintf(stderr, message, ap);
6990 va_end(ap);
6994 * return noupdate state (-s)
6997 devfsadm_noupdate(void)
6999 return (file_mods == TRUE ? DEVFSADM_TRUE : DEVFSADM_FALSE);
7003 * return current root update path (-r)
7005 const char *
7006 devfsadm_root_path(void)
7008 if (root_dir[0] == '\0') {
7009 return ("/");
7010 } else {
7011 return ((const char *)root_dir);
7015 void
7016 devfsadm_free_dev_names(char **dev_names, int len)
7018 int i;
7020 for (i = 0; i < len; i++)
7021 free(dev_names[i]);
7022 free(dev_names);
7026 * Return all devlinks corresponding to phys_path as an array of strings.
7027 * The number of entries in the array is returned through lenp.
7028 * devfsadm_free_dev_names() is used to free the returned array.
7029 * NULL is returned on failure or when there are no matching devlinks.
7031 * re is an extended regular expression in regex(5) format used to further
7032 * match devlinks pointing to phys_path; it may be NULL to match all
7034 char **
7035 devfsadm_lookup_dev_names(char *phys_path, char *re, int *lenp)
7037 struct devlink_cb_arg cb_arg;
7038 char **dev_names = NULL;
7039 int i;
7041 *lenp = 0;
7042 cb_arg.count = 0;
7043 cb_arg.rv = 0;
7044 (void) di_devlink_cache_walk(devlink_cache, re, phys_path,
7045 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7047 if (cb_arg.rv == -1 || cb_arg.count <= 0)
7048 return (NULL);
7050 dev_names = s_malloc(cb_arg.count * sizeof (char *));
7051 if (dev_names == NULL)
7052 goto out;
7054 for (i = 0; i < cb_arg.count; i++) {
7055 dev_names[i] = s_strdup(cb_arg.dev_names[i]);
7056 if (dev_names[i] == NULL) {
7057 devfsadm_free_dev_names(dev_names, i);
7058 dev_names = NULL;
7059 goto out;
7062 *lenp = cb_arg.count;
7064 out:
7065 free_dev_names(&cb_arg);
7066 return (dev_names);
7069 /* common exit function which ensures releasing locks */
7070 static void
7071 devfsadm_exit(int status)
7073 if (DEVFSADM_DEBUG_ON) {
7074 vprint(INFO_MID, "exit status = %d\n", status);
7077 exit_dev_lock(1);
7078 exit_daemon_lock(1);
7080 if (logflag == TRUE) {
7081 closelog();
7084 exit(status);
7085 /*NOTREACHED*/
7089 * set root_dir, devices_dir, dev_dir using optarg.
7091 static void
7092 set_root_devices_dev_dir(char *dir)
7094 size_t len;
7096 root_dir = s_strdup(dir);
7097 len = strlen(dir) + strlen(DEVICES) + 1;
7098 devices_dir = s_malloc(len);
7099 (void) snprintf(devices_dir, len, "%s%s", root_dir, DEVICES);
7100 len = strlen(root_dir) + strlen(DEV) + 1;
7101 dev_dir = s_malloc(len);
7102 (void) snprintf(dev_dir, len, "%s%s", root_dir, DEV);
7106 * Removes quotes.
7108 static char *
7109 dequote(char *src)
7111 char *dst;
7112 int len;
7114 len = strlen(src);
7115 dst = s_malloc(len + 1);
7116 if (src[0] == '\"' && src[len - 1] == '\"') {
7117 len -= 2;
7118 (void) strncpy(dst, &src[1], len);
7119 dst[len] = '\0';
7120 } else {
7121 (void) strcpy(dst, src);
7123 return (dst);
7127 * For a given physical device pathname and spectype, return the
7128 * ownership and permissions attributes by looking in data from
7129 * /etc/minor_perm. If currently in installation mode, check for
7130 * possible major number translations from the miniroot to the installed
7131 * root's name_to_major table. Note that there can be multiple matches,
7132 * but the last match takes effect. pts seems to rely on this
7133 * implementation behavior.
7135 static void
7136 getattr(char *phy_path, char *aminor, int spectype, dev_t dev, mode_t *mode,
7137 uid_t *uid, gid_t *gid)
7139 char devname[PATH_MAX + 1];
7140 char *node_name;
7141 char *minor_name;
7142 int match = FALSE;
7143 int is_clone;
7144 int mp_drvname_matches_node_name;
7145 int mp_drvname_matches_minor_name;
7146 int mp_drvname_is_clone;
7147 int mp_drvname_matches_drvname;
7148 struct mperm *mp;
7149 major_t major_no;
7150 char driver[PATH_MAX + 1];
7153 * Get the driver name based on the major number since the name
7154 * in /devices may be generic. Could be running with more major
7155 * numbers than are in /etc/name_to_major, so get it from the kernel
7157 major_no = major(dev);
7159 if (modctl(MODGETNAME, driver, sizeof (driver), &major_no) != 0) {
7160 /* return default values */
7161 goto use_defaults;
7164 (void) strcpy(devname, phy_path);
7166 node_name = strrchr(devname, '/'); /* node name is the last */
7167 /* component */
7168 if (node_name == NULL) {
7169 err_print(NO_NODE, devname);
7170 goto use_defaults;
7173 minor_name = strchr(++node_name, '@'); /* see if it has address part */
7175 if (minor_name != NULL) {
7176 *minor_name++ = '\0';
7177 } else {
7178 minor_name = node_name;
7181 minor_name = strchr(minor_name, ':'); /* look for minor name */
7183 if (minor_name == NULL) {
7184 err_print(NO_MINOR, devname);
7185 goto use_defaults;
7187 *minor_name++ = '\0';
7190 * mp->mp_drvname = device name from minor_perm
7191 * mp->mp_minorname = minor part of device name from
7192 * minor_perm
7193 * drvname = name of driver for this device
7196 is_clone = (strcmp(node_name, "clone") == 0 ? TRUE : FALSE);
7197 for (mp = minor_perms; mp != NULL; mp = mp->mp_next) {
7198 mp_drvname_matches_node_name =
7199 (strcmp(mp->mp_drvname, node_name) == 0 ? TRUE : FALSE);
7200 mp_drvname_matches_minor_name =
7201 (strcmp(mp->mp_drvname, minor_name) == 0 ? TRUE:FALSE);
7202 mp_drvname_is_clone =
7203 (strcmp(mp->mp_drvname, "clone") == 0 ? TRUE : FALSE);
7204 mp_drvname_matches_drvname =
7205 (strcmp(mp->mp_drvname, driver) == 0 ? TRUE : FALSE);
7208 * If one of the following cases is true, then we try to change
7209 * the permissions if a "shell global pattern match" of
7210 * mp_>mp_minorname matches minor_name.
7212 * 1. mp->mp_drvname matches driver.
7214 * OR
7216 * 2. mp->mp_drvname matches node_name and this
7217 * name is an alias of the driver name
7219 * OR
7221 * 3. /devices entry is the clone device and either
7222 * minor_perm entry is the clone device or matches
7223 * the minor part of the clone device.
7226 if ((mp_drvname_matches_drvname == TRUE)||
7227 ((mp_drvname_matches_node_name == TRUE) &&
7228 (alias(driver, node_name) == TRUE)) ||
7229 ((is_clone == TRUE) &&
7230 ((mp_drvname_is_clone == TRUE) ||
7231 (mp_drvname_matches_minor_name == TRUE)))) {
7233 * Check that the minor part of the
7234 * device name from the minor_perm
7235 * entry matches and if so, set the
7236 * permissions.
7238 * Under real devfs, clone minor name is changed
7239 * to match the driver name, but minor_perm may
7240 * not match. We reconcile it here.
7242 if (aminor != NULL)
7243 minor_name = aminor;
7245 if (gmatch(minor_name, mp->mp_minorname) != 0) {
7246 *uid = mp->mp_uid;
7247 *gid = mp->mp_gid;
7248 *mode = spectype | mp->mp_mode;
7249 match = TRUE;
7254 if (match == TRUE) {
7255 return;
7258 use_defaults:
7259 /* not found in minor_perm, so just use default values */
7260 *uid = root_uid;
7261 *gid = sys_gid;
7262 *mode = (spectype | 0600);
7266 * Called by devfs_read_minor_perm() to report errors
7267 * key is:
7268 * line number: ignoring line number error
7269 * errno: open/close errors
7270 * size: alloc errors
7272 static void
7273 minorperm_err_cb(minorperm_err_t mp_err, int key)
7275 switch (mp_err) {
7276 case MP_FOPEN_ERR:
7277 err_print(FOPEN_FAILED, MINOR_PERM_FILE, strerror(key));
7278 break;
7279 case MP_FCLOSE_ERR:
7280 err_print(FCLOSE_FAILED, MINOR_PERM_FILE, strerror(key));
7281 break;
7282 case MP_IGNORING_LINE_ERR:
7283 err_print(IGNORING_LINE_IN, key, MINOR_PERM_FILE);
7284 break;
7285 case MP_ALLOC_ERR:
7286 err_print(MALLOC_FAILED, key);
7287 break;
7288 case MP_NVLIST_ERR:
7289 err_print(NVLIST_ERROR, MINOR_PERM_FILE, strerror(key));
7290 break;
7291 case MP_CANT_FIND_USER_ERR:
7292 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
7293 break;
7294 case MP_CANT_FIND_GROUP_ERR:
7295 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
7296 break;
7300 static void
7301 read_minor_perm_file(void)
7303 static int cached = FALSE;
7304 static struct stat cached_sb;
7305 struct stat current_sb;
7307 (void) stat(MINOR_PERM_FILE, &current_sb);
7309 /* If already cached, check to see if it is still valid */
7310 if (cached == TRUE) {
7312 if (current_sb.st_mtime == cached_sb.st_mtime) {
7313 vprint(FILES_MID, "%s cache valid\n", MINOR_PERM_FILE);
7314 return;
7316 devfs_free_minor_perm(minor_perms);
7317 minor_perms = NULL;
7318 } else {
7319 cached = TRUE;
7322 (void) stat(MINOR_PERM_FILE, &cached_sb);
7324 vprint(FILES_MID, "loading binding file: %s\n", MINOR_PERM_FILE);
7326 minor_perms = devfs_read_minor_perm(minorperm_err_cb);
7329 static void
7330 load_minor_perm_file(void)
7332 read_minor_perm_file();
7333 if (devfs_load_minor_perm(minor_perms, minorperm_err_cb) != 0)
7334 err_print(gettext("minor_perm load failed\n"));
7337 static char *
7338 convert_to_re(char *dev)
7340 char *p, *l, *out;
7341 int i;
7343 out = s_malloc(PATH_MAX);
7345 for (l = p = dev, i = 0; (*p != '\0') && (i < (PATH_MAX - 1));
7346 ++p, i++) {
7347 if ((*p == '*') && ((l != p) && (*l == '/'))) {
7348 out[i++] = '.';
7349 out[i] = '+';
7350 } else {
7351 out[i] = *p;
7353 l = p;
7355 out[i] = '\0';
7356 p = (char *)s_malloc(strlen(out) + 1);
7357 (void) strlcpy(p, out, strlen(out) + 1);
7358 free(out);
7360 vprint(FILES_MID, "converted %s -> %s\n", dev, p);
7362 return (p);
7365 static void
7366 read_logindevperm_file(void)
7368 static int cached = FALSE;
7369 static struct stat cached_sb;
7370 struct stat current_sb;
7371 struct login_dev *ldev;
7372 FILE *fp;
7373 char line[MAX_LDEV_LINE];
7374 int ln, perm, rv;
7375 char *cp, *console, *dlist, *dev;
7376 char *lasts, *devlasts, *permstr, *drv;
7377 struct driver_list *list, *next;
7379 /* Read logindevperm only when enabled */
7380 if (login_dev_enable != TRUE)
7381 return;
7383 if (cached == TRUE) {
7384 if (stat(LDEV_FILE, &current_sb) == 0 &&
7385 current_sb.st_mtime == cached_sb.st_mtime) {
7386 vprint(FILES_MID, "%s cache valid\n", LDEV_FILE);
7387 return;
7389 vprint(FILES_MID, "invalidating %s cache\n", LDEV_FILE);
7390 while (login_dev_cache != NULL) {
7392 ldev = login_dev_cache;
7393 login_dev_cache = ldev->ldev_next;
7394 free(ldev->ldev_console);
7395 free(ldev->ldev_device);
7396 regfree(&ldev->ldev_device_regex);
7397 list = ldev->ldev_driver_list;
7398 while (list) {
7399 next = list->next;
7400 free(list);
7401 list = next;
7403 free(ldev);
7405 } else {
7406 cached = TRUE;
7409 assert(login_dev_cache == NULL);
7411 if (stat(LDEV_FILE, &cached_sb) != 0) {
7412 cached = FALSE;
7413 return;
7416 vprint(FILES_MID, "loading file: %s\n", LDEV_FILE);
7418 if ((fp = fopen(LDEV_FILE, "r")) == NULL) {
7419 /* Not fatal to devfsadm */
7420 cached = FALSE;
7421 err_print(FOPEN_FAILED, LDEV_FILE, strerror(errno));
7422 return;
7425 ln = 0;
7426 while (fgets(line, MAX_LDEV_LINE, fp) != NULL) {
7427 ln++;
7429 /* Remove comments */
7430 if ((cp = strchr(line, '#')) != NULL)
7431 *cp = '\0';
7433 if ((console = strtok_r(line, LDEV_DELIMS, &lasts)) == NULL)
7434 continue; /* Blank line */
7436 if ((permstr = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7437 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7438 continue; /* Malformed line */
7442 * permstr is string in octal format. Convert to int
7444 cp = NULL;
7445 errno = 0;
7446 perm = strtol(permstr, &cp, 8);
7447 if (errno || perm < 0 || perm > 0777 || *cp != '\0') {
7448 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7449 continue;
7452 if ((dlist = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7453 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7454 continue;
7457 dev = strtok_r(dlist, LDEV_DEV_DELIM, &devlasts);
7458 while (dev) {
7460 ldev = (struct login_dev *)s_zalloc(
7461 sizeof (struct login_dev));
7462 ldev->ldev_console = s_strdup(console);
7463 ldev->ldev_perms = perm;
7466 * the logical device name may contain '*' which
7467 * we convert to a regular expression
7469 ldev->ldev_device = convert_to_re(dev);
7470 if (ldev->ldev_device &&
7471 (rv = regcomp(&ldev->ldev_device_regex,
7472 ldev->ldev_device, REG_EXTENDED))) {
7473 bzero(&ldev->ldev_device_regex,
7474 sizeof (ldev->ldev_device_regex));
7475 err_print(REGCOMP_FAILED,
7476 ldev->ldev_device, rv);
7478 ldev->ldev_next = login_dev_cache;
7479 login_dev_cache = ldev;
7480 dev = strtok_r(NULL, LDEV_DEV_DELIM, &devlasts);
7483 drv = strtok_r(NULL, LDEV_DRVLIST_DELIMS, &lasts);
7484 if (drv) {
7485 if (strcmp(drv, LDEV_DRVLIST_NAME) == 0) {
7487 drv = strtok_r(NULL, LDEV_DRV_DELIMS, &lasts);
7489 while (drv) {
7490 vprint(FILES_MID,
7491 "logindevperm driver=%s\n", drv);
7494 * create a linked list of driver
7495 * names
7497 list = (struct driver_list *)
7498 s_zalloc(
7499 sizeof (struct driver_list));
7500 (void) strlcpy(list->driver_name, drv,
7501 sizeof (list->driver_name));
7502 list->next = ldev->ldev_driver_list;
7503 ldev->ldev_driver_list = list;
7504 drv = strtok_r(NULL, LDEV_DRV_DELIMS,
7505 &lasts);
7510 (void) fclose(fp);
7514 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
7516 * Returns DEVFSADM_SUCCESS if token found, DEVFSADM_FAILURE otherwise.
7518 static int
7519 getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar)
7521 char *cp;
7522 char *cp1;
7523 char *tokenp;
7525 cp = next;
7526 while (*cp == ' ' || *cp == '\t') {
7527 cp++; /* skip leading spaces */
7529 tokenp = cp; /* start of token */
7530 while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' &&
7531 *cp != ':' && *cp != '=' && *cp != '&' &&
7532 *cp != '|' && *cp != ';') {
7533 cp++; /* point to next character */
7536 * If terminating character is a space or tab, look ahead to see if
7537 * there's another terminator that's not a space or a tab.
7538 * (This code handles trailing spaces.)
7540 if (*cp == ' ' || *cp == '\t') {
7541 cp1 = cp;
7542 while (*++cp1 == ' ' || *cp1 == '\t')
7544 if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' ||
7545 *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') {
7546 *cp = NULL; /* terminate token */
7547 cp = cp1;
7550 if (tchar != NULL) {
7551 *tchar = *cp; /* save terminating character */
7552 if (*tchar == '\0') {
7553 *tchar = '\n';
7556 *cp++ = '\0'; /* terminate token, point to next */
7557 *nextp = cp; /* set pointer to next character */
7558 if (cp - tokenp - 1 == 0) {
7559 return (DEVFSADM_FAILURE);
7561 *tokenpp = tokenp;
7562 return (DEVFSADM_SUCCESS);
7566 * read or reread the driver aliases file
7568 static void
7569 read_driver_aliases_file(void)
7572 driver_alias_t *save;
7573 driver_alias_t *lst_tail;
7574 driver_alias_t *ap;
7575 static int cached = FALSE;
7576 FILE *afd;
7577 char line[256];
7578 char *cp;
7579 char *p;
7580 char t;
7581 int ln = 0;
7582 static struct stat cached_sb;
7583 struct stat current_sb;
7585 (void) stat(ALIASFILE, &current_sb);
7587 /* If already cached, check to see if it is still valid */
7588 if (cached == TRUE) {
7590 if (current_sb.st_mtime == cached_sb.st_mtime) {
7591 vprint(FILES_MID, "%s cache valid\n", ALIASFILE);
7592 return;
7595 vprint(FILES_MID, "invalidating %s cache\n", ALIASFILE);
7596 while (driver_aliases != NULL) {
7597 free(driver_aliases->alias_name);
7598 free(driver_aliases->driver_name);
7599 save = driver_aliases;
7600 driver_aliases = driver_aliases->next;
7601 free(save);
7603 } else {
7604 cached = TRUE;
7607 (void) stat(ALIASFILE, &cached_sb);
7609 vprint(FILES_MID, "loading binding file: %s\n", ALIASFILE);
7611 if ((afd = fopen(ALIASFILE, "r")) == NULL) {
7612 err_print(FOPEN_FAILED, ALIASFILE, strerror(errno));
7613 devfsadm_exit(1);
7614 /*NOTREACHED*/
7617 while (fgets(line, sizeof (line), afd) != NULL) {
7618 ln++;
7619 /* cut off comments starting with '#' */
7620 if ((cp = strchr(line, '#')) != NULL)
7621 *cp = '\0';
7622 /* ignore comment or blank lines */
7623 if (is_blank(line))
7624 continue;
7625 cp = line;
7626 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7627 err_print(IGNORING_LINE_IN, ln, ALIASFILE);
7628 continue;
7630 if (t == '\n' || t == '\0') {
7631 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7632 continue;
7634 ap = (struct driver_alias *)
7635 s_zalloc(sizeof (struct driver_alias));
7636 ap->driver_name = s_strdup(p);
7637 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7638 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7639 free(ap->driver_name);
7640 free(ap);
7641 continue;
7643 if (*p == '"') {
7644 if (p[strlen(p) - 1] == '"') {
7645 p[strlen(p) - 1] = '\0';
7646 p++;
7649 ap->alias_name = s_strdup(p);
7650 if (driver_aliases == NULL) {
7651 driver_aliases = ap;
7652 lst_tail = ap;
7653 } else {
7654 lst_tail->next = ap;
7655 lst_tail = ap;
7658 if (fclose(afd) == EOF) {
7659 err_print(FCLOSE_FAILED, ALIASFILE, strerror(errno));
7664 * return TRUE if alias_name is an alias for driver_name, otherwise
7665 * return FALSE.
7667 static int
7668 alias(char *driver_name, char *alias_name)
7670 driver_alias_t *alias;
7673 * check for a match
7675 for (alias = driver_aliases; alias != NULL; alias = alias->next) {
7676 if ((strcmp(alias->driver_name, driver_name) == 0) &&
7677 (strcmp(alias->alias_name, alias_name) == 0)) {
7678 return (TRUE);
7681 return (FALSE);
7685 * convenience functions
7687 static int
7688 s_stat(const char *path, struct stat *sbufp)
7690 int rv;
7691 retry:
7692 if ((rv = stat(path, sbufp)) == -1) {
7693 if (errno == EINTR)
7694 goto retry;
7696 return (rv);
7699 static void *
7700 s_malloc(const size_t size)
7702 void *rp;
7704 rp = malloc(size);
7705 if (rp == NULL) {
7706 err_print(MALLOC_FAILED, size);
7707 devfsadm_exit(1);
7708 /*NOTREACHED*/
7710 return (rp);
7714 * convenience functions
7716 static void *
7717 s_realloc(void *ptr, const size_t size)
7719 ptr = realloc(ptr, size);
7720 if (ptr == NULL) {
7721 err_print(REALLOC_FAILED, size);
7722 devfsadm_exit(1);
7723 /*NOTREACHED*/
7725 return (ptr);
7728 static void *
7729 s_zalloc(const size_t size)
7731 void *rp;
7733 rp = calloc(1, size);
7734 if (rp == NULL) {
7735 err_print(CALLOC_FAILED, size);
7736 devfsadm_exit(1);
7737 /*NOTREACHED*/
7739 return (rp);
7742 char *
7743 s_strdup(const char *ptr)
7745 void *rp;
7747 rp = strdup(ptr);
7748 if (rp == NULL) {
7749 err_print(STRDUP_FAILED, ptr);
7750 devfsadm_exit(1);
7751 /*NOTREACHED*/
7753 return (rp);
7756 static void
7757 s_closedir(DIR *dirp)
7759 retry:
7760 if (closedir(dirp) != 0) {
7761 if (errno == EINTR)
7762 goto retry;
7763 err_print(CLOSEDIR_FAILED, strerror(errno));
7767 static void
7768 s_mkdirp(const char *path, const mode_t mode)
7770 vprint(CHATTY_MID, "mkdirp(%s, 0x%lx)\n", path, mode);
7771 if (mkdirp(path, mode) == -1) {
7772 if (errno != EEXIST) {
7773 err_print(MKDIR_FAILED, path, mode, strerror(errno));
7778 static void
7779 s_unlink(const char *file)
7781 retry:
7782 if (unlink(file) == -1) {
7783 if (errno == EINTR || errno == EAGAIN)
7784 goto retry;
7785 if (errno != ENOENT) {
7786 err_print(UNLINK_FAILED, file, strerror(errno));
7791 static void
7792 add_verbose_id(char *mid)
7794 num_verbose++;
7795 verbose = s_realloc(verbose, num_verbose * sizeof (char *));
7796 verbose[num_verbose - 1] = mid;
7800 * returns DEVFSADM_TRUE if contents is a minor node in /devices.
7801 * If mn_root is not NULL, mn_root is set to:
7802 * if contents is a /dev node, mn_root = contents
7803 * OR
7804 * if contents is a /devices node, mn_root set to the '/'
7805 * following /devices.
7807 static int
7808 is_minor_node(char *contents, char **mn_root)
7810 char *ptr;
7811 char device_prefix[100];
7813 (void) snprintf(device_prefix, sizeof (device_prefix), "../devices/");
7815 if ((ptr = strstr(contents, device_prefix)) != NULL) {
7816 if (mn_root != NULL) {
7817 /* mn_root should point to the / following /devices */
7818 *mn_root = ptr += strlen(device_prefix) - 1;
7820 return (DEVFSADM_TRUE);
7823 (void) snprintf(device_prefix, sizeof (device_prefix), "/devices/");
7825 if (strncmp(contents, device_prefix, strlen(device_prefix)) == 0) {
7826 if (mn_root != NULL) {
7827 /* mn_root should point to the / following /devices */
7828 *mn_root = contents + strlen(device_prefix) - 1;
7830 return (DEVFSADM_TRUE);
7833 if (mn_root != NULL) {
7834 *mn_root = contents;
7836 return (DEVFSADM_FALSE);
7840 * Add the specified property to nvl.
7841 * Returns:
7842 * 0 successfully added
7843 * -1 an error occurred
7844 * 1 could not add the property for reasons not due to errors.
7846 static int
7847 add_property(nvlist_t *nvl, di_prop_t prop)
7849 char *name;
7850 char *attr_name;
7851 int n, len;
7852 int32_t *int32p;
7853 int64_t *int64p;
7854 char *str;
7855 char **strarray;
7856 uchar_t *bytep;
7857 int rv = 0;
7858 int i;
7860 if ((name = di_prop_name(prop)) == NULL)
7861 return (-1);
7863 len = sizeof (DEV_PROP_PREFIX) + strlen(name);
7864 if ((attr_name = malloc(len)) == NULL)
7865 return (-1);
7867 (void) strlcpy(attr_name, DEV_PROP_PREFIX, len);
7868 (void) strlcat(attr_name, name, len);
7870 switch (di_prop_type(prop)) {
7871 case DI_PROP_TYPE_BOOLEAN:
7872 if (nvlist_add_boolean(nvl, attr_name) != 0)
7873 goto out;
7874 break;
7876 case DI_PROP_TYPE_INT:
7877 if ((n = di_prop_ints(prop, &int32p)) < 1)
7878 goto out;
7880 if (n <= (PROP_LEN_LIMIT / sizeof (int32_t))) {
7881 if (nvlist_add_int32_array(nvl, attr_name, int32p,
7882 n) != 0)
7883 goto out;
7884 } else
7885 rv = 1;
7886 break;
7888 case DI_PROP_TYPE_INT64:
7889 if ((n = di_prop_int64(prop, &int64p)) < 1)
7890 goto out;
7892 if (n <= (PROP_LEN_LIMIT / sizeof (int64_t))) {
7893 if (nvlist_add_int64_array(nvl, attr_name, int64p,
7894 n) != 0)
7895 goto out;
7896 } else
7897 rv = 1;
7898 break;
7900 case DI_PROP_TYPE_BYTE:
7901 case DI_PROP_TYPE_UNKNOWN:
7902 if ((n = di_prop_bytes(prop, &bytep)) < 1)
7903 goto out;
7905 if (n <= PROP_LEN_LIMIT) {
7906 if (nvlist_add_byte_array(nvl, attr_name, bytep, n)
7907 != 0)
7908 goto out;
7909 } else
7910 rv = 1;
7911 break;
7913 case DI_PROP_TYPE_STRING:
7914 if ((n = di_prop_strings(prop, &str)) < 1)
7915 goto out;
7917 if ((strarray = malloc(n * sizeof (char *))) == NULL)
7918 goto out;
7920 len = 0;
7921 for (i = 0; i < n; i++) {
7922 strarray[i] = str + len;
7923 len += strlen(strarray[i]) + 1;
7926 if (len <= PROP_LEN_LIMIT) {
7927 if (nvlist_add_string_array(nvl, attr_name, strarray,
7928 n) != 0) {
7929 free(strarray);
7930 goto out;
7932 } else
7933 rv = 1;
7934 free(strarray);
7935 break;
7937 default:
7938 rv = 1;
7939 break;
7942 free(attr_name);
7943 return (rv);
7945 out:
7946 free(attr_name);
7947 return (-1);
7950 static void
7951 free_dev_names(struct devlink_cb_arg *x)
7953 int i;
7955 for (i = 0; i < x->count; i++) {
7956 free(x->dev_names[i]);
7957 free(x->link_contents[i]);
7961 /* callback function for di_devlink_cache_walk */
7962 static int
7963 devlink_cb(di_devlink_t dl, void *arg)
7965 struct devlink_cb_arg *x = (struct devlink_cb_arg *)arg;
7966 const char *path;
7967 const char *content;
7969 if ((path = di_devlink_path(dl)) == NULL ||
7970 (content = di_devlink_content(dl)) == NULL ||
7971 (x->dev_names[x->count] = s_strdup(path)) == NULL)
7972 goto out;
7974 if ((x->link_contents[x->count] = s_strdup(content)) == NULL) {
7975 free(x->dev_names[x->count]);
7976 goto out;
7979 x->count++;
7980 if (x->count >= MAX_DEV_NAME_COUNT)
7981 return (DI_WALK_TERMINATE);
7983 return (DI_WALK_CONTINUE);
7985 out:
7986 x->rv = -1;
7987 free_dev_names(x);
7988 return (DI_WALK_TERMINATE);
7992 * Lookup dev name corresponding to the phys_path.
7993 * phys_path is path to a node or minor node.
7994 * Returns:
7995 * 0 with *dev_name set to the dev name
7996 * Lookup succeeded and dev_name found
7997 * 0 with *dev_name set to NULL
7998 * Lookup encountered no errors but dev name not found
7999 * -1
8000 * Lookup failed
8002 static int
8003 lookup_dev_name(char *phys_path, char **dev_name)
8005 struct devlink_cb_arg cb_arg;
8007 *dev_name = NULL;
8009 cb_arg.count = 0;
8010 cb_arg.rv = 0;
8011 (void) di_devlink_cache_walk(devlink_cache, NULL, phys_path,
8012 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8014 if (cb_arg.rv == -1)
8015 return (-1);
8017 if (cb_arg.count > 0) {
8018 *dev_name = s_strdup(cb_arg.dev_names[0]);
8019 free_dev_names(&cb_arg);
8020 if (*dev_name == NULL)
8021 return (-1);
8024 return (0);
8027 static char *
8028 lookup_disk_dev_name(char *node_path)
8030 struct devlink_cb_arg cb_arg;
8031 char *dev_name = NULL;
8032 int i;
8033 char *p;
8034 int len1, len2;
8036 #define DEV_RDSK "/dev/rdsk/"
8037 #define DISK_RAW_MINOR ",raw"
8039 cb_arg.count = 0;
8040 cb_arg.rv = 0;
8041 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8042 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8044 if (cb_arg.rv == -1 || cb_arg.count == 0)
8045 return (NULL);
8047 /* first try lookup based on /dev/rdsk name */
8048 for (i = 0; i < cb_arg.count; i++) {
8049 if (strncmp(cb_arg.dev_names[i], DEV_RDSK,
8050 sizeof (DEV_RDSK) - 1) == 0) {
8051 dev_name = s_strdup(cb_arg.dev_names[i]);
8052 break;
8056 if (dev_name == NULL) {
8057 /* now try lookup based on a minor name ending with ",raw" */
8058 len1 = sizeof (DISK_RAW_MINOR) - 1;
8059 for (i = 0; i < cb_arg.count; i++) {
8060 len2 = strlen(cb_arg.link_contents[i]);
8061 if (len2 >= len1 &&
8062 strcmp(cb_arg.link_contents[i] + len2 - len1,
8063 DISK_RAW_MINOR) == 0) {
8064 dev_name = s_strdup(cb_arg.dev_names[i]);
8065 break;
8070 free_dev_names(&cb_arg);
8072 if (dev_name == NULL)
8073 return (NULL);
8074 if (strlen(dev_name) == 0) {
8075 free(dev_name);
8076 return (NULL);
8079 /* if the name contains slice or partition number strip it */
8080 p = dev_name + strlen(dev_name) - 1;
8081 if (isdigit(*p)) {
8082 while (p != dev_name && isdigit(*p))
8083 p--;
8084 if (*p == 's' || *p == 'p')
8085 *p = '\0';
8088 return (dev_name);
8091 static char *
8092 lookup_lofi_dev_name(char *node_path, char *minor)
8094 struct devlink_cb_arg cb_arg;
8095 char *dev_name = NULL;
8096 int i;
8097 int len1, len2;
8099 cb_arg.count = 0;
8100 cb_arg.rv = 0;
8101 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8102 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8104 if (cb_arg.rv == -1 || cb_arg.count == 0)
8105 return (NULL);
8107 /* lookup based on a minor name ending with ",raw" */
8108 len1 = strlen(minor);
8109 for (i = 0; i < cb_arg.count; i++) {
8110 len2 = strlen(cb_arg.link_contents[i]);
8111 if (len2 >= len1 &&
8112 strcmp(cb_arg.link_contents[i] + len2 - len1,
8113 minor) == 0) {
8114 dev_name = s_strdup(cb_arg.dev_names[i]);
8115 break;
8119 free_dev_names(&cb_arg);
8121 if (dev_name == NULL)
8122 return (NULL);
8123 if (strlen(dev_name) == 0) {
8124 free(dev_name);
8125 return (NULL);
8128 return (dev_name);
8131 static char *
8132 lookup_network_dev_name(char *node_path, char *driver_name)
8134 char *dev_name = NULL;
8135 char phys_path[MAXPATHLEN];
8137 if (lookup_dev_name(node_path, &dev_name) == -1)
8138 return (NULL);
8140 if (dev_name == NULL) {
8141 /* dlpi style-2 only interface */
8142 (void) snprintf(phys_path, sizeof (phys_path),
8143 "/pseudo/clone@0:%s", driver_name);
8144 if (lookup_dev_name(phys_path, &dev_name) == -1 ||
8145 dev_name == NULL)
8146 return (NULL);
8149 return (dev_name);
8152 static char *
8153 lookup_printer_dev_name(char *node_path)
8155 struct devlink_cb_arg cb_arg;
8156 char *dev_name = NULL;
8157 int i;
8159 #define DEV_PRINTERS "/dev/printers/"
8161 cb_arg.count = 0;
8162 cb_arg.rv = 0;
8163 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8164 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8166 if (cb_arg.rv == -1 || cb_arg.count == 0)
8167 return (NULL);
8169 /* first try lookup based on /dev/printers name */
8170 for (i = 0; i < cb_arg.count; i++) {
8171 if (strncmp(cb_arg.dev_names[i], DEV_PRINTERS,
8172 sizeof (DEV_PRINTERS) - 1) == 0) {
8173 dev_name = s_strdup(cb_arg.dev_names[i]);
8174 break;
8178 /* fallback to the first name */
8179 if ((dev_name == NULL) && (cb_arg.count > 0))
8180 dev_name = s_strdup(cb_arg.dev_names[0]);
8182 free_dev_names(&cb_arg);
8184 return (dev_name);
8188 * Build an nvlist containing all attributes for devfs events.
8189 * Returns nvlist pointer on success, NULL on failure.
8191 static nvlist_t *
8192 build_event_attributes(char *class, char *subclass, char *node_path,
8193 di_node_t node, char *driver_name, int instance, char *minor)
8195 nvlist_t *nvl;
8196 int err = 0;
8197 di_prop_t prop;
8198 int count;
8199 char *prop_name;
8200 int x;
8201 char *dev_name = NULL;
8202 int dev_name_lookup_err = 0;
8204 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) {
8205 nvl = NULL;
8206 goto out;
8209 if ((err = nvlist_add_int32(nvl, EV_VERSION, EV_V1)) != 0)
8210 goto out;
8212 if ((err = nvlist_add_string(nvl, DEV_PHYS_PATH, node_path)) != 0)
8213 goto out;
8215 if (strcmp(class, EC_DEV_ADD) != 0 &&
8216 strcmp(class, EC_DEV_REMOVE) != 0)
8217 return (nvl);
8219 if (driver_name == NULL || instance == -1)
8220 goto out;
8222 if (strcmp(subclass, ESC_DISK) == 0) {
8224 * While we're removing labeled lofi device, we will receive
8225 * event for every registered minor device and lastly,
8226 * an event with minor set to NULL, as in following example:
8227 * class: EC_dev_remove subclass: disk
8228 * node_path: /pseudo/lofi@1 driver: lofi minor: u,raw
8229 * class: EC_dev_remove subclass: disk
8230 * node_path: /pseudo/lofi@1 driver: lofi minor: NULL
8232 * When we receive this last event with minor set to NULL,
8233 * all lofi minor devices are already removed and the call to
8234 * lookup_disk_dev_name() would result in error.
8235 * To prevent name lookup error messages for this case, we
8236 * need to filter out that last event.
8238 if (strcmp(class, EC_DEV_REMOVE) == 0 &&
8239 strcmp(driver_name, "lofi") == 0 && minor == NULL) {
8240 nvlist_free(nvl);
8241 return (NULL);
8243 if ((dev_name = lookup_disk_dev_name(node_path)) == NULL) {
8244 dev_name_lookup_err = 1;
8245 goto out;
8247 } else if (strcmp(subclass, ESC_NETWORK) == 0) {
8248 if ((dev_name = lookup_network_dev_name(node_path, driver_name))
8249 == NULL) {
8250 dev_name_lookup_err = 1;
8251 goto out;
8253 } else if (strcmp(subclass, ESC_PRINTER) == 0) {
8254 if ((dev_name = lookup_printer_dev_name(node_path)) == NULL) {
8255 dev_name_lookup_err = 1;
8256 goto out;
8258 } else if (strcmp(subclass, ESC_LOFI) == 0) {
8260 * The raw minor node is created or removed after the block
8261 * node. Lofi devfs events are dependent on this behavior.
8262 * Generate the sysevent only for the raw minor node.
8264 * If the lofi mapping is created, we will receive the following
8265 * event: class: EC_dev_add subclass: lofi minor: NULL
8267 * As in case of EC_dev_add, the minor is NULL pointer,
8268 * to get device links created, we will need to provide the
8269 * type of minor node for lookup_lofi_dev_name()
8271 * If the lofi device is unmapped, we will receive following
8272 * events:
8273 * class: EC_dev_remove subclass: lofi minor: disk
8274 * class: EC_dev_remove subclass: lofi minor: disk,raw
8275 * class: EC_dev_remove subclass: lofi minor: NULL
8278 if (strcmp(class, EC_DEV_ADD) == 0 && minor == NULL)
8279 minor = "disk,raw";
8281 if (minor == NULL || strstr(minor, "raw") == NULL) {
8282 nvlist_free(nvl);
8283 return (NULL);
8285 if ((dev_name = lookup_lofi_dev_name(node_path, minor)) ==
8286 NULL) {
8287 dev_name_lookup_err = 1;
8288 goto out;
8292 if (dev_name) {
8293 if ((err = nvlist_add_string(nvl, DEV_NAME, dev_name)) != 0)
8294 goto out;
8295 free(dev_name);
8296 dev_name = NULL;
8299 if ((err = nvlist_add_string(nvl, DEV_DRIVER_NAME, driver_name)) != 0)
8300 goto out;
8302 if ((err = nvlist_add_int32(nvl, DEV_INSTANCE, instance)) != 0)
8303 goto out;
8305 if (strcmp(class, EC_DEV_ADD) == 0) {
8306 /* add properties */
8307 count = 0;
8308 for (prop = di_prop_next(node, DI_PROP_NIL);
8309 prop != DI_PROP_NIL && count < MAX_PROP_COUNT;
8310 prop = di_prop_next(node, prop)) {
8312 if (di_prop_devt(prop) != DDI_DEV_T_NONE)
8313 continue;
8315 if ((x = add_property(nvl, prop)) == 0)
8316 count++;
8317 else if (x == -1) {
8318 if ((prop_name = di_prop_name(prop)) == NULL)
8319 prop_name = "";
8320 err_print(PROP_ADD_FAILED, prop_name);
8321 goto out;
8326 return (nvl);
8328 out:
8329 nvlist_free(nvl);
8331 if (dev_name)
8332 free(dev_name);
8334 if (dev_name_lookup_err) {
8336 * If a lofi mount fails, the /devices node may well have
8337 * disappeared by the time we run, so let's not complain.
8339 if (strcmp(subclass, ESC_LOFI) != 0)
8340 err_print(DEV_NAME_LOOKUP_FAILED, node_path);
8341 } else {
8342 err_print(BUILD_EVENT_ATTR_FAILED, (err) ? strerror(err) : "");
8344 return (NULL);
8347 static void
8348 log_event(char *class, char *subclass, nvlist_t *nvl)
8350 sysevent_id_t eid;
8352 if (sysevent_post_event(class, subclass, "SUNW", DEVFSADMD,
8353 nvl, &eid) != 0) {
8354 err_print(LOG_EVENT_FAILED, strerror(errno));
8359 * When devfsadmd needs to generate sysevents, they are queued for later
8360 * delivery this allows them to be delivered after the devlinks db cache has
8361 * been flushed guaranteeing that applications consuming these events have
8362 * access to an accurate devlinks db. The queue is a FIFO, sysevents to be
8363 * inserted in the front of the queue and consumed off the back.
8365 static void
8366 enqueue_sysevent(char *class, char *subclass, nvlist_t *nvl)
8368 syseventq_t *tmp;
8370 if ((tmp = s_zalloc(sizeof (*tmp))) == NULL)
8371 return;
8373 tmp->class = s_strdup(class);
8374 tmp->subclass = s_strdup(subclass);
8375 tmp->nvl = nvl;
8377 (void) mutex_lock(&syseventq_mutex);
8378 if (syseventq_front != NULL)
8379 syseventq_front->next = tmp;
8380 else
8381 syseventq_back = tmp;
8382 syseventq_front = tmp;
8383 (void) mutex_unlock(&syseventq_mutex);
8386 static void
8387 process_syseventq()
8389 (void) mutex_lock(&syseventq_mutex);
8390 while (syseventq_back != NULL) {
8391 syseventq_t *tmp = syseventq_back;
8393 vprint(CHATTY_MID, "sending queued event: %s, %s\n",
8394 tmp->class, tmp->subclass);
8396 log_event(tmp->class, tmp->subclass, tmp->nvl);
8398 if (tmp->class != NULL)
8399 free(tmp->class);
8400 if (tmp->subclass != NULL)
8401 free(tmp->subclass);
8402 nvlist_free(tmp->nvl);
8403 syseventq_back = syseventq_back->next;
8404 if (syseventq_back == NULL)
8405 syseventq_front = NULL;
8406 free(tmp);
8408 (void) mutex_unlock(&syseventq_mutex);
8411 static void
8412 build_and_enq_event(char *class, char *subclass, char *node_path,
8413 di_node_t node, char *minor)
8415 nvlist_t *nvl;
8417 vprint(CHATTY_MID, "build_and_enq_event(%s, %s, %s, 0x%8.8x)\n",
8418 class, subclass, node_path, (int)node);
8420 if (node != DI_NODE_NIL)
8421 nvl = build_event_attributes(class, subclass, node_path, node,
8422 di_driver_name(node), di_instance(node), minor);
8423 else
8424 nvl = build_event_attributes(class, subclass, node_path, node,
8425 NULL, -1, minor);
8427 if (nvl) {
8428 enqueue_sysevent(class, subclass, nvl);
8433 * is_blank() returns 1 (true) if a line specified is composed of
8434 * whitespace characters only. otherwise, it returns 0 (false).
8436 * Note. the argument (line) must be null-terminated.
8438 static int
8439 is_blank(char *line)
8441 for (/* nothing */; *line != '\0'; line++)
8442 if (!isspace(*line))
8443 return (0);
8444 return (1);
8448 * Functions to deal with the no-further-processing hash
8451 static void
8452 nfphash_create(void)
8454 assert(nfp_hash == NULL);
8455 nfp_hash = s_zalloc(NFP_HASH_SZ * sizeof (item_t *));
8458 static int
8459 nfphash_fcn(char *key)
8461 int i;
8462 uint64_t sum = 0;
8464 for (i = 0; key[i] != '\0'; i++) {
8465 sum += (uchar_t)key[i];
8468 return (sum % NFP_HASH_SZ);
8471 static item_t *
8472 nfphash_lookup(char *key)
8474 int index;
8475 item_t *ip;
8477 index = nfphash_fcn(key);
8479 assert(index >= 0);
8481 for (ip = nfp_hash[index]; ip; ip = ip->i_next) {
8482 if (strcmp(ip->i_key, key) == 0)
8483 return (ip);
8486 return (NULL);
8489 static void
8490 nfphash_insert(char *key)
8492 item_t *ip;
8493 int index;
8495 index = nfphash_fcn(key);
8497 assert(index >= 0);
8499 ip = s_zalloc(sizeof (item_t));
8500 ip->i_key = s_strdup(key);
8502 ip->i_next = nfp_hash[index];
8503 nfp_hash[index] = ip;
8506 static void
8507 nfphash_destroy(void)
8509 int i;
8510 item_t *ip;
8512 for (i = 0; i < NFP_HASH_SZ; i++) {
8513 /*LINTED*/
8514 while (ip = nfp_hash[i]) {
8515 nfp_hash[i] = ip->i_next;
8516 free(ip->i_key);
8517 free(ip);
8521 free(nfp_hash);
8522 nfp_hash = NULL;
8525 static int
8526 devname_kcall(int subcmd, void *args)
8528 int error = 0;
8530 switch (subcmd) {
8531 case MODDEVNAME_LOOKUPDOOR:
8532 error = modctl(MODDEVNAME, subcmd, (uintptr_t)args);
8533 if (error) {
8534 vprint(INFO_MID, "modctl(MODDEVNAME, "
8535 "MODDEVNAME_LOOKUPDOOR) failed - %s\n",
8536 strerror(errno));
8538 break;
8539 default:
8540 error = EINVAL;
8541 break;
8543 return (error);
8546 /* ARGSUSED */
8547 static void
8548 devname_lookup_handler(void *cookie, char *argp, size_t arg_size,
8549 door_desc_t *dp, uint_t n_desc)
8551 int32_t error = 0;
8552 door_cred_t dcred;
8553 struct dca_impl dci;
8554 uint8_t cmd;
8555 sdev_door_res_t res;
8556 sdev_door_arg_t *args;
8558 if (argp == NULL || arg_size == 0) {
8559 vprint(DEVNAME_MID, "devname_lookup_handler: argp wrong\n");
8560 error = DEVFSADM_RUN_INVALID;
8561 goto done;
8563 vprint(DEVNAME_MID, "devname_lookup_handler\n");
8565 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
8566 vprint(DEVNAME_MID, "devname_lookup_handler: cred wrong\n");
8567 error = DEVFSADM_RUN_EPERM;
8568 goto done;
8571 args = (sdev_door_arg_t *)argp;
8572 cmd = args->devfsadm_cmd;
8574 vprint(DEVNAME_MID, "devname_lookup_handler: cmd %d\n", cmd);
8575 switch (cmd) {
8576 case DEVFSADMD_RUN_ALL:
8578 * run "devfsadm"
8580 dci.dci_root = "/";
8581 dci.dci_minor = NULL;
8582 dci.dci_driver = NULL;
8583 dci.dci_error = 0;
8584 dci.dci_flags = 0;
8585 dci.dci_arg = NULL;
8587 lock_dev();
8588 update_drvconf((major_t)-1, 0);
8589 dci.dci_flags |= DCA_FLUSH_PATHINST;
8591 pre_and_post_cleanup(RM_PRE);
8592 devi_tree_walk(&dci, DI_CACHE_SNAPSHOT_FLAGS, NULL);
8593 error = (int32_t)dci.dci_error;
8594 if (!error) {
8595 pre_and_post_cleanup(RM_POST);
8596 update_database = TRUE;
8597 unlock_dev(SYNC_STATE);
8598 update_database = FALSE;
8599 } else {
8600 if (DEVFSADM_DEBUG_ON) {
8601 vprint(INFO_MID, "devname_lookup_handler: "
8602 "DEVFSADMD_RUN_ALL failed\n");
8605 unlock_dev(SYNC_STATE);
8607 break;
8608 default:
8609 /* log an error here? */
8610 error = DEVFSADM_RUN_NOTSUP;
8611 break;
8614 done:
8615 vprint(DEVNAME_MID, "devname_lookup_handler: error %d\n", error);
8616 res.devfsadm_error = error;
8617 (void) door_return((char *)&res, sizeof (struct sdev_door_res),
8618 NULL, 0);
8622 di_devlink_handle_t
8623 devfsadm_devlink_cache(void)
8625 return (devlink_cache);
8629 devfsadm_reserve_id_cache(devlink_re_t re_array[], enumerate_file_t *head)
8631 enumerate_file_t *entry;
8632 int nelem;
8633 int i;
8634 int subex;
8635 char *re;
8636 size_t size;
8637 regmatch_t *pmch;
8640 * Check the <RE, subexp> array passed in and compile it.
8642 for (i = 0; re_array[i].d_re; i++) {
8643 if (re_array[i].d_subexp == 0) {
8644 err_print("bad subexp value in RE: %s\n",
8645 re_array[i].d_re);
8646 goto bad_re;
8649 re = re_array[i].d_re;
8650 if (regcomp(&re_array[i].d_rcomp, re, REG_EXTENDED) != 0) {
8651 err_print("reg. exp. failed to compile: %s\n", re);
8652 goto bad_re;
8654 subex = re_array[i].d_subexp;
8655 nelem = subex + 1;
8656 re_array[i].d_pmatch = s_malloc(sizeof (regmatch_t) * nelem);
8659 entry = head ? head : enumerate_reserved;
8660 for (; entry; entry = entry->er_next) {
8661 if (entry->er_id) {
8662 vprint(RSBY_MID, "entry %s already has ID %s\n",
8663 entry->er_file, entry->er_id);
8664 continue;
8666 for (i = 0; re_array[i].d_re; i++) {
8667 subex = re_array[i].d_subexp;
8668 pmch = re_array[i].d_pmatch;
8669 if (regexec(&re_array[i].d_rcomp, entry->er_file,
8670 subex + 1, pmch, 0) != 0) {
8671 /* No match */
8672 continue;
8674 size = pmch[subex].rm_eo - pmch[subex].rm_so;
8675 entry->er_id = s_malloc(size + 1);
8676 (void) strncpy(entry->er_id,
8677 &entry->er_file[pmch[subex].rm_so], size);
8678 entry->er_id[size] = '\0';
8679 if (head) {
8680 vprint(RSBY_MID, "devlink(%s) matches RE(%s). "
8681 "ID is %s\n", entry->er_file,
8682 re_array[i].d_re, entry->er_id);
8683 } else {
8684 vprint(RSBY_MID, "rsrv entry(%s) matches "
8685 "RE(%s) ID is %s\n", entry->er_file,
8686 re_array[i].d_re, entry->er_id);
8688 break;
8692 for (i = 0; re_array[i].d_re; i++) {
8693 regfree(&re_array[i].d_rcomp);
8694 assert(re_array[i].d_pmatch);
8695 free(re_array[i].d_pmatch);
8698 entry = head ? head : enumerate_reserved;
8699 for (; entry; entry = entry->er_next) {
8700 if (entry->er_id == NULL)
8701 continue;
8702 if (head) {
8703 vprint(RSBY_MID, "devlink: %s\n", entry->er_file);
8704 vprint(RSBY_MID, "ID: %s\n", entry->er_id);
8705 } else {
8706 vprint(RSBY_MID, "reserve file entry: %s\n",
8707 entry->er_file);
8708 vprint(RSBY_MID, "reserve file id: %s\n",
8709 entry->er_id);
8713 return (DEVFSADM_SUCCESS);
8715 bad_re:
8716 for (i = i-1; i >= 0; i--) {
8717 regfree(&re_array[i].d_rcomp);
8718 assert(re_array[i].d_pmatch);
8719 free(re_array[i].d_pmatch);
8721 return (DEVFSADM_FAILURE);
8725 * Return 1 if we have reserved links.
8728 devfsadm_have_reserved()
8730 return (enumerate_reserved ? 1 : 0);
8734 * This functions errs on the side of caution. If there is any error
8735 * we assume that the devlink is *not* reserved
8738 devfsadm_is_reserved(devlink_re_t re_array[], char *devlink)
8740 int match;
8741 enumerate_file_t estruct = {NULL};
8742 enumerate_file_t *entry;
8744 match = 0;
8745 estruct.er_file = devlink;
8746 estruct.er_id = NULL;
8747 estruct.er_next = NULL;
8749 if (devfsadm_reserve_id_cache(re_array, &estruct) != DEVFSADM_SUCCESS) {
8750 err_print("devfsadm_is_reserved: devlink (%s) does not "
8751 "match RE\n", devlink);
8752 return (0);
8754 if (estruct.er_id == NULL) {
8755 err_print("devfsadm_is_reserved: ID derived from devlink %s "
8756 "is NULL\n", devlink);
8757 return (0);
8760 entry = enumerate_reserved;
8761 for (; entry; entry = entry->er_next) {
8762 if (entry->er_id == NULL)
8763 continue;
8764 if (strcmp(entry->er_id, estruct.er_id) != 0)
8765 continue;
8766 match = 1;
8767 vprint(RSBY_MID, "reserve file entry (%s) and devlink (%s) "
8768 "match\n", entry->er_file, devlink);
8769 break;
8772 free(estruct.er_id);
8773 return (match);