dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / devfsadm / devfsadm.c
blobec8335b061e9daaf73037a99bd35333084bb4db4
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 <bsm/devices.h>
40 #include <bsm/devalloc.h>
41 #include <utime.h>
42 #include <sys/param.h>
43 #include <bsm/libbsm.h>
44 #include <zone.h>
45 #include "devfsadm_impl.h"
47 /* externs from devalloc.c */
48 extern void _reset_devalloc(int);
49 extern void _update_devalloc_db(devlist_t *, int, int, char *, char *);
50 extern int _da_check_for_usb(char *, char *);
52 /* create or remove nodes or links. unset with -n */
53 static int file_mods = TRUE;
55 /* cleanup mode. Set with -C */
56 static int cleanup = FALSE;
58 /* devlinks -d compatibility */
59 static int devlinks_debug = FALSE;
61 /* flag to enable/disable device allocation with -e/-d */
62 static int devalloc_flag = 0;
64 /* flag that indicates if device allocation is on or not */
65 static int devalloc_is_on = 0;
68 * devices to be deallocated with -d :
69 * audio, floppy, cd, floppy, tape, rmdisk.
71 static char *devalloc_list[10] = {DDI_NT_AUDIO, DDI_NT_CD, DDI_NT_CD_CHAN,
72 DDI_NT_FD, DDI_NT_TAPE, DDI_NT_BLOCK_CHAN,
73 DDI_NT_UGEN, DDI_NT_USB_ATTACHMENT_POINT,
74 DDI_NT_SCSI_NEXUS, NULL};
76 /* list of allocatable devices */
77 static devlist_t devlist;
79 /* load a single driver only. set with -i */
80 static int single_drv = FALSE;
81 static char *driver = NULL;
83 /* attempt to load drivers or defer attach nodes */
84 static int load_attach_drv = TRUE;
86 /* reload all driver.conf files */
87 static int update_all_drivers = FALSE;
89 /* set if invoked via /usr/lib/devfsadm/devfsadmd */
90 static int daemon_mode = FALSE;
92 /* set if event_handler triggered */
93 int event_driven = FALSE;
95 /* output directed to syslog during daemon mode if set */
96 static int logflag = FALSE;
98 /* build links in /dev. -x to turn off */
99 static int build_dev = TRUE;
101 /* build nodes in /devices. -y to turn off */
102 static int build_devices = TRUE;
104 /* -z to turn off */
105 static int flush_path_to_inst_enable = TRUE;
107 /* variables used for path_to_inst flushing */
108 static int inst_count = 0;
109 static mutex_t count_lock;
110 static cond_t cv;
112 /* variables for minor_fini thread */
113 static mutex_t minor_fini_mutex;
114 static int minor_fini_canceled = TRUE;
115 static int minor_fini_delayed = FALSE;
116 static cond_t minor_fini_cv;
117 static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
119 /* single-threads /dev modification */
120 static sema_t dev_sema;
122 /* the program we were invoked as; ie argv[0] */
123 static char *prog;
125 /* pointers to create/remove link lists */
126 static create_list_t *create_head = NULL;
127 static remove_list_t *remove_head = NULL;
129 /* supports the class -c option */
130 static char **classes = NULL;
131 static int num_classes = 0;
133 /* used with verbose option -v or -V */
134 static int num_verbose = 0;
135 static char **verbose = NULL;
137 static struct mperm *minor_perms = NULL;
138 static driver_alias_t *driver_aliases = NULL;
140 /* set if -r alternate root given */
141 static char *root_dir = "";
143 /* /devices or <rootdir>/devices */
144 static char *devices_dir = DEVICES;
146 /* /dev or <rootdir>/dev */
147 static char *dev_dir = DEV;
149 /* /etc/dev or <rootdir>/etc/dev */
150 static char *etc_dev_dir = ETCDEV;
153 * writable root (for lock files and doors during install).
154 * This is also root dir for /dev attr dir during install.
156 static char *attr_root = NULL;
158 /* /etc/path_to_inst unless -p used */
159 static char *inst_file = INSTANCE_FILE;
161 /* /usr/lib/devfsadm/linkmods unless -l used */
162 static char *module_dirs = MODULE_DIRS;
164 /* default uid/gid used if /etc/minor_perm entry not found */
165 static uid_t root_uid;
166 static gid_t sys_gid;
168 /* /etc/devlink.tab unless devlinks -t used */
169 static char *devlinktab_file = NULL;
171 /* File and data structure to reserve enumerate IDs */
172 static char *enumerate_file = ENUMERATE_RESERVED;
173 static enumerate_file_t *enumerate_reserved = NULL;
175 /* set if /dev link is new. speeds up rm_stale_links */
176 static int linknew = TRUE;
178 /* variables for devlink.tab compat processing */
179 static devlinktab_list_t *devlinktab_list = NULL;
180 static unsigned int devlinktab_line = 0;
182 /* cache head for devfsadm_enumerate*() functions */
183 static numeral_set_t *head_numeral_set = NULL;
185 /* list list of devfsadm modules */
186 static module_t *module_head = NULL;
188 /* name_to_major list used in utility function */
189 static n2m_t *n2m_list = NULL;
191 /* cache of some links used for performance */
192 static linkhead_t *headlinkhead = NULL;
194 /* locking variables to prevent multiples writes to /dev */
195 static int hold_dev_lock = FALSE;
196 static int hold_daemon_lock = FALSE;
197 static int dev_lock_fd;
198 static int daemon_lock_fd;
199 static char dev_lockfile[PATH_MAX + 1];
200 static char daemon_lockfile[PATH_MAX + 1];
202 /* last devinfo node/minor processed. used for performance */
203 static di_node_t lnode;
204 static di_minor_t lminor;
205 static char lphy_path[PATH_MAX + 1] = {""};
207 /* Globals used by the link database */
208 static di_devlink_handle_t devlink_cache;
209 static int update_database = FALSE;
211 /* Globals used to set logindev perms */
212 static struct login_dev *login_dev_cache = NULL;
213 static int login_dev_enable = FALSE;
215 /* Global to use devinfo snapshot cache */
216 static int use_snapshot_cache = FALSE;
218 /* Global for no-further-processing hash */
219 static item_t **nfp_hash;
220 static mutex_t nfp_mutex = DEFAULTMUTEX;
223 * Directories not removed even when empty. They are packaged, or may
224 * be referred to from a non-global zone. The dirs must be listed in
225 * canonical form i.e. without leading "/dev/"
227 static char *sticky_dirs[] =
228 {"dsk", "rdsk", "term", "lofi", "rlofi", NULL};
230 /* Devname globals */
231 static int lookup_door_fd = -1;
232 static char *lookup_door_path;
234 static void load_dev_acl(void);
235 static void update_drvconf(major_t, int);
236 static void check_reconfig_state(void);
237 static int s_stat(const char *, struct stat *);
239 static int is_blank(char *);
241 /* sysevent queue related globals */
242 static mutex_t syseventq_mutex = DEFAULTMUTEX;
243 static syseventq_t *syseventq_front;
244 static syseventq_t *syseventq_back;
245 static void process_syseventq();
247 static di_node_t devi_root_node = DI_NODE_NIL;
250 main(int argc, char *argv[])
252 struct passwd *pw;
253 struct group *gp;
254 pid_t pid;
256 (void) setlocale(LC_ALL, "");
257 (void) textdomain(TEXT_DOMAIN);
259 if ((prog = strrchr(argv[0], '/')) == NULL) {
260 prog = argv[0];
261 } else {
262 prog++;
265 if (getuid() != 0) {
266 err_print(MUST_BE_ROOT);
267 devfsadm_exit(1);
268 /*NOTREACHED*/
271 if (getzoneid() != GLOBAL_ZONEID) {
272 err_print(MUST_BE_GLOBAL_ZONE);
273 devfsadm_exit(1);
277 * Close all files except stdin/stdout/stderr
279 closefrom(3);
281 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
282 root_uid = pw->pw_uid;
283 } else {
284 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
285 root_uid = (uid_t)0; /* assume 0 is root */
288 /* the default group is sys */
290 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
291 sys_gid = gp->gr_gid;
292 } else {
293 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
294 sys_gid = (gid_t)3; /* assume 3 is sys */
297 (void) umask(0);
300 * Check if device allocation is enabled.
302 devalloc_is_on = (da_is_on() == 1) ? 1 : 0;
304 parse_args(argc, argv);
306 (void) sema_init(&dev_sema, 1, USYNC_THREAD, NULL);
308 /* Initialize device allocation list */
309 devlist.audio = devlist.cd = devlist.floppy = devlist.tape =
310 devlist.rmdisk = NULL;
312 if (daemon_mode == TRUE) {
314 * Build /dev and /devices before daemonizing if
315 * reconfig booting and daemon invoked with alternate
316 * root. This is to support install.
318 if (getenv(RECONFIG_BOOT) != NULL && root_dir[0] != '\0') {
319 vprint(INFO_MID, CONFIGURING);
320 load_dev_acl();
321 update_drvconf((major_t)-1, 0);
322 process_devinfo_tree();
323 (void) modctl(MODSETMINIROOT);
327 * fork before detaching from tty in order to print error
328 * message if unable to acquire file lock. locks not preserved
329 * across forks. Even under debug we want to fork so that
330 * when executed at boot we don't hang.
332 if (fork() != 0) {
333 devfsadm_exit(0);
334 /*NOTREACHED*/
337 /* set directory to / so it coredumps there */
338 if (chdir("/") == -1) {
339 err_print(CHROOT_FAILED, strerror(errno));
342 /* only one daemon can run at a time */
343 if ((pid = enter_daemon_lock()) == getpid()) {
344 detachfromtty();
345 (void) cond_init(&cv, USYNC_THREAD, 0);
346 (void) mutex_init(&count_lock, USYNC_THREAD, 0);
347 if (thr_create(NULL, 0,
348 (void *(*)(void *))instance_flush_thread,
349 NULL, THR_DETACHED, NULL) != 0) {
350 err_print(CANT_CREATE_THREAD, "daemon",
351 strerror(errno));
352 devfsadm_exit(1);
353 /*NOTREACHED*/
356 /* start the minor_fini_thread */
357 (void) mutex_init(&minor_fini_mutex, USYNC_THREAD, 0);
358 (void) cond_init(&minor_fini_cv, USYNC_THREAD, 0);
359 if (thr_create(NULL, 0,
360 (void *(*)(void *))minor_fini_thread,
361 NULL, THR_DETACHED, NULL)) {
362 err_print(CANT_CREATE_THREAD, "minor_fini",
363 strerror(errno));
364 devfsadm_exit(1);
365 /*NOTREACHED*/
370 * logindevperms need only be set
371 * in daemon mode and when root dir is "/".
373 if (root_dir[0] == '\0')
374 login_dev_enable = TRUE;
375 daemon_update();
376 devfsadm_exit(0);
377 /*NOTREACHED*/
378 } else {
379 err_print(DAEMON_RUNNING, pid);
380 devfsadm_exit(1);
381 /*NOTREACHED*/
383 } else {
384 /* not a daemon, so just build /dev and /devices */
387 * If turning off device allocation, load the
388 * minor_perm file because process_devinfo_tree() will
389 * need this in order to reset the permissions of the
390 * device files.
392 if (devalloc_flag == DA_OFF) {
393 read_minor_perm_file();
396 process_devinfo_tree();
397 if (devalloc_flag != 0)
398 /* Enable/disable device allocation */
399 _reset_devalloc(devalloc_flag);
401 return (0);
404 static void
405 update_drvconf(major_t major, int flags)
407 if (modctl(MODLOADDRVCONF, major, flags) != 0)
408 err_print(gettext("update_drvconf failed for major %d\n"),
409 major);
412 static void
413 load_dev_acl()
415 if (load_devpolicy() != 0)
416 err_print(gettext("device policy load failed\n"));
417 load_minor_perm_file();
421 * As devfsadm is run early in boot to provide the kernel with
422 * minor_perm info, we might as well check for reconfig at the
423 * same time to avoid running devfsadm twice. This gets invoked
424 * earlier than the env variable RECONFIG_BOOT is set up.
426 static void
427 check_reconfig_state()
429 struct stat sb;
431 if (s_stat("/reconfigure", &sb) == 0) {
432 (void) modctl(MODDEVNAME, MODDEVNAME_RECONFIG, 0);
436 static void
437 modctl_sysavail()
440 * Inform /dev that system is available, that
441 * implicit reconfig can now be performed.
443 (void) modctl(MODDEVNAME, MODDEVNAME_SYSAVAIL, 0);
446 static void
447 set_lock_root(void)
449 struct stat sb;
450 char *lock_root;
451 size_t len;
453 lock_root = attr_root ? attr_root : root_dir;
455 len = strlen(lock_root) + strlen(ETCDEV) + 1;
456 etc_dev_dir = s_malloc(len);
457 (void) snprintf(etc_dev_dir, len, "%s%s", lock_root, ETCDEV);
459 if (s_stat(etc_dev_dir, &sb) != 0) {
460 s_mkdirp(etc_dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
461 } else if (!S_ISDIR(sb.st_mode)) {
462 err_print(NOT_DIR, etc_dev_dir);
463 devfsadm_exit(1);
464 /*NOTREACHED*/
470 * Parse arguments for all 6 programs handled from devfsadm.
472 static void
473 parse_args(int argc, char *argv[])
475 char opt;
476 char get_linkcompat_opts = FALSE;
477 char *compat_class;
478 int num_aliases = 0;
479 int len;
480 int retval;
481 int config = TRUE;
482 int bind = FALSE;
483 int force_flag = FALSE;
484 struct aliases *ap = NULL;
485 struct aliases *a_head = NULL;
486 struct aliases *a_tail = NULL;
487 struct modconfig mc;
489 (void) bzero(&mc, sizeof (mc));
491 if (strcmp(prog, DISKS) == 0) {
492 compat_class = "disk";
493 get_linkcompat_opts = TRUE;
495 } else if (strcmp(prog, TAPES) == 0) {
496 compat_class = "tape";
497 get_linkcompat_opts = TRUE;
499 } else if (strcmp(prog, PORTS) == 0) {
500 compat_class = "port";
501 get_linkcompat_opts = TRUE;
503 } else if (strcmp(prog, AUDLINKS) == 0) {
504 compat_class = "audio";
505 get_linkcompat_opts = TRUE;
507 } else if (strcmp(prog, DEVLINKS) == 0) {
508 devlinktab_file = DEVLINKTAB_FILE;
510 build_devices = FALSE;
511 load_attach_drv = FALSE;
513 while ((opt = getopt(argc, argv, "dnr:st:vV:")) != EOF) {
514 switch (opt) {
515 case 'd':
516 file_mods = FALSE;
517 flush_path_to_inst_enable = FALSE;
518 devlinks_debug = TRUE;
519 break;
520 case 'n':
521 /* prevent driver loading and deferred attach */
522 load_attach_drv = FALSE;
523 break;
524 case 'r':
525 set_root_devices_dev_dir(optarg);
526 if (zone_pathcheck(root_dir) !=
527 DEVFSADM_SUCCESS)
528 devfsadm_exit(1);
529 /*NOTREACHED*/
530 break;
531 case 's':
533 * suppress. don't create/remove links/nodes
534 * useful with -v or -V
536 file_mods = FALSE;
537 flush_path_to_inst_enable = FALSE;
538 break;
539 case 't':
540 /* supply a non-default table file */
541 devlinktab_file = optarg;
542 break;
543 case 'v':
544 /* documented verbose flag */
545 add_verbose_id(VERBOSE_MID);
546 break;
547 case 'V':
548 /* undocumented for extra verbose levels */
549 add_verbose_id(optarg);
550 break;
551 default:
552 usage();
553 break;
557 if (optind < argc) {
558 usage();
561 } else if (strcmp(prog, DRVCONFIG) == 0) {
562 int update_only = 0;
563 build_dev = FALSE;
565 while ((opt =
566 getopt(argc, argv, "a:bc:dfi:m:np:R:r:suvV:x")) != EOF) {
567 switch (opt) {
568 case 'a':
569 ap = calloc(sizeof (struct aliases), 1);
570 ap->a_name = dequote(optarg);
571 len = strlen(ap->a_name) + 1;
572 if (len > MAXMODCONFNAME) {
573 err_print(ALIAS_TOO_LONG,
574 MAXMODCONFNAME, ap->a_name);
575 devfsadm_exit(1);
576 /*NOTREACHED*/
578 ap->a_len = len;
579 if (a_tail == NULL) {
580 a_head = ap;
581 } else {
582 a_tail->a_next = ap;
584 a_tail = ap;
585 num_aliases++;
586 bind = TRUE;
587 break;
588 case 'b':
589 bind = TRUE;
590 break;
591 case 'c':
592 (void) strcpy(mc.drvclass, optarg);
593 break;
594 case 'd':
596 * need to keep for compatibility, but
597 * do nothing.
599 break;
600 case 'f':
601 force_flag = TRUE;
602 break;
603 case 'i':
604 single_drv = TRUE;
605 (void) strcpy(mc.drvname, optarg);
606 driver = s_strdup(optarg);
607 break;
608 case 'm':
609 mc.major = atoi(optarg);
610 break;
611 case 'n':
612 /* prevent driver loading and deferred attach */
613 load_attach_drv = FALSE;
614 break;
615 case 'p':
616 /* specify alternate path_to_inst file */
617 inst_file = s_strdup(optarg);
618 break;
619 case 'R':
621 * Private flag for suninstall to populate
622 * device information on the installed root.
624 root_dir = s_strdup(optarg);
625 if (zone_pathcheck(root_dir) !=
626 DEVFSADM_SUCCESS)
627 devfsadm_exit(devfsadm_copy());
628 /*NOTREACHED*/
629 break;
630 case 'r':
631 devices_dir = s_strdup(optarg);
632 if (zone_pathcheck(devices_dir) !=
633 DEVFSADM_SUCCESS)
634 devfsadm_exit(1);
635 /*NOTREACHED*/
636 break;
637 case 's':
639 * suppress. don't create nodes
640 * useful with -v or -V
642 file_mods = FALSE;
643 flush_path_to_inst_enable = FALSE;
644 break;
645 case 'u':
647 * Invoked via update_drv(1m) to update
648 * the kernel's driver/alias binding
649 * when removing one or more aliases.
651 config = FALSE;
652 break;
653 case 'v':
654 /* documented verbose flag */
655 add_verbose_id(VERBOSE_MID);
656 break;
657 case 'V':
658 /* undocumented for extra verbose levels */
659 add_verbose_id(optarg);
660 break;
661 case 'x':
662 update_only = 1;
663 break;
664 default:
665 usage();
669 if (optind < argc) {
670 usage();
673 if (bind == TRUE) {
674 if ((mc.major == -1) || (mc.drvname[0] == '\0')) {
675 err_print(MAJOR_AND_B_FLAG);
676 devfsadm_exit(1);
677 /*NOTREACHED*/
679 mc.flags = 0;
680 if (force_flag)
681 mc.flags |= MOD_UNBIND_OVERRIDE;
682 if (update_only)
683 mc.flags |= MOD_ADDMAJBIND_UPDATE;
684 mc.num_aliases = num_aliases;
685 mc.ap = a_head;
686 retval = modctl((config == TRUE) ? MODADDMAJBIND :
687 MODREMDRVALIAS, NULL, (caddr_t)&mc);
688 if (retval < 0) {
689 err_print((config == TRUE) ? MODCTL_ADDMAJBIND :
690 MODCTL_REMMAJBIND);
692 devfsadm_exit(retval);
693 /*NOTREACHED*/
696 } else if ((strcmp(prog, DEVFSADM) == 0) ||
697 (strcmp(prog, DEVFSADMD) == 0)) {
698 char *zonename = NULL;
699 int init_drvconf = 0;
700 int init_perm = 0;
701 int public_mode = 0;
702 int init_sysavail = 0;
704 if (strcmp(prog, DEVFSADMD) == 0) {
705 daemon_mode = TRUE;
708 devlinktab_file = DEVLINKTAB_FILE;
710 while ((opt = getopt(argc, argv,
711 "a:Cc:deIi:l:np:PR:r:sSt:uvV:x:")) != EOF) {
712 if (opt == 'I' || opt == 'P' || opt == 'S') {
713 if (public_mode)
714 usage();
715 } else {
716 if (init_perm || init_drvconf || init_sysavail)
717 usage();
718 public_mode = 1;
720 switch (opt) {
721 case 'a':
722 attr_root = s_strdup(optarg);
723 break;
724 case 'C':
725 cleanup = TRUE;
726 break;
727 case 'c':
728 num_classes++;
729 classes = s_realloc(classes,
730 num_classes * sizeof (char *));
731 classes[num_classes - 1] = optarg;
732 break;
733 case 'd':
734 if (daemon_mode == FALSE) {
736 * Device allocation to be disabled.
738 devalloc_flag = DA_OFF;
739 build_dev = FALSE;
741 break;
742 case 'e':
743 if (daemon_mode == FALSE) {
745 * Device allocation to be enabled.
747 devalloc_flag = DA_ON;
748 build_dev = FALSE;
750 break;
751 case 'I': /* update kernel driver.conf cache */
752 if (daemon_mode == TRUE)
753 usage();
754 init_drvconf = 1;
755 break;
756 case 'i':
757 single_drv = TRUE;
758 driver = s_strdup(optarg);
759 break;
760 case 'l':
761 /* specify an alternate module load path */
762 module_dirs = s_strdup(optarg);
763 break;
764 case 'n':
765 /* prevent driver loading and deferred attach */
766 load_attach_drv = FALSE;
767 break;
768 case 'p':
769 /* specify alternate path_to_inst file */
770 inst_file = s_strdup(optarg);
771 break;
772 case 'P':
773 if (daemon_mode == TRUE)
774 usage();
775 /* load minor_perm and device_policy */
776 init_perm = 1;
777 break;
778 case 'R':
780 * Private flag for suninstall to populate
781 * device information on the installed root.
783 root_dir = s_strdup(optarg);
784 devfsadm_exit(devfsadm_copy());
785 /*NOTREACHED*/
786 break;
787 case 'r':
788 set_root_devices_dev_dir(optarg);
789 break;
790 case 's':
792 * suppress. don't create/remove links/nodes
793 * useful with -v or -V
795 file_mods = FALSE;
796 flush_path_to_inst_enable = FALSE;
797 break;
798 case 'S':
799 if (daemon_mode == TRUE)
800 usage();
801 init_sysavail = 1;
802 break;
803 case 't':
804 devlinktab_file = optarg;
805 break;
806 case 'u': /* complete configuration after */
807 /* adding a driver update-only */
808 if (daemon_mode == TRUE)
809 usage();
810 update_all_drivers = TRUE;
811 break;
812 case 'v':
813 /* documented verbose flag */
814 add_verbose_id(VERBOSE_MID);
815 break;
816 case 'V':
817 /* undocumented: specify verbose lvl */
818 add_verbose_id(optarg);
819 break;
820 case 'x':
822 * x is the "private switch" option. The
823 * goal is to not suck up all the other
824 * option letters.
826 if (strcmp(optarg, "update_devlinksdb") == 0) {
827 update_database = TRUE;
828 } else if (strcmp(optarg, "no_dev") == 0) {
829 /* don't build /dev */
830 build_dev = FALSE;
831 } else if (strcmp(optarg, "no_devices") == 0) {
832 /* don't build /devices */
833 build_devices = FALSE;
834 } else if (strcmp(optarg, "no_p2i") == 0) {
835 /* don't flush path_to_inst */
836 flush_path_to_inst_enable = FALSE;
837 } else if (strcmp(optarg, "use_dicache") == 0) {
838 use_snapshot_cache = TRUE;
839 } else {
840 usage();
842 break;
843 default:
844 usage();
845 break;
848 if (optind < argc) {
849 usage();
853 * We're not in zone mode; Check to see if the rootpath
854 * collides with any zonepaths.
856 if (zonename == NULL) {
857 if (zone_pathcheck(root_dir) != DEVFSADM_SUCCESS)
858 devfsadm_exit(1);
859 /*NOTREACHED*/
862 if (init_drvconf || init_perm || init_sysavail) {
864 * Load minor perm before force-loading drivers
865 * so the correct permissions are picked up.
867 if (init_perm) {
868 check_reconfig_state();
869 load_dev_acl();
871 if (init_drvconf)
872 update_drvconf((major_t)-1, 0);
873 if (init_sysavail)
874 modctl_sysavail();
875 devfsadm_exit(0);
876 /*NOTREACHED*/
881 if (get_linkcompat_opts == TRUE) {
883 build_devices = FALSE;
884 load_attach_drv = FALSE;
885 num_classes++;
886 classes = s_realloc(classes, num_classes *
887 sizeof (char *));
888 classes[num_classes - 1] = compat_class;
890 while ((opt = getopt(argc, argv, "Cnr:svV:")) != EOF) {
891 switch (opt) {
892 case 'C':
893 cleanup = TRUE;
894 break;
895 case 'n':
896 /* prevent driver loading or deferred attach */
897 load_attach_drv = FALSE;
898 break;
899 case 'r':
900 set_root_devices_dev_dir(optarg);
901 if (zone_pathcheck(root_dir) !=
902 DEVFSADM_SUCCESS)
903 devfsadm_exit(1);
904 /*NOTREACHED*/
905 break;
906 case 's':
907 /* suppress. don't create/remove links/nodes */
908 /* useful with -v or -V */
909 file_mods = FALSE;
910 flush_path_to_inst_enable = FALSE;
911 break;
912 case 'v':
913 /* documented verbose flag */
914 add_verbose_id(VERBOSE_MID);
915 break;
916 case 'V':
917 /* undocumented for extra verbose levels */
918 add_verbose_id(optarg);
919 break;
920 default:
921 usage();
924 if (optind < argc) {
925 usage();
928 set_lock_root();
931 void
932 usage(void)
934 if (strcmp(prog, DEVLINKS) == 0) {
935 err_print(DEVLINKS_USAGE);
936 } else if (strcmp(prog, DRVCONFIG) == 0) {
937 err_print(DRVCONFIG_USAGE);
938 } else if ((strcmp(prog, DEVFSADM) == 0) ||
939 (strcmp(prog, DEVFSADMD) == 0)) {
940 err_print(DEVFSADM_USAGE);
941 } else {
942 err_print(COMPAT_LINK_USAGE);
945 devfsadm_exit(1);
946 /*NOTREACHED*/
949 static void
950 devi_tree_walk(struct dca_impl *dcip, int flags, char *ev_subclass)
952 char *msg, *name;
953 struct mlist mlist = {0};
954 di_node_t node;
956 vprint(CHATTY_MID, "devi_tree_walk: root=%s, minor=%s, driver=%s,"
957 " error=%d, flags=%u\n", dcip->dci_root,
958 dcip->dci_minor ? dcip->dci_minor : "<NULL>",
959 dcip->dci_driver ? dcip->dci_driver : "<NULL>", dcip->dci_error,
960 dcip->dci_flags);
962 assert(dcip->dci_root);
964 if (dcip->dci_flags & DCA_LOAD_DRV) {
965 node = di_init_driver(dcip->dci_driver, flags);
966 msg = DRIVER_FAILURE;
967 name = dcip->dci_driver;
968 } else {
969 node = di_init(dcip->dci_root, flags);
970 msg = DI_INIT_FAILED;
971 name = dcip->dci_root;
974 if (node == DI_NODE_NIL) {
975 dcip->dci_error = errno;
977 * Rapid hotplugging (commonly seen during USB testing),
978 * may remove a device before the create event for it
979 * has been processed. To prevent alarming users with
980 * a superfluous message, we suppress error messages
981 * for ENXIO and hotplug.
983 if (!(errno == ENXIO && (dcip->dci_flags & DCA_HOT_PLUG)))
984 err_print(msg, name, strerror(dcip->dci_error));
985 return;
988 if (dcip->dci_flags & DCA_FLUSH_PATHINST)
989 flush_path_to_inst();
991 dcip->dci_arg = &mlist;
992 devi_root_node = node; /* protected by lock_dev() */
994 vprint(CHATTY_MID, "walking device tree\n");
996 (void) di_walk_minor(node, NULL, DI_CHECK_ALIAS, dcip,
997 check_minor_type);
999 process_deferred_links(dcip, DCA_CREATE_LINK);
1001 dcip->dci_arg = NULL;
1004 * Finished creating devfs files and dev links.
1005 * Log sysevent.
1007 if (ev_subclass)
1008 build_and_enq_event(EC_DEV_ADD, ev_subclass, dcip->dci_root,
1009 node, dcip->dci_minor);
1011 devi_root_node = DI_NODE_NIL; /* protected by lock_dev() */
1012 di_fini(node);
1015 static void
1016 process_deferred_links(struct dca_impl *dcip, int flags)
1018 struct mlist *dep;
1019 struct minor *mp, *smp;
1021 vprint(CHATTY_MID, "processing deferred links\n");
1023 dep = dcip->dci_arg;
1026 * The list head is not used during the deferred create phase
1028 dcip->dci_arg = NULL;
1030 assert(dep);
1031 assert((dep->head == NULL) ^ (dep->tail != NULL));
1032 assert(flags == DCA_FREE_LIST || flags == DCA_CREATE_LINK);
1034 for (smp = NULL, mp = dep->head; mp; mp = mp->next) {
1035 if (flags == DCA_CREATE_LINK)
1036 (void) check_minor_type(mp->node, mp->minor, dcip);
1037 free(smp);
1038 smp = mp;
1041 free(smp);
1045 * Called in non-daemon mode to take a snap shot of the devinfo tree.
1046 * Then it calls the appropriate functions to build /devices and /dev.
1047 * It also flushes path_to_inst.
1048 * Except in the devfsadm -i (single driver case), the flags used by devfsadm
1049 * needs to match DI_CACHE_SNAPSHOT_FLAGS. That will make DINFOCACHE snapshot
1050 * updated.
1052 void
1053 process_devinfo_tree()
1055 uint_t flags;
1056 struct dca_impl dci;
1057 char name[MAXNAMELEN];
1058 char *fcn = "process_devinfo_tree: ";
1060 vprint(CHATTY_MID, "%senter\n", fcn);
1062 dca_impl_init("/", NULL, &dci);
1064 lock_dev();
1067 * Update kernel driver.conf cache when devfsadm/drvconfig
1068 * is invoked to build /devices and /dev.
1070 if (update_all_drivers || load_attach_drv) {
1071 update_drvconf((major_t)-1,
1072 update_all_drivers ? MOD_LOADDRVCONF_RECONF : 0);
1075 if (single_drv == TRUE) {
1077 * load a single driver, but walk the entire devinfo tree
1079 if (load_attach_drv == FALSE)
1080 err_print(DRV_LOAD_REQD);
1082 vprint(CHATTY_MID, "%sattaching driver (%s)\n", fcn, driver);
1084 dci.dci_flags |= DCA_LOAD_DRV;
1085 (void) snprintf(name, sizeof (name), "%s", driver);
1086 dci.dci_driver = name;
1087 flags = DINFOCPYALL | DINFOPATH;
1089 } else if (load_attach_drv == TRUE) {
1091 * Load and attach all drivers, then walk the entire tree.
1092 * If the cache flag is set, use DINFOCACHE to get cached
1093 * data.
1095 if (use_snapshot_cache == TRUE) {
1096 flags = DINFOCACHE;
1097 vprint(CHATTY_MID, "%susing snapshot cache\n", fcn);
1098 } else {
1099 vprint(CHATTY_MID, "%sattaching all drivers\n", fcn);
1100 flags = DI_CACHE_SNAPSHOT_FLAGS;
1101 if (cleanup) {
1103 * remove dangling entries from /etc/devices
1104 * files.
1106 flags |= DINFOCLEANUP;
1109 } else {
1111 * For devlinks, disks, ports, tapes and devfsadm -n,
1112 * just need to take a snapshot with active devices.
1114 vprint(CHATTY_MID, "%staking snapshot of active devices\n",
1115 fcn);
1116 flags = DINFOCPYALL;
1119 if (((load_attach_drv == TRUE) || (single_drv == TRUE)) &&
1120 (build_devices == TRUE)) {
1121 dci.dci_flags |= DCA_FLUSH_PATHINST;
1124 /* handle pre-cleanup operations desired by the modules. */
1125 pre_and_post_cleanup(RM_PRE);
1127 devi_tree_walk(&dci, flags, NULL);
1129 if (dci.dci_error) {
1130 devfsadm_exit(1);
1131 /*NOTREACHED*/
1134 /* handle post-cleanup operations desired by the modules. */
1135 pre_and_post_cleanup(RM_POST);
1137 unlock_dev(SYNC_STATE);
1140 /*ARGSUSED*/
1141 static void
1142 print_cache_signal(int signo)
1144 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1145 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1146 devfsadm_exit(1);
1147 /*NOTREACHED*/
1151 static void
1152 revoke_lookup_door(void)
1154 if (lookup_door_fd != -1) {
1155 if (door_revoke(lookup_door_fd) == -1) {
1156 err_print("door_revoke of %s failed - %s\n",
1157 lookup_door_path, strerror(errno));
1162 /*ARGSUSED*/
1163 static void
1164 catch_exit(int signo)
1166 revoke_lookup_door();
1170 * Register with eventd for messages. Create doors for synchronous
1171 * link creation.
1173 static void
1174 daemon_update(void)
1176 int fd;
1177 char *fcn = "daemon_update: ";
1178 char door_file[MAXPATHLEN];
1179 const char *subclass_list;
1180 sysevent_handle_t *sysevent_hp;
1181 vprint(CHATTY_MID, "%senter\n", fcn);
1183 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1184 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1185 devfsadm_exit(1);
1186 /*NOTREACHED*/
1188 if (signal(SIGTERM, catch_exit) == SIG_ERR) {
1189 err_print("signal SIGTERM failed: %s\n", strerror(errno));
1190 devfsadm_exit(1);
1191 /*NOTREACHED*/
1194 if (snprintf(door_file, sizeof (door_file),
1195 "%s%s", attr_root ? attr_root : root_dir, DEVFSADM_SERVICE_DOOR)
1196 >= sizeof (door_file)) {
1197 err_print("update_daemon failed to open sysevent service "
1198 "door\n");
1199 devfsadm_exit(1);
1200 /*NOTREACHED*/
1202 if ((sysevent_hp = sysevent_open_channel_alt(
1203 door_file)) == NULL) {
1204 err_print(CANT_CREATE_DOOR,
1205 door_file, strerror(errno));
1206 devfsadm_exit(1);
1207 /*NOTREACHED*/
1209 if (sysevent_bind_subscriber(sysevent_hp, event_handler) != 0) {
1210 err_print(CANT_CREATE_DOOR,
1211 door_file, strerror(errno));
1212 (void) sysevent_close_channel(sysevent_hp);
1213 devfsadm_exit(1);
1214 /*NOTREACHED*/
1216 subclass_list = EC_SUB_ALL;
1217 if (sysevent_register_event(sysevent_hp, EC_ALL, &subclass_list, 1)
1218 != 0) {
1219 err_print(CANT_CREATE_DOOR,
1220 door_file, strerror(errno));
1221 (void) sysevent_unbind_subscriber(sysevent_hp);
1222 (void) sysevent_close_channel(sysevent_hp);
1223 devfsadm_exit(1);
1224 /*NOTREACHED*/
1226 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1227 etc_dev_dir, DEVFSADM_SYNCH_DOOR) >= sizeof (door_file)) {
1228 err_print(CANT_CREATE_DOOR, DEVFSADM_SYNCH_DOOR,
1229 strerror(ENAMETOOLONG));
1230 devfsadm_exit(1);
1231 /*NOTREACHED*/
1234 (void) s_unlink(door_file);
1235 if ((fd = open(door_file, O_RDWR | O_CREAT, SYNCH_DOOR_PERMS)) == -1) {
1236 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1237 devfsadm_exit(1);
1238 /*NOTREACHED*/
1240 (void) close(fd);
1242 if ((fd = door_create(sync_handler, NULL,
1243 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1244 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1245 (void) s_unlink(door_file);
1246 devfsadm_exit(1);
1247 /*NOTREACHED*/
1250 if (fattach(fd, door_file) == -1) {
1251 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1252 (void) s_unlink(door_file);
1253 devfsadm_exit(1);
1254 /*NOTREACHED*/
1258 * devname_lookup_door
1260 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1261 etc_dev_dir, DEVNAME_LOOKUP_DOOR) >= sizeof (door_file)) {
1262 err_print(CANT_CREATE_DOOR, DEVNAME_LOOKUP_DOOR,
1263 strerror(ENAMETOOLONG));
1264 devfsadm_exit(1);
1265 /*NOTREACHED*/
1268 (void) s_unlink(door_file);
1269 if ((fd = open(door_file, O_RDWR | O_CREAT, S_IRUSR|S_IWUSR)) == -1) {
1270 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1271 devfsadm_exit(1);
1272 /*NOTREACHED*/
1274 (void) close(fd);
1276 if ((fd = door_create(devname_lookup_handler, NULL,
1277 DOOR_REFUSE_DESC)) == -1) {
1278 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1279 (void) s_unlink(door_file);
1280 devfsadm_exit(1);
1281 /*NOTREACHED*/
1284 (void) fdetach(door_file);
1285 lookup_door_path = s_strdup(door_file);
1286 retry:
1287 if (fattach(fd, door_file) == -1) {
1288 if (errno == EBUSY)
1289 goto retry;
1290 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1291 (void) s_unlink(door_file);
1292 devfsadm_exit(1);
1293 /*NOTREACHED*/
1295 lookup_door_fd = fd;
1297 /* pass down the door name to kernel for door_ki_open */
1298 if (devname_kcall(MODDEVNAME_LOOKUPDOOR, (void *)door_file) != 0)
1299 err_print(DEVNAME_CONTACT_FAILED, strerror(errno));
1301 vprint(CHATTY_MID, "%spausing\n", fcn);
1302 for (;;) {
1303 (void) pause();
1307 /*ARGSUSED*/
1308 static void
1309 sync_handler(void *cookie, char *ap, size_t asize,
1310 door_desc_t *dp, uint_t ndesc)
1312 door_cred_t dcred;
1313 struct dca_off *dcp, rdca;
1314 struct dca_impl dci;
1317 * Must be root to make this call
1318 * If caller is not root, don't touch its data.
1320 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
1321 dcp = &rdca;
1322 dcp->dca_error = EPERM;
1323 goto out;
1326 assert(ap);
1327 assert(asize == sizeof (*dcp));
1329 dcp = (void *)ap;
1332 * Root is always present and is the first component of "name" member
1334 assert(dcp->dca_root == 0);
1337 * The structure passed in by the door_client uses offsets
1338 * instead of pointers to work across address space boundaries.
1339 * Now copy the data into a structure (dca_impl) which uses
1340 * pointers.
1342 dci.dci_root = &dcp->dca_name[dcp->dca_root];
1343 dci.dci_minor = dcp->dca_minor ? &dcp->dca_name[dcp->dca_minor] : NULL;
1344 dci.dci_driver =
1345 dcp->dca_driver ? &dcp->dca_name[dcp->dca_driver] : NULL;
1346 dci.dci_error = 0;
1347 dci.dci_flags = dcp->dca_flags | (dci.dci_driver ? DCA_LOAD_DRV : 0);
1348 dci.dci_arg = NULL;
1350 lock_dev();
1351 devi_tree_walk(&dci, DINFOCPYALL, NULL);
1352 dcp->dca_error = dci.dci_error;
1354 if (dcp->dca_flags & DCA_DEVLINK_SYNC)
1355 unlock_dev(SYNC_STATE);
1356 else
1357 unlock_dev(CACHE_STATE);
1359 out: (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0);
1362 static void
1363 lock_dev(void)
1365 vprint(CHATTY_MID, "lock_dev(): entered\n");
1367 if (build_dev == FALSE)
1368 return;
1370 /* lockout other threads from /dev */
1371 while (sema_wait(&dev_sema) != 0)
1375 * Lock out other devfsadm processes from /dev.
1376 * If this wasn't the last process to run,
1377 * clear caches
1379 if (enter_dev_lock() != getpid()) {
1380 invalidate_enumerate_cache();
1381 rm_all_links_from_cache();
1382 (void) di_devlink_close(&devlink_cache, DI_LINK_ERROR);
1384 /* send any sysevents that were queued up. */
1385 process_syseventq();
1389 * (re)load the reverse links database if not
1390 * already cached.
1392 if (devlink_cache == NULL)
1393 devlink_cache = di_devlink_open(root_dir, 0);
1396 * If modules were unloaded, reload them. Also use module status
1397 * as an indication that we should check to see if other binding
1398 * files need to be reloaded.
1400 if (module_head == NULL) {
1401 load_modules();
1402 read_minor_perm_file();
1403 read_driver_aliases_file();
1404 read_devlinktab_file();
1405 read_logindevperm_file();
1406 read_enumerate_file();
1409 if (module_head != NULL)
1410 return;
1412 if (strcmp(prog, DEVLINKS) == 0) {
1413 if (devlinktab_list == NULL) {
1414 err_print(NO_LINKTAB, devlinktab_file);
1415 err_print(NO_MODULES, module_dirs);
1416 err_print(ABORTING);
1417 devfsadm_exit(1);
1418 /*NOTREACHED*/
1420 } else {
1421 err_print(NO_MODULES, module_dirs);
1422 if (strcmp(prog, DEVFSADM) == 0) {
1423 err_print(MODIFY_PATH);
1429 * Unlock the device. If we are processing a CACHE_STATE call, we signal a
1430 * minor_fini_thread delayed SYNC_STATE at the end of the call. If we are
1431 * processing a SYNC_STATE call, we cancel any minor_fini_thread SYNC_STATE
1432 * at both the start and end of the call since we will be doing the SYNC_STATE.
1434 static void
1435 unlock_dev(int flag)
1437 assert(flag == SYNC_STATE || flag == CACHE_STATE);
1439 vprint(CHATTY_MID, "unlock_dev(): entered\n");
1441 /* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */
1442 if (flag == SYNC_STATE) {
1443 (void) mutex_lock(&minor_fini_mutex);
1444 minor_fini_canceled = TRUE;
1445 minor_fini_delayed = FALSE;
1446 (void) mutex_unlock(&minor_fini_mutex);
1449 if (build_dev == FALSE)
1450 return;
1452 if (devlink_cache == NULL) {
1453 err_print(NO_DEVLINK_CACHE);
1455 assert(devlink_cache);
1457 if (flag == SYNC_STATE) {
1458 unload_modules();
1459 if (update_database)
1460 (void) di_devlink_update(devlink_cache);
1461 (void) di_devlink_close(&devlink_cache, 0);
1464 * now that the devlinks db cache has been flushed, it is safe
1465 * to send any sysevents that were queued up.
1467 process_syseventq();
1470 exit_dev_lock(0);
1472 (void) mutex_lock(&minor_fini_mutex);
1473 if (flag == SYNC_STATE) {
1474 /* We did a SYNC_STATE, cancel minor_fini_thread SYNC */
1475 minor_fini_canceled = TRUE;
1476 minor_fini_delayed = FALSE;
1477 } else {
1478 /* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */
1479 minor_fini_canceled = FALSE;
1480 minor_fini_delayed = TRUE;
1481 (void) cond_signal(&minor_fini_cv);
1483 (void) mutex_unlock(&minor_fini_mutex);
1485 (void) sema_post(&dev_sema);
1489 * Check that if -r is set, it is not any part of a zone--- that is, that
1490 * the zonepath is not a substring of the root path.
1492 static int
1493 zone_pathcheck(char *checkpath)
1495 void *dlhdl = NULL;
1496 char *name;
1497 char root[MAXPATHLEN]; /* resolved devfsadm root path */
1498 char zroot[MAXPATHLEN]; /* zone root path */
1499 char rzroot[MAXPATHLEN]; /* resolved zone root path */
1500 char tmp[MAXPATHLEN];
1501 FILE *cookie;
1502 int err = DEVFSADM_SUCCESS;
1504 if (checkpath[0] == '\0')
1505 return (DEVFSADM_SUCCESS);
1508 * Check if zones is available on this system.
1510 if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
1511 return (DEVFSADM_SUCCESS);
1514 bzero(root, sizeof (root));
1515 if (resolvepath(checkpath, root, sizeof (root) - 1) == -1) {
1517 * In this case the user has done "devfsadm -r" on some path
1518 * which does not yet exist, or we got some other misc. error.
1519 * We punt and don't resolve the path in this case.
1521 (void) strlcpy(root, checkpath, sizeof (root));
1524 if (strlen(root) > 0 && (root[strlen(root) - 1] != '/')) {
1525 (void) snprintf(tmp, sizeof (tmp), "%s/", root);
1526 (void) strlcpy(root, tmp, sizeof (root));
1529 cookie = setzoneent();
1530 while ((name = getzoneent(cookie)) != NULL) {
1531 /* Skip the global zone */
1532 if (strcmp(name, GLOBAL_ZONENAME) == 0) {
1533 free(name);
1534 continue;
1537 if (zone_get_zonepath(name, zroot, sizeof (zroot)) != Z_OK) {
1538 free(name);
1539 continue;
1542 bzero(rzroot, sizeof (rzroot));
1543 if (resolvepath(zroot, rzroot, sizeof (rzroot) - 1) == -1) {
1545 * Zone path doesn't exist, or other misc error,
1546 * so we try using the non-resolved pathname.
1548 (void) strlcpy(rzroot, zroot, sizeof (rzroot));
1550 if (strlen(rzroot) > 0 && (rzroot[strlen(rzroot) - 1] != '/')) {
1551 (void) snprintf(tmp, sizeof (tmp), "%s/", rzroot);
1552 (void) strlcpy(rzroot, tmp, sizeof (rzroot));
1556 * Finally, the comparison. If the zone root path is a
1557 * leading substring of the root path, fail.
1559 if (strncmp(rzroot, root, strlen(rzroot)) == 0) {
1560 err_print(ZONE_PATHCHECK, root, name);
1561 err = DEVFSADM_FAILURE;
1562 free(name);
1563 break;
1565 free(name);
1567 endzoneent(cookie);
1568 (void) dlclose(dlhdl);
1569 return (err);
1573 * Called by the daemon when it receives an event from the devfsadm SLM
1574 * to syseventd.
1576 * The devfsadm SLM uses a private event channel for communication to
1577 * devfsadmd set-up via private libsysevent interfaces. This handler is
1578 * used to bind to the devfsadmd channel for event delivery.
1579 * The devfsadmd SLM insures single calls to this routine as well as
1580 * synchronized event delivery.
1583 static void
1584 event_handler(sysevent_t *ev)
1586 char *path;
1587 char *minor;
1588 char *subclass;
1589 char *dev_ev_subclass;
1590 char *driver_name;
1591 nvlist_t *attr_list = NULL;
1592 int err = 0;
1593 int instance;
1594 int branch_event = 0;
1597 * If this is event-driven, then we cannot trust the static devlist
1598 * to be correct.
1601 event_driven = TRUE;
1602 subclass = sysevent_get_subclass_name(ev);
1603 vprint(EVENT_MID, "event_handler: %s id:0X%llx\n",
1604 subclass, sysevent_get_seq(ev));
1606 if (strcmp(subclass, ESC_DEVFS_START) == 0) {
1607 return;
1610 /* Check if event is an instance modification */
1611 if (strcmp(subclass, ESC_DEVFS_INSTANCE_MOD) == 0) {
1612 devfs_instance_mod();
1613 return;
1615 if (sysevent_get_attr_list(ev, &attr_list) != 0) {
1616 vprint(EVENT_MID, "event_handler: can not get attr list\n");
1617 return;
1620 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0 ||
1621 strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0 ||
1622 strcmp(subclass, ESC_DEVFS_MINOR_CREATE) == 0 ||
1623 strcmp(subclass, ESC_DEVFS_MINOR_REMOVE) == 0) {
1624 if ((err = nvlist_lookup_string(attr_list, DEVFS_PATHNAME,
1625 &path)) != 0)
1626 goto out;
1628 if (nvlist_lookup_string(attr_list, DEVFS_DEVI_CLASS,
1629 &dev_ev_subclass) != 0)
1630 dev_ev_subclass = NULL;
1632 if (nvlist_lookup_string(attr_list, DEVFS_DRIVER_NAME,
1633 &driver_name) != 0)
1634 driver_name = NULL;
1636 if (nvlist_lookup_int32(attr_list, DEVFS_INSTANCE,
1637 &instance) != 0)
1638 instance = -1;
1640 if (nvlist_lookup_int32(attr_list, DEVFS_BRANCH_EVENT,
1641 &branch_event) != 0)
1642 branch_event = 0;
1644 if (nvlist_lookup_string(attr_list, DEVFS_MINOR_NAME,
1645 &minor) != 0)
1646 minor = NULL;
1648 lock_dev();
1650 if (strcmp(ESC_DEVFS_DEVI_ADD, subclass) == 0) {
1651 add_minor_pathname(path, NULL, dev_ev_subclass);
1652 if (branch_event) {
1653 build_and_enq_event(EC_DEV_BRANCH,
1654 ESC_DEV_BRANCH_ADD, path, DI_NODE_NIL,
1655 NULL);
1658 } else if (strcmp(ESC_DEVFS_MINOR_CREATE, subclass) == 0) {
1659 add_minor_pathname(path, minor, dev_ev_subclass);
1661 } else if (strcmp(ESC_DEVFS_MINOR_REMOVE, subclass) == 0) {
1662 hot_cleanup(path, minor, dev_ev_subclass, driver_name,
1663 instance);
1665 } else { /* ESC_DEVFS_DEVI_REMOVE */
1666 hot_cleanup(path, NULL, dev_ev_subclass,
1667 driver_name, instance);
1668 if (branch_event) {
1669 build_and_enq_event(EC_DEV_BRANCH,
1670 ESC_DEV_BRANCH_REMOVE, path, DI_NODE_NIL,
1671 NULL);
1675 unlock_dev(CACHE_STATE);
1677 } else if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0 ||
1678 strcmp(subclass, ESC_DEVFS_BRANCH_REMOVE) == 0) {
1679 if ((err = nvlist_lookup_string(attr_list,
1680 DEVFS_PATHNAME, &path)) != 0)
1681 goto out;
1683 /* just log ESC_DEV_BRANCH... event */
1684 if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0)
1685 dev_ev_subclass = ESC_DEV_BRANCH_ADD;
1686 else
1687 dev_ev_subclass = ESC_DEV_BRANCH_REMOVE;
1689 lock_dev();
1690 build_and_enq_event(EC_DEV_BRANCH, dev_ev_subclass, path,
1691 DI_NODE_NIL, NULL);
1692 unlock_dev(CACHE_STATE);
1693 } else
1694 err_print(UNKNOWN_EVENT, subclass);
1696 out:
1697 if (err)
1698 err_print(EVENT_ATTR_LOOKUP_FAILED, strerror(err));
1699 nvlist_free(attr_list);
1702 static void
1703 dca_impl_init(char *root, char *minor, struct dca_impl *dcip)
1705 assert(root);
1707 dcip->dci_root = root;
1708 dcip->dci_minor = minor;
1709 dcip->dci_driver = NULL;
1710 dcip->dci_error = 0;
1711 dcip->dci_flags = 0;
1712 dcip->dci_arg = NULL;
1716 * Kernel logs a message when a devinfo node is attached. Try to create
1717 * /dev and /devices for each minor node. minorname can be NULL.
1719 void
1720 add_minor_pathname(char *node, char *minor, char *ev_subclass)
1722 struct dca_impl dci;
1724 vprint(CHATTY_MID, "add_minor_pathname: node_path=%s minor=%s\n",
1725 node, minor ? minor : "NULL");
1727 dca_impl_init(node, minor, &dci);
1730 * Restrict hotplug link creation if daemon
1731 * started with -i option.
1733 if (single_drv == TRUE) {
1734 dci.dci_driver = driver;
1738 * We are being invoked in response to a hotplug event.
1740 dci.dci_flags = DCA_HOT_PLUG | DCA_CHECK_TYPE;
1742 devi_tree_walk(&dci, DINFOPROP|DINFOMINOR, ev_subclass);
1745 static di_node_t
1746 find_clone_node()
1748 static di_node_t clone_node = DI_NODE_NIL;
1750 if (clone_node == DI_NODE_NIL)
1751 clone_node = di_init("/pseudo/clone@0", DINFOPROP);
1752 return (clone_node);
1755 static int
1756 is_descendent_of(di_node_t node, char *driver)
1758 while (node != DI_NODE_NIL) {
1759 char *drv = di_driver_name(node);
1760 if (strcmp(drv, driver) == 0)
1761 return (1);
1762 node = di_parent_node(node);
1764 return (0);
1768 * Checks the minor type. If it is an alias node, then lookup
1769 * the real node/minor first, then call minor_process() to
1770 * do the real work.
1772 static int
1773 check_minor_type(di_node_t node, di_minor_t minor, void *arg)
1775 ddi_minor_type minor_type;
1776 di_node_t clone_node;
1777 char *mn;
1778 char *nt;
1779 struct mlist *dep;
1780 struct dca_impl *dcip = arg;
1782 assert(dcip);
1784 dep = dcip->dci_arg;
1786 mn = di_minor_name(minor);
1789 * We match driver here instead of in minor_process
1790 * as we want the actual driver name. This check is
1791 * unnecessary during deferred processing.
1793 if (dep &&
1794 ((dcip->dci_driver && !is_descendent_of(node, dcip->dci_driver)) ||
1795 (dcip->dci_minor && strcmp(mn, dcip->dci_minor)))) {
1796 return (DI_WALK_CONTINUE);
1799 if ((dcip->dci_flags & DCA_CHECK_TYPE) &&
1800 (nt = di_minor_nodetype(minor)) &&
1801 (strcmp(nt, DDI_NT_NET) == 0)) {
1802 dcip->dci_flags &= ~DCA_CHECK_TYPE;
1805 minor_type = di_minor_type(minor);
1807 if (minor_type == DDM_MINOR) {
1808 minor_process(node, minor, dep);
1810 } else if (minor_type == DDM_ALIAS) {
1811 struct mlist *cdep, clone_del = {0};
1813 clone_node = find_clone_node();
1814 if (clone_node == DI_NODE_NIL) {
1815 err_print(DI_INIT_FAILED, "clone", strerror(errno));
1816 return (DI_WALK_CONTINUE);
1819 cdep = dep ? &clone_del : NULL;
1821 minor_process(clone_node, minor, cdep);
1824 * cache "alias" minor node and free "clone" minor
1826 if (cdep != NULL && cdep->head != NULL) {
1827 assert(cdep->tail != NULL);
1828 cache_deferred_minor(dep, node, minor);
1829 dcip->dci_arg = cdep;
1830 process_deferred_links(dcip, DCA_FREE_LIST);
1831 dcip->dci_arg = dep;
1835 return (DI_WALK_CONTINUE);
1840 * This is the entry point for each minor node, whether walking
1841 * the entire tree via di_walk_minor() or processing a hotplug event
1842 * for a single devinfo node (via hotplug ndi_devi_online()).
1844 /*ARGSUSED*/
1845 static void
1846 minor_process(di_node_t node, di_minor_t minor, struct mlist *dep)
1848 create_list_t *create;
1849 int defer;
1851 vprint(CHATTY_MID, "minor_process: node=%s, minor=%s\n",
1852 di_node_name(node), di_minor_name(minor));
1854 if (dep != NULL) {
1857 * Reset /devices node to minor_perm perm/ownership
1858 * if we are here to deactivate device allocation
1860 if (build_devices == TRUE) {
1861 reset_node_permissions(node, minor);
1864 if (build_dev == FALSE) {
1865 return;
1869 * This function will create any nodes for /etc/devlink.tab.
1870 * If devlink.tab handles link creation, we don't call any
1871 * devfsadm modules since that could cause duplicate caching
1872 * in the enumerate functions if different re strings are
1873 * passed that are logically identical. I'm still not
1874 * convinced this would cause any harm, but better to be safe.
1876 * Deferred processing is available only for devlinks
1877 * created through devfsadm modules.
1879 if (process_devlink_compat(minor, node) == TRUE) {
1880 return;
1882 } else {
1883 vprint(CHATTY_MID, "minor_process: deferred processing\n");
1887 * look for relevant link create rules in the modules, and
1888 * invoke the link create callback function to build a link
1889 * if there is a match.
1891 defer = 0;
1892 for (create = create_head; create != NULL; create = create->next) {
1893 if ((minor_matches_rule(node, minor, create) == TRUE) &&
1894 class_ok(create->create->device_class) ==
1895 DEVFSADM_SUCCESS) {
1896 if (call_minor_init(create->modptr) ==
1897 DEVFSADM_FAILURE) {
1898 continue;
1902 * If NOT doing the deferred creates (i.e. 1st pass) and
1903 * rule requests deferred processing cache the minor
1904 * data.
1906 * If deferred processing (2nd pass), create links
1907 * ONLY if rule requests deferred processing.
1909 if (dep && ((create->create->flags & CREATE_MASK) ==
1910 CREATE_DEFER)) {
1911 defer = 1;
1912 continue;
1913 } else if (dep == NULL &&
1914 ((create->create->flags & CREATE_MASK) !=
1915 CREATE_DEFER)) {
1916 continue;
1919 if ((*(create->create->callback_fcn))
1920 (minor, node) == DEVFSADM_TERMINATE) {
1921 break;
1926 if (defer)
1927 cache_deferred_minor(dep, node, minor);
1932 * Cache node and minor in defer list.
1934 static void
1935 cache_deferred_minor(
1936 struct mlist *dep,
1937 di_node_t node,
1938 di_minor_t minor)
1940 struct minor *mp;
1941 const char *fcn = "cache_deferred_minor";
1943 vprint(CHATTY_MID, "%s node=%s, minor=%s\n", fcn,
1944 di_node_name(node), di_minor_name(minor));
1946 if (dep == NULL) {
1947 vprint(CHATTY_MID, "%s: cannot cache during "
1948 "deferred processing. Ignoring minor\n", fcn);
1949 return;
1952 mp = (struct minor *)s_zalloc(sizeof (struct minor));
1953 mp->node = node;
1954 mp->minor = minor;
1955 mp->next = NULL;
1957 assert(dep->head == NULL || dep->tail != NULL);
1958 if (dep->head == NULL) {
1959 dep->head = mp;
1960 } else {
1961 dep->tail->next = mp;
1963 dep->tail = mp;
1967 * Check to see if "create" link creation rule matches this node/minor.
1968 * If it does, return TRUE.
1970 static int
1971 minor_matches_rule(di_node_t node, di_minor_t minor, create_list_t *create)
1973 char *m_nodetype, *m_drvname;
1975 if (create->create->node_type != NULL) {
1977 m_nodetype = di_minor_nodetype(minor);
1978 assert(m_nodetype != NULL);
1980 switch (create->create->flags & TYPE_MASK) {
1981 case TYPE_EXACT:
1982 if (strcmp(create->create->node_type, m_nodetype) !=
1983 0) {
1984 return (FALSE);
1986 break;
1987 case TYPE_PARTIAL:
1988 if (strncmp(create->create->node_type, m_nodetype,
1989 strlen(create->create->node_type)) != 0) {
1990 return (FALSE);
1992 break;
1993 case TYPE_RE:
1994 if (regexec(&(create->node_type_comp), m_nodetype,
1995 0, NULL, 0) != 0) {
1996 return (FALSE);
1998 break;
2002 if (create->create->drv_name != NULL) {
2003 m_drvname = di_driver_name(node);
2004 switch (create->create->flags & DRV_MASK) {
2005 case DRV_EXACT:
2006 if (strcmp(create->create->drv_name, m_drvname) != 0) {
2007 return (FALSE);
2009 break;
2010 case DRV_RE:
2011 if (regexec(&(create->drv_name_comp), m_drvname,
2012 0, NULL, 0) != 0) {
2013 return (FALSE);
2015 break;
2019 return (TRUE);
2023 * If no classes were given on the command line, then return DEVFSADM_SUCCESS.
2024 * Otherwise, return DEVFSADM_SUCCESS if the device "class" from the module
2025 * matches one of the device classes given on the command line,
2026 * otherwise, return DEVFSADM_FAILURE.
2028 static int
2029 class_ok(char *class)
2031 int i;
2033 if (num_classes == 0) {
2034 return (DEVFSADM_SUCCESS);
2037 for (i = 0; i < num_classes; i++) {
2038 if (strcmp(class, classes[i]) == 0) {
2039 return (DEVFSADM_SUCCESS);
2042 return (DEVFSADM_FAILURE);
2046 * call minor_fini on active modules, then unload ALL modules
2048 static void
2049 unload_modules(void)
2051 module_t *module_free;
2052 create_list_t *create_free;
2053 remove_list_t *remove_free;
2055 while (create_head != NULL) {
2056 create_free = create_head;
2057 create_head = create_head->next;
2059 if ((create_free->create->flags & TYPE_RE) == TYPE_RE) {
2060 regfree(&(create_free->node_type_comp));
2062 if ((create_free->create->flags & DRV_RE) == DRV_RE) {
2063 regfree(&(create_free->drv_name_comp));
2065 free(create_free);
2068 while (remove_head != NULL) {
2069 remove_free = remove_head;
2070 remove_head = remove_head->next;
2071 free(remove_free);
2074 while (module_head != NULL) {
2076 if ((module_head->minor_fini != NULL) &&
2077 ((module_head->flags & MODULE_ACTIVE) == MODULE_ACTIVE)) {
2078 (void) (*(module_head->minor_fini))();
2081 vprint(MODLOAD_MID, "unloading module %s\n", module_head->name);
2082 free(module_head->name);
2083 (void) dlclose(module_head->dlhandle);
2085 module_free = module_head;
2086 module_head = module_head->next;
2087 free(module_free);
2092 * Load devfsadm logical link processing modules.
2094 static void
2095 load_modules(void)
2097 DIR *mod_dir;
2098 struct dirent *entp;
2099 char cdir[PATH_MAX + 1];
2100 char *last;
2101 char *mdir = module_dirs;
2102 char *fcn = "load_modules: ";
2104 while (*mdir != '\0') {
2106 while (*mdir == ':') {
2107 mdir++;
2110 if (*mdir == '\0') {
2111 continue;
2114 last = strchr(mdir, ':');
2116 if (last == NULL) {
2117 last = mdir + strlen(mdir);
2120 (void) strncpy(cdir, mdir, last - mdir);
2121 cdir[last - mdir] = '\0';
2122 mdir += strlen(cdir);
2124 if ((mod_dir = opendir(cdir)) == NULL) {
2125 vprint(MODLOAD_MID, "%sopendir(%s): %s\n",
2126 fcn, cdir, strerror(errno));
2127 continue;
2130 while ((entp = readdir(mod_dir)) != NULL) {
2132 if ((strcmp(entp->d_name, ".") == 0) ||
2133 (strcmp(entp->d_name, "..") == 0)) {
2134 continue;
2137 load_module(entp->d_name, cdir);
2139 s_closedir(mod_dir);
2143 static void
2144 load_module(char *mname, char *cdir)
2146 _devfsadm_create_reg_t *create_reg;
2147 _devfsadm_remove_reg_V1_t *remove_reg;
2148 create_list_t *create_list_element;
2149 create_list_t **create_list_next;
2150 remove_list_t *remove_list_element;
2151 remove_list_t **remove_list_next;
2152 char epath[PATH_MAX + 1], *end;
2153 char *fcn = "load_module: ";
2154 char *dlerrstr;
2155 void *dlhandle;
2156 module_t *module;
2157 int flags;
2158 int n;
2159 int i;
2161 /* ignore any file which does not end in '.so' */
2162 if ((end = strstr(mname, MODULE_SUFFIX)) != NULL) {
2163 if (end[strlen(MODULE_SUFFIX)] != '\0') {
2164 return;
2166 } else {
2167 return;
2170 (void) snprintf(epath, sizeof (epath), "%s/%s", cdir, mname);
2172 if ((dlhandle = dlopen(epath, RTLD_LAZY)) == NULL) {
2173 dlerrstr = dlerror();
2174 err_print(DLOPEN_FAILED, epath,
2175 dlerrstr ? dlerrstr : "unknown error");
2176 return;
2179 /* dlsym the _devfsadm_create_reg structure */
2180 if (NULL == (create_reg = (_devfsadm_create_reg_t *)
2181 dlsym(dlhandle, _DEVFSADM_CREATE_REG))) {
2182 vprint(MODLOAD_MID, "dlsym(%s, %s): symbol not found\n", epath,
2183 _DEVFSADM_CREATE_REG);
2184 } else {
2185 vprint(MODLOAD_MID, "%sdlsym(%s, %s) succeeded\n",
2186 fcn, epath, _DEVFSADM_CREATE_REG);
2189 /* dlsym the _devfsadm_remove_reg structure */
2190 if (NULL == (remove_reg = (_devfsadm_remove_reg_V1_t *)
2191 dlsym(dlhandle, _DEVFSADM_REMOVE_REG))) {
2192 vprint(MODLOAD_MID, "dlsym(%s,\n\t%s): symbol not found\n",
2193 epath, _DEVFSADM_REMOVE_REG);
2194 } else {
2195 vprint(MODLOAD_MID, "dlsym(%s, %s): succeeded\n",
2196 epath, _DEVFSADM_REMOVE_REG);
2199 vprint(MODLOAD_MID, "module %s loaded\n", epath);
2201 module = (module_t *)s_malloc(sizeof (module_t));
2202 module->name = s_strdup(epath);
2203 module->dlhandle = dlhandle;
2205 /* dlsym other module functions, to be called later */
2206 module->minor_fini = (int (*)())dlsym(dlhandle, MINOR_FINI);
2207 module->minor_init = (int (*)())dlsym(dlhandle, MINOR_INIT);
2208 module->flags = 0;
2211 * put a ptr to each struct devfsadm_create on "create_head"
2212 * list sorted in interpose_lvl.
2214 if (create_reg != NULL) {
2215 for (i = 0; i < create_reg->count; i++) {
2216 int flags = create_reg->tblp[i].flags;
2218 create_list_element = (create_list_t *)
2219 s_malloc(sizeof (create_list_t));
2221 create_list_element->create = &(create_reg->tblp[i]);
2222 create_list_element->modptr = module;
2224 if (((flags & CREATE_MASK) != 0) &&
2225 ((flags & CREATE_MASK) != CREATE_DEFER)) {
2226 free(create_list_element);
2227 err_print("illegal flag combination in "
2228 "module create\n");
2229 err_print(IGNORING_ENTRY, i, epath);
2230 continue;
2233 if (((flags & TYPE_MASK) == 0) ^
2234 (create_reg->tblp[i].node_type == NULL)) {
2235 free(create_list_element);
2236 err_print("flags value incompatible with "
2237 "node_type value in module create\n");
2238 err_print(IGNORING_ENTRY, i, epath);
2239 continue;
2242 if (((flags & TYPE_MASK) != 0) &&
2243 ((flags & TYPE_MASK) != TYPE_EXACT) &&
2244 ((flags & TYPE_MASK) != TYPE_RE) &&
2245 ((flags & TYPE_MASK) != TYPE_PARTIAL)) {
2246 free(create_list_element);
2247 err_print("illegal TYPE_* flag combination in "
2248 "module create\n");
2249 err_print(IGNORING_ENTRY, i, epath);
2250 continue;
2253 /* precompile regular expression for efficiency */
2254 if ((flags & TYPE_RE) == TYPE_RE) {
2255 if ((n = regcomp(&(create_list_element->
2256 node_type_comp),
2257 create_reg->tblp[i].node_type,
2258 REG_EXTENDED)) != 0) {
2259 free(create_list_element);
2260 err_print(REGCOMP_FAILED,
2261 create_reg->tblp[i].node_type, n);
2262 err_print(IGNORING_ENTRY, i, epath);
2263 continue;
2267 if (((flags & DRV_MASK) == 0) ^
2268 (create_reg->tblp[i].drv_name == NULL)) {
2269 if ((flags & TYPE_RE) == TYPE_RE) {
2270 regfree(&(create_list_element->
2271 node_type_comp));
2273 free(create_list_element);
2274 err_print("flags value incompatible with "
2275 "drv_name value in module create\n");
2276 err_print(IGNORING_ENTRY, i, epath);
2277 continue;
2280 if (((flags & DRV_MASK) != 0) &&
2281 ((flags & DRV_MASK) != DRV_EXACT) &&
2282 ((flags & DRV_MASK) != DRV_RE)) {
2283 if ((flags & TYPE_RE) == TYPE_RE) {
2284 regfree(&(create_list_element->
2285 node_type_comp));
2287 free(create_list_element);
2288 err_print("illegal DRV_* flag combination in "
2289 "module create\n");
2290 err_print(IGNORING_ENTRY, i, epath);
2291 continue;
2294 /* precompile regular expression for efficiency */
2295 if ((create_reg->tblp[i].flags & DRV_RE) == DRV_RE) {
2296 if ((n = regcomp(&(create_list_element->
2297 drv_name_comp),
2298 create_reg->tblp[i].drv_name,
2299 REG_EXTENDED)) != 0) {
2300 if ((flags & TYPE_RE) == TYPE_RE) {
2301 regfree(&(create_list_element->
2302 node_type_comp));
2304 free(create_list_element);
2305 err_print(REGCOMP_FAILED,
2306 create_reg->tblp[i].drv_name, n);
2307 err_print(IGNORING_ENTRY, i, epath);
2308 continue;
2313 /* add to list sorted by interpose level */
2314 for (create_list_next = &(create_head);
2315 (*create_list_next != NULL) &&
2316 (*create_list_next)->create->interpose_lvl >=
2317 create_list_element->create->interpose_lvl;
2318 create_list_next = &((*create_list_next)->next))
2320 create_list_element->next = *create_list_next;
2321 *create_list_next = create_list_element;
2326 * put a ptr to each struct devfsadm_remove on "remove_head"
2327 * list sorted by interpose_lvl.
2329 flags = 0;
2330 if (remove_reg != NULL) {
2331 if (remove_reg->version < DEVFSADM_V1)
2332 flags |= RM_NOINTERPOSE;
2333 for (i = 0; i < remove_reg->count; i++) {
2335 remove_list_element = (remove_list_t *)
2336 s_malloc(sizeof (remove_list_t));
2338 remove_list_element->remove = &(remove_reg->tblp[i]);
2339 remove_list_element->remove->flags |= flags;
2340 remove_list_element->modptr = module;
2342 for (remove_list_next = &(remove_head);
2343 (*remove_list_next != NULL) &&
2344 (*remove_list_next)->remove->interpose_lvl >=
2345 remove_list_element->remove->interpose_lvl;
2346 remove_list_next = &((*remove_list_next)->next))
2348 remove_list_element->next = *remove_list_next;
2349 *remove_list_next = remove_list_element;
2353 module->next = module_head;
2354 module_head = module;
2358 * After we have completed a CACHE_STATE, if a SYNC_STATE does not occur
2359 * within 'timeout' secs the minor_fini_thread needs to do a SYNC_STATE
2360 * so that we still call the minor_fini routines.
2362 /*ARGSUSED*/
2363 static void
2364 minor_fini_thread(void *arg)
2366 timestruc_t abstime;
2368 vprint(INITFINI_MID, "minor_fini_thread starting\n");
2370 (void) mutex_lock(&minor_fini_mutex);
2371 for (;;) {
2372 /* wait the gather period, or until signaled */
2373 abstime.tv_sec = time(NULL) + minor_fini_timeout;
2374 abstime.tv_nsec = 0;
2375 (void) cond_timedwait(&minor_fini_cv,
2376 &minor_fini_mutex, &abstime);
2378 /* if minor_fini was canceled, go wait again */
2379 if (minor_fini_canceled == TRUE)
2380 continue;
2382 /* if minor_fini was delayed, go wait again */
2383 if (minor_fini_delayed == TRUE) {
2384 minor_fini_delayed = FALSE;
2385 continue;
2388 /* done with cancellations and delays, do the SYNC_STATE */
2389 (void) mutex_unlock(&minor_fini_mutex);
2391 lock_dev();
2392 unlock_dev(SYNC_STATE);
2393 vprint(INITFINI_MID, "minor_fini sync done\n");
2395 (void) mutex_lock(&minor_fini_mutex);
2401 * Attempt to initialize module, if a minor_init routine exists. Set
2402 * the active flag if the routine exists and succeeds. If it doesn't
2403 * exist, just set the active flag.
2405 static int
2406 call_minor_init(module_t *module)
2408 char *fcn = "call_minor_init: ";
2410 if ((module->flags & MODULE_ACTIVE) == MODULE_ACTIVE) {
2411 return (DEVFSADM_SUCCESS);
2414 vprint(INITFINI_MID, "%smodule %s. current state: inactive\n",
2415 fcn, module->name);
2417 if (module->minor_init == NULL) {
2418 module->flags |= MODULE_ACTIVE;
2419 vprint(INITFINI_MID, "minor_init not defined\n");
2420 return (DEVFSADM_SUCCESS);
2423 if ((*(module->minor_init))() == DEVFSADM_FAILURE) {
2424 err_print(FAILED_FOR_MODULE, MINOR_INIT, module->name);
2425 return (DEVFSADM_FAILURE);
2428 vprint(INITFINI_MID, "minor_init() returns DEVFSADM_SUCCESS. "
2429 "new state: active\n");
2431 module->flags |= MODULE_ACTIVE;
2432 return (DEVFSADM_SUCCESS);
2436 * Creates a symlink 'link' to the physical path of node:minor.
2437 * Construct link contents, then call create_link_common().
2439 /*ARGSUSED*/
2441 devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags)
2443 char rcontents[PATH_MAX];
2444 char devlink[PATH_MAX];
2445 char phy_path[PATH_MAX];
2446 char *acontents;
2447 char *dev_path;
2448 int numslashes;
2449 int rv;
2450 int i, link_exists;
2451 int last_was_slash = FALSE;
2454 * try to use devices path
2456 if ((node == lnode) && (minor == lminor)) {
2457 acontents = lphy_path;
2458 } else if (di_minor_type(minor) == DDM_ALIAS) {
2459 /* use /pseudo/clone@0:<driver> as the phys path */
2460 (void) snprintf(phy_path, sizeof (phy_path),
2461 "/pseudo/clone@0:%s",
2462 di_driver_name(di_minor_devinfo(minor)));
2463 acontents = phy_path;
2464 } else {
2465 if ((dev_path = di_devfs_path(node)) == NULL) {
2466 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2467 devfsadm_exit(1);
2468 /*NOTREACHED*/
2470 (void) snprintf(phy_path, sizeof (phy_path), "%s:%s",
2471 dev_path, di_minor_name(minor));
2472 di_devfs_path_free(dev_path);
2473 acontents = phy_path;
2476 /* prepend link with dev_dir contents */
2477 (void) strlcpy(devlink, dev_dir, sizeof (devlink));
2478 (void) strlcat(devlink, "/", sizeof (devlink));
2479 (void) strlcat(devlink, link, sizeof (devlink));
2482 * Calculate # of ../ to add. Account for double '//' in path.
2483 * Ignore all leading slashes.
2485 for (i = 0; link[i] == '/'; i++)
2487 for (numslashes = 0; link[i] != '\0'; i++) {
2488 if (link[i] == '/') {
2489 if (last_was_slash == FALSE) {
2490 numslashes++;
2491 last_was_slash = TRUE;
2493 } else {
2494 last_was_slash = FALSE;
2497 /* Don't count any trailing '/' */
2498 if (link[i-1] == '/') {
2499 numslashes--;
2502 rcontents[0] = '\0';
2503 do {
2504 (void) strlcat(rcontents, "../", sizeof (rcontents));
2505 } while (numslashes-- != 0);
2507 (void) strlcat(rcontents, "devices", sizeof (rcontents));
2508 (void) strlcat(rcontents, acontents, sizeof (rcontents));
2510 if (devlinks_debug == TRUE) {
2511 vprint(INFO_MID, "adding link %s ==> %s\n", devlink, rcontents);
2514 if ((rv = create_link_common(devlink, rcontents, &link_exists))
2515 == DEVFSADM_SUCCESS) {
2516 linknew = TRUE;
2517 add_link_to_cache(link, acontents);
2518 } else {
2519 linknew = FALSE;
2522 if (link_exists == TRUE) {
2523 /* Link exists or was just created */
2524 (void) di_devlink_add_link(devlink_cache, link, rcontents,
2525 DI_PRIMARY_LINK);
2528 return (rv);
2532 * Creates a symlink link to primary_link. Calculates relative
2533 * directory offsets, then calls link_common().
2535 /*ARGSUSED*/
2537 devfsadm_secondary_link(char *link, char *primary_link, int flags)
2539 char contents[PATH_MAX + 1];
2540 char devlink[PATH_MAX + 1];
2541 int rv, link_exists;
2542 char *fpath;
2543 char *tpath;
2544 char *op;
2546 /* prepend link with dev_dir contents */
2547 (void) strcpy(devlink, dev_dir);
2548 (void) strcat(devlink, "/");
2549 (void) strcat(devlink, link);
2551 * building extra link, so use first link as link contents, but first
2552 * make it relative.
2554 fpath = link;
2555 tpath = primary_link;
2556 op = contents;
2558 while (*fpath == *tpath && *fpath != '\0') {
2559 fpath++, tpath++;
2562 /* Count directories to go up, if any, and add "../" */
2563 while (*fpath != '\0') {
2564 if (*fpath == '/') {
2565 (void) strcpy(op, "../");
2566 op += 3;
2568 fpath++;
2572 * Back up to the start of the current path component, in
2573 * case in the middle
2575 while (tpath != primary_link && *(tpath-1) != '/') {
2576 tpath--;
2578 (void) strcpy(op, tpath);
2580 if (devlinks_debug == TRUE) {
2581 vprint(INFO_MID, "adding extra link %s ==> %s\n",
2582 devlink, contents);
2585 if ((rv = create_link_common(devlink, contents, &link_exists))
2586 == DEVFSADM_SUCCESS) {
2588 * we need to save the ultimate /devices contents, and not the
2589 * secondary link, since hotcleanup only looks at /devices path.
2590 * Since we don't have devices path here, we can try to get it
2591 * by readlink'ing the secondary link. This assumes the primary
2592 * link was created first.
2594 add_link_to_cache(link, lphy_path);
2595 linknew = TRUE;
2596 } else {
2597 linknew = FALSE;
2601 * If link exists or was just created, add it to the database
2603 if (link_exists == TRUE) {
2604 (void) di_devlink_add_link(devlink_cache, link, contents,
2605 DI_SECONDARY_LINK);
2608 return (rv);
2611 /* returns pointer to the devices directory */
2612 char *
2613 devfsadm_get_devices_dir()
2615 return (devices_dir);
2619 * Does the actual link creation. VERBOSE_MID only used if there is
2620 * a change. CHATTY_MID used otherwise.
2622 static int
2623 create_link_common(char *devlink, char *contents, int *exists)
2625 int try;
2626 int linksize;
2627 int max_tries = 0;
2628 static int prev_link_existed = TRUE;
2629 char checkcontents[PATH_MAX + 1];
2630 char *hide;
2632 *exists = FALSE;
2634 /* Database is not updated when file_mods == FALSE */
2635 if (file_mods == FALSE) {
2636 /* we want *actual* link contents so no alias redirection */
2637 linksize = readlink(devlink, checkcontents, PATH_MAX);
2638 if (linksize > 0) {
2639 checkcontents[linksize] = '\0';
2640 if (strcmp(checkcontents, contents) != 0) {
2641 vprint(CHATTY_MID, REMOVING_LINK,
2642 devlink, checkcontents);
2643 return (DEVFSADM_SUCCESS);
2644 } else {
2645 vprint(CHATTY_MID, "link exists and is correct:"
2646 " %s -> %s\n", devlink, contents);
2647 /* failure only in that the link existed */
2648 return (DEVFSADM_FAILURE);
2650 } else {
2651 vprint(VERBOSE_MID, CREATING_LINK, devlink, contents);
2652 return (DEVFSADM_SUCCESS);
2657 * systems calls are expensive, so predict whether to readlink
2658 * or symlink first, based on previous attempt
2660 if (prev_link_existed == FALSE) {
2661 try = CREATE_LINK;
2662 } else {
2663 try = READ_LINK;
2666 while (++max_tries <= 3) {
2668 switch (try) {
2669 case CREATE_LINK:
2671 if (symlink(contents, devlink) == 0) {
2672 vprint(VERBOSE_MID, CREATING_LINK, devlink,
2673 contents);
2674 prev_link_existed = FALSE;
2675 /* link successfully created */
2676 *exists = TRUE;
2677 set_logindev_perms(devlink);
2678 return (DEVFSADM_SUCCESS);
2679 } else {
2680 switch (errno) {
2682 case ENOENT:
2683 /* dirpath to node doesn't exist */
2684 hide = strrchr(devlink, '/');
2685 *hide = '\0';
2686 s_mkdirp(devlink, S_IRWXU|S_IRGRP|
2687 S_IXGRP|S_IROTH|S_IXOTH);
2688 *hide = '/';
2689 break;
2690 case EEXIST:
2691 try = READ_LINK;
2692 break;
2693 default:
2694 err_print(SYMLINK_FAILED, devlink,
2695 contents, strerror(errno));
2696 return (DEVFSADM_FAILURE);
2699 break;
2701 case READ_LINK:
2704 * If there is redirection, new phys path
2705 * and old phys path will not match and the
2706 * link will be created with new phys path
2707 * which is what we want. So we want real
2708 * contents.
2710 linksize = readlink(devlink, checkcontents, PATH_MAX);
2711 if (linksize >= 0) {
2712 checkcontents[linksize] = '\0';
2713 if (strcmp(checkcontents, contents) != 0) {
2714 s_unlink(devlink);
2715 vprint(VERBOSE_MID, REMOVING_LINK,
2716 devlink, checkcontents);
2717 try = CREATE_LINK;
2718 } else {
2719 prev_link_existed = TRUE;
2720 vprint(CHATTY_MID,
2721 "link exists and is correct:"
2722 " %s -> %s\n", devlink, contents);
2723 *exists = TRUE;
2724 /* failure in that the link existed */
2725 return (DEVFSADM_FAILURE);
2727 } else {
2728 switch (errno) {
2729 case EINVAL:
2730 /* not a symlink, remove and create */
2731 s_unlink(devlink);
2732 default:
2733 /* maybe it didn't exist at all */
2734 try = CREATE_LINK;
2735 break;
2738 break;
2741 err_print(MAX_ATTEMPTS, devlink, contents);
2742 return (DEVFSADM_FAILURE);
2745 static void
2746 set_logindev_perms(char *devlink)
2748 struct login_dev *newdev;
2749 struct passwd pwd, *resp;
2750 char pwd_buf[PATH_MAX];
2751 int rv;
2752 struct stat sb;
2753 char *devfs_path = NULL;
2756 * We only want logindev perms to be set when a device is
2757 * hotplugged or an application requests synchronous creates.
2758 * So we enable this only in daemon mode. In addition,
2759 * login(1) only fixes the std. /dev dir. So we don't
2760 * change perms if alternate root is set.
2761 * login_dev_enable is TRUE only in these cases.
2763 if (login_dev_enable != TRUE)
2764 return;
2767 * Normally, /etc/logindevperm has few (8 - 10 entries) which
2768 * may be regular expressions (globs were converted to RE).
2769 * So just do a linear search through the list.
2771 for (newdev = login_dev_cache; newdev; newdev = newdev->ldev_next) {
2772 vprint(FILES_MID, "matching %s with %s\n", devlink,
2773 newdev->ldev_device);
2775 if (regexec(&newdev->ldev_device_regex, devlink, 0,
2776 NULL, 0) == 0) {
2777 vprint(FILES_MID, "matched %s with %s\n", devlink,
2778 newdev->ldev_device);
2779 break;
2783 if (newdev == NULL)
2784 return;
2787 * we have a match, now find the driver associated with this
2788 * minor node using a snapshot on the physical path
2790 (void) resolve_link(devlink, NULL, NULL, &devfs_path, 0);
2792 * We dont need redirection here - the actual link contents
2793 * whether "alias" or "current" are fine
2795 if (devfs_path) {
2796 di_node_t node;
2797 char *drv;
2798 struct driver_list *list;
2799 char *p;
2801 /* truncate on : so we can take a snapshot */
2802 (void) strcpy(pwd_buf, devfs_path);
2803 p = strrchr(pwd_buf, ':');
2804 if (p == NULL) {
2805 free(devfs_path);
2806 return;
2808 *p = '\0';
2810 vprint(FILES_MID, "link=%s->physpath=%s\n",
2811 devlink, pwd_buf);
2813 node = di_init(pwd_buf, DINFOMINOR);
2815 drv = NULL;
2816 if (node) {
2817 drv = di_driver_name(node);
2819 if (drv) {
2820 vprint(FILES_MID, "%s: driver is %s\n",
2821 devlink, drv);
2824 /* search thru the driver list specified in logindevperm */
2825 list = newdev->ldev_driver_list;
2826 if ((drv != NULL) && (list != NULL)) {
2827 while (list) {
2828 if (strcmp(list->driver_name,
2829 drv) == 0) {
2830 vprint(FILES_MID,
2831 "driver %s match!\n", drv);
2832 break;
2834 list = list->next;
2836 if (list == NULL) {
2837 vprint(FILES_MID, "no driver match!\n");
2838 free(devfs_path);
2839 return;
2842 free(devfs_path);
2843 di_fini(node);
2844 } else {
2845 return;
2848 vprint(FILES_MID, "changing permissions of %s\n", devlink);
2851 * We have a match. We now attempt to determine the
2852 * owner and group of the console user.
2854 * stat() the console device newdev->ldev_console
2855 * which will always exist - it will have the right owner but
2856 * not the right group. Use getpwuid_r() to determine group for this
2857 * uid.
2858 * Note, it is safe to use name service here since if name services
2859 * are not available (during boot or in single-user mode), then
2860 * console owner will be root and its gid can be found in
2861 * local files.
2863 if (stat(newdev->ldev_console, &sb) == -1) {
2864 vprint(VERBOSE_MID, STAT_FAILED, newdev->ldev_console,
2865 strerror(errno));
2866 return;
2869 resp = NULL;
2870 rv = getpwuid_r(sb.st_uid, &pwd, pwd_buf, sizeof (pwd_buf), &resp);
2871 if (rv || resp == NULL) {
2872 rv = rv ? rv : EINVAL;
2873 vprint(VERBOSE_MID, GID_FAILED, sb.st_uid,
2874 strerror(rv));
2875 return;
2878 assert(&pwd == resp);
2880 sb.st_gid = resp->pw_gid;
2882 if (chmod(devlink, newdev->ldev_perms) == -1) {
2883 vprint(VERBOSE_MID, CHMOD_FAILED, devlink,
2884 strerror(errno));
2885 return;
2888 if (chown(devlink, sb.st_uid, sb.st_gid) == -1) {
2889 vprint(VERBOSE_MID, CHOWN_FAILED, devlink,
2890 strerror(errno));
2895 * Reset /devices node with appropriate permissions and
2896 * ownership as specified in /etc/minor_perm.
2898 static void
2899 reset_node_permissions(di_node_t node, di_minor_t minor)
2901 int spectype;
2902 char phy_path[PATH_MAX + 1];
2903 mode_t mode;
2904 dev_t dev;
2905 uid_t uid;
2906 gid_t gid;
2907 struct stat sb;
2908 char *dev_path, *aminor = NULL;
2910 /* lphy_path starts with / */
2911 if ((dev_path = di_devfs_path(node)) == NULL) {
2912 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2913 devfsadm_exit(1);
2914 /*NOTREACHED*/
2916 (void) strcpy(lphy_path, dev_path);
2917 di_devfs_path_free(dev_path);
2919 (void) strcat(lphy_path, ":");
2920 if (di_minor_type(minor) == DDM_ALIAS) {
2921 char *driver;
2922 aminor = di_minor_name(minor);
2923 driver = di_driver_name(di_minor_devinfo(minor));
2924 (void) strcat(lphy_path, driver);
2925 } else
2926 (void) strcat(lphy_path, di_minor_name(minor));
2928 (void) strcpy(phy_path, devices_dir);
2929 (void) strcat(phy_path, lphy_path);
2931 lnode = node;
2932 lminor = minor;
2934 vprint(CHATTY_MID, "reset_node_permissions: phy_path=%s lphy_path=%s\n",
2935 phy_path, lphy_path);
2937 dev = di_minor_devt(minor);
2938 spectype = di_minor_spectype(minor); /* block or char */
2940 getattr(phy_path, aminor, spectype, dev, &mode, &uid, &gid);
2943 * compare and set permissions and ownership
2945 * Under devfs, a quick insertion and removal of USB devices
2946 * would cause stat of physical path to fail. In this case,
2947 * we emit a verbose message, but don't print errors.
2949 if ((stat(phy_path, &sb) == -1) || (sb.st_rdev != dev)) {
2950 vprint(VERBOSE_MID, NO_DEVFS_NODE, phy_path);
2951 return;
2955 * If we are here for a new device
2956 * If device allocation is on
2957 * then
2958 * set ownership to root:other and permissions to 0000
2959 * else
2960 * set ownership and permissions as specified in minor_perm
2961 * If we are here for an existing device
2962 * If device allocation is to be turned on
2963 * then
2964 * reset ownership to root:other and permissions to 0000
2965 * else if device allocation is to be turned off
2966 * reset ownership and permissions to those specified in
2967 * minor_perm
2968 * else
2969 * preserve existing/user-modified ownership and
2970 * permissions
2972 * devfs indicates a new device by faking access time to be zero.
2974 if (sb.st_atime != 0) {
2975 int i;
2976 char *nt;
2978 if ((devalloc_flag == 0) && (devalloc_is_on != 1))
2980 * Leave existing devices as they are if we are not
2981 * turning device allocation on/off.
2983 return;
2985 nt = di_minor_nodetype(minor);
2987 if (nt == NULL)
2988 return;
2990 for (i = 0; devalloc_list[i]; i++) {
2991 if (strcmp(nt, devalloc_list[i]) == 0)
2993 * One of the types recognized by devalloc,
2994 * reset attrs.
2996 break;
2998 if (devalloc_list[i] == NULL)
2999 return;
3002 if (file_mods == FALSE) {
3003 /* Nothing more to do if simulating */
3004 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3005 return;
3008 if ((devalloc_flag == DA_ON) ||
3009 ((devalloc_is_on == 1) && (devalloc_flag != DA_OFF))) {
3011 * we are here either to turn device allocation on or
3012 * to add a new device while device allocation is on
3013 * (and we've confirmed that we're not turning it
3014 * off).
3016 mode = DEALLOC_MODE;
3017 uid = DA_UID;
3018 gid = DA_GID;
3021 if ((devalloc_is_on == 1) || (devalloc_flag == DA_ON) ||
3022 (sb.st_mode != mode)) {
3023 if (chmod(phy_path, mode) == -1)
3024 vprint(VERBOSE_MID, CHMOD_FAILED,
3025 phy_path, strerror(errno));
3027 if ((devalloc_is_on == 1) || (devalloc_flag == DA_ON) ||
3028 (sb.st_uid != uid || sb.st_gid != gid)) {
3029 if (chown(phy_path, uid, gid) == -1)
3030 vprint(VERBOSE_MID, CHOWN_FAILED,
3031 phy_path, strerror(errno));
3034 /* Report that we actually did something */
3035 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3039 * Removes logical link and the minor node it refers to. If file is a
3040 * link, we recurse and try to remove the minor node (or link if path is
3041 * a double link) that file's link contents refer to.
3043 static void
3044 devfsadm_rm_work(char *file, int recurse, int file_type)
3046 char *fcn = "devfsadm_rm_work: ";
3047 int linksize;
3048 char contents[PATH_MAX + 1];
3049 char nextfile[PATH_MAX + 1];
3050 char newfile[PATH_MAX + 1];
3051 char *ptr;
3053 vprint(REMOVE_MID, "%s%s\n", fcn, file);
3056 * Note: we don't remove /devices (non-links) entries because they are
3057 * covered by devfs.
3059 if (file_type != TYPE_LINK) {
3060 return;
3063 /* split into multiple if's due to excessive indentations */
3064 (void) strcpy(newfile, dev_dir);
3065 (void) strcat(newfile, "/");
3066 (void) strcat(newfile, file);
3069 * we dont care about the content of the symlink, so
3070 * redirection is not needed.
3072 if ((recurse == TRUE) &&
3073 ((linksize = readlink(newfile, contents, PATH_MAX)) > 0)) {
3074 contents[linksize] = '\0';
3077 * recurse if link points to another link
3079 if (is_minor_node(contents, &ptr) != DEVFSADM_TRUE) {
3080 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
3081 devfsadm_rm_work(&contents[strlen(DEV) + 1],
3082 TRUE, TYPE_LINK);
3083 } else {
3084 if ((ptr = strrchr(file, '/')) != NULL) {
3085 *ptr = '\0';
3086 (void) strcpy(nextfile, file);
3087 *ptr = '/';
3088 (void) strcat(nextfile, "/");
3089 } else {
3090 (void) strcpy(nextfile, "");
3092 (void) strcat(nextfile, contents);
3093 devfsadm_rm_work(nextfile, TRUE, TYPE_LINK);
3098 vprint(VERBOSE_MID, DEVFSADM_UNLINK, newfile);
3099 if (file_mods == TRUE) {
3100 rm_link_from_cache(file);
3101 s_unlink(newfile);
3102 rm_parent_dir_if_empty(newfile);
3103 invalidate_enumerate_cache();
3104 (void) di_devlink_rm_link(devlink_cache, file);
3108 void
3109 devfsadm_rm_link(char *file)
3111 devfsadm_rm_work(file, FALSE, TYPE_LINK);
3114 void
3115 devfsadm_rm_all(char *file)
3117 devfsadm_rm_work(file, TRUE, TYPE_LINK);
3120 static int
3121 s_rmdir(char *path)
3123 int i;
3124 char *rpath, *dir;
3125 const char *fcn = "s_rmdir";
3128 * Certain directories are created at install time by packages.
3129 * Some of them (listed in sticky_dirs[]) are required by apps
3130 * and need to be present even when empty.
3132 vprint(REMOVE_MID, "%s: checking if %s is sticky\n", fcn, path);
3134 rpath = path + strlen(dev_dir) + 1;
3136 for (i = 0; (dir = sticky_dirs[i]) != NULL; i++) {
3137 if (*rpath == *dir) {
3138 if (strcmp(rpath, dir) == 0) {
3139 vprint(REMOVE_MID, "%s: skipping sticky dir: "
3140 "%s\n", fcn, path);
3141 errno = EEXIST;
3142 return (-1);
3147 return (rmdir(path));
3151 * Try to remove any empty directories up the tree. It is assumed that
3152 * pathname is a file that was removed, so start with its parent, and
3153 * work up the tree.
3155 static void
3156 rm_parent_dir_if_empty(char *pathname)
3158 char *ptr, path[PATH_MAX + 1];
3159 char *fcn = "rm_parent_dir_if_empty: ";
3161 vprint(REMOVE_MID, "%schecking %s if empty\n", fcn, pathname);
3163 (void) strcpy(path, pathname);
3166 * ascend up the dir tree, deleting all empty dirs.
3167 * Return immediately if a dir is not empty.
3169 for (;;) {
3171 if ((ptr = strrchr(path, '/')) == NULL) {
3172 return;
3175 *ptr = '\0';
3177 if (finddev_emptydir(path)) {
3178 /* directory is empty */
3179 if (s_rmdir(path) == 0) {
3180 vprint(REMOVE_MID,
3181 "%sremoving empty dir %s\n", fcn, path);
3182 } else if (errno == EEXIST) {
3183 vprint(REMOVE_MID,
3184 "%sfailed to remove dir: %s\n", fcn, path);
3185 return;
3187 } else {
3188 /* some other file is here, so return */
3189 vprint(REMOVE_MID, "%sdir not empty: %s\n", fcn, path);
3190 return;
3196 * This function and all the functions it calls below were added to
3197 * handle the unique problem with world wide names (WWN). The problem is
3198 * that if a WWN device is moved to another address on the same controller
3199 * its logical link will change, while the physical node remains the same.
3200 * The result is that two logical links will point to the same physical path
3201 * in /devices, the valid link and a stale link. This function will
3202 * find all the stale nodes, though at a significant performance cost.
3204 * Caching is used to increase performance.
3205 * A cache will be built from disk if the cache tag doesn't already exist.
3206 * The cache tag is a regular expression "dir_re", which selects a
3207 * subset of disks to search from typically something like
3208 * "dev/cXt[0-9]+d[0-9]+s[0-9]+". After the cache is built, consistency must
3209 * be maintained, so entries are added as new links are created, and removed
3210 * as old links are deleted. The whole cache is flushed if we are a daemon,
3211 * and another devfsadm process ran in between.
3213 * Once the cache is built, this function finds the cache which matches
3214 * dir_re, and then it searches all links in that cache looking for
3215 * any link whose contents match "valid_link_contents" with a corresponding link
3216 * which does not match "valid_link". Any such matches are stale and removed.
3218 * This happens outside the context of a "reparenting" so we dont need
3219 * redirection.
3221 void
3222 devfsadm_rm_stale_links(char *dir_re, char *valid_link, di_node_t node,
3223 di_minor_t minor)
3225 link_t *link;
3226 linkhead_t *head;
3227 char phy_path[PATH_MAX + 1];
3228 char *valid_link_contents;
3229 char *dev_path;
3230 char rmlink[PATH_MAX + 1];
3233 * try to use devices path
3235 if ((node == lnode) && (minor == lminor)) {
3236 valid_link_contents = lphy_path;
3237 } else {
3238 if ((dev_path = di_devfs_path(node)) == NULL) {
3239 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
3240 devfsadm_exit(1);
3241 /*NOTREACHED*/
3243 (void) strcpy(phy_path, dev_path);
3244 di_devfs_path_free(dev_path);
3246 (void) strcat(phy_path, ":");
3247 (void) strcat(phy_path, di_minor_name(minor));
3248 valid_link_contents = phy_path;
3252 * As an optimization, check to make sure the corresponding
3253 * devlink was just created before continuing.
3256 if (linknew == FALSE) {
3257 return;
3260 head = get_cached_links(dir_re);
3262 assert(head->nextlink == NULL);
3264 for (link = head->link; link != NULL; link = head->nextlink) {
3266 * See hot_cleanup() for why we do this
3268 head->nextlink = link->next;
3269 if ((strcmp(link->contents, valid_link_contents) == 0) &&
3270 (strcmp(link->devlink, valid_link) != 0)) {
3271 vprint(CHATTY_MID, "removing %s -> %s\n"
3272 "valid link is: %s -> %s\n",
3273 link->devlink, link->contents,
3274 valid_link, valid_link_contents);
3276 * Use a copy of the cached link name as the
3277 * cache entry will go away during link removal
3279 (void) snprintf(rmlink, sizeof (rmlink), "%s",
3280 link->devlink);
3281 devfsadm_rm_link(rmlink);
3287 * Return previously created cache, or create cache.
3289 static linkhead_t *
3290 get_cached_links(char *dir_re)
3292 recurse_dev_t rd;
3293 linkhead_t *linkhead;
3294 int n;
3296 vprint(BUILDCACHE_MID, "get_cached_links: %s\n", dir_re);
3298 for (linkhead = headlinkhead; linkhead != NULL;
3299 linkhead = linkhead->nexthead) {
3300 if (strcmp(linkhead->dir_re, dir_re) == 0) {
3301 return (linkhead);
3306 * This tag is not in cache, so add it, along with all its
3307 * matching /dev entries. This is the only time we go to disk.
3309 linkhead = s_malloc(sizeof (linkhead_t));
3310 linkhead->nexthead = headlinkhead;
3311 headlinkhead = linkhead;
3312 linkhead->dir_re = s_strdup(dir_re);
3314 if ((n = regcomp(&(linkhead->dir_re_compiled), dir_re,
3315 REG_EXTENDED)) != 0) {
3316 err_print(REGCOMP_FAILED, dir_re, n);
3319 linkhead->nextlink = NULL;
3320 linkhead->link = NULL;
3322 rd.fcn = build_devlink_list;
3323 rd.data = (void *)linkhead;
3325 vprint(BUILDCACHE_MID, "get_cached_links: calling recurse_dev_re\n");
3327 /* call build_devlink_list for each directory in the dir_re RE */
3328 if (dir_re[0] == '/') {
3329 recurse_dev_re("/", &dir_re[1], &rd);
3330 } else {
3331 recurse_dev_re(dev_dir, dir_re, &rd);
3334 return (linkhead);
3337 static void
3338 build_devlink_list(char *devlink, void *data)
3340 char *fcn = "build_devlink_list: ";
3341 char *ptr;
3342 char *r_contents;
3343 char *r_devlink;
3344 char contents[PATH_MAX + 1];
3345 char newlink[PATH_MAX + 1];
3346 char stage_link[PATH_MAX + 1];
3347 int linksize;
3348 linkhead_t *linkhead = (linkhead_t *)data;
3349 link_t *link;
3350 int i = 0;
3352 vprint(BUILDCACHE_MID, "%scheck_link: %s\n", fcn, devlink);
3354 (void) strcpy(newlink, devlink);
3356 do {
3358 * None of the consumers of this function need redirection
3359 * so this readlink gets the "current" contents
3361 linksize = readlink(newlink, contents, PATH_MAX);
3362 if (linksize <= 0) {
3364 * The first pass through the do loop we may readlink()
3365 * non-symlink files(EINVAL) from false regexec matches.
3366 * Suppress error messages in those cases or if the link
3367 * content is the empty string.
3369 if (linksize < 0 && (i || errno != EINVAL))
3370 err_print(READLINK_FAILED, "build_devlink_list",
3371 newlink, strerror(errno));
3372 return;
3374 contents[linksize] = '\0';
3375 i = 1;
3377 if (is_minor_node(contents, &r_contents) == DEVFSADM_FALSE) {
3379 * assume that link contents is really a pointer to
3380 * another link, so recurse and read its link contents.
3382 * some link contents are absolute:
3383 * /dev/audio -> /dev/sound/0
3385 if (strncmp(contents, DEV "/",
3386 strlen(DEV) + strlen("/")) != 0) {
3388 if ((ptr = strrchr(newlink, '/')) == NULL) {
3389 vprint(REMOVE_MID, "%s%s -> %s invalid "
3390 "link. missing '/'\n", fcn,
3391 newlink, contents);
3392 return;
3394 *ptr = '\0';
3395 (void) strcpy(stage_link, newlink);
3396 *ptr = '/';
3397 (void) strcat(stage_link, "/");
3398 (void) strcat(stage_link, contents);
3399 (void) strcpy(newlink, stage_link);
3400 } else {
3401 (void) strcpy(newlink, dev_dir);
3402 (void) strcat(newlink, "/");
3403 (void) strcat(newlink,
3404 &contents[strlen(DEV) + strlen("/")]);
3407 } else {
3408 newlink[0] = '\0';
3410 } while (newlink[0] != '\0');
3412 if (strncmp(devlink, dev_dir, strlen(dev_dir)) != 0) {
3413 vprint(BUILDCACHE_MID, "%sinvalid link: %s\n", fcn, devlink);
3414 return;
3417 r_devlink = devlink + strlen(dev_dir);
3419 if (r_devlink[0] != '/')
3420 return;
3422 link = s_malloc(sizeof (link_t));
3424 /* don't store the '/' after rootdir/dev */
3425 r_devlink += 1;
3427 vprint(BUILDCACHE_MID, "%scaching link: %s\n", fcn, r_devlink);
3428 link->devlink = s_strdup(r_devlink);
3430 link->contents = s_strdup(r_contents);
3432 link->next = linkhead->link;
3433 linkhead->link = link;
3437 * to be consistent, devlink must not begin with / and must be
3438 * relative to /dev/, whereas physpath must contain / and be
3439 * relative to /devices.
3441 static void
3442 add_link_to_cache(char *devlink, char *physpath)
3444 linkhead_t *linkhead;
3445 link_t *link;
3446 int added = 0;
3448 if (file_mods == FALSE) {
3449 return;
3452 vprint(CACHE_MID, "add_link_to_cache: %s -> %s ",
3453 devlink, physpath);
3455 for (linkhead = headlinkhead; linkhead != NULL;
3456 linkhead = linkhead->nexthead) {
3457 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3458 == 0) {
3459 added++;
3460 link = s_malloc(sizeof (link_t));
3461 link->devlink = s_strdup(devlink);
3462 link->contents = s_strdup(physpath);
3463 link->next = linkhead->link;
3464 linkhead->link = link;
3468 vprint(CACHE_MID,
3469 " %d %s\n", added, added == 0 ? "NOT ADDED" : "ADDED");
3473 * Remove devlink from cache. Devlink must be relative to /dev/ and not start
3474 * with /.
3476 static void
3477 rm_link_from_cache(char *devlink)
3479 linkhead_t *linkhead;
3480 link_t **linkp;
3481 link_t *save;
3483 vprint(CACHE_MID, "rm_link_from_cache enter: %s\n", devlink);
3485 for (linkhead = headlinkhead; linkhead != NULL;
3486 linkhead = linkhead->nexthead) {
3487 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3488 == 0) {
3490 for (linkp = &(linkhead->link); *linkp != NULL; ) {
3491 if ((strcmp((*linkp)->devlink, devlink) == 0)) {
3492 save = *linkp;
3493 *linkp = (*linkp)->next;
3495 * We are removing our caller's
3496 * "next" link. Update the nextlink
3497 * field in the head so that our
3498 * callers accesses the next valid
3499 * link
3501 if (linkhead->nextlink == save)
3502 linkhead->nextlink = *linkp;
3503 free(save->devlink);
3504 free(save->contents);
3505 free(save);
3506 vprint(CACHE_MID, " %s FREED FROM "
3507 "CACHE\n", devlink);
3508 } else {
3509 linkp = &((*linkp)->next);
3516 static void
3517 rm_all_links_from_cache()
3519 linkhead_t *linkhead;
3520 linkhead_t *nextlinkhead;
3521 link_t *link;
3522 link_t *nextlink;
3524 vprint(CACHE_MID, "rm_all_links_from_cache\n");
3526 for (linkhead = headlinkhead; linkhead != NULL;
3527 linkhead = nextlinkhead) {
3529 nextlinkhead = linkhead->nexthead;
3530 assert(linkhead->nextlink == NULL);
3531 for (link = linkhead->link; link != NULL; link = nextlink) {
3532 nextlink = link->next;
3533 free(link->devlink);
3534 free(link->contents);
3535 free(link);
3537 regfree(&(linkhead->dir_re_compiled));
3538 free(linkhead->dir_re);
3539 free(linkhead);
3541 headlinkhead = NULL;
3545 * Called when the kernel has modified the incore path_to_inst data. This
3546 * function will schedule a flush of the data to the filesystem.
3548 static void
3549 devfs_instance_mod(void)
3551 char *fcn = "devfs_instance_mod: ";
3552 vprint(PATH2INST_MID, "%senter\n", fcn);
3554 /* signal instance thread */
3555 (void) mutex_lock(&count_lock);
3556 inst_count++;
3557 (void) cond_signal(&cv);
3558 (void) mutex_unlock(&count_lock);
3561 static void
3562 instance_flush_thread(void)
3564 int i;
3565 int idle;
3567 for (;;) {
3569 (void) mutex_lock(&count_lock);
3570 while (inst_count == 0) {
3571 (void) cond_wait(&cv, &count_lock);
3573 inst_count = 0;
3575 vprint(PATH2INST_MID, "signaled to flush path_to_inst."
3576 " Enter delay loop\n");
3578 * Wait MAX_IDLE_DELAY seconds after getting the last flush
3579 * path_to_inst event before invoking a flush, but never wait
3580 * more than MAX_DELAY seconds after getting the first event.
3582 for (idle = 0, i = 0; i < MAX_DELAY; i++) {
3584 (void) mutex_unlock(&count_lock);
3585 (void) sleep(1);
3586 (void) mutex_lock(&count_lock);
3588 /* shorten the delay if we are idle */
3589 if (inst_count == 0) {
3590 idle++;
3591 if (idle > MAX_IDLE_DELAY) {
3592 break;
3594 } else {
3595 inst_count = idle = 0;
3599 (void) mutex_unlock(&count_lock);
3601 flush_path_to_inst();
3606 * Helper function for flush_path_to_inst() below; this routine calls the
3607 * inst_sync syscall to flush the path_to_inst database to the given file.
3609 static int
3610 do_inst_sync(char *filename, char *instfilename)
3612 void (*sigsaved)(int);
3613 int err = 0, flags = INST_SYNC_IF_REQUIRED;
3614 struct stat sb;
3616 if (stat(instfilename, &sb) == -1 && errno == ENOENT)
3617 flags = INST_SYNC_ALWAYS;
3619 vprint(INSTSYNC_MID, "do_inst_sync: about to flush %s\n", filename);
3620 sigsaved = sigset(SIGSYS, SIG_IGN);
3621 if (inst_sync(filename, flags) == -1)
3622 err = errno;
3623 (void) sigset(SIGSYS, sigsaved);
3625 switch (err) {
3626 case 0:
3627 return (DEVFSADM_SUCCESS);
3628 case EALREADY: /* no-op, path_to_inst already up to date */
3629 return (EALREADY);
3630 case ENOSYS:
3631 err_print(CANT_LOAD_SYSCALL);
3632 break;
3633 case EPERM:
3634 err_print(SUPER_TO_SYNC);
3635 break;
3636 default:
3637 err_print(INSTSYNC_FAILED, filename, strerror(err));
3638 break;
3640 return (DEVFSADM_FAILURE);
3644 * Flush the kernel's path_to_inst database to /etc/path_to_inst. To do so
3645 * safely, the database is flushed to a temporary file, then moved into place.
3647 * The following files are used during this process:
3648 * /etc/path_to_inst: The path_to_inst file
3649 * /etc/path_to_inst.<pid>: Contains data flushed from the kernel
3650 * /etc/path_to_inst.old: The backup file
3651 * /etc/path_to_inst.old.<pid>: Temp file for creating backup
3654 static void
3655 flush_path_to_inst(void)
3657 char *new_inst_file = NULL;
3658 char *old_inst_file = NULL;
3659 char *old_inst_file_npid = NULL;
3660 FILE *inst_file_fp = NULL;
3661 FILE *old_inst_file_fp = NULL;
3662 struct stat sb;
3663 int err = 0;
3664 int c;
3665 int inst_strlen;
3667 vprint(PATH2INST_MID, "flush_path_to_inst: %s\n",
3668 (flush_path_to_inst_enable == TRUE) ? "ENABLED" : "DISABLED");
3670 if (flush_path_to_inst_enable == FALSE) {
3671 return;
3674 inst_strlen = strlen(inst_file);
3675 new_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 2);
3676 old_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 6);
3677 old_inst_file_npid = s_malloc(inst_strlen +
3678 sizeof (INSTANCE_FILE_SUFFIX));
3680 (void) snprintf(new_inst_file, inst_strlen + PID_STR_LEN + 2,
3681 "%s.%ld", inst_file, getpid());
3683 if (stat(new_inst_file, &sb) == 0) {
3684 s_unlink(new_inst_file);
3687 err = do_inst_sync(new_inst_file, inst_file);
3688 if (err != DEVFSADM_SUCCESS) {
3689 goto out;
3690 /*NOTREACHED*/
3694 * Now we deal with the somewhat tricky updating and renaming
3695 * of this critical piece of kernel state.
3699 * Copy the current instance file into a temporary file.
3700 * Then rename the temporary file into the backup (.old)
3701 * file and rename the newly flushed kernel data into
3702 * the instance file.
3703 * Of course if 'inst_file' doesn't exist, there's much
3704 * less for us to do .. tee hee.
3706 if ((inst_file_fp = fopen(inst_file, "r")) == NULL) {
3708 * No such file. Rename the new onto the old
3710 if ((err = rename(new_inst_file, inst_file)) != 0)
3711 err_print(RENAME_FAILED, inst_file, strerror(errno));
3712 goto out;
3713 /*NOTREACHED*/
3716 (void) snprintf(old_inst_file, inst_strlen + PID_STR_LEN + 6,
3717 "%s.old.%ld", inst_file, getpid());
3719 if (stat(old_inst_file, &sb) == 0) {
3720 s_unlink(old_inst_file);
3723 if ((old_inst_file_fp = fopen(old_inst_file, "w")) == NULL) {
3725 * Can't open the 'old_inst_file' file for writing.
3726 * This is somewhat strange given that the syscall
3727 * just succeeded to write a file out.. hmm.. maybe
3728 * the fs just filled up or something nasty.
3730 * Anyway, abort what we've done so far.
3732 err_print(CANT_UPDATE, old_inst_file);
3733 err = DEVFSADM_FAILURE;
3734 goto out;
3735 /*NOTREACHED*/
3739 * Copy current instance file into the temporary file
3741 err = 0;
3742 while ((c = getc(inst_file_fp)) != EOF) {
3743 if ((err = putc(c, old_inst_file_fp)) == EOF) {
3744 break;
3748 if (fclose(old_inst_file_fp) == EOF || err == EOF) {
3749 vprint(INFO_MID, CANT_UPDATE, old_inst_file);
3750 err = DEVFSADM_FAILURE;
3751 goto out;
3752 /* NOTREACHED */
3756 * Set permissions to be the same on the backup as
3757 * /etc/path_to_inst.
3759 (void) chmod(old_inst_file, 0444);
3762 * So far, everything we've done is more or less reversible.
3763 * But now we're going to commit ourselves.
3766 (void) snprintf(old_inst_file_npid,
3767 inst_strlen + sizeof (INSTANCE_FILE_SUFFIX),
3768 "%s%s", inst_file, INSTANCE_FILE_SUFFIX);
3770 if ((err = rename(old_inst_file, old_inst_file_npid)) != 0) {
3771 err_print(RENAME_FAILED, old_inst_file_npid,
3772 strerror(errno));
3773 } else if ((err = rename(new_inst_file, inst_file)) != 0) {
3774 err_print(RENAME_FAILED, inst_file, strerror(errno));
3777 out:
3778 if (inst_file_fp != NULL) {
3779 if (fclose(inst_file_fp) == EOF) {
3780 err_print(FCLOSE_FAILED, inst_file, strerror(errno));
3784 if (stat(new_inst_file, &sb) == 0) {
3785 s_unlink(new_inst_file);
3787 free(new_inst_file);
3789 if (stat(old_inst_file, &sb) == 0) {
3790 s_unlink(old_inst_file);
3792 free(old_inst_file);
3794 free(old_inst_file_npid);
3796 if (err != 0 && err != EALREADY) {
3797 err_print(FAILED_TO_UPDATE, inst_file);
3802 * detach from tty. For daemon mode.
3804 void
3805 detachfromtty()
3807 (void) setsid();
3808 if (DEVFSADM_DEBUG_ON == TRUE) {
3809 return;
3812 (void) close(0);
3813 (void) close(1);
3814 (void) close(2);
3815 (void) open("/dev/null", O_RDWR, 0);
3816 (void) dup(0);
3817 (void) dup(0);
3818 openlog(DEVFSADMD, LOG_PID, LOG_DAEMON);
3819 (void) setlogmask(LOG_UPTO(LOG_INFO));
3820 logflag = TRUE;
3824 * Use an advisory lock to synchronize updates to /dev. If the lock is
3825 * held by another process, block in the fcntl() system call until that
3826 * process drops the lock or exits. The lock file itself is
3827 * DEV_LOCK_FILE. The process id of the current and last process owning
3828 * the lock is kept in the lock file. After acquiring the lock, read the
3829 * process id and return it. It is the process ID which last owned the
3830 * lock, and will be used to determine if caches need to be flushed.
3832 * NOTE: if the devlink database is held open by the caller, it may
3833 * be closed by this routine. This is to enforce the following lock ordering:
3834 * 1) /dev lock 2) database open
3836 pid_t
3837 enter_dev_lock()
3839 struct flock lock;
3840 int n;
3841 pid_t pid;
3842 pid_t last_owner_pid;
3844 if (file_mods == FALSE) {
3845 return (0);
3848 (void) snprintf(dev_lockfile, sizeof (dev_lockfile),
3849 "%s/%s", etc_dev_dir, DEV_LOCK_FILE);
3851 vprint(LOCK_MID, "enter_dev_lock: lock file %s\n", dev_lockfile);
3853 dev_lock_fd = open(dev_lockfile, O_CREAT|O_RDWR, 0644);
3854 if (dev_lock_fd < 0) {
3855 err_print(OPEN_FAILED, dev_lockfile, strerror(errno));
3856 devfsadm_exit(1);
3857 /*NOTREACHED*/
3860 lock.l_type = F_WRLCK;
3861 lock.l_whence = SEEK_SET;
3862 lock.l_start = 0;
3863 lock.l_len = 0;
3865 /* try for the lock, but don't wait */
3866 if (fcntl(dev_lock_fd, F_SETLK, &lock) == -1) {
3867 if ((errno == EACCES) || (errno == EAGAIN)) {
3868 pid = 0;
3869 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3870 vprint(LOCK_MID, "waiting for PID %d to complete\n",
3871 (int)pid);
3872 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3873 err_print(LSEEK_FAILED, dev_lockfile,
3874 strerror(errno));
3875 devfsadm_exit(1);
3876 /*NOTREACHED*/
3879 * wait for the dev lock. If we have the database open,
3880 * close it first - the order of lock acquisition should
3881 * always be: 1) dev_lock 2) database
3882 * This is to prevent deadlocks with any locks the
3883 * database code may hold.
3885 (void) di_devlink_close(&devlink_cache, 0);
3887 /* send any sysevents that were queued up. */
3888 process_syseventq();
3890 if (fcntl(dev_lock_fd, F_SETLKW, &lock) == -1) {
3891 err_print(LOCK_FAILED, dev_lockfile,
3892 strerror(errno));
3893 devfsadm_exit(1);
3894 /*NOTREACHED*/
3899 hold_dev_lock = TRUE;
3900 pid = 0;
3901 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3902 if (n == sizeof (pid_t) && pid == getpid()) {
3903 return (pid);
3906 last_owner_pid = pid;
3908 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3909 err_print(LSEEK_FAILED, dev_lockfile, strerror(errno));
3910 devfsadm_exit(1);
3911 /*NOTREACHED*/
3913 pid = getpid();
3914 n = write(dev_lock_fd, &pid, sizeof (pid_t));
3915 if (n != sizeof (pid_t)) {
3916 err_print(WRITE_FAILED, dev_lockfile, strerror(errno));
3917 devfsadm_exit(1);
3918 /*NOTREACHED*/
3921 return (last_owner_pid);
3925 * Drop the advisory /dev lock, close lock file. Close and re-open the
3926 * file every time so to ensure a resync if for some reason the lock file
3927 * gets removed.
3929 void
3930 exit_dev_lock(int exiting)
3932 struct flock unlock;
3934 if (hold_dev_lock == FALSE) {
3935 return;
3938 vprint(LOCK_MID, "exit_dev_lock: lock file %s, exiting = %d\n",
3939 dev_lockfile, exiting);
3941 unlock.l_type = F_UNLCK;
3942 unlock.l_whence = SEEK_SET;
3943 unlock.l_start = 0;
3944 unlock.l_len = 0;
3946 if (fcntl(dev_lock_fd, F_SETLK, &unlock) == -1) {
3947 err_print(UNLOCK_FAILED, dev_lockfile, strerror(errno));
3950 hold_dev_lock = FALSE;
3952 if (close(dev_lock_fd) == -1) {
3953 err_print(CLOSE_FAILED, dev_lockfile, strerror(errno));
3954 if (!exiting)
3955 devfsadm_exit(1);
3956 /*NOTREACHED*/
3962 * Use an advisory lock to ensure that only one daemon process is active
3963 * in the system at any point in time. If the lock is held by another
3964 * process, do not block but return the pid owner of the lock to the
3965 * caller immediately. The lock is cleared if the holding daemon process
3966 * exits for any reason even if the lock file remains, so the daemon can
3967 * be restarted if necessary. The lock file is DAEMON_LOCK_FILE.
3969 pid_t
3970 enter_daemon_lock(void)
3972 struct flock lock;
3974 (void) snprintf(daemon_lockfile, sizeof (daemon_lockfile),
3975 "%s/%s", etc_dev_dir, DAEMON_LOCK_FILE);
3977 vprint(LOCK_MID, "enter_daemon_lock: lock file %s\n", daemon_lockfile);
3979 daemon_lock_fd = open(daemon_lockfile, O_CREAT|O_RDWR, 0644);
3980 if (daemon_lock_fd < 0) {
3981 err_print(OPEN_FAILED, daemon_lockfile, strerror(errno));
3982 devfsadm_exit(1);
3983 /*NOTREACHED*/
3986 lock.l_type = F_WRLCK;
3987 lock.l_whence = SEEK_SET;
3988 lock.l_start = 0;
3989 lock.l_len = 0;
3991 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
3993 if (errno == EAGAIN || errno == EDEADLK) {
3994 if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) {
3995 err_print(LOCK_FAILED, daemon_lockfile,
3996 strerror(errno));
3997 devfsadm_exit(1);
3998 /*NOTREACHED*/
4000 return (lock.l_pid);
4003 hold_daemon_lock = TRUE;
4004 return (getpid());
4008 * Drop the advisory daemon lock, close lock file
4010 void
4011 exit_daemon_lock(int exiting)
4013 struct flock lock;
4015 if (hold_daemon_lock == FALSE) {
4016 return;
4019 vprint(LOCK_MID, "exit_daemon_lock: lock file %s, exiting = %d\n",
4020 daemon_lockfile, exiting);
4022 lock.l_type = F_UNLCK;
4023 lock.l_whence = SEEK_SET;
4024 lock.l_start = 0;
4025 lock.l_len = 0;
4027 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
4028 err_print(UNLOCK_FAILED, daemon_lockfile, strerror(errno));
4031 if (close(daemon_lock_fd) == -1) {
4032 err_print(CLOSE_FAILED, daemon_lockfile, strerror(errno));
4033 if (!exiting)
4034 devfsadm_exit(1);
4035 /*NOTREACHED*/
4040 * Called to removed danging nodes in two different modes: RM_PRE, RM_POST.
4041 * RM_PRE mode is called before processing the entire devinfo tree, and RM_POST
4042 * is called after processing the entire devinfo tree.
4044 static void
4045 pre_and_post_cleanup(int flags)
4047 remove_list_t *rm;
4048 recurse_dev_t rd;
4049 cleanup_data_t cleanup_data;
4050 char *fcn = "pre_and_post_cleanup: ";
4052 if (build_dev == FALSE)
4053 return;
4055 vprint(CHATTY_MID, "attempting %s-cleanup\n",
4056 flags == RM_PRE ? "pre" : "post");
4057 vprint(REMOVE_MID, "%sflags = %d\n", fcn, flags);
4060 * the generic function recurse_dev_re is shared among different
4061 * functions, so set the method and data that it should use for
4062 * matches.
4064 rd.fcn = matching_dev;
4065 rd.data = (void *)&cleanup_data;
4066 cleanup_data.flags = flags;
4068 (void) mutex_lock(&nfp_mutex);
4069 nfphash_create();
4071 for (rm = remove_head; rm != NULL; rm = rm->next) {
4072 if ((flags & rm->remove->flags) == flags) {
4073 cleanup_data.rm = rm;
4075 * If reached this point, RM_PRE or RM_POST cleanup is
4076 * desired. clean_ok() decides whether to clean
4077 * under the given circumstances.
4079 vprint(REMOVE_MID, "%scleanup: PRE or POST\n", fcn);
4080 if (clean_ok(rm->remove) == DEVFSADM_SUCCESS) {
4081 vprint(REMOVE_MID, "cleanup: cleanup OK\n");
4082 recurse_dev_re(dev_dir,
4083 rm->remove->dev_dirs_re, &rd);
4087 nfphash_destroy();
4088 (void) mutex_unlock(&nfp_mutex);
4092 * clean_ok() determines whether cleanup should be done according
4093 * to the following matrix:
4095 * command line arguments RM_PRE RM_POST RM_PRE && RM_POST &&
4096 * RM_ALWAYS RM_ALWAYS
4097 * ---------------------- ------ ----- --------- ----------
4099 * <neither -c nor -C> - - pre-clean post-clean
4101 * -C pre-clean post-clean pre-clean post-clean
4103 * -C -c class pre-clean post-clean pre-clean post-clean
4104 * if class if class if class if class
4105 * matches matches matches matches
4107 * -c class - - pre-clean post-clean
4108 * if class if class
4109 * matches matches
4112 static int
4113 clean_ok(devfsadm_remove_V1_t *remove)
4115 int i;
4117 if (single_drv == TRUE) {
4118 /* no cleanup at all when using -i option */
4119 return (DEVFSADM_FAILURE);
4123 * no cleanup if drivers are not loaded. We make an exception
4124 * for the "disks" program however, since disks has a public
4125 * cleanup flag (-C) and disk drivers are usually never
4126 * unloaded.
4128 if (load_attach_drv == FALSE && strcmp(prog, DISKS) != 0) {
4129 return (DEVFSADM_FAILURE);
4132 /* if the cleanup flag was not specified, return false */
4133 if ((cleanup == FALSE) && ((remove->flags & RM_ALWAYS) == 0)) {
4134 return (DEVFSADM_FAILURE);
4137 if (num_classes == 0) {
4138 return (DEVFSADM_SUCCESS);
4142 * if reached this point, check to see if the class in the given
4143 * remove structure matches a class given on the command line
4146 for (i = 0; i < num_classes; i++) {
4147 if (strcmp(remove->device_class, classes[i]) == 0) {
4148 return (DEVFSADM_SUCCESS);
4152 return (DEVFSADM_FAILURE);
4156 * Called to remove dangling nodes after receiving a hotplug event
4157 * containing the physical node pathname to be removed.
4159 void
4160 hot_cleanup(char *node_path, char *minor_name, char *ev_subclass,
4161 char *driver_name, int instance)
4163 link_t *link;
4164 linkhead_t *head;
4165 remove_list_t *rm;
4166 char *fcn = "hot_cleanup: ";
4167 char path[PATH_MAX + 1];
4168 int path_len;
4169 char rmlink[PATH_MAX + 1];
4170 nvlist_t *nvl = NULL;
4171 int skip;
4172 int ret;
4175 * dev links can go away as part of hot cleanup.
4176 * So first build event attributes in order capture dev links.
4178 if (ev_subclass != NULL)
4179 nvl = build_event_attributes(EC_DEV_REMOVE, ev_subclass,
4180 node_path, DI_NODE_NIL, driver_name, instance, minor_name);
4182 (void) strcpy(path, node_path);
4183 (void) strcat(path, ":");
4184 (void) strcat(path, minor_name == NULL ? "" : minor_name);
4186 path_len = strlen(path);
4188 vprint(REMOVE_MID, "%spath=%s\n", fcn, path);
4190 (void) mutex_lock(&nfp_mutex);
4191 nfphash_create();
4193 for (rm = remove_head; rm != NULL; rm = rm->next) {
4194 if ((RM_HOT & rm->remove->flags) == RM_HOT) {
4195 head = get_cached_links(rm->remove->dev_dirs_re);
4196 assert(head->nextlink == NULL);
4197 for (link = head->link;
4198 link != NULL; link = head->nextlink) {
4200 * The remove callback below may remove
4201 * the current and/or any or all of the
4202 * subsequent links in the list.
4203 * Save the next link in the head. If
4204 * the callback removes the next link
4205 * the saved pointer in the head will be
4206 * updated by the callback to point at
4207 * the next valid link.
4209 head->nextlink = link->next;
4212 * if devlink is in no-further-process hash,
4213 * skip its remove
4215 if (nfphash_lookup(link->devlink) != NULL)
4216 continue;
4218 if (minor_name)
4219 skip = strcmp(link->contents, path);
4220 else
4221 skip = strncmp(link->contents, path,
4222 path_len);
4223 if (skip ||
4224 (call_minor_init(rm->modptr) ==
4225 DEVFSADM_FAILURE))
4226 continue;
4228 vprint(REMOVE_MID,
4229 "%sremoving %s -> %s\n", fcn,
4230 link->devlink, link->contents);
4232 * Use a copy of the cached link name
4233 * as the cache entry will go away
4234 * during link removal
4236 (void) snprintf(rmlink, sizeof (rmlink),
4237 "%s", link->devlink);
4238 if (rm->remove->flags & RM_NOINTERPOSE) {
4239 ((void (*)(char *))
4240 (rm->remove->callback_fcn))(rmlink);
4241 } else {
4242 ret = ((int (*)(char *))
4243 (rm->remove->callback_fcn))(rmlink);
4244 if (ret == DEVFSADM_TERMINATE)
4245 nfphash_insert(rmlink);
4251 nfphash_destroy();
4252 (void) mutex_unlock(&nfp_mutex);
4254 /* now log an event */
4255 if (nvl) {
4256 log_event(EC_DEV_REMOVE, ev_subclass, nvl);
4257 free(nvl);
4262 * Open the dir current_dir. For every file which matches the first dir
4263 * component of path_re, recurse. If there are no more *dir* path
4264 * components left in path_re (ie no more /), then call function rd->fcn.
4266 static void
4267 recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
4269 regex_t re1;
4270 char *slash;
4271 char new_path[PATH_MAX + 1];
4272 char *anchored_path_re;
4273 size_t len;
4274 finddevhdl_t fhandle;
4275 const char *fp;
4277 vprint(RECURSEDEV_MID, "recurse_dev_re: curr = %s path=%s\n",
4278 current_dir, path_re);
4280 if (finddev_readdir(current_dir, &fhandle) != 0)
4281 return;
4283 len = strlen(path_re);
4284 if ((slash = strchr(path_re, '/')) != NULL) {
4285 len = (slash - path_re);
4288 anchored_path_re = s_malloc(len + 3);
4289 (void) sprintf(anchored_path_re, "^%.*s$", len, path_re);
4291 if (regcomp(&re1, anchored_path_re, REG_EXTENDED) != 0) {
4292 free(anchored_path_re);
4293 goto out;
4296 free(anchored_path_re);
4298 while ((fp = finddev_next(fhandle)) != NULL) {
4300 if (regexec(&re1, fp, 0, NULL, 0) == 0) {
4301 /* match */
4302 (void) strcpy(new_path, current_dir);
4303 (void) strcat(new_path, "/");
4304 (void) strcat(new_path, fp);
4306 vprint(RECURSEDEV_MID, "recurse_dev_re: match, new "
4307 "path = %s\n", new_path);
4309 if (slash != NULL) {
4310 recurse_dev_re(new_path, slash + 1, rd);
4311 } else {
4312 /* reached the leaf component of path_re */
4313 vprint(RECURSEDEV_MID,
4314 "recurse_dev_re: calling fcn\n");
4315 (*(rd->fcn))(new_path, rd->data);
4320 regfree(&re1);
4322 out:
4323 finddev_close(fhandle);
4327 * Found a devpath which matches a RE in the remove structure.
4328 * Now check to see if it is dangling.
4330 static void
4331 matching_dev(char *devpath, void *data)
4333 cleanup_data_t *cleanup_data = data;
4334 int norm_len = strlen(dev_dir) + strlen("/");
4335 int ret;
4336 char *fcn = "matching_dev: ";
4338 vprint(RECURSEDEV_MID, "%sexamining devpath = '%s'\n", fcn,
4339 devpath);
4342 * If the link is in the no-further-process hash
4343 * don't do any remove operation on it.
4345 if (nfphash_lookup(devpath + norm_len) != NULL)
4346 return;
4349 * Dangling check will work whether "alias" or "current"
4350 * so no need to redirect.
4352 if (resolve_link(devpath, NULL, NULL, NULL, 1) == TRUE) {
4353 if (call_minor_init(cleanup_data->rm->modptr) ==
4354 DEVFSADM_FAILURE) {
4355 return;
4358 devpath += norm_len;
4360 vprint(RECURSEDEV_MID, "%scalling callback %s\n", fcn, devpath);
4361 if (cleanup_data->rm->remove->flags & RM_NOINTERPOSE)
4362 ((void (*)(char *))
4363 (cleanup_data->rm->remove->callback_fcn))(devpath);
4364 else {
4365 ret = ((int (*)(char *))
4366 (cleanup_data->rm->remove->callback_fcn))(devpath);
4367 if (ret == DEVFSADM_TERMINATE) {
4369 * We want no further remove processing for
4370 * this link. Add it to the nfp_hash;
4372 nfphash_insert(devpath);
4379 devfsadm_read_link(di_node_t anynode, char *link, char **devfs_path)
4381 char devlink[PATH_MAX];
4382 char *path;
4384 *devfs_path = NULL;
4386 /* prepend link with dev_dir contents */
4387 (void) strcpy(devlink, dev_dir);
4388 (void) strcat(devlink, "/");
4389 (void) strcat(devlink, link);
4391 /* We *don't* want a stat of the /devices node */
4392 path = NULL;
4393 (void) resolve_link(devlink, NULL, NULL, &path, 0);
4394 if (path != NULL) {
4395 /* redirect if alias to current */
4396 *devfs_path = di_alias2curr(anynode, path);
4397 free(path);
4399 return (*devfs_path ? DEVFSADM_SUCCESS : DEVFSADM_FAILURE);
4403 devfsadm_link_valid(di_node_t anynode, char *link)
4405 struct stat sb;
4406 char devlink[PATH_MAX + 1], *contents, *raw_contents;
4407 int rv, type;
4409 /* prepend link with dev_dir contents */
4410 (void) strcpy(devlink, dev_dir);
4411 (void) strcat(devlink, "/");
4412 (void) strcat(devlink, link);
4414 if (!device_exists(devlink) || lstat(devlink, &sb) != 0) {
4415 return (DEVFSADM_FALSE);
4418 raw_contents = NULL;
4419 type = 0;
4420 if (resolve_link(devlink, &raw_contents, &type, NULL, 1) == TRUE) {
4421 rv = DEVFSADM_FALSE;
4422 } else {
4423 rv = DEVFSADM_TRUE;
4427 * resolve alias paths for primary links
4429 contents = raw_contents;
4430 if (type == DI_PRIMARY_LINK) {
4431 contents = di_alias2curr(anynode, raw_contents);
4432 free(raw_contents);
4436 * The link exists. Add it to the database
4438 (void) di_devlink_add_link(devlink_cache, link, contents, type);
4439 free(contents);
4441 return (rv);
4445 * devpath: Absolute path to /dev link
4446 * content_p: Returns malloced string (link content)
4447 * type_p: Returns link type: primary or secondary
4448 * devfs_path: Returns malloced string: /devices path w/out "/devices"
4449 * dangle: if set, check if link is dangling
4450 * Returns:
4451 * TRUE if dangling
4452 * FALSE if not or if caller doesn't care
4453 * Caller is assumed to have initialized pointer contents to NULL
4456 static int
4457 resolve_link(char *devpath, char **content_p, int *type_p, char **devfs_path,
4458 int dangle)
4460 char contents[PATH_MAX + 1];
4461 char stage_link[PATH_MAX + 1];
4462 char *fcn = "resolve_link: ";
4463 char *ptr;
4464 int linksize;
4465 int rv = TRUE;
4466 struct stat sb;
4469 * This routine will return the "raw" contents. It is upto the
4470 * the caller to redirect "alias" to "current" (or vice versa)
4472 linksize = readlink(devpath, contents, PATH_MAX);
4474 if (linksize <= 0) {
4475 return (FALSE);
4476 } else {
4477 contents[linksize] = '\0';
4479 vprint(REMOVE_MID, "%s %s -> %s\n", fcn, devpath, contents);
4481 if (content_p) {
4482 *content_p = s_strdup(contents);
4486 * Check to see if this is a link pointing to another link in /dev. The
4487 * cheap way to do this is to look for a lack of ../devices/.
4490 if (is_minor_node(contents, &ptr) == DEVFSADM_FALSE) {
4492 if (type_p) {
4493 *type_p = DI_SECONDARY_LINK;
4497 * assume that linkcontents is really a pointer to another
4498 * link, and if so recurse and read its link contents.
4500 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
4501 (void) strcpy(stage_link, dev_dir);
4502 (void) strcat(stage_link, "/");
4503 (void) strcpy(stage_link,
4504 &contents[strlen(DEV) + strlen("/")]);
4505 } else {
4506 if ((ptr = strrchr(devpath, '/')) == NULL) {
4507 vprint(REMOVE_MID, "%s%s -> %s invalid link. "
4508 "missing '/'\n", fcn, devpath, contents);
4509 return (TRUE);
4511 *ptr = '\0';
4512 (void) strcpy(stage_link, devpath);
4513 *ptr = '/';
4514 (void) strcat(stage_link, "/");
4515 (void) strcat(stage_link, contents);
4517 return (resolve_link(stage_link, NULL, NULL, devfs_path,
4518 dangle));
4521 /* Current link points at a /devices minor node */
4522 if (type_p) {
4523 *type_p = DI_PRIMARY_LINK;
4526 if (devfs_path)
4527 *devfs_path = s_strdup(ptr);
4529 rv = FALSE;
4530 if (dangle)
4531 rv = (stat(ptr - strlen(DEVICES), &sb) == -1);
4533 vprint(REMOVE_MID, "%slink=%s, returning %s\n", fcn,
4534 devpath, ((rv == TRUE) ? "TRUE" : "FALSE"));
4536 return (rv);
4540 * Returns the substring of interest, given a path.
4542 static char *
4543 alloc_cmp_str(const char *path, devfsadm_enumerate_t *dep)
4545 uint_t match;
4546 char *np, *ap, *mp;
4547 char *cmp_str = NULL;
4548 char at[] = "@";
4549 char *fcn = "alloc_cmp_str";
4551 np = ap = mp = NULL;
4554 * extract match flags from the flags argument.
4556 match = (dep->flags & MATCH_MASK);
4558 vprint(ENUM_MID, "%s: enumeration match type: 0x%x"
4559 " path: %s\n", fcn, match, path);
4562 * MATCH_CALLBACK and MATCH_ALL are the only flags
4563 * which may be used if "path" is a /dev path
4565 if (match == MATCH_CALLBACK) {
4566 if (dep->sel_fcn == NULL) {
4567 vprint(ENUM_MID, "%s: invalid enumerate"
4568 " callback: path: %s\n", fcn, path);
4569 return (NULL);
4571 cmp_str = dep->sel_fcn(path, dep->cb_arg);
4572 return (cmp_str);
4575 cmp_str = s_strdup(path);
4577 if (match == MATCH_ALL) {
4578 return (cmp_str);
4582 * The remaining flags make sense only for /devices
4583 * paths
4585 if ((mp = strrchr(cmp_str, ':')) == NULL) {
4586 vprint(ENUM_MID, "%s: invalid path: %s\n",
4587 fcn, path);
4588 goto err;
4591 if (match == MATCH_MINOR) {
4592 /* A NULL "match_arg" values implies entire minor */
4593 if (get_component(mp + 1, dep->match_arg) == NULL) {
4594 vprint(ENUM_MID, "%s: invalid minor component:"
4595 " path: %s\n", fcn, path);
4596 goto err;
4598 return (cmp_str);
4601 if ((np = strrchr(cmp_str, '/')) == NULL) {
4602 vprint(ENUM_MID, "%s: invalid path: %s\n", fcn, path);
4603 goto err;
4606 if (match == MATCH_PARENT) {
4607 if (strcmp(cmp_str, "/") == 0) {
4608 vprint(ENUM_MID, "%s: invalid path: %s\n",
4609 fcn, path);
4610 goto err;
4613 if (np == cmp_str) {
4614 *(np + 1) = '\0';
4615 } else {
4616 *np = '\0';
4618 return (cmp_str);
4621 /* ap can be NULL - Leaf address may not exist or be empty string */
4622 ap = strchr(np+1, '@');
4624 /* minor is no longer of interest */
4625 *mp = '\0';
4627 if (match == MATCH_NODE) {
4628 if (ap)
4629 *ap = '\0';
4630 return (cmp_str);
4631 } else if (match == MATCH_ADDR) {
4633 * The empty string is a valid address. The only MATCH_ADDR
4634 * allowed in this case is against the whole address or
4635 * the first component of the address (match_arg=NULL/"0"/"1")
4636 * Note that in this case, the path won't have an "@"
4637 * As a result ap will be NULL. We fake up an ap = @'\0'
4638 * so that get_component() will work correctly.
4640 if (ap == NULL) {
4641 ap = at;
4644 if (get_component(ap + 1, dep->match_arg) == NULL) {
4645 vprint(ENUM_MID, "%s: invalid leaf addr. component:"
4646 " path: %s\n", fcn, path);
4647 goto err;
4649 return (cmp_str);
4652 vprint(ENUM_MID, "%s: invalid enumeration flags: 0x%x"
4653 " path: %s\n", fcn, dep->flags, path);
4655 /*FALLTHRU*/
4656 err:
4657 free(cmp_str);
4658 return (NULL);
4663 * "str" is expected to be a string with components separated by ','
4664 * The terminating null char is considered a separator.
4665 * get_component() will remove the portion of the string beyond
4666 * the component indicated.
4667 * If comp_str is NULL, the entire "str" is returned.
4669 static char *
4670 get_component(char *str, const char *comp_str)
4672 long comp;
4673 char *cp;
4675 if (str == NULL) {
4676 return (NULL);
4679 if (comp_str == NULL) {
4680 return (str);
4683 errno = 0;
4684 comp = strtol(comp_str, &cp, 10);
4685 if (errno != 0 || *cp != '\0' || comp < 0) {
4686 return (NULL);
4689 if (comp == 0)
4690 return (str);
4692 for (cp = str; ; cp++) {
4693 if (*cp == ',' || *cp == '\0')
4694 comp--;
4695 if (*cp == '\0' || comp <= 0) {
4696 break;
4700 if (comp == 0) {
4701 *cp = '\0';
4702 } else {
4703 str = NULL;
4706 return (str);
4711 * Enumerate serves as a generic counter as well as a means to determine
4712 * logical unit/controller numbers for such items as disk and tape
4713 * drives.
4715 * rules[] is an array of devfsadm_enumerate_t structures which defines
4716 * the enumeration rules to be used for a specified set of links in /dev.
4717 * The set of links is specified through regular expressions (of the flavor
4718 * described in regex(5)). These regular expressions are used to determine
4719 * the set of links in /dev to examine. The last path component in these
4720 * regular expressions MUST contain a parenthesized subexpression surrounding
4721 * the RE which is to be considered the enumerating component. The subexp
4722 * member in a rule is the subexpression number of the enumerating
4723 * component. Subexpressions in the last path component are numbered starting
4724 * from 1.
4726 * A cache of current id assignments is built up from existing symlinks and
4727 * new assignments use the lowest unused id. Assignments are based on a
4728 * match of a specified substring of a symlink's contents. If the specified
4729 * component for the devfs_path argument matches the corresponding substring
4730 * for a existing symlink's contents, the cached id is returned. Else, a new
4731 * id is created and returned in *buf. *buf must be freed by the caller.
4733 * An id assignment may be governed by a combination of rules, each rule
4734 * applicable to a different subset of links in /dev. For example, controller
4735 * numbers may be determined by a combination of disk symlinks in /dev/[r]dsk
4736 * and controller symlinks in /dev/cfg, with the two sets requiring different
4737 * rules to derive the "substring of interest". In such cases, the rules
4738 * array will have more than one element.
4741 devfsadm_enumerate_int(char *devfs_path, int index, char **buf,
4742 devfsadm_enumerate_t rules[], int nrules)
4744 return (find_enum_id(rules, nrules,
4745 devfs_path, index, "0", INTEGER, buf, 0));
4749 disk_enumerate_int(char *devfs_path, int index, char **buf,
4750 devfsadm_enumerate_t rules[], int nrules)
4752 return (find_enum_id(rules, nrules,
4753 devfs_path, index, "0", INTEGER, buf, 1));
4757 * Same as above, but allows a starting value to be specified.
4758 * Private to devfsadm.... used by devlinks.
4760 static int
4761 devfsadm_enumerate_int_start(char *devfs_path, int index, char **buf,
4762 devfsadm_enumerate_t rules[], int nrules, char *start)
4764 return (find_enum_id(rules, nrules,
4765 devfs_path, index, start, INTEGER, buf, 0));
4769 * devfsadm_enumerate_char serves as a generic counter returning
4770 * a single letter.
4773 devfsadm_enumerate_char(char *devfs_path, int index, char **buf,
4774 devfsadm_enumerate_t rules[], int nrules)
4776 return (find_enum_id(rules, nrules,
4777 devfs_path, index, "a", LETTER, buf, 0));
4781 * Same as above, but allows a starting char to be specified.
4782 * Private to devfsadm - used by ports module (port_link.c)
4785 devfsadm_enumerate_char_start(char *devfs_path, int index, char **buf,
4786 devfsadm_enumerate_t rules[], int nrules, char *start)
4788 return (find_enum_id(rules, nrules,
4789 devfs_path, index, start, LETTER, buf, 0));
4794 * For a given numeral_set (see get_cached_set for desc of numeral_set),
4795 * search all cached entries looking for matches on a specified substring
4796 * of devfs_path. The substring is derived from devfs_path based on the
4797 * rule specified by "index". If a match is found on a cached entry,
4798 * return the enumerated id in buf. Otherwise, create a new id by calling
4799 * new_id, then cache and return that entry.
4801 static int
4802 find_enum_id(devfsadm_enumerate_t rules[], int nrules,
4803 char *devfs_path, int index, char *min, int type, char **buf,
4804 int multiple)
4806 numeral_t *matchnp;
4807 numeral_t *numeral;
4808 int matchcount = 0;
4809 char *cmp_str;
4810 char *fcn = "find_enum_id";
4811 numeral_set_t *set;
4813 if (rules == NULL) {
4814 vprint(ENUM_MID, "%s: no rules. path: %s\n",
4815 fcn, devfs_path ? devfs_path : "<NULL path>");
4816 return (DEVFSADM_FAILURE);
4819 if (devfs_path == NULL) {
4820 vprint(ENUM_MID, "%s: NULL path\n", fcn);
4821 return (DEVFSADM_FAILURE);
4824 if (nrules <= 0 || index < 0 || index >= nrules || buf == NULL) {
4825 vprint(ENUM_MID, "%s: invalid arguments. path: %s\n",
4826 fcn, devfs_path);
4827 return (DEVFSADM_FAILURE);
4830 *buf = NULL;
4833 cmp_str = alloc_cmp_str(devfs_path, &rules[index]);
4834 if (cmp_str == NULL) {
4835 return (DEVFSADM_FAILURE);
4838 if ((set = get_enum_cache(rules, nrules)) == NULL) {
4839 free(cmp_str);
4840 return (DEVFSADM_FAILURE);
4843 assert(nrules == set->re_count);
4846 * Check and see if a matching entry is already cached.
4848 matchcount = lookup_enum_cache(set, cmp_str, rules, index,
4849 &matchnp);
4851 if (matchcount < 0 || matchcount > 1) {
4852 free(cmp_str);
4853 if (multiple && matchcount > 1)
4854 return (DEVFSADM_MULTIPLE);
4855 else
4856 return (DEVFSADM_FAILURE);
4859 /* if matching entry already cached, return it */
4860 if (matchcount == 1) {
4861 /* should never create a link with a reserved ID */
4862 vprint(ENUM_MID, "%s: 1 match w/ ID: %s\n", fcn, matchnp->id);
4863 assert(matchnp->flags == 0);
4864 *buf = s_strdup(matchnp->id);
4865 free(cmp_str);
4866 return (DEVFSADM_SUCCESS);
4870 * no cached entry, initialize a numeral struct
4871 * by calling new_id() and cache onto the numeral_set
4873 numeral = s_malloc(sizeof (numeral_t));
4874 numeral->id = new_id(set->headnumeral, type, min);
4875 numeral->full_path = s_strdup(devfs_path);
4876 numeral->rule_index = index;
4877 numeral->cmp_str = cmp_str;
4878 cmp_str = NULL;
4879 numeral->flags = 0;
4880 vprint(RSRV_MID, "%s: alloc new_id: %s numeral flags = %d\n",
4881 fcn, numeral->id, numeral->flags);
4884 /* insert to head of list for fast lookups */
4885 numeral->next = set->headnumeral;
4886 set->headnumeral = numeral;
4888 *buf = s_strdup(numeral->id);
4889 return (DEVFSADM_SUCCESS);
4894 * Looks up the specified cache for a match with a specified string
4895 * Returns:
4896 * -1 : on error.
4897 * 0/1/2 : Number of matches.
4898 * Returns the matching element only if there is a single match.
4899 * If the "uncached" flag is set, derives the "cmp_str" afresh
4900 * for the match instead of using cached values.
4902 static int
4903 lookup_enum_cache(numeral_set_t *set, char *cmp_str,
4904 devfsadm_enumerate_t rules[], int index, numeral_t **matchnpp)
4906 int matchcount = 0, rv = -1;
4907 int uncached;
4908 numeral_t *np;
4909 char *fcn = "lookup_enum_cache";
4910 char *cp;
4912 *matchnpp = NULL;
4914 assert(index < set->re_count);
4916 if (cmp_str == NULL) {
4917 return (-1);
4920 uncached = 0;
4921 if ((rules[index].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
4922 uncached = 1;
4926 * Check and see if a matching entry is already cached.
4928 for (np = set->headnumeral; np != NULL; np = np->next) {
4931 * Skip reserved IDs
4933 if (np->flags & NUMERAL_RESERVED) {
4934 vprint(RSRV_MID, "lookup_enum_cache: "
4935 "Cannot Match with reserved ID (%s), "
4936 "skipping\n", np->id);
4937 assert(np->flags == NUMERAL_RESERVED);
4938 continue;
4939 } else {
4940 vprint(RSRV_MID, "lookup_enum_cache: "
4941 "Attempting match with numeral ID: %s"
4942 " numeral flags = %d\n", np->id, np->flags);
4943 assert(np->flags == 0);
4946 if (np->cmp_str == NULL) {
4947 vprint(ENUM_MID, "%s: invalid entry in enumerate"
4948 " cache. path: %s\n", fcn, np->full_path);
4949 return (-1);
4952 if (uncached) {
4953 vprint(CHATTY_MID, "%s: bypassing enumerate cache."
4954 " path: %s\n", fcn, cmp_str);
4955 cp = alloc_cmp_str(np->full_path,
4956 &rules[np->rule_index]);
4957 if (cp == NULL)
4958 return (-1);
4959 rv = strcmp(cmp_str, cp);
4960 free(cp);
4961 } else {
4962 rv = strcmp(cmp_str, np->cmp_str);
4965 if (rv == 0) {
4966 if (matchcount++ != 0) {
4967 break; /* more than 1 match. */
4969 *matchnpp = np;
4973 return (matchcount);
4976 #ifdef DEBUG
4977 static void
4978 dump_enum_cache(numeral_set_t *setp)
4980 int i;
4981 numeral_t *np;
4982 char *fcn = "dump_enum_cache";
4984 vprint(ENUM_MID, "%s: re_count = %d\n", fcn, setp->re_count);
4985 for (i = 0; i < setp->re_count; i++) {
4986 vprint(ENUM_MID, "%s: re[%d] = %s\n", fcn, i, setp->re[i]);
4989 for (np = setp->headnumeral; np != NULL; np = np->next) {
4990 vprint(ENUM_MID, "%s: id: %s\n", fcn, np->id);
4991 vprint(ENUM_MID, "%s: full_path: %s\n", fcn, np->full_path);
4992 vprint(ENUM_MID, "%s: rule_index: %d\n", fcn, np->rule_index);
4993 vprint(ENUM_MID, "%s: cmp_str: %s\n", fcn, np->cmp_str);
4994 vprint(ENUM_MID, "%s: flags: %d\n", fcn, np->flags);
4997 #endif
5000 * For a given set of regular expressions in rules[], this function returns
5001 * either a previously cached struct numeral_set or it will create and
5002 * cache a new struct numeral_set. There is only one struct numeral_set
5003 * for the combination of REs present in rules[]. Each numeral_set contains
5004 * the regular expressions in rules[] used for cache selection AND a linked
5005 * list of struct numerals, ONE FOR EACH *UNIQUE* numeral or character ID
5006 * selected by the grouping parenthesized subexpression found in the last
5007 * path component of each rules[].re. For example, the RE: "rmt/([0-9]+)"
5008 * selects all the logical nodes of the correct form in dev/rmt/.
5009 * Each rmt/X will store a *single* struct numeral... ie 0, 1, 2 each get a
5010 * single struct numeral. There is no need to store more than a single logical
5011 * node matching X since the information desired in the devfspath would be
5012 * identical for the portion of the devfspath of interest. (the part up to,
5013 * but not including the minor name in this example.)
5015 * If the given numeral_set is not yet cached, call enumerate_recurse to
5016 * create it.
5018 static numeral_set_t *
5019 get_enum_cache(devfsadm_enumerate_t rules[], int nrules)
5021 /* linked list of numeral sets */
5022 numeral_set_t *setp;
5023 int i;
5024 int ret;
5025 char *path_left;
5026 enumerate_file_t *entry;
5027 char *fcn = "get_enum_cache";
5030 * See if we've already cached this numeral set.
5032 for (setp = head_numeral_set; setp != NULL; setp = setp->next) {
5034 * check all regexp's passed in function against
5035 * those in cached set.
5037 if (nrules != setp->re_count) {
5038 continue;
5041 for (i = 0; i < nrules; i++) {
5042 if (strcmp(setp->re[i], rules[i].re) != 0) {
5043 break;
5047 if (i == nrules) {
5048 return (setp);
5053 * If the MATCH_UNCACHED flag is set, we should not be here.
5055 for (i = 0; i < nrules; i++) {
5056 if ((rules[i].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
5057 vprint(ENUM_MID, "%s: invalid enumeration flags: "
5058 "0x%x\n", fcn, rules[i].flags);
5059 return (NULL);
5064 * Since we made it here, we have not yet cached the given set of
5065 * logical nodes matching the passed re. Create a cached entry
5066 * struct numeral_set and populate it with a minimal set of
5067 * logical nodes from /dev.
5070 setp = s_malloc(sizeof (numeral_set_t));
5071 setp->re = s_malloc(sizeof (char *) * nrules);
5072 for (i = 0; i < nrules; i++) {
5073 setp->re[i] = s_strdup(rules[i].re);
5075 setp->re_count = nrules;
5076 setp->headnumeral = NULL;
5078 /* put this new cached set on the cached set list */
5079 setp->next = head_numeral_set;
5080 head_numeral_set = setp;
5083 * For each RE, search the "reserved" list to create numeral IDs that
5084 * are reserved.
5086 for (entry = enumerate_reserved; entry; entry = entry->er_next) {
5088 vprint(RSRV_MID, "parsing rstring: %s\n", entry->er_file);
5090 for (i = 0; i < nrules; i++) {
5091 path_left = s_strdup(setp->re[i]);
5092 vprint(RSRV_MID, "parsing rule RE: %s\n", path_left);
5093 ret = enumerate_parse(entry->er_file, path_left,
5094 setp, rules, i);
5095 free(path_left);
5096 if (ret == 1) {
5098 * We found the reserved ID for this entry.
5099 * We still keep the entry since it is needed
5100 * by the new link bypass code in disks
5102 vprint(RSRV_MID, "found rsv ID: rstring: %s "
5103 "rule RE: %s\n", entry->er_file, path_left);
5104 break;
5110 * For each RE, search disk and cache any matches on the
5111 * numeral list.
5113 for (i = 0; i < nrules; i++) {
5114 path_left = s_strdup(setp->re[i]);
5115 enumerate_recurse(dev_dir, path_left, setp, rules, i);
5116 free(path_left);
5119 #ifdef DEBUG
5120 dump_enum_cache(setp);
5121 #endif
5123 return (setp);
5128 * This function stats the pathname namebuf. If this is a directory
5129 * entry, we recurse down dname/fname until we find the first symbolic
5130 * link, and then stat and return it. This is valid for the same reason
5131 * that we only need to read a single pathname for multiple matching
5132 * logical ID's... ie, all the logical nodes should contain identical
5133 * physical paths for the parts we are interested.
5136 get_stat_info(char *namebuf, struct stat *sb)
5138 char *cp;
5139 finddevhdl_t fhandle;
5140 const char *fp;
5142 if (lstat(namebuf, sb) < 0) {
5143 (void) err_print(LSTAT_FAILED, namebuf, strerror(errno));
5144 return (DEVFSADM_FAILURE);
5147 if ((sb->st_mode & S_IFMT) == S_IFLNK) {
5148 return (DEVFSADM_SUCCESS);
5152 * If it is a dir, recurse down until we find a link and
5153 * then use the link.
5155 if ((sb->st_mode & S_IFMT) == S_IFDIR) {
5157 if (finddev_readdir(namebuf, &fhandle) != 0) {
5158 return (DEVFSADM_FAILURE);
5162 * Search each dir entry looking for a symlink. Return
5163 * the first symlink found in namebuf. Recurse dirs.
5165 while ((fp = finddev_next(fhandle)) != NULL) {
5166 cp = namebuf + strlen(namebuf);
5167 if ((strlcat(namebuf, "/", PATH_MAX) >= PATH_MAX) ||
5168 (strlcat(namebuf, fp, PATH_MAX) >= PATH_MAX)) {
5169 *cp = '\0';
5170 finddev_close(fhandle);
5171 return (DEVFSADM_FAILURE);
5173 if (get_stat_info(namebuf, sb) == DEVFSADM_SUCCESS) {
5174 finddev_close(fhandle);
5175 return (DEVFSADM_SUCCESS);
5177 *cp = '\0';
5179 finddev_close(fhandle);
5182 /* no symlink found, so return error */
5183 return (DEVFSADM_FAILURE);
5187 * An existing matching ID was not found, so this function is called to
5188 * create the next lowest ID. In the INTEGER case, return the next
5189 * lowest unused integer. In the case of LETTER, return the next lowest
5190 * unused letter. Return empty string if all 26 are used.
5191 * Only IDs >= min will be returned.
5193 char *
5194 new_id(numeral_t *numeral, int type, char *min)
5196 int imin;
5197 temp_t *temp;
5198 temp_t *ptr;
5199 temp_t **previous;
5200 temp_t *head = NULL;
5201 char *retval;
5202 static char tempbuff[8];
5203 numeral_t *np;
5205 if (type == LETTER) {
5207 char letter[26], i;
5209 if (numeral == NULL) {
5210 return (s_strdup(min));
5213 for (i = 0; i < 26; i++) {
5214 letter[i] = 0;
5217 for (np = numeral; np != NULL; np = np->next) {
5218 assert(np->flags == 0 ||
5219 np->flags == NUMERAL_RESERVED);
5220 letter[*np->id - 'a']++;
5223 imin = *min - 'a';
5225 for (i = imin; i < 26; i++) {
5226 if (letter[i] == 0) {
5227 retval = s_malloc(2);
5228 retval[0] = 'a' + i;
5229 retval[1] = '\0';
5230 return (retval);
5234 return (s_strdup(""));
5237 if (type == INTEGER) {
5239 if (numeral == NULL) {
5240 return (s_strdup(min));
5243 imin = atoi(min);
5245 /* sort list */
5246 for (np = numeral; np != NULL; np = np->next) {
5247 assert(np->flags == 0 ||
5248 np->flags == NUMERAL_RESERVED);
5249 temp = s_malloc(sizeof (temp_t));
5250 temp->integer = atoi(np->id);
5251 temp->next = NULL;
5253 previous = &head;
5254 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5255 if (temp->integer < ptr->integer) {
5256 temp->next = ptr;
5257 *previous = temp;
5258 break;
5260 previous = &(ptr->next);
5262 if (ptr == NULL) {
5263 *previous = temp;
5267 /* now search sorted list for first hole >= imin */
5268 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5269 if (imin == ptr->integer) {
5270 imin++;
5271 } else {
5272 if (imin < ptr->integer) {
5273 break;
5279 /* free temp list */
5280 for (ptr = head; ptr != NULL; ) {
5281 temp = ptr;
5282 ptr = ptr->next;
5283 free(temp);
5286 (void) sprintf(tempbuff, "%d", imin);
5287 return (s_strdup(tempbuff));
5290 return (s_strdup(""));
5293 static int
5294 enumerate_parse(char *rsvstr, char *path_left, numeral_set_t *setp,
5295 devfsadm_enumerate_t rules[], int index)
5297 char *slash1 = NULL;
5298 char *slash2 = NULL;
5299 char *numeral_id;
5300 char *path_left_save;
5301 char *rsvstr_save;
5302 int ret = 0;
5303 static int warned = 0;
5305 rsvstr_save = rsvstr;
5306 path_left_save = path_left;
5308 if (rsvstr == NULL || rsvstr[0] == '\0' || rsvstr[0] == '/') {
5309 if (!warned) {
5310 err_print("invalid reserved filepath: %s\n",
5311 rsvstr ? rsvstr : "<NULL>");
5312 warned = 1;
5314 return (0);
5317 vprint(RSRV_MID, "processing rule: %s, rstring: %s\n",
5318 path_left, rsvstr);
5321 for (;;) {
5322 /* get rid of any extra '/' in the reserve string */
5323 while (*rsvstr == '/') {
5324 rsvstr++;
5327 /* get rid of any extra '/' in the RE */
5328 while (*path_left == '/') {
5329 path_left++;
5332 if (slash1 = strchr(path_left, '/')) {
5333 *slash1 = '\0';
5335 if (slash2 = strchr(rsvstr, '/')) {
5336 *slash2 = '\0';
5339 if ((slash1 != NULL) ^ (slash2 != NULL)) {
5340 ret = 0;
5341 vprint(RSRV_MID, "mismatch in # of path components\n");
5342 goto out;
5346 * Returns true if path_left matches the list entry.
5347 * If it is the last path component, pass subexp
5348 * so that it will return the corresponding ID in
5349 * numeral_id.
5351 numeral_id = NULL;
5352 if (match_path_component(path_left, rsvstr, &numeral_id,
5353 slash1 ? 0 : rules[index].subexp)) {
5355 /* We have a match. */
5356 if (slash1 == NULL) {
5357 /* Is last path component */
5358 vprint(RSRV_MID, "match and last component\n");
5359 create_reserved_numeral(setp, numeral_id);
5360 if (numeral_id != NULL) {
5361 free(numeral_id);
5363 ret = 1;
5364 goto out;
5365 } else {
5366 /* Not last path component. Continue parsing */
5367 *slash1 = '/';
5368 *slash2 = '/';
5369 path_left = slash1 + 1;
5370 rsvstr = slash2 + 1;
5371 vprint(RSRV_MID,
5372 "match and NOT last component\n");
5373 continue;
5375 } else {
5376 /* No match */
5377 ret = 0;
5378 vprint(RSRV_MID, "No match: rule RE = %s, "
5379 "rstring = %s\n", path_left, rsvstr);
5380 goto out;
5384 out:
5385 if (slash1)
5386 *slash1 = '/';
5387 if (slash2)
5388 *slash2 = '/';
5390 if (ret == 1) {
5391 vprint(RSRV_MID, "match: rule RE: %s, rstring: %s\n",
5392 path_left_save, rsvstr_save);
5393 } else {
5394 vprint(RSRV_MID, "NO match: rule RE: %s, rstring: %s\n",
5395 path_left_save, rsvstr_save);
5398 return (ret);
5402 * Search current_dir for all files which match the first path component
5403 * of path_left, which is an RE. If a match is found, but there are more
5404 * components of path_left, then recurse, otherwise, if we have reached
5405 * the last component of path_left, call create_cached_numerals for each
5406 * file. At some point, recurse_dev_re() should be rewritten so that this
5407 * function can be eliminated.
5409 static void
5410 enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
5411 devfsadm_enumerate_t rules[], int index)
5413 char *slash;
5414 char *new_path;
5415 char *numeral_id;
5416 finddevhdl_t fhandle;
5417 const char *fp;
5419 if (finddev_readdir(current_dir, &fhandle) != 0) {
5420 return;
5423 /* get rid of any extra '/' */
5424 while (*path_left == '/') {
5425 path_left++;
5428 if (slash = strchr(path_left, '/')) {
5429 *slash = '\0';
5432 while ((fp = finddev_next(fhandle)) != NULL) {
5435 * Returns true if path_left matches the list entry.
5436 * If it is the last path component, pass subexp
5437 * so that it will return the corresponding ID in
5438 * numeral_id.
5440 numeral_id = NULL;
5441 if (match_path_component(path_left, (char *)fp, &numeral_id,
5442 slash ? 0 : rules[index].subexp)) {
5444 new_path = s_malloc(strlen(current_dir) +
5445 strlen(fp) + 2);
5447 (void) strcpy(new_path, current_dir);
5448 (void) strcat(new_path, "/");
5449 (void) strcat(new_path, fp);
5451 if (slash != NULL) {
5452 enumerate_recurse(new_path, slash + 1,
5453 setp, rules, index);
5454 } else {
5455 create_cached_numeral(new_path, setp,
5456 numeral_id, rules, index);
5457 if (numeral_id != NULL) {
5458 free(numeral_id);
5461 free(new_path);
5465 if (slash != NULL) {
5466 *slash = '/';
5468 finddev_close(fhandle);
5473 * Returns true if file matches file_re. If subexp is non-zero, it means
5474 * we are searching the last path component and need to return the
5475 * parenthesized subexpression subexp in id.
5478 static int
5479 match_path_component(char *file_re, char *file, char **id, int subexp)
5481 regex_t re1;
5482 int match = 0;
5483 int nelements;
5484 regmatch_t *pmatch;
5486 if (subexp != 0) {
5487 nelements = subexp + 1;
5488 pmatch =
5489 (regmatch_t *)s_malloc(sizeof (regmatch_t) * nelements);
5490 } else {
5491 pmatch = NULL;
5492 nelements = 0;
5495 if (regcomp(&re1, file_re, REG_EXTENDED) != 0) {
5496 if (pmatch != NULL) {
5497 free(pmatch);
5499 return (0);
5502 if (regexec(&re1, file, nelements, pmatch, 0) == 0) {
5503 match = 1;
5506 if ((match != 0) && (subexp != 0)) {
5507 int size = pmatch[subexp].rm_eo - pmatch[subexp].rm_so;
5508 *id = s_malloc(size + 1);
5509 (void) strncpy(*id, &file[pmatch[subexp].rm_so], size);
5510 (*id)[size] = '\0';
5513 if (pmatch != NULL) {
5514 free(pmatch);
5516 regfree(&re1);
5517 return (match);
5520 static void
5521 create_reserved_numeral(numeral_set_t *setp, char *numeral_id)
5523 numeral_t *np;
5525 vprint(RSRV_MID, "Attempting to create reserved numeral: %s\n",
5526 numeral_id);
5529 * We found a numeral_id from an entry in the enumerate_reserved file
5530 * which matched the re passed in from devfsadm_enumerate. We only
5531 * need to make sure ONE copy of numeral_id exists on the numeral list.
5532 * We only need to store /dev/dsk/cNtod0s0 and no other entries
5533 * hanging off of controller N.
5535 for (np = setp->headnumeral; np != NULL; np = np->next) {
5536 if (strcmp(numeral_id, np->id) == 0) {
5537 vprint(RSRV_MID, "ID: %s, already reserved\n", np->id);
5538 assert(np->flags == NUMERAL_RESERVED);
5539 return;
5540 } else {
5541 assert(np->flags == 0 ||
5542 np->flags == NUMERAL_RESERVED);
5546 /* NOT on list, so add it */
5547 np = s_malloc(sizeof (numeral_t));
5548 np->id = s_strdup(numeral_id);
5549 np->full_path = NULL;
5550 np->rule_index = 0;
5551 np->cmp_str = NULL;
5552 np->flags = NUMERAL_RESERVED;
5553 np->next = setp->headnumeral;
5554 setp->headnumeral = np;
5556 vprint(RSRV_MID, "Reserved numeral ID: %s\n", np->id);
5560 * This function is called for every file which matched the leaf
5561 * component of the RE. If the "numeral_id" is not already on the
5562 * numeral set's numeral list, add it and its physical path.
5564 static void
5565 create_cached_numeral(char *path, numeral_set_t *setp, char *numeral_id,
5566 devfsadm_enumerate_t rules[], int index)
5568 char linkbuf[PATH_MAX + 1];
5569 char lpath[PATH_MAX + 1];
5570 char *linkptr, *cmp_str;
5571 numeral_t *np;
5572 int linksize;
5573 struct stat sb;
5574 char *contents;
5575 const char *fcn = "create_cached_numeral";
5577 assert(index >= 0 && index < setp->re_count);
5578 assert(strcmp(rules[index].re, setp->re[index]) == 0);
5581 * We found a numeral_id from an entry in /dev which matched
5582 * the re passed in from devfsadm_enumerate. We only need to make sure
5583 * ONE copy of numeral_id exists on the numeral list. We only need
5584 * to store /dev/dsk/cNtod0s0 and no other entries hanging off
5585 * of controller N.
5587 for (np = setp->headnumeral; np != NULL; np = np->next) {
5588 assert(np->flags == 0 || np->flags == NUMERAL_RESERVED);
5589 if (strcmp(numeral_id, np->id) == 0) {
5591 * Note that we can't assert that the flags field
5592 * of the numeral is 0, since both reserved and
5593 * unreserved links in /dev come here
5595 if (np->flags == NUMERAL_RESERVED) {
5596 vprint(RSRV_MID, "ID derived from /dev link is"
5597 " reserved: %s\n", np->id);
5598 } else {
5599 vprint(RSRV_MID, "ID derived from /dev link is"
5600 " NOT reserved: %s\n", np->id);
5602 return;
5606 /* NOT on list, so add it */
5608 (void) strcpy(lpath, path);
5610 * If path is a dir, it is changed to the first symbolic link it find
5611 * if it finds one.
5613 if (get_stat_info(lpath, &sb) == DEVFSADM_FAILURE) {
5614 return;
5617 /* If we get here, we found a symlink */
5618 linksize = readlink(lpath, linkbuf, PATH_MAX);
5620 if (linksize <= 0) {
5621 err_print(READLINK_FAILED, fcn, lpath, strerror(errno));
5622 return;
5625 linkbuf[linksize] = '\0';
5628 * redirect alias path to current path
5629 * devi_root_node is protected by lock_dev()
5631 contents = di_alias2curr(devi_root_node, linkbuf);
5634 * the following just points linkptr to the root of the /devices
5635 * node if it is a minor node, otherwise, to the first char of
5636 * linkbuf if it is a link.
5638 (void) is_minor_node(contents, &linkptr);
5640 cmp_str = alloc_cmp_str(linkptr, &rules[index]);
5641 if (cmp_str == NULL) {
5642 free(contents);
5643 return;
5646 np = s_malloc(sizeof (numeral_t));
5648 np->id = s_strdup(numeral_id);
5649 np->full_path = s_strdup(linkptr);
5650 np->rule_index = index;
5651 np->cmp_str = cmp_str;
5652 np->flags = 0;
5654 np->next = setp->headnumeral;
5655 setp->headnumeral = np;
5657 free(contents);
5662 * This should be called either before or after granting access to a
5663 * command line version of devfsadm running, since it may have changed
5664 * the state of /dev. It forces future enumerate calls to re-build
5665 * cached information from /dev.
5667 void
5668 invalidate_enumerate_cache(void)
5670 numeral_set_t *setp;
5671 numeral_set_t *savedsetp;
5672 numeral_t *savednumset;
5673 numeral_t *numset;
5674 int i;
5676 for (setp = head_numeral_set; setp != NULL; ) {
5678 * check all regexp's passed in function against
5679 * those in cached set.
5682 savedsetp = setp;
5683 setp = setp->next;
5685 for (i = 0; i < savedsetp->re_count; i++) {
5686 free(savedsetp->re[i]);
5688 free(savedsetp->re);
5690 for (numset = savedsetp->headnumeral; numset != NULL; ) {
5691 savednumset = numset;
5692 numset = numset->next;
5693 assert(savednumset->rule_index < savedsetp->re_count);
5694 free(savednumset->id);
5695 free(savednumset->full_path);
5696 free(savednumset->cmp_str);
5697 free(savednumset);
5699 free(savedsetp);
5701 head_numeral_set = NULL;
5705 * Copies over links from /dev to <root>/dev and device special files in
5706 * /devices to <root>/devices, preserving the existing file modes. If
5707 * the link or special file already exists on <root>, skip the copy. (it
5708 * would exist only if a package hard coded it there, so assume package
5709 * knows best?). Use /etc/name_to_major and <root>/etc/name_to_major to
5710 * make translations for major numbers on device special files. No need to
5711 * make a translation on minor_perm since if the file was created in the
5712 * miniroot then it would presumably have the same minor_perm entry in
5713 * <root>/etc/minor_perm. To be used only by install.
5716 devfsadm_copy(void)
5718 char filename[PATH_MAX + 1];
5720 /* load the installed root's name_to_major for translations */
5721 (void) snprintf(filename, sizeof (filename), "%s%s", root_dir,
5722 NAME_TO_MAJOR);
5723 if (load_n2m_table(filename) == DEVFSADM_FAILURE) {
5724 return (DEVFSADM_FAILURE);
5727 /* Copy /dev to target disk. No need to copy /devices with devfs */
5728 (void) nftw(DEV, devfsadm_copy_file, 20, FTW_PHYS);
5730 /* Let install handle copying over path_to_inst */
5732 return (DEVFSADM_SUCCESS);
5736 * This function copies links, dirs, and device special files.
5737 * Note that it always returns DEVFSADM_SUCCESS, so that nftw doesn't
5738 * abort.
5740 /*ARGSUSED*/
5741 static int
5742 devfsadm_copy_file(const char *file, const struct stat *stat,
5743 int flags, struct FTW *ftw)
5745 struct stat sp;
5746 dev_t newdev;
5747 char newfile[PATH_MAX + 1];
5748 char linkcontents[PATH_MAX + 1];
5749 int bytes;
5750 const char *fcn = "devfsadm_copy_file";
5752 (void) strcpy(newfile, root_dir);
5753 (void) strcat(newfile, "/");
5754 (void) strcat(newfile, file);
5756 if (lstat(newfile, &sp) == 0) {
5757 /* newfile already exists, so no need to continue */
5758 return (DEVFSADM_SUCCESS);
5761 if (((stat->st_mode & S_IFMT) == S_IFBLK) ||
5762 ((stat->st_mode & S_IFMT) == S_IFCHR)) {
5763 if (translate_major(stat->st_rdev, &newdev) ==
5764 DEVFSADM_FAILURE) {
5765 return (DEVFSADM_SUCCESS);
5767 if (mknod(newfile, stat->st_mode, newdev) == -1) {
5768 err_print(MKNOD_FAILED, newfile, strerror(errno));
5769 return (DEVFSADM_SUCCESS);
5771 } else if ((stat->st_mode & S_IFMT) == S_IFDIR) {
5772 if (mknod(newfile, stat->st_mode, 0) == -1) {
5773 err_print(MKNOD_FAILED, newfile, strerror(errno));
5774 return (DEVFSADM_SUCCESS);
5776 } else if ((stat->st_mode & S_IFMT) == S_IFLNK) {
5778 * No need to redirect alias paths. We want a
5779 * true copy. The system on first boot after install
5780 * will redirect paths
5782 if ((bytes = readlink(file, linkcontents, PATH_MAX)) == -1) {
5783 err_print(READLINK_FAILED, fcn, file, strerror(errno));
5784 return (DEVFSADM_SUCCESS);
5786 linkcontents[bytes] = '\0';
5787 if (symlink(linkcontents, newfile) == -1) {
5788 err_print(SYMLINK_FAILED, newfile, newfile,
5789 strerror(errno));
5790 return (DEVFSADM_SUCCESS);
5794 (void) lchown(newfile, stat->st_uid, stat->st_gid);
5795 return (DEVFSADM_SUCCESS);
5799 * Given a dev_t from the running kernel, return the new_dev_t
5800 * by translating to the major number found on the installed
5801 * target's root name_to_major file.
5803 static int
5804 translate_major(dev_t old_dev, dev_t *new_dev)
5806 major_t oldmajor;
5807 major_t newmajor;
5808 minor_t oldminor;
5809 minor_t newminor;
5810 char cdriver[FILENAME_MAX + 1];
5811 char driver[FILENAME_MAX + 1];
5812 char *fcn = "translate_major: ";
5814 oldmajor = major(old_dev);
5815 if (modctl(MODGETNAME, driver, sizeof (driver), &oldmajor) != 0) {
5816 return (DEVFSADM_FAILURE);
5819 if (strcmp(driver, "clone") != 0) {
5820 /* non-clone case */
5822 /* look up major number is target's name2major */
5823 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5824 return (DEVFSADM_FAILURE);
5827 *new_dev = makedev(newmajor, minor(old_dev));
5828 if (old_dev != *new_dev) {
5829 vprint(CHATTY_MID, "%sdriver: %s old: %lu,%lu "
5830 "new: %lu,%lu\n", fcn, driver, major(old_dev),
5831 minor(old_dev), major(*new_dev), minor(*new_dev));
5833 return (DEVFSADM_SUCCESS);
5834 } else {
5836 * The clone is a special case. Look at its minor
5837 * number since it is the major number of the real driver.
5839 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5840 return (DEVFSADM_FAILURE);
5843 oldminor = minor(old_dev);
5844 if (modctl(MODGETNAME, cdriver, sizeof (cdriver),
5845 &oldminor) != 0) {
5846 err_print(MODGETNAME_FAILED, oldminor);
5847 return (DEVFSADM_FAILURE);
5850 if (get_major_no(cdriver, &newminor) == DEVFSADM_FAILURE) {
5851 return (DEVFSADM_FAILURE);
5854 *new_dev = makedev(newmajor, newminor);
5855 if (old_dev != *new_dev) {
5856 vprint(CHATTY_MID, "%sdriver: %s old: "
5857 "%lu,%lu new: %lu,%lu\n", fcn, driver,
5858 major(old_dev), minor(old_dev),
5859 major(*new_dev), minor(*new_dev));
5861 return (DEVFSADM_SUCCESS);
5867 * Find the major number for driver, searching the n2m_list that was
5868 * built in load_n2m_table().
5870 static int
5871 get_major_no(char *driver, major_t *major)
5873 n2m_t *ptr;
5875 for (ptr = n2m_list; ptr != NULL; ptr = ptr->next) {
5876 if (strcmp(ptr->driver, driver) == 0) {
5877 *major = ptr->major;
5878 return (DEVFSADM_SUCCESS);
5881 err_print(FIND_MAJOR_FAILED, driver);
5882 return (DEVFSADM_FAILURE);
5886 * Loads a name_to_major table into memory. Used only for suninstall's
5887 * private -R option to devfsadm, to translate major numbers from the
5888 * running to the installed target disk.
5890 static int
5891 load_n2m_table(char *file)
5893 FILE *fp;
5894 char line[1024], *cp;
5895 char driver[PATH_MAX + 1];
5896 major_t major;
5897 n2m_t *ptr;
5898 int ln = 0;
5900 if ((fp = fopen(file, "r")) == NULL) {
5901 err_print(FOPEN_FAILED, file, strerror(errno));
5902 return (DEVFSADM_FAILURE);
5905 while (fgets(line, sizeof (line), fp) != NULL) {
5906 ln++;
5907 /* cut off comments starting with '#' */
5908 if ((cp = strchr(line, '#')) != NULL)
5909 *cp = '\0';
5910 /* ignore comment or blank lines */
5911 if (is_blank(line))
5912 continue;
5913 /* sanity-check */
5914 if (sscanf(line, "%1024s%lu", driver, &major) != 2) {
5915 err_print(IGNORING_LINE_IN, ln, file);
5916 continue;
5918 ptr = (n2m_t *)s_malloc(sizeof (n2m_t));
5919 ptr->major = major;
5920 ptr->driver = s_strdup(driver);
5921 ptr->next = n2m_list;
5922 n2m_list = ptr;
5924 if (fclose(fp) == EOF) {
5925 err_print(FCLOSE_FAILED, file, strerror(errno));
5927 return (DEVFSADM_SUCCESS);
5931 * Called at devfsadm startup to read the file /etc/dev/enumerate_reserved
5932 * Creates a linked list of devlinks from which reserved IDs can be derived
5934 static void
5935 read_enumerate_file(void)
5937 FILE *fp;
5938 int linenum;
5939 char line[PATH_MAX+1];
5940 enumerate_file_t *entry;
5941 struct stat current_sb;
5942 static struct stat cached_sb;
5943 static int cached = FALSE;
5945 assert(enumerate_file);
5947 if (stat(enumerate_file, &current_sb) == -1) {
5948 vprint(RSRV_MID, "No reserved file: %s\n", enumerate_file);
5949 cached = FALSE;
5950 if (enumerate_reserved != NULL) {
5951 vprint(RSRV_MID, "invalidating %s cache\n",
5952 enumerate_file);
5954 while (enumerate_reserved != NULL) {
5955 entry = enumerate_reserved;
5956 enumerate_reserved = entry->er_next;
5957 free(entry->er_file);
5958 free(entry->er_id);
5959 free(entry);
5961 return;
5964 /* if already cached, check to see if it is still valid */
5965 if (cached == TRUE) {
5967 if (current_sb.st_mtime == cached_sb.st_mtime) {
5968 vprint(RSRV_MID, "%s cache valid\n", enumerate_file);
5969 vprint(FILES_MID, "%s cache valid\n", enumerate_file);
5970 return;
5973 vprint(RSRV_MID, "invalidating %s cache\n", enumerate_file);
5974 vprint(FILES_MID, "invalidating %s cache\n", enumerate_file);
5976 while (enumerate_reserved != NULL) {
5977 entry = enumerate_reserved;
5978 enumerate_reserved = entry->er_next;
5979 free(entry->er_file);
5980 free(entry->er_id);
5981 free(entry);
5983 vprint(RSRV_MID, "Recaching file: %s\n", enumerate_file);
5984 } else {
5985 vprint(RSRV_MID, "Caching file (first time): %s\n",
5986 enumerate_file);
5987 cached = TRUE;
5990 (void) stat(enumerate_file, &cached_sb);
5992 if ((fp = fopen(enumerate_file, "r")) == NULL) {
5993 err_print(FOPEN_FAILED, enumerate_file, strerror(errno));
5994 return;
5997 vprint(RSRV_MID, "Reading reserve file: %s\n", enumerate_file);
5998 linenum = 0;
5999 while (fgets(line, sizeof (line), fp) != NULL) {
6000 char *cp, *ncp;
6002 linenum++;
6004 /* remove newline */
6005 cp = strchr(line, '\n');
6006 if (cp)
6007 *cp = '\0';
6009 vprint(RSRV_MID, "Reserve file: line %d: %s\n", linenum, line);
6011 /* skip over space and tab */
6012 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
6015 if (*cp == '\0' || *cp == '#') {
6016 vprint(RSRV_MID, "Skipping line: '%s'\n", line);
6017 continue; /* blank line or comment line */
6020 ncp = cp;
6022 /* delete trailing blanks */
6023 for (; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
6025 *cp = '\0';
6027 entry = s_zalloc(sizeof (enumerate_file_t));
6028 entry->er_file = s_strdup(ncp);
6029 entry->er_id = NULL;
6030 entry->er_next = enumerate_reserved;
6031 enumerate_reserved = entry;
6034 if (fclose(fp) == EOF) {
6035 err_print(FCLOSE_FAILED, enumerate_file, strerror(errno));
6040 * Called at devfsadm startup to read in the devlink.tab file. Creates
6041 * a linked list of devlinktab_list structures which will be
6042 * searched for every minor node.
6044 static void
6045 read_devlinktab_file(void)
6047 devlinktab_list_t *headp = NULL;
6048 devlinktab_list_t *entryp;
6049 devlinktab_list_t **previous;
6050 devlinktab_list_t *save;
6051 char line[MAX_DEVLINK_LINE], *cp;
6052 char *selector;
6053 char *p_link;
6054 char *s_link;
6055 FILE *fp;
6056 int i;
6057 static struct stat cached_sb;
6058 struct stat current_sb;
6059 static int cached = FALSE;
6061 if (devlinktab_file == NULL) {
6062 return;
6065 (void) stat(devlinktab_file, &current_sb);
6067 /* if already cached, check to see if it is still valid */
6068 if (cached == TRUE) {
6070 if (current_sb.st_mtime == cached_sb.st_mtime) {
6071 vprint(FILES_MID, "%s cache valid\n", devlinktab_file);
6072 return;
6075 vprint(FILES_MID, "invalidating %s cache\n", devlinktab_file);
6077 while (devlinktab_list != NULL) {
6078 free_link_list(devlinktab_list->p_link);
6079 free_link_list(devlinktab_list->s_link);
6080 free_selector_list(devlinktab_list->selector);
6081 free(devlinktab_list->selector_pattern);
6082 free(devlinktab_list->p_link_pattern);
6083 if (devlinktab_list->s_link_pattern != NULL) {
6084 free(devlinktab_list->s_link_pattern);
6086 save = devlinktab_list;
6087 devlinktab_list = devlinktab_list->next;
6088 free(save);
6090 } else {
6091 cached = TRUE;
6094 (void) stat(devlinktab_file, &cached_sb);
6096 if ((fp = fopen(devlinktab_file, "r")) == NULL) {
6097 err_print(FOPEN_FAILED, devlinktab_file, strerror(errno));
6098 return;
6101 previous = &headp;
6103 while (fgets(line, sizeof (line), fp) != NULL) {
6104 devlinktab_line++;
6105 i = strlen(line);
6106 if (line[i-1] == NEWLINE) {
6107 line[i-1] = '\0';
6108 } else if (i == sizeof (line-1)) {
6109 err_print(LINE_TOO_LONG, devlinktab_line,
6110 devlinktab_file, sizeof (line)-1);
6111 while (((i = getc(fp)) != '\n') && (i != EOF))
6113 continue;
6116 /* cut off comments starting with '#' */
6117 if ((cp = strchr(line, '#')) != NULL)
6118 *cp = '\0';
6119 /* ignore comment or blank lines */
6120 if (is_blank(line))
6121 continue;
6123 vprint(DEVLINK_MID, "table: %s line %d: '%s'\n",
6124 devlinktab_file, devlinktab_line, line);
6126 /* break each entry into fields. s_link may be NULL */
6127 if (split_devlinktab_entry(line, &selector, &p_link,
6128 &s_link) == DEVFSADM_FAILURE) {
6129 vprint(DEVLINK_MID, "split_entry returns failure\n");
6130 continue;
6131 } else {
6132 vprint(DEVLINK_MID, "split_entry selector='%s' "
6133 "p_link='%s' s_link='%s'\n\n", selector,
6134 p_link, (s_link == NULL) ? "" : s_link);
6137 entryp =
6138 (devlinktab_list_t *)s_malloc(sizeof (devlinktab_list_t));
6140 entryp->line_number = devlinktab_line;
6142 if ((entryp->selector = create_selector_list(selector))
6143 == NULL) {
6144 free(entryp);
6145 continue;
6147 entryp->selector_pattern = s_strdup(selector);
6149 if ((entryp->p_link = create_link_list(p_link)) == NULL) {
6150 free_selector_list(entryp->selector);
6151 free(entryp->selector_pattern);
6152 free(entryp);
6153 continue;
6156 entryp->p_link_pattern = s_strdup(p_link);
6158 if (s_link != NULL) {
6159 if ((entryp->s_link =
6160 create_link_list(s_link)) == NULL) {
6161 free_selector_list(entryp->selector);
6162 free_link_list(entryp->p_link);
6163 free(entryp->selector_pattern);
6164 free(entryp->p_link_pattern);
6165 free(entryp);
6166 continue;
6168 entryp->s_link_pattern = s_strdup(s_link);
6169 } else {
6170 entryp->s_link = NULL;
6171 entryp->s_link_pattern = NULL;
6175 /* append to end of list */
6177 entryp->next = NULL;
6178 *previous = entryp;
6179 previous = &(entryp->next);
6181 if (fclose(fp) == EOF) {
6182 err_print(FCLOSE_FAILED, devlinktab_file, strerror(errno));
6184 devlinktab_list = headp;
6189 * For a single line entry in devlink.tab, split the line into fields
6190 * selector, p_link, and an optionally s_link. If s_link field is not
6191 * present, then return NULL in s_link (not NULL string).
6193 static int
6194 split_devlinktab_entry(char *entry, char **selector, char **p_link,
6195 char **s_link)
6197 char *tab;
6199 *selector = entry;
6201 if ((tab = strchr(entry, TAB)) != NULL) {
6202 *tab = '\0';
6203 *p_link = ++tab;
6204 } else {
6205 err_print(MISSING_TAB, devlinktab_line, devlinktab_file);
6206 return (DEVFSADM_FAILURE);
6209 if (*p_link == '\0') {
6210 err_print(MISSING_DEVNAME, devlinktab_line, devlinktab_file);
6211 return (DEVFSADM_FAILURE);
6214 if ((tab = strchr(*p_link, TAB)) != NULL) {
6215 *tab = '\0';
6216 *s_link = ++tab;
6217 if (strchr(*s_link, TAB) != NULL) {
6218 err_print(TOO_MANY_FIELDS, devlinktab_line,
6219 devlinktab_file);
6220 return (DEVFSADM_FAILURE);
6222 } else {
6223 *s_link = NULL;
6226 return (DEVFSADM_SUCCESS);
6230 * For a given devfs_spec field, for each element in the field, add it to
6231 * a linked list of devfs_spec structures. Return the linked list in
6232 * devfs_spec_list.
6234 static selector_list_t *
6235 create_selector_list(char *selector)
6237 char *key;
6238 char *val;
6239 int error = FALSE;
6240 selector_list_t *head_selector_list = NULL;
6241 selector_list_t *selector_list;
6243 /* parse_devfs_spec splits the next field into keyword & value */
6244 while ((*selector != '\0') && (error == FALSE)) {
6245 if (parse_selector(&selector, &key, &val) == DEVFSADM_FAILURE) {
6246 error = TRUE;
6247 break;
6248 } else {
6249 selector_list = (selector_list_t *)
6250 s_malloc(sizeof (selector_list_t));
6251 if (strcmp(NAME_S, key) == 0) {
6252 selector_list->key = NAME;
6253 } else if (strcmp(TYPE_S, key) == 0) {
6254 selector_list->key = TYPE;
6255 } else if (strncmp(ADDR_S, key, ADDR_S_LEN) == 0) {
6256 selector_list->key = ADDR;
6257 if (key[ADDR_S_LEN] == '\0') {
6258 selector_list->arg = 0;
6259 } else if (isdigit(key[ADDR_S_LEN]) != FALSE) {
6260 selector_list->arg =
6261 atoi(&key[ADDR_S_LEN]);
6262 } else {
6263 error = TRUE;
6264 free(selector_list);
6265 err_print(BADKEYWORD, key,
6266 devlinktab_line, devlinktab_file);
6267 break;
6269 } else if (strncmp(MINOR_S, key, MINOR_S_LEN) == 0) {
6270 selector_list->key = MINOR;
6271 if (key[MINOR_S_LEN] == '\0') {
6272 selector_list->arg = 0;
6273 } else if (isdigit(key[MINOR_S_LEN]) != FALSE) {
6274 selector_list->arg =
6275 atoi(&key[MINOR_S_LEN]);
6276 } else {
6277 error = TRUE;
6278 free(selector_list);
6279 err_print(BADKEYWORD, key,
6280 devlinktab_line, devlinktab_file);
6281 break;
6283 vprint(DEVLINK_MID, "MINOR = %s\n", val);
6284 } else {
6285 err_print(UNRECOGNIZED_KEY, key,
6286 devlinktab_line, devlinktab_file);
6287 error = TRUE;
6288 free(selector_list);
6289 break;
6291 selector_list->val = s_strdup(val);
6292 selector_list->next = head_selector_list;
6293 head_selector_list = selector_list;
6294 vprint(DEVLINK_MID, "key='%s' val='%s' arg=%d\n",
6295 key, val, selector_list->arg);
6299 if ((error == FALSE) && (head_selector_list != NULL)) {
6300 return (head_selector_list);
6301 } else {
6302 /* parse failed. Free any allocated structs */
6303 free_selector_list(head_selector_list);
6304 return (NULL);
6309 * Takes a semicolon separated list of selector elements and breaks up
6310 * into a keyword-value pair. semicolon and equal characters are
6311 * replaced with NULL's. On success, selector is updated to point to the
6312 * terminating NULL character terminating the keyword-value pair, and the
6313 * function returns DEVFSADM_SUCCESS. If there is a syntax error,
6314 * devfs_spec is not modified and function returns DEVFSADM_FAILURE.
6316 static int
6317 parse_selector(char **selector, char **key, char **val)
6319 char *equal;
6320 char *semi_colon;
6322 *key = *selector;
6324 if ((equal = strchr(*key, '=')) != NULL) {
6325 *equal = '\0';
6326 } else {
6327 err_print(MISSING_EQUAL, devlinktab_line, devlinktab_file);
6328 return (DEVFSADM_FAILURE);
6331 *val = ++equal;
6332 if ((semi_colon = strchr(equal, ';')) != NULL) {
6333 *semi_colon = '\0';
6334 *selector = semi_colon + 1;
6335 } else {
6336 *selector = equal + strlen(equal);
6338 return (DEVFSADM_SUCCESS);
6342 * link is either the second or third field of devlink.tab. Parse link
6343 * into a linked list of devlink structures and return ptr to list. Each
6344 * list element is either a constant string, or one of the following
6345 * escape sequences: \M, \A, \N, or \D. The first three escape sequences
6346 * take a numerical argument.
6348 static link_list_t *
6349 create_link_list(char *link)
6351 int x = 0;
6352 int error = FALSE;
6353 int counter_found = FALSE;
6354 link_list_t *head = NULL;
6355 link_list_t **ptr;
6356 link_list_t *link_list;
6357 char constant[MAX_DEVLINK_LINE];
6358 char *error_str;
6360 if (link == NULL) {
6361 return (NULL);
6364 while ((*link != '\0') && (error == FALSE)) {
6365 link_list = (link_list_t *)s_malloc(sizeof (link_list_t));
6366 link_list->next = NULL;
6368 while ((*link != '\0') && (*link != '\\')) {
6369 /* a non-escaped string */
6370 constant[x++] = *(link++);
6372 if (x != 0) {
6373 constant[x] = '\0';
6374 link_list->type = CONSTANT;
6375 link_list->constant = s_strdup(constant);
6376 x = 0;
6377 vprint(DEVLINK_MID, "CONSTANT FOUND %s\n", constant);
6378 } else {
6379 switch (*(++link)) {
6380 case 'M':
6381 link_list->type = MINOR;
6382 break;
6383 case 'A':
6384 link_list->type = ADDR;
6385 break;
6386 case 'N':
6387 if (counter_found == TRUE) {
6388 error = TRUE;
6389 error_str =
6390 "multiple counters not permitted";
6391 free(link_list);
6392 } else {
6393 counter_found = TRUE;
6394 link_list->type = COUNTER;
6396 break;
6397 case 'D':
6398 link_list->type = NAME;
6399 break;
6400 default:
6401 error = TRUE;
6402 free(link_list);
6403 error_str = "unrecognized escape sequence";
6404 break;
6406 if (*(link++) != 'D') {
6407 if (isdigit(*link) == FALSE) {
6408 error_str = "escape sequence must be "
6409 "followed by a digit\n";
6410 error = TRUE;
6411 free(link_list);
6412 } else {
6413 link_list->arg =
6414 (int)strtoul(link, &link, 10);
6415 vprint(DEVLINK_MID, "link_list->arg = "
6416 "%d\n", link_list->arg);
6420 /* append link_list struct to end of list */
6421 if (error == FALSE) {
6422 for (ptr = &head; *ptr != NULL; ptr = &((*ptr)->next))
6424 *ptr = link_list;
6428 if (error == FALSE) {
6429 return (head);
6430 } else {
6431 err_print(CONFIG_INCORRECT, devlinktab_line, devlinktab_file,
6432 error_str);
6433 free_link_list(head);
6434 return (NULL);
6439 * Called for each minor node devfsadm processes; for each minor node,
6440 * look for matches in the devlinktab_list list which was created on
6441 * startup read_devlinktab_file(). If there is a match, call build_links()
6442 * to build a logical devlink and a possible extra devlink.
6444 static int
6445 process_devlink_compat(di_minor_t minor, di_node_t node)
6447 int link_built = FALSE;
6448 devlinktab_list_t *entry;
6449 char *nodetype;
6450 char *dev_path;
6452 if (devlinks_debug == TRUE) {
6453 nodetype = di_minor_nodetype(minor);
6454 assert(nodetype != NULL);
6455 if ((dev_path = di_devfs_path(node)) != NULL) {
6456 vprint(INFO_MID, "'%s' entry: %s:%s\n",
6457 nodetype, dev_path,
6458 di_minor_name(minor) ? di_minor_name(minor) : "");
6459 di_devfs_path_free(dev_path);
6465 /* don't process devlink.tab if devfsadm invoked with -c <class> */
6466 if (num_classes > 0) {
6467 return (FALSE);
6470 for (entry = devlinktab_list; entry != NULL; entry = entry->next) {
6471 if (devlink_matches(entry, minor, node) == DEVFSADM_SUCCESS) {
6472 link_built = TRUE;
6473 (void) build_links(entry, minor, node);
6476 return (link_built);
6480 * For a given devlink.tab devlinktab_list entry, see if the selector
6481 * field matches this minor node. If it does, return DEVFSADM_SUCCESS,
6482 * otherwise DEVFSADM_FAILURE.
6484 static int
6485 devlink_matches(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6487 selector_list_t *selector = entry->selector;
6488 char *addr;
6489 char *minor_name;
6490 char *node_type;
6492 for (; selector != NULL; selector = selector->next) {
6493 switch (selector->key) {
6494 case NAME:
6495 if (strcmp(di_node_name(node), selector->val) != 0) {
6496 return (DEVFSADM_FAILURE);
6498 break;
6499 case TYPE:
6500 node_type = di_minor_nodetype(minor);
6501 assert(node_type != NULL);
6502 if (strcmp(node_type, selector->val) != 0) {
6503 return (DEVFSADM_FAILURE);
6505 break;
6506 case ADDR:
6507 if ((addr = di_bus_addr(node)) == NULL) {
6508 return (DEVFSADM_FAILURE);
6510 if (selector->arg == 0) {
6511 if (strcmp(addr, selector->val) != 0) {
6512 return (DEVFSADM_FAILURE);
6514 } else {
6515 if (compare_field(addr, selector->val,
6516 selector->arg) == DEVFSADM_FAILURE) {
6517 return (DEVFSADM_FAILURE);
6520 break;
6521 case MINOR:
6522 if ((minor_name = di_minor_name(minor)) == NULL) {
6523 return (DEVFSADM_FAILURE);
6525 if (selector->arg == 0) {
6526 if (strcmp(minor_name, selector->val) != 0) {
6527 return (DEVFSADM_FAILURE);
6529 } else {
6530 if (compare_field(minor_name, selector->val,
6531 selector->arg) == DEVFSADM_FAILURE) {
6532 return (DEVFSADM_FAILURE);
6535 break;
6536 default:
6537 return (DEVFSADM_FAILURE);
6541 return (DEVFSADM_SUCCESS);
6545 * For the given minor node and devlinktab_list entry from devlink.tab,
6546 * build a logical dev link and a possible extra devlink.
6547 * Return DEVFSADM_SUCCESS if link is created, otherwise DEVFSADM_FAILURE.
6549 static int
6550 build_links(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6552 char secondary_link[PATH_MAX + 1];
6553 char primary_link[PATH_MAX + 1];
6554 char contents[PATH_MAX + 1];
6555 char *dev_path;
6557 if ((dev_path = di_devfs_path(node)) == NULL) {
6558 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
6559 devfsadm_exit(1);
6560 /*NOTREACHED*/
6562 (void) strcpy(contents, dev_path);
6563 di_devfs_path_free(dev_path);
6565 (void) strcat(contents, ":");
6566 (void) strcat(contents, di_minor_name(minor));
6568 if (construct_devlink(primary_link, entry->p_link, contents,
6569 minor, node, entry->p_link_pattern) == DEVFSADM_FAILURE) {
6570 return (DEVFSADM_FAILURE);
6572 (void) devfsadm_mklink(primary_link, node, minor, 0);
6574 if (entry->s_link == NULL) {
6575 return (DEVFSADM_SUCCESS);
6578 if (construct_devlink(secondary_link, entry->s_link, primary_link,
6579 minor, node, entry->s_link_pattern) == DEVFSADM_FAILURE) {
6580 return (DEVFSADM_FAILURE);
6583 (void) devfsadm_secondary_link(secondary_link, primary_link, 0);
6585 return (DEVFSADM_SUCCESS);
6589 * The counter rule for devlink.tab entries is implemented via
6590 * devfsadm_enumerate_int_start(). One of the arguments to this function
6591 * is a path, where each path component is treated as a regular expression.
6592 * For devlink.tab entries, this path regular expression is derived from
6593 * the devlink spec. get_anchored_re() accepts path regular expressions derived
6594 * from devlink.tab entries and inserts the anchors '^' and '$' at the beginning
6595 * and end respectively of each path component. This is done to prevent
6596 * false matches. For example, without anchors, "a/([0-9]+)" will match "ab/c9"
6597 * and incorrect links will be generated.
6599 static int
6600 get_anchored_re(char *link, char *anchored_re, char *pattern)
6602 if (*link == '/' || *link == '\0') {
6603 err_print(INVALID_DEVLINK_SPEC, pattern);
6604 return (DEVFSADM_FAILURE);
6607 *anchored_re++ = '^';
6608 for (; *link != '\0'; ) {
6609 if (*link == '/') {
6610 while (*link == '/')
6611 link++;
6612 *anchored_re++ = '$';
6613 *anchored_re++ = '/';
6614 if (*link != '\0') {
6615 *anchored_re++ = '^';
6617 } else {
6618 *anchored_re++ = *link++;
6619 if (*link == '\0') {
6620 *anchored_re++ = '$';
6624 *anchored_re = '\0';
6626 return (DEVFSADM_SUCCESS);
6629 static int
6630 construct_devlink(char *link, link_list_t *link_build, char *contents,
6631 di_minor_t minor, di_node_t node, char *pattern)
6633 int counter_offset = -1;
6634 devfsadm_enumerate_t rules[1] = {NULL};
6635 char templink[PATH_MAX + 1];
6636 char *buff;
6637 char start[10];
6638 char *node_path;
6639 char anchored_re[PATH_MAX + 1];
6641 link[0] = '\0';
6643 for (; link_build != NULL; link_build = link_build->next) {
6644 switch (link_build->type) {
6645 case NAME:
6646 (void) strcat(link, di_node_name(node));
6647 break;
6648 case CONSTANT:
6649 (void) strcat(link, link_build->constant);
6650 break;
6651 case ADDR:
6652 if (component_cat(link, di_bus_addr(node),
6653 link_build->arg) == DEVFSADM_FAILURE) {
6654 node_path = di_devfs_path(node);
6655 err_print(CANNOT_BE_USED, pattern, node_path,
6656 di_minor_name(minor));
6657 di_devfs_path_free(node_path);
6658 return (DEVFSADM_FAILURE);
6660 break;
6661 case MINOR:
6662 if (component_cat(link, di_minor_name(minor),
6663 link_build->arg) == DEVFSADM_FAILURE) {
6664 node_path = di_devfs_path(node);
6665 err_print(CANNOT_BE_USED, pattern, node_path,
6666 di_minor_name(minor));
6667 di_devfs_path_free(node_path);
6668 return (DEVFSADM_FAILURE);
6670 break;
6671 case COUNTER:
6672 counter_offset = strlen(link);
6673 (void) strcat(link, "([0-9]+)");
6674 (void) sprintf(start, "%d", link_build->arg);
6675 break;
6676 default:
6677 return (DEVFSADM_FAILURE);
6681 if (counter_offset != -1) {
6683 * copy anything appended after "([0-9]+)" into
6684 * templink
6687 (void) strcpy(templink,
6688 &link[counter_offset + strlen("([0-9]+)")]);
6689 if (get_anchored_re(link, anchored_re, pattern)
6690 != DEVFSADM_SUCCESS) {
6691 return (DEVFSADM_FAILURE);
6693 rules[0].re = anchored_re;
6694 rules[0].subexp = 1;
6695 rules[0].flags = MATCH_ALL;
6696 if (devfsadm_enumerate_int_start(contents, 0, &buff,
6697 rules, 1, start) == DEVFSADM_FAILURE) {
6698 return (DEVFSADM_FAILURE);
6700 (void) strcpy(&link[counter_offset], buff);
6701 free(buff);
6702 (void) strcat(link, templink);
6703 vprint(DEVLINK_MID, "COUNTER is %s\n", link);
6705 return (DEVFSADM_SUCCESS);
6709 * Compares "field" number of the comma separated list "full_name" with
6710 * field_item. Returns DEVFSADM_SUCCESS for match,
6711 * DEVFSADM_FAILURE for no match.
6713 static int
6714 compare_field(char *full_name, char *field_item, int field)
6716 --field;
6717 while ((*full_name != '\0') && (field != 0)) {
6718 if (*(full_name++) == ',') {
6719 field--;
6723 if (field != 0) {
6724 return (DEVFSADM_FAILURE);
6727 while ((*full_name != '\0') && (*field_item != '\0') &&
6728 (*full_name != ',')) {
6729 if (*(full_name++) != *(field_item++)) {
6730 return (DEVFSADM_FAILURE);
6734 if (*field_item != '\0') {
6735 return (DEVFSADM_FAILURE);
6738 if ((*full_name == '\0') || (*full_name == ','))
6739 return (DEVFSADM_SUCCESS);
6741 return (DEVFSADM_FAILURE);
6745 * strcat() field # "field" of comma separated list "name" to "link".
6746 * Field 0 is the entire name.
6747 * Return DEVFSADM_SUCCESS or DEVFSADM_FAILURE.
6749 static int
6750 component_cat(char *link, char *name, int field)
6753 if (name == NULL) {
6754 return (DEVFSADM_FAILURE);
6757 if (field == 0) {
6758 (void) strcat(link, name);
6759 return (DEVFSADM_SUCCESS);
6762 while (*link != '\0') {
6763 link++;
6766 --field;
6767 while ((*name != '\0') && (field != 0)) {
6768 if (*(name++) == ',') {
6769 --field;
6773 if (field != 0) {
6774 return (DEVFSADM_FAILURE);
6777 while ((*name != '\0') && (*name != ',')) {
6778 *(link++) = *(name++);
6781 *link = '\0';
6782 return (DEVFSADM_SUCCESS);
6785 static void
6786 free_selector_list(selector_list_t *head)
6788 selector_list_t *temp;
6790 while (head != NULL) {
6791 temp = head;
6792 head = head->next;
6793 free(temp->val);
6794 free(temp);
6798 static void
6799 free_link_list(link_list_t *head)
6801 link_list_t *temp;
6803 while (head != NULL) {
6804 temp = head;
6805 head = head->next;
6806 if (temp->type == CONSTANT) {
6807 free(temp->constant);
6809 free(temp);
6814 * Prints only if level matches one of the debug levels
6815 * given on command line. INFO_MID is always printed.
6817 * See devfsadm.h for a listing of globally defined levels and
6818 * meanings. Modules should prefix the level with their
6819 * module name to prevent collisions.
6821 /*PRINTFLIKE2*/
6822 void
6823 devfsadm_print(char *msgid, char *message, ...)
6825 va_list ap;
6826 static int newline = TRUE;
6827 int x;
6829 if (msgid != NULL) {
6830 for (x = 0; x < num_verbose; x++) {
6831 if (strcmp(verbose[x], msgid) == 0) {
6832 break;
6834 if (strcmp(verbose[x], ALL_MID) == 0) {
6835 break;
6838 if (x == num_verbose) {
6839 return;
6843 va_start(ap, message);
6845 if (msgid == NULL) {
6846 if (logflag == TRUE) {
6847 (void) vsyslog(LOG_NOTICE, message, ap);
6848 } else {
6849 (void) vfprintf(stdout, message, ap);
6852 } else {
6853 if (logflag == TRUE) {
6854 (void) syslog(LOG_DEBUG, "%s[%ld]: %s: ",
6855 prog, getpid(), msgid);
6856 (void) vsyslog(LOG_DEBUG, message, ap);
6857 } else {
6858 if (newline == TRUE) {
6859 (void) fprintf(stdout, "%s[%ld]: %s: ",
6860 prog, getpid(), msgid);
6862 (void) vfprintf(stdout, message, ap);
6866 if (message[strlen(message) - 1] == '\n') {
6867 newline = TRUE;
6868 } else {
6869 newline = FALSE;
6871 va_end(ap);
6875 * print error messages to the terminal or to syslog
6877 /*PRINTFLIKE1*/
6878 void
6879 devfsadm_errprint(char *message, ...)
6881 va_list ap;
6883 va_start(ap, message);
6885 if (logflag == TRUE) {
6886 (void) vsyslog(LOG_ERR, message, ap);
6887 } else {
6888 (void) fprintf(stderr, "%s: ", prog);
6889 (void) vfprintf(stderr, message, ap);
6891 va_end(ap);
6895 * return noupdate state (-s)
6898 devfsadm_noupdate(void)
6900 return (file_mods == TRUE ? DEVFSADM_TRUE : DEVFSADM_FALSE);
6904 * return current root update path (-r)
6906 const char *
6907 devfsadm_root_path(void)
6909 if (root_dir[0] == '\0') {
6910 return ("/");
6911 } else {
6912 return ((const char *)root_dir);
6916 void
6917 devfsadm_free_dev_names(char **dev_names, int len)
6919 int i;
6921 for (i = 0; i < len; i++)
6922 free(dev_names[i]);
6923 free(dev_names);
6927 * Return all devlinks corresponding to phys_path as an array of strings.
6928 * The number of entries in the array is returned through lenp.
6929 * devfsadm_free_dev_names() is used to free the returned array.
6930 * NULL is returned on failure or when there are no matching devlinks.
6932 * re is an extended regular expression in regex(5) format used to further
6933 * match devlinks pointing to phys_path; it may be NULL to match all
6935 char **
6936 devfsadm_lookup_dev_names(char *phys_path, char *re, int *lenp)
6938 struct devlink_cb_arg cb_arg;
6939 char **dev_names = NULL;
6940 int i;
6942 *lenp = 0;
6943 cb_arg.count = 0;
6944 cb_arg.rv = 0;
6945 (void) di_devlink_cache_walk(devlink_cache, re, phys_path,
6946 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
6948 if (cb_arg.rv == -1 || cb_arg.count <= 0)
6949 return (NULL);
6951 dev_names = s_malloc(cb_arg.count * sizeof (char *));
6952 if (dev_names == NULL)
6953 goto out;
6955 for (i = 0; i < cb_arg.count; i++) {
6956 dev_names[i] = s_strdup(cb_arg.dev_names[i]);
6957 if (dev_names[i] == NULL) {
6958 devfsadm_free_dev_names(dev_names, i);
6959 dev_names = NULL;
6960 goto out;
6963 *lenp = cb_arg.count;
6965 out:
6966 free_dev_names(&cb_arg);
6967 return (dev_names);
6970 /* common exit function which ensures releasing locks */
6971 static void
6972 devfsadm_exit(int status)
6974 if (DEVFSADM_DEBUG_ON) {
6975 vprint(INFO_MID, "exit status = %d\n", status);
6978 exit_dev_lock(1);
6979 exit_daemon_lock(1);
6981 if (logflag == TRUE) {
6982 closelog();
6985 exit(status);
6986 /*NOTREACHED*/
6990 * set root_dir, devices_dir, dev_dir using optarg.
6992 static void
6993 set_root_devices_dev_dir(char *dir)
6995 size_t len;
6997 root_dir = s_strdup(dir);
6998 len = strlen(dir) + strlen(DEVICES) + 1;
6999 devices_dir = s_malloc(len);
7000 (void) snprintf(devices_dir, len, "%s%s", root_dir, DEVICES);
7001 len = strlen(root_dir) + strlen(DEV) + 1;
7002 dev_dir = s_malloc(len);
7003 (void) snprintf(dev_dir, len, "%s%s", root_dir, DEV);
7007 * Removes quotes.
7009 static char *
7010 dequote(char *src)
7012 char *dst;
7013 int len;
7015 len = strlen(src);
7016 dst = s_malloc(len + 1);
7017 if (src[0] == '\"' && src[len - 1] == '\"') {
7018 len -= 2;
7019 (void) strncpy(dst, &src[1], len);
7020 dst[len] = '\0';
7021 } else {
7022 (void) strcpy(dst, src);
7024 return (dst);
7028 * For a given physical device pathname and spectype, return the
7029 * ownership and permissions attributes by looking in data from
7030 * /etc/minor_perm. If currently in installation mode, check for
7031 * possible major number translations from the miniroot to the installed
7032 * root's name_to_major table. Note that there can be multiple matches,
7033 * but the last match takes effect. pts seems to rely on this
7034 * implementation behavior.
7036 static void
7037 getattr(char *phy_path, char *aminor, int spectype, dev_t dev, mode_t *mode,
7038 uid_t *uid, gid_t *gid)
7040 char devname[PATH_MAX + 1];
7041 char *node_name;
7042 char *minor_name;
7043 int match = FALSE;
7044 int is_clone;
7045 int mp_drvname_matches_node_name;
7046 int mp_drvname_matches_minor_name;
7047 int mp_drvname_is_clone;
7048 int mp_drvname_matches_drvname;
7049 struct mperm *mp;
7050 major_t major_no;
7051 char driver[PATH_MAX + 1];
7054 * Get the driver name based on the major number since the name
7055 * in /devices may be generic. Could be running with more major
7056 * numbers than are in /etc/name_to_major, so get it from the kernel
7058 major_no = major(dev);
7060 if (modctl(MODGETNAME, driver, sizeof (driver), &major_no) != 0) {
7061 /* return default values */
7062 goto use_defaults;
7065 (void) strcpy(devname, phy_path);
7067 node_name = strrchr(devname, '/'); /* node name is the last */
7068 /* component */
7069 if (node_name == NULL) {
7070 err_print(NO_NODE, devname);
7071 goto use_defaults;
7074 minor_name = strchr(++node_name, '@'); /* see if it has address part */
7076 if (minor_name != NULL) {
7077 *minor_name++ = '\0';
7078 } else {
7079 minor_name = node_name;
7082 minor_name = strchr(minor_name, ':'); /* look for minor name */
7084 if (minor_name == NULL) {
7085 err_print(NO_MINOR, devname);
7086 goto use_defaults;
7088 *minor_name++ = '\0';
7091 * mp->mp_drvname = device name from minor_perm
7092 * mp->mp_minorname = minor part of device name from
7093 * minor_perm
7094 * drvname = name of driver for this device
7097 is_clone = (strcmp(node_name, "clone") == 0 ? TRUE : FALSE);
7098 for (mp = minor_perms; mp != NULL; mp = mp->mp_next) {
7099 mp_drvname_matches_node_name =
7100 (strcmp(mp->mp_drvname, node_name) == 0 ? TRUE : FALSE);
7101 mp_drvname_matches_minor_name =
7102 (strcmp(mp->mp_drvname, minor_name) == 0 ? TRUE:FALSE);
7103 mp_drvname_is_clone =
7104 (strcmp(mp->mp_drvname, "clone") == 0 ? TRUE : FALSE);
7105 mp_drvname_matches_drvname =
7106 (strcmp(mp->mp_drvname, driver) == 0 ? TRUE : FALSE);
7109 * If one of the following cases is true, then we try to change
7110 * the permissions if a "shell global pattern match" of
7111 * mp_>mp_minorname matches minor_name.
7113 * 1. mp->mp_drvname matches driver.
7115 * OR
7117 * 2. mp->mp_drvname matches node_name and this
7118 * name is an alias of the driver name
7120 * OR
7122 * 3. /devices entry is the clone device and either
7123 * minor_perm entry is the clone device or matches
7124 * the minor part of the clone device.
7127 if ((mp_drvname_matches_drvname == TRUE)||
7128 ((mp_drvname_matches_node_name == TRUE) &&
7129 (alias(driver, node_name) == TRUE)) ||
7130 ((is_clone == TRUE) &&
7131 ((mp_drvname_is_clone == TRUE) ||
7132 (mp_drvname_matches_minor_name == TRUE)))) {
7134 * Check that the minor part of the
7135 * device name from the minor_perm
7136 * entry matches and if so, set the
7137 * permissions.
7139 * Under real devfs, clone minor name is changed
7140 * to match the driver name, but minor_perm may
7141 * not match. We reconcile it here.
7143 if (aminor != NULL)
7144 minor_name = aminor;
7146 if (gmatch(minor_name, mp->mp_minorname) != 0) {
7147 *uid = mp->mp_uid;
7148 *gid = mp->mp_gid;
7149 *mode = spectype | mp->mp_mode;
7150 match = TRUE;
7155 if (match == TRUE) {
7156 return;
7159 use_defaults:
7160 /* not found in minor_perm, so just use default values */
7161 *uid = root_uid;
7162 *gid = sys_gid;
7163 *mode = (spectype | 0600);
7167 * Called by devfs_read_minor_perm() to report errors
7168 * key is:
7169 * line number: ignoring line number error
7170 * errno: open/close errors
7171 * size: alloc errors
7173 static void
7174 minorperm_err_cb(minorperm_err_t mp_err, int key)
7176 switch (mp_err) {
7177 case MP_FOPEN_ERR:
7178 err_print(FOPEN_FAILED, MINOR_PERM_FILE, strerror(key));
7179 break;
7180 case MP_FCLOSE_ERR:
7181 err_print(FCLOSE_FAILED, MINOR_PERM_FILE, strerror(key));
7182 break;
7183 case MP_IGNORING_LINE_ERR:
7184 err_print(IGNORING_LINE_IN, key, MINOR_PERM_FILE);
7185 break;
7186 case MP_ALLOC_ERR:
7187 err_print(MALLOC_FAILED, key);
7188 break;
7189 case MP_NVLIST_ERR:
7190 err_print(NVLIST_ERROR, MINOR_PERM_FILE, strerror(key));
7191 break;
7192 case MP_CANT_FIND_USER_ERR:
7193 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
7194 break;
7195 case MP_CANT_FIND_GROUP_ERR:
7196 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
7197 break;
7201 static void
7202 read_minor_perm_file(void)
7204 static int cached = FALSE;
7205 static struct stat cached_sb;
7206 struct stat current_sb;
7208 (void) stat(MINOR_PERM_FILE, &current_sb);
7210 /* If already cached, check to see if it is still valid */
7211 if (cached == TRUE) {
7213 if (current_sb.st_mtime == cached_sb.st_mtime) {
7214 vprint(FILES_MID, "%s cache valid\n", MINOR_PERM_FILE);
7215 return;
7217 devfs_free_minor_perm(minor_perms);
7218 minor_perms = NULL;
7219 } else {
7220 cached = TRUE;
7223 (void) stat(MINOR_PERM_FILE, &cached_sb);
7225 vprint(FILES_MID, "loading binding file: %s\n", MINOR_PERM_FILE);
7227 minor_perms = devfs_read_minor_perm(minorperm_err_cb);
7230 static void
7231 load_minor_perm_file(void)
7233 read_minor_perm_file();
7234 if (devfs_load_minor_perm(minor_perms, minorperm_err_cb) != 0)
7235 err_print(gettext("minor_perm load failed\n"));
7238 static char *
7239 convert_to_re(char *dev)
7241 char *p, *l, *out;
7242 int i;
7244 out = s_malloc(PATH_MAX);
7246 for (l = p = dev, i = 0; (*p != '\0') && (i < (PATH_MAX - 1));
7247 ++p, i++) {
7248 if ((*p == '*') && ((l != p) && (*l == '/'))) {
7249 out[i++] = '.';
7250 out[i] = '+';
7251 } else {
7252 out[i] = *p;
7254 l = p;
7256 out[i] = '\0';
7257 p = (char *)s_malloc(strlen(out) + 1);
7258 (void) strlcpy(p, out, strlen(out) + 1);
7259 free(out);
7261 vprint(FILES_MID, "converted %s -> %s\n", dev, p);
7263 return (p);
7266 static void
7267 read_logindevperm_file(void)
7269 static int cached = FALSE;
7270 static struct stat cached_sb;
7271 struct stat current_sb;
7272 struct login_dev *ldev;
7273 FILE *fp;
7274 char line[MAX_LDEV_LINE];
7275 int ln, perm, rv;
7276 char *cp, *console, *dlist, *dev;
7277 char *lasts, *devlasts, *permstr, *drv;
7278 struct driver_list *list, *next;
7280 /* Read logindevperm only when enabled */
7281 if (login_dev_enable != TRUE)
7282 return;
7284 if (cached == TRUE) {
7285 if (stat(LDEV_FILE, &current_sb) == 0 &&
7286 current_sb.st_mtime == cached_sb.st_mtime) {
7287 vprint(FILES_MID, "%s cache valid\n", LDEV_FILE);
7288 return;
7290 vprint(FILES_MID, "invalidating %s cache\n", LDEV_FILE);
7291 while (login_dev_cache != NULL) {
7293 ldev = login_dev_cache;
7294 login_dev_cache = ldev->ldev_next;
7295 free(ldev->ldev_console);
7296 free(ldev->ldev_device);
7297 regfree(&ldev->ldev_device_regex);
7298 list = ldev->ldev_driver_list;
7299 while (list) {
7300 next = list->next;
7301 free(list);
7302 list = next;
7304 free(ldev);
7306 } else {
7307 cached = TRUE;
7310 assert(login_dev_cache == NULL);
7312 if (stat(LDEV_FILE, &cached_sb) != 0) {
7313 cached = FALSE;
7314 return;
7317 vprint(FILES_MID, "loading file: %s\n", LDEV_FILE);
7319 if ((fp = fopen(LDEV_FILE, "r")) == NULL) {
7320 /* Not fatal to devfsadm */
7321 cached = FALSE;
7322 err_print(FOPEN_FAILED, LDEV_FILE, strerror(errno));
7323 return;
7326 ln = 0;
7327 while (fgets(line, MAX_LDEV_LINE, fp) != NULL) {
7328 ln++;
7330 /* Remove comments */
7331 if ((cp = strchr(line, '#')) != NULL)
7332 *cp = '\0';
7334 if ((console = strtok_r(line, LDEV_DELIMS, &lasts)) == NULL)
7335 continue; /* Blank line */
7337 if ((permstr = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7338 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7339 continue; /* Malformed line */
7343 * permstr is string in octal format. Convert to int
7345 cp = NULL;
7346 errno = 0;
7347 perm = strtol(permstr, &cp, 8);
7348 if (errno || perm < 0 || perm > 0777 || *cp != '\0') {
7349 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7350 continue;
7353 if ((dlist = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7354 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7355 continue;
7358 dev = strtok_r(dlist, LDEV_DEV_DELIM, &devlasts);
7359 while (dev) {
7361 ldev = (struct login_dev *)s_zalloc(
7362 sizeof (struct login_dev));
7363 ldev->ldev_console = s_strdup(console);
7364 ldev->ldev_perms = perm;
7367 * the logical device name may contain '*' which
7368 * we convert to a regular expression
7370 ldev->ldev_device = convert_to_re(dev);
7371 if (ldev->ldev_device &&
7372 (rv = regcomp(&ldev->ldev_device_regex,
7373 ldev->ldev_device, REG_EXTENDED))) {
7374 bzero(&ldev->ldev_device_regex,
7375 sizeof (ldev->ldev_device_regex));
7376 err_print(REGCOMP_FAILED,
7377 ldev->ldev_device, rv);
7379 ldev->ldev_next = login_dev_cache;
7380 login_dev_cache = ldev;
7381 dev = strtok_r(NULL, LDEV_DEV_DELIM, &devlasts);
7384 drv = strtok_r(NULL, LDEV_DRVLIST_DELIMS, &lasts);
7385 if (drv) {
7386 if (strcmp(drv, LDEV_DRVLIST_NAME) == 0) {
7388 drv = strtok_r(NULL, LDEV_DRV_DELIMS, &lasts);
7390 while (drv) {
7391 vprint(FILES_MID,
7392 "logindevperm driver=%s\n", drv);
7395 * create a linked list of driver
7396 * names
7398 list = (struct driver_list *)
7399 s_zalloc(
7400 sizeof (struct driver_list));
7401 (void) strlcpy(list->driver_name, drv,
7402 sizeof (list->driver_name));
7403 list->next = ldev->ldev_driver_list;
7404 ldev->ldev_driver_list = list;
7405 drv = strtok_r(NULL, LDEV_DRV_DELIMS,
7406 &lasts);
7411 (void) fclose(fp);
7415 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
7417 * Returns DEVFSADM_SUCCESS if token found, DEVFSADM_FAILURE otherwise.
7419 static int
7420 getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar)
7422 char *cp;
7423 char *cp1;
7424 char *tokenp;
7426 cp = next;
7427 while (*cp == ' ' || *cp == '\t') {
7428 cp++; /* skip leading spaces */
7430 tokenp = cp; /* start of token */
7431 while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' &&
7432 *cp != ':' && *cp != '=' && *cp != '&' &&
7433 *cp != '|' && *cp != ';') {
7434 cp++; /* point to next character */
7437 * If terminating character is a space or tab, look ahead to see if
7438 * there's another terminator that's not a space or a tab.
7439 * (This code handles trailing spaces.)
7441 if (*cp == ' ' || *cp == '\t') {
7442 cp1 = cp;
7443 while (*++cp1 == ' ' || *cp1 == '\t')
7445 if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' ||
7446 *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') {
7447 *cp = '\0'; /* terminate token */
7448 cp = cp1;
7451 if (tchar != NULL) {
7452 *tchar = *cp; /* save terminating character */
7453 if (*tchar == '\0') {
7454 *tchar = '\n';
7457 *cp++ = '\0'; /* terminate token, point to next */
7458 *nextp = cp; /* set pointer to next character */
7459 if (cp - tokenp - 1 == 0) {
7460 return (DEVFSADM_FAILURE);
7462 *tokenpp = tokenp;
7463 return (DEVFSADM_SUCCESS);
7467 * read or reread the driver aliases file
7469 static void
7470 read_driver_aliases_file(void)
7473 driver_alias_t *save;
7474 driver_alias_t *lst_tail;
7475 driver_alias_t *ap;
7476 static int cached = FALSE;
7477 FILE *afd;
7478 char line[256];
7479 char *cp;
7480 char *p;
7481 char t;
7482 int ln = 0;
7483 static struct stat cached_sb;
7484 struct stat current_sb;
7486 (void) stat(ALIASFILE, &current_sb);
7488 /* If already cached, check to see if it is still valid */
7489 if (cached == TRUE) {
7491 if (current_sb.st_mtime == cached_sb.st_mtime) {
7492 vprint(FILES_MID, "%s cache valid\n", ALIASFILE);
7493 return;
7496 vprint(FILES_MID, "invalidating %s cache\n", ALIASFILE);
7497 while (driver_aliases != NULL) {
7498 free(driver_aliases->alias_name);
7499 free(driver_aliases->driver_name);
7500 save = driver_aliases;
7501 driver_aliases = driver_aliases->next;
7502 free(save);
7504 } else {
7505 cached = TRUE;
7508 (void) stat(ALIASFILE, &cached_sb);
7510 vprint(FILES_MID, "loading binding file: %s\n", ALIASFILE);
7512 if ((afd = fopen(ALIASFILE, "r")) == NULL) {
7513 err_print(FOPEN_FAILED, ALIASFILE, strerror(errno));
7514 devfsadm_exit(1);
7515 /*NOTREACHED*/
7518 while (fgets(line, sizeof (line), afd) != NULL) {
7519 ln++;
7520 /* cut off comments starting with '#' */
7521 if ((cp = strchr(line, '#')) != NULL)
7522 *cp = '\0';
7523 /* ignore comment or blank lines */
7524 if (is_blank(line))
7525 continue;
7526 cp = line;
7527 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7528 err_print(IGNORING_LINE_IN, ln, ALIASFILE);
7529 continue;
7531 if (t == '\n' || t == '\0') {
7532 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7533 continue;
7535 ap = (struct driver_alias *)
7536 s_zalloc(sizeof (struct driver_alias));
7537 ap->driver_name = s_strdup(p);
7538 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7539 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7540 free(ap->driver_name);
7541 free(ap);
7542 continue;
7544 if (*p == '"') {
7545 if (p[strlen(p) - 1] == '"') {
7546 p[strlen(p) - 1] = '\0';
7547 p++;
7550 ap->alias_name = s_strdup(p);
7551 if (driver_aliases == NULL) {
7552 driver_aliases = ap;
7553 lst_tail = ap;
7554 } else {
7555 lst_tail->next = ap;
7556 lst_tail = ap;
7559 if (fclose(afd) == EOF) {
7560 err_print(FCLOSE_FAILED, ALIASFILE, strerror(errno));
7565 * return TRUE if alias_name is an alias for driver_name, otherwise
7566 * return FALSE.
7568 static int
7569 alias(char *driver_name, char *alias_name)
7571 driver_alias_t *alias;
7574 * check for a match
7576 for (alias = driver_aliases; alias != NULL; alias = alias->next) {
7577 if ((strcmp(alias->driver_name, driver_name) == 0) &&
7578 (strcmp(alias->alias_name, alias_name) == 0)) {
7579 return (TRUE);
7582 return (FALSE);
7586 * convenience functions
7588 static int
7589 s_stat(const char *path, struct stat *sbufp)
7591 int rv;
7592 retry:
7593 if ((rv = stat(path, sbufp)) == -1) {
7594 if (errno == EINTR)
7595 goto retry;
7597 return (rv);
7600 static void *
7601 s_malloc(const size_t size)
7603 void *rp;
7605 rp = malloc(size);
7606 if (rp == NULL) {
7607 err_print(MALLOC_FAILED, size);
7608 devfsadm_exit(1);
7609 /*NOTREACHED*/
7611 return (rp);
7615 * convenience functions
7617 static void *
7618 s_realloc(void *ptr, const size_t size)
7620 ptr = realloc(ptr, size);
7621 if (ptr == NULL) {
7622 err_print(REALLOC_FAILED, size);
7623 devfsadm_exit(1);
7624 /*NOTREACHED*/
7626 return (ptr);
7629 static void *
7630 s_zalloc(const size_t size)
7632 void *rp;
7634 rp = calloc(1, size);
7635 if (rp == NULL) {
7636 err_print(CALLOC_FAILED, size);
7637 devfsadm_exit(1);
7638 /*NOTREACHED*/
7640 return (rp);
7643 char *
7644 s_strdup(const char *ptr)
7646 void *rp;
7648 rp = strdup(ptr);
7649 if (rp == NULL) {
7650 err_print(STRDUP_FAILED, ptr);
7651 devfsadm_exit(1);
7652 /*NOTREACHED*/
7654 return (rp);
7657 static void
7658 s_closedir(DIR *dirp)
7660 retry:
7661 if (closedir(dirp) != 0) {
7662 if (errno == EINTR)
7663 goto retry;
7664 err_print(CLOSEDIR_FAILED, strerror(errno));
7668 static void
7669 s_mkdirp(const char *path, const mode_t mode)
7671 vprint(CHATTY_MID, "mkdirp(%s, 0x%lx)\n", path, mode);
7672 if (mkdirp(path, mode) == -1) {
7673 if (errno != EEXIST) {
7674 err_print(MKDIR_FAILED, path, mode, strerror(errno));
7679 static void
7680 s_unlink(const char *file)
7682 retry:
7683 if (unlink(file) == -1) {
7684 if (errno == EINTR || errno == EAGAIN)
7685 goto retry;
7686 if (errno != ENOENT) {
7687 err_print(UNLINK_FAILED, file, strerror(errno));
7692 static void
7693 add_verbose_id(char *mid)
7695 num_verbose++;
7696 verbose = s_realloc(verbose, num_verbose * sizeof (char *));
7697 verbose[num_verbose - 1] = mid;
7701 * returns DEVFSADM_TRUE if contents is a minor node in /devices.
7702 * If mn_root is not NULL, mn_root is set to:
7703 * if contents is a /dev node, mn_root = contents
7704 * OR
7705 * if contents is a /devices node, mn_root set to the '/'
7706 * following /devices.
7708 static int
7709 is_minor_node(char *contents, char **mn_root)
7711 char *ptr;
7712 char device_prefix[100];
7714 (void) snprintf(device_prefix, sizeof (device_prefix), "../devices/");
7716 if ((ptr = strstr(contents, device_prefix)) != NULL) {
7717 if (mn_root != NULL) {
7718 /* mn_root should point to the / following /devices */
7719 *mn_root = ptr += strlen(device_prefix) - 1;
7721 return (DEVFSADM_TRUE);
7724 (void) snprintf(device_prefix, sizeof (device_prefix), "/devices/");
7726 if (strncmp(contents, device_prefix, strlen(device_prefix)) == 0) {
7727 if (mn_root != NULL) {
7728 /* mn_root should point to the / following /devices */
7729 *mn_root = contents + strlen(device_prefix) - 1;
7731 return (DEVFSADM_TRUE);
7734 if (mn_root != NULL) {
7735 *mn_root = contents;
7737 return (DEVFSADM_FALSE);
7741 * Add the specified property to nvl.
7742 * Returns:
7743 * 0 successfully added
7744 * -1 an error occurred
7745 * 1 could not add the property for reasons not due to errors.
7747 static int
7748 add_property(nvlist_t *nvl, di_prop_t prop)
7750 char *name;
7751 char *attr_name;
7752 int n, len;
7753 int32_t *int32p;
7754 int64_t *int64p;
7755 char *str;
7756 char **strarray;
7757 uchar_t *bytep;
7758 int rv = 0;
7759 int i;
7761 if ((name = di_prop_name(prop)) == NULL)
7762 return (-1);
7764 len = sizeof (DEV_PROP_PREFIX) + strlen(name);
7765 if ((attr_name = malloc(len)) == NULL)
7766 return (-1);
7768 (void) strlcpy(attr_name, DEV_PROP_PREFIX, len);
7769 (void) strlcat(attr_name, name, len);
7771 switch (di_prop_type(prop)) {
7772 case DI_PROP_TYPE_BOOLEAN:
7773 if (nvlist_add_boolean(nvl, attr_name) != 0)
7774 goto out;
7775 break;
7777 case DI_PROP_TYPE_INT:
7778 if ((n = di_prop_ints(prop, &int32p)) < 1)
7779 goto out;
7781 if (n <= (PROP_LEN_LIMIT / sizeof (int32_t))) {
7782 if (nvlist_add_int32_array(nvl, attr_name, int32p,
7783 n) != 0)
7784 goto out;
7785 } else
7786 rv = 1;
7787 break;
7789 case DI_PROP_TYPE_INT64:
7790 if ((n = di_prop_int64(prop, &int64p)) < 1)
7791 goto out;
7793 if (n <= (PROP_LEN_LIMIT / sizeof (int64_t))) {
7794 if (nvlist_add_int64_array(nvl, attr_name, int64p,
7795 n) != 0)
7796 goto out;
7797 } else
7798 rv = 1;
7799 break;
7801 case DI_PROP_TYPE_BYTE:
7802 case DI_PROP_TYPE_UNKNOWN:
7803 if ((n = di_prop_bytes(prop, &bytep)) < 1)
7804 goto out;
7806 if (n <= PROP_LEN_LIMIT) {
7807 if (nvlist_add_byte_array(nvl, attr_name, bytep, n)
7808 != 0)
7809 goto out;
7810 } else
7811 rv = 1;
7812 break;
7814 case DI_PROP_TYPE_STRING:
7815 if ((n = di_prop_strings(prop, &str)) < 1)
7816 goto out;
7818 if ((strarray = malloc(n * sizeof (char *))) == NULL)
7819 goto out;
7821 len = 0;
7822 for (i = 0; i < n; i++) {
7823 strarray[i] = str + len;
7824 len += strlen(strarray[i]) + 1;
7827 if (len <= PROP_LEN_LIMIT) {
7828 if (nvlist_add_string_array(nvl, attr_name, strarray,
7829 n) != 0) {
7830 free(strarray);
7831 goto out;
7833 } else
7834 rv = 1;
7835 free(strarray);
7836 break;
7838 default:
7839 rv = 1;
7840 break;
7843 free(attr_name);
7844 return (rv);
7846 out:
7847 free(attr_name);
7848 return (-1);
7851 static void
7852 free_dev_names(struct devlink_cb_arg *x)
7854 int i;
7856 for (i = 0; i < x->count; i++) {
7857 free(x->dev_names[i]);
7858 free(x->link_contents[i]);
7862 /* callback function for di_devlink_cache_walk */
7863 static int
7864 devlink_cb(di_devlink_t dl, void *arg)
7866 struct devlink_cb_arg *x = (struct devlink_cb_arg *)arg;
7867 const char *path;
7868 const char *content;
7870 if ((path = di_devlink_path(dl)) == NULL ||
7871 (content = di_devlink_content(dl)) == NULL ||
7872 (x->dev_names[x->count] = s_strdup(path)) == NULL)
7873 goto out;
7875 if ((x->link_contents[x->count] = s_strdup(content)) == NULL) {
7876 free(x->dev_names[x->count]);
7877 goto out;
7880 x->count++;
7881 if (x->count >= MAX_DEV_NAME_COUNT)
7882 return (DI_WALK_TERMINATE);
7884 return (DI_WALK_CONTINUE);
7886 out:
7887 x->rv = -1;
7888 free_dev_names(x);
7889 return (DI_WALK_TERMINATE);
7893 * Lookup dev name corresponding to the phys_path.
7894 * phys_path is path to a node or minor node.
7895 * Returns:
7896 * 0 with *dev_name set to the dev name
7897 * Lookup succeeded and dev_name found
7898 * 0 with *dev_name set to NULL
7899 * Lookup encountered no errors but dev name not found
7900 * -1
7901 * Lookup failed
7903 static int
7904 lookup_dev_name(char *phys_path, char **dev_name)
7906 struct devlink_cb_arg cb_arg;
7908 *dev_name = NULL;
7910 cb_arg.count = 0;
7911 cb_arg.rv = 0;
7912 (void) di_devlink_cache_walk(devlink_cache, NULL, phys_path,
7913 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7915 if (cb_arg.rv == -1)
7916 return (-1);
7918 if (cb_arg.count > 0) {
7919 *dev_name = s_strdup(cb_arg.dev_names[0]);
7920 free_dev_names(&cb_arg);
7921 if (*dev_name == NULL)
7922 return (-1);
7925 return (0);
7928 static char *
7929 lookup_disk_dev_name(char *node_path)
7931 struct devlink_cb_arg cb_arg;
7932 char *dev_name = NULL;
7933 int i;
7934 char *p;
7935 int len1, len2;
7937 #define DEV_RDSK "/dev/rdsk/"
7938 #define DISK_RAW_MINOR ",raw"
7940 cb_arg.count = 0;
7941 cb_arg.rv = 0;
7942 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
7943 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7945 if (cb_arg.rv == -1 || cb_arg.count == 0)
7946 return (NULL);
7948 /* first try lookup based on /dev/rdsk name */
7949 for (i = 0; i < cb_arg.count; i++) {
7950 if (strncmp(cb_arg.dev_names[i], DEV_RDSK,
7951 sizeof (DEV_RDSK) - 1) == 0) {
7952 dev_name = s_strdup(cb_arg.dev_names[i]);
7953 break;
7957 if (dev_name == NULL) {
7958 /* now try lookup based on a minor name ending with ",raw" */
7959 len1 = sizeof (DISK_RAW_MINOR) - 1;
7960 for (i = 0; i < cb_arg.count; i++) {
7961 len2 = strlen(cb_arg.link_contents[i]);
7962 if (len2 >= len1 &&
7963 strcmp(cb_arg.link_contents[i] + len2 - len1,
7964 DISK_RAW_MINOR) == 0) {
7965 dev_name = s_strdup(cb_arg.dev_names[i]);
7966 break;
7971 free_dev_names(&cb_arg);
7973 if (dev_name == NULL)
7974 return (NULL);
7975 if (strlen(dev_name) == 0) {
7976 free(dev_name);
7977 return (NULL);
7980 /* if the name contains slice or partition number strip it */
7981 p = dev_name + strlen(dev_name) - 1;
7982 if (isdigit(*p)) {
7983 while (p != dev_name && isdigit(*p))
7984 p--;
7985 if (*p == 's' || *p == 'p')
7986 *p = '\0';
7989 return (dev_name);
7992 static char *
7993 lookup_lofi_dev_name(char *node_path, char *minor)
7995 struct devlink_cb_arg cb_arg;
7996 char *dev_name = NULL;
7997 int i;
7998 int len1, len2;
8000 cb_arg.count = 0;
8001 cb_arg.rv = 0;
8002 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8003 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8005 if (cb_arg.rv == -1 || cb_arg.count == 0)
8006 return (NULL);
8008 /* lookup based on a minor name ending with ",raw" */
8009 len1 = strlen(minor);
8010 for (i = 0; i < cb_arg.count; i++) {
8011 len2 = strlen(cb_arg.link_contents[i]);
8012 if (len2 >= len1 &&
8013 strcmp(cb_arg.link_contents[i] + len2 - len1,
8014 minor) == 0) {
8015 dev_name = s_strdup(cb_arg.dev_names[i]);
8016 break;
8020 free_dev_names(&cb_arg);
8022 if (dev_name == NULL)
8023 return (NULL);
8024 if (strlen(dev_name) == 0) {
8025 free(dev_name);
8026 return (NULL);
8029 return (dev_name);
8032 static char *
8033 lookup_network_dev_name(char *node_path, char *driver_name)
8035 char *dev_name = NULL;
8036 char phys_path[MAXPATHLEN];
8038 if (lookup_dev_name(node_path, &dev_name) == -1)
8039 return (NULL);
8041 if (dev_name == NULL) {
8042 /* dlpi style-2 only interface */
8043 (void) snprintf(phys_path, sizeof (phys_path),
8044 "/pseudo/clone@0:%s", driver_name);
8045 if (lookup_dev_name(phys_path, &dev_name) == -1 ||
8046 dev_name == NULL)
8047 return (NULL);
8050 return (dev_name);
8053 static char *
8054 lookup_printer_dev_name(char *node_path)
8056 struct devlink_cb_arg cb_arg;
8057 char *dev_name = NULL;
8058 int i;
8060 #define DEV_PRINTERS "/dev/printers/"
8062 cb_arg.count = 0;
8063 cb_arg.rv = 0;
8064 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8065 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8067 if (cb_arg.rv == -1 || cb_arg.count == 0)
8068 return (NULL);
8070 /* first try lookup based on /dev/printers name */
8071 for (i = 0; i < cb_arg.count; i++) {
8072 if (strncmp(cb_arg.dev_names[i], DEV_PRINTERS,
8073 sizeof (DEV_PRINTERS) - 1) == 0) {
8074 dev_name = s_strdup(cb_arg.dev_names[i]);
8075 break;
8079 /* fallback to the first name */
8080 if ((dev_name == NULL) && (cb_arg.count > 0))
8081 dev_name = s_strdup(cb_arg.dev_names[0]);
8083 free_dev_names(&cb_arg);
8085 return (dev_name);
8089 * Build an nvlist containing all attributes for devfs events.
8090 * Returns nvlist pointer on success, NULL on failure.
8092 static nvlist_t *
8093 build_event_attributes(char *class, char *subclass, char *node_path,
8094 di_node_t node, char *driver_name, int instance, char *minor)
8096 nvlist_t *nvl;
8097 int err = 0;
8098 di_prop_t prop;
8099 int count;
8100 char *prop_name;
8101 int x;
8102 char *dev_name = NULL;
8103 int dev_name_lookup_err = 0;
8105 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) {
8106 nvl = NULL;
8107 goto out;
8110 if ((err = nvlist_add_int32(nvl, EV_VERSION, EV_V1)) != 0)
8111 goto out;
8113 if ((err = nvlist_add_string(nvl, DEV_PHYS_PATH, node_path)) != 0)
8114 goto out;
8116 if (strcmp(class, EC_DEV_ADD) != 0 &&
8117 strcmp(class, EC_DEV_REMOVE) != 0)
8118 return (nvl);
8120 if (driver_name == NULL || instance == -1)
8121 goto out;
8123 if (strcmp(subclass, ESC_DISK) == 0) {
8125 * While we're removing labeled lofi device, we will receive
8126 * event for every registered minor device and lastly,
8127 * an event with minor set to NULL, as in following example:
8128 * class: EC_dev_remove subclass: disk
8129 * node_path: /pseudo/lofi@1 driver: lofi minor: u,raw
8130 * class: EC_dev_remove subclass: disk
8131 * node_path: /pseudo/lofi@1 driver: lofi minor: NULL
8133 * When we receive this last event with minor set to NULL,
8134 * all lofi minor devices are already removed and the call to
8135 * lookup_disk_dev_name() would result in error.
8136 * To prevent name lookup error messages for this case, we
8137 * need to filter out that last event.
8139 if (strcmp(class, EC_DEV_REMOVE) == 0 &&
8140 strcmp(driver_name, "lofi") == 0 && minor == NULL) {
8141 nvlist_free(nvl);
8142 return (NULL);
8144 if ((dev_name = lookup_disk_dev_name(node_path)) == NULL) {
8145 dev_name_lookup_err = 1;
8146 goto out;
8148 } else if (strcmp(subclass, ESC_NETWORK) == 0) {
8149 if ((dev_name = lookup_network_dev_name(node_path, driver_name))
8150 == NULL) {
8151 dev_name_lookup_err = 1;
8152 goto out;
8154 } else if (strcmp(subclass, ESC_PRINTER) == 0) {
8155 if ((dev_name = lookup_printer_dev_name(node_path)) == NULL) {
8156 dev_name_lookup_err = 1;
8157 goto out;
8159 } else if (strcmp(subclass, ESC_LOFI) == 0) {
8161 * The raw minor node is created or removed after the block
8162 * node. Lofi devfs events are dependent on this behavior.
8163 * Generate the sysevent only for the raw minor node.
8165 * If the lofi mapping is created, we will receive the following
8166 * event: class: EC_dev_add subclass: lofi minor: NULL
8168 * As in case of EC_dev_add, the minor is NULL pointer,
8169 * to get device links created, we will need to provide the
8170 * type of minor node for lookup_lofi_dev_name()
8172 * If the lofi device is unmapped, we will receive following
8173 * events:
8174 * class: EC_dev_remove subclass: lofi minor: disk
8175 * class: EC_dev_remove subclass: lofi minor: disk,raw
8176 * class: EC_dev_remove subclass: lofi minor: NULL
8179 if (strcmp(class, EC_DEV_ADD) == 0 && minor == NULL)
8180 minor = "disk,raw";
8182 if (minor == NULL || strstr(minor, "raw") == NULL) {
8183 nvlist_free(nvl);
8184 return (NULL);
8186 if ((dev_name = lookup_lofi_dev_name(node_path, minor)) ==
8187 NULL) {
8188 dev_name_lookup_err = 1;
8189 goto out;
8193 if (dev_name) {
8194 if ((err = nvlist_add_string(nvl, DEV_NAME, dev_name)) != 0)
8195 goto out;
8196 free(dev_name);
8197 dev_name = NULL;
8200 if ((err = nvlist_add_string(nvl, DEV_DRIVER_NAME, driver_name)) != 0)
8201 goto out;
8203 if ((err = nvlist_add_int32(nvl, DEV_INSTANCE, instance)) != 0)
8204 goto out;
8206 if (strcmp(class, EC_DEV_ADD) == 0) {
8207 /* add properties */
8208 count = 0;
8209 for (prop = di_prop_next(node, DI_PROP_NIL);
8210 prop != DI_PROP_NIL && count < MAX_PROP_COUNT;
8211 prop = di_prop_next(node, prop)) {
8213 if (di_prop_devt(prop) != DDI_DEV_T_NONE)
8214 continue;
8216 if ((x = add_property(nvl, prop)) == 0)
8217 count++;
8218 else if (x == -1) {
8219 if ((prop_name = di_prop_name(prop)) == NULL)
8220 prop_name = "";
8221 err_print(PROP_ADD_FAILED, prop_name);
8222 goto out;
8227 return (nvl);
8229 out:
8230 nvlist_free(nvl);
8232 free(dev_name);
8234 if (dev_name_lookup_err) {
8236 * If a lofi mount fails, the /devices node may well have
8237 * disappeared by the time we run, so let's not complain.
8239 if (strcmp(subclass, ESC_LOFI) != 0)
8240 err_print(DEV_NAME_LOOKUP_FAILED, node_path);
8241 } else {
8242 err_print(BUILD_EVENT_ATTR_FAILED, (err) ? strerror(err) : "");
8244 return (NULL);
8247 static void
8248 log_event(char *class, char *subclass, nvlist_t *nvl)
8250 sysevent_id_t eid;
8252 if (sysevent_post_event(class, subclass, "SUNW", DEVFSADMD,
8253 nvl, &eid) != 0) {
8254 err_print(LOG_EVENT_FAILED, strerror(errno));
8259 * When devfsadmd needs to generate sysevents, they are queued for later
8260 * delivery this allows them to be delivered after the devlinks db cache has
8261 * been flushed guaranteeing that applications consuming these events have
8262 * access to an accurate devlinks db. The queue is a FIFO, sysevents to be
8263 * inserted in the front of the queue and consumed off the back.
8265 static void
8266 enqueue_sysevent(char *class, char *subclass, nvlist_t *nvl)
8268 syseventq_t *tmp;
8270 if ((tmp = s_zalloc(sizeof (*tmp))) == NULL)
8271 return;
8273 tmp->class = s_strdup(class);
8274 tmp->subclass = s_strdup(subclass);
8275 tmp->nvl = nvl;
8277 (void) mutex_lock(&syseventq_mutex);
8278 if (syseventq_front != NULL)
8279 syseventq_front->next = tmp;
8280 else
8281 syseventq_back = tmp;
8282 syseventq_front = tmp;
8283 (void) mutex_unlock(&syseventq_mutex);
8286 static void
8287 process_syseventq()
8289 (void) mutex_lock(&syseventq_mutex);
8290 while (syseventq_back != NULL) {
8291 syseventq_t *tmp = syseventq_back;
8293 vprint(CHATTY_MID, "sending queued event: %s, %s\n",
8294 tmp->class, tmp->subclass);
8296 log_event(tmp->class, tmp->subclass, tmp->nvl);
8298 free(tmp->class);
8299 free(tmp->subclass);
8300 nvlist_free(tmp->nvl);
8301 syseventq_back = syseventq_back->next;
8302 if (syseventq_back == NULL)
8303 syseventq_front = NULL;
8304 free(tmp);
8306 (void) mutex_unlock(&syseventq_mutex);
8309 static void
8310 build_and_enq_event(char *class, char *subclass, char *node_path,
8311 di_node_t node, char *minor)
8313 nvlist_t *nvl;
8315 vprint(CHATTY_MID, "build_and_enq_event(%s, %s, %s, 0x%8.8x)\n",
8316 class, subclass, node_path, (int)node);
8318 if (node != DI_NODE_NIL)
8319 nvl = build_event_attributes(class, subclass, node_path, node,
8320 di_driver_name(node), di_instance(node), minor);
8321 else
8322 nvl = build_event_attributes(class, subclass, node_path, node,
8323 NULL, -1, minor);
8325 if (nvl) {
8326 enqueue_sysevent(class, subclass, nvl);
8331 * is_blank() returns 1 (true) if a line specified is composed of
8332 * whitespace characters only. otherwise, it returns 0 (false).
8334 * Note. the argument (line) must be null-terminated.
8336 static int
8337 is_blank(char *line)
8339 for (/* nothing */; *line != '\0'; line++)
8340 if (!isspace(*line))
8341 return (0);
8342 return (1);
8346 * Functions to deal with the no-further-processing hash
8349 static void
8350 nfphash_create(void)
8352 assert(nfp_hash == NULL);
8353 nfp_hash = s_zalloc(NFP_HASH_SZ * sizeof (item_t *));
8356 static int
8357 nfphash_fcn(char *key)
8359 int i;
8360 uint64_t sum = 0;
8362 for (i = 0; key[i] != '\0'; i++) {
8363 sum += (uchar_t)key[i];
8366 return (sum % NFP_HASH_SZ);
8369 static item_t *
8370 nfphash_lookup(char *key)
8372 int index;
8373 item_t *ip;
8375 index = nfphash_fcn(key);
8377 assert(index >= 0);
8379 for (ip = nfp_hash[index]; ip; ip = ip->i_next) {
8380 if (strcmp(ip->i_key, key) == 0)
8381 return (ip);
8384 return (NULL);
8387 static void
8388 nfphash_insert(char *key)
8390 item_t *ip;
8391 int index;
8393 index = nfphash_fcn(key);
8395 assert(index >= 0);
8397 ip = s_zalloc(sizeof (item_t));
8398 ip->i_key = s_strdup(key);
8400 ip->i_next = nfp_hash[index];
8401 nfp_hash[index] = ip;
8404 static void
8405 nfphash_destroy(void)
8407 int i;
8408 item_t *ip;
8410 for (i = 0; i < NFP_HASH_SZ; i++) {
8411 /*LINTED*/
8412 while (ip = nfp_hash[i]) {
8413 nfp_hash[i] = ip->i_next;
8414 free(ip->i_key);
8415 free(ip);
8419 free(nfp_hash);
8420 nfp_hash = NULL;
8423 static int
8424 devname_kcall(int subcmd, void *args)
8426 int error = 0;
8428 switch (subcmd) {
8429 case MODDEVNAME_LOOKUPDOOR:
8430 error = modctl(MODDEVNAME, subcmd, (uintptr_t)args);
8431 if (error) {
8432 vprint(INFO_MID, "modctl(MODDEVNAME, "
8433 "MODDEVNAME_LOOKUPDOOR) failed - %s\n",
8434 strerror(errno));
8436 break;
8437 default:
8438 error = EINVAL;
8439 break;
8441 return (error);
8444 /* ARGSUSED */
8445 static void
8446 devname_lookup_handler(void *cookie, char *argp, size_t arg_size,
8447 door_desc_t *dp, uint_t n_desc)
8449 int32_t error = 0;
8450 door_cred_t dcred;
8451 struct dca_impl dci;
8452 uint8_t cmd;
8453 sdev_door_res_t res;
8454 sdev_door_arg_t *args;
8456 if (argp == NULL || arg_size == 0) {
8457 vprint(DEVNAME_MID, "devname_lookup_handler: argp wrong\n");
8458 error = DEVFSADM_RUN_INVALID;
8459 goto done;
8461 vprint(DEVNAME_MID, "devname_lookup_handler\n");
8463 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
8464 vprint(DEVNAME_MID, "devname_lookup_handler: cred wrong\n");
8465 error = DEVFSADM_RUN_EPERM;
8466 goto done;
8469 args = (sdev_door_arg_t *)argp;
8470 cmd = args->devfsadm_cmd;
8472 vprint(DEVNAME_MID, "devname_lookup_handler: cmd %d\n", cmd);
8473 switch (cmd) {
8474 case DEVFSADMD_RUN_ALL:
8476 * run "devfsadm"
8478 dci.dci_root = "/";
8479 dci.dci_minor = NULL;
8480 dci.dci_driver = NULL;
8481 dci.dci_error = 0;
8482 dci.dci_flags = 0;
8483 dci.dci_arg = NULL;
8485 lock_dev();
8486 update_drvconf((major_t)-1, 0);
8487 dci.dci_flags |= DCA_FLUSH_PATHINST;
8489 pre_and_post_cleanup(RM_PRE);
8490 devi_tree_walk(&dci, DI_CACHE_SNAPSHOT_FLAGS, NULL);
8491 error = (int32_t)dci.dci_error;
8492 if (!error) {
8493 pre_and_post_cleanup(RM_POST);
8494 update_database = TRUE;
8495 unlock_dev(SYNC_STATE);
8496 update_database = FALSE;
8497 } else {
8498 if (DEVFSADM_DEBUG_ON) {
8499 vprint(INFO_MID, "devname_lookup_handler: "
8500 "DEVFSADMD_RUN_ALL failed\n");
8503 unlock_dev(SYNC_STATE);
8505 break;
8506 default:
8507 /* log an error here? */
8508 error = DEVFSADM_RUN_NOTSUP;
8509 break;
8512 done:
8513 vprint(DEVNAME_MID, "devname_lookup_handler: error %d\n", error);
8514 res.devfsadm_error = error;
8515 (void) door_return((char *)&res, sizeof (struct sdev_door_res),
8516 NULL, 0);
8520 di_devlink_handle_t
8521 devfsadm_devlink_cache(void)
8523 return (devlink_cache);
8527 devfsadm_reserve_id_cache(devlink_re_t re_array[], enumerate_file_t *head)
8529 enumerate_file_t *entry;
8530 int nelem;
8531 int i;
8532 int subex;
8533 char *re;
8534 size_t size;
8535 regmatch_t *pmch;
8538 * Check the <RE, subexp> array passed in and compile it.
8540 for (i = 0; re_array[i].d_re; i++) {
8541 if (re_array[i].d_subexp == 0) {
8542 err_print("bad subexp value in RE: %s\n",
8543 re_array[i].d_re);
8544 goto bad_re;
8547 re = re_array[i].d_re;
8548 if (regcomp(&re_array[i].d_rcomp, re, REG_EXTENDED) != 0) {
8549 err_print("reg. exp. failed to compile: %s\n", re);
8550 goto bad_re;
8552 subex = re_array[i].d_subexp;
8553 nelem = subex + 1;
8554 re_array[i].d_pmatch = s_malloc(sizeof (regmatch_t) * nelem);
8557 entry = head ? head : enumerate_reserved;
8558 for (; entry; entry = entry->er_next) {
8559 if (entry->er_id) {
8560 vprint(RSBY_MID, "entry %s already has ID %s\n",
8561 entry->er_file, entry->er_id);
8562 continue;
8564 for (i = 0; re_array[i].d_re; i++) {
8565 subex = re_array[i].d_subexp;
8566 pmch = re_array[i].d_pmatch;
8567 if (regexec(&re_array[i].d_rcomp, entry->er_file,
8568 subex + 1, pmch, 0) != 0) {
8569 /* No match */
8570 continue;
8572 size = pmch[subex].rm_eo - pmch[subex].rm_so;
8573 entry->er_id = s_malloc(size + 1);
8574 (void) strncpy(entry->er_id,
8575 &entry->er_file[pmch[subex].rm_so], size);
8576 entry->er_id[size] = '\0';
8577 if (head) {
8578 vprint(RSBY_MID, "devlink(%s) matches RE(%s). "
8579 "ID is %s\n", entry->er_file,
8580 re_array[i].d_re, entry->er_id);
8581 } else {
8582 vprint(RSBY_MID, "rsrv entry(%s) matches "
8583 "RE(%s) ID is %s\n", entry->er_file,
8584 re_array[i].d_re, entry->er_id);
8586 break;
8590 for (i = 0; re_array[i].d_re; i++) {
8591 regfree(&re_array[i].d_rcomp);
8592 assert(re_array[i].d_pmatch);
8593 free(re_array[i].d_pmatch);
8596 entry = head ? head : enumerate_reserved;
8597 for (; entry; entry = entry->er_next) {
8598 if (entry->er_id == NULL)
8599 continue;
8600 if (head) {
8601 vprint(RSBY_MID, "devlink: %s\n", entry->er_file);
8602 vprint(RSBY_MID, "ID: %s\n", entry->er_id);
8603 } else {
8604 vprint(RSBY_MID, "reserve file entry: %s\n",
8605 entry->er_file);
8606 vprint(RSBY_MID, "reserve file id: %s\n",
8607 entry->er_id);
8611 return (DEVFSADM_SUCCESS);
8613 bad_re:
8614 for (i = i-1; i >= 0; i--) {
8615 regfree(&re_array[i].d_rcomp);
8616 assert(re_array[i].d_pmatch);
8617 free(re_array[i].d_pmatch);
8619 return (DEVFSADM_FAILURE);
8623 * Return 1 if we have reserved links.
8626 devfsadm_have_reserved()
8628 return (enumerate_reserved ? 1 : 0);
8632 * This functions errs on the side of caution. If there is any error
8633 * we assume that the devlink is *not* reserved
8636 devfsadm_is_reserved(devlink_re_t re_array[], char *devlink)
8638 int match;
8639 enumerate_file_t estruct = {NULL};
8640 enumerate_file_t *entry;
8642 match = 0;
8643 estruct.er_file = devlink;
8644 estruct.er_id = NULL;
8645 estruct.er_next = NULL;
8647 if (devfsadm_reserve_id_cache(re_array, &estruct) != DEVFSADM_SUCCESS) {
8648 err_print("devfsadm_is_reserved: devlink (%s) does not "
8649 "match RE\n", devlink);
8650 return (0);
8652 if (estruct.er_id == NULL) {
8653 err_print("devfsadm_is_reserved: ID derived from devlink %s "
8654 "is NULL\n", devlink);
8655 return (0);
8658 entry = enumerate_reserved;
8659 for (; entry; entry = entry->er_next) {
8660 if (entry->er_id == NULL)
8661 continue;
8662 if (strcmp(entry->er_id, estruct.er_id) != 0)
8663 continue;
8664 match = 1;
8665 vprint(RSBY_MID, "reserve file entry (%s) and devlink (%s) "
8666 "match\n", entry->er_file, devlink);
8667 break;
8670 free(estruct.er_id);
8671 return (match);