Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / cmd / devfsadm / devfsadm.c
blobfb8232979cf5f0fa34a3c67d0ba0606463af3817
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 <utime.h>
40 #include <sys/param.h>
41 #include <zone.h>
42 #include "devfsadm_impl.h"
44 /* create or remove nodes or links. unset with -n */
45 static int file_mods = TRUE;
47 /* cleanup mode. Set with -C */
48 static int cleanup = FALSE;
50 /* devlinks -d compatibility */
51 static int devlinks_debug = FALSE;
53 /* load a single driver only. set with -i */
54 static int single_drv = FALSE;
55 static char *driver = NULL;
57 /* attempt to load drivers or defer attach nodes */
58 static int load_attach_drv = TRUE;
60 /* reload all driver.conf files */
61 static int update_all_drivers = FALSE;
63 /* set if invoked via /usr/lib/devfsadm/devfsadmd */
64 static int daemon_mode = FALSE;
66 /* set if event_handler triggered */
67 int event_driven = FALSE;
69 /* output directed to syslog during daemon mode if set */
70 static int logflag = FALSE;
72 /* build links in /dev. -x to turn off */
73 static int build_dev = TRUE;
75 /* build nodes in /devices. -y to turn off */
76 static int build_devices = TRUE;
78 /* -z to turn off */
79 static int flush_path_to_inst_enable = TRUE;
81 /* variables used for path_to_inst flushing */
82 static int inst_count = 0;
83 static mutex_t count_lock;
84 static cond_t cv;
86 /* variables for minor_fini thread */
87 static mutex_t minor_fini_mutex;
88 static int minor_fini_canceled = TRUE;
89 static int minor_fini_delayed = FALSE;
90 static cond_t minor_fini_cv;
91 static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
93 /* single-threads /dev modification */
94 static sema_t dev_sema;
96 /* the program we were invoked as; ie argv[0] */
97 static char *prog;
99 /* pointers to create/remove link lists */
100 static create_list_t *create_head = NULL;
101 static remove_list_t *remove_head = NULL;
103 /* supports the class -c option */
104 static char **classes = NULL;
105 static int num_classes = 0;
107 /* used with verbose option -v or -V */
108 static int num_verbose = 0;
109 static char **verbose = NULL;
111 static struct mperm *minor_perms = NULL;
112 static driver_alias_t *driver_aliases = NULL;
114 /* set if -r alternate root given */
115 static char *root_dir = "";
117 /* /devices or <rootdir>/devices */
118 static char *devices_dir = DEVICES;
120 /* /dev or <rootdir>/dev */
121 static char *dev_dir = DEV;
123 /* /etc/dev or <rootdir>/etc/dev */
124 static char *etc_dev_dir = ETCDEV;
127 * writable root (for lock files and doors during install).
128 * This is also root dir for /dev attr dir during install.
130 static char *attr_root = NULL;
132 /* /etc/path_to_inst unless -p used */
133 static char *inst_file = INSTANCE_FILE;
135 /* /usr/lib/devfsadm/linkmods unless -l used */
136 static char *module_dirs = MODULE_DIRS;
138 /* default uid/gid used if /etc/minor_perm entry not found */
139 static uid_t root_uid;
140 static gid_t sys_gid;
142 /* /etc/devlink.tab unless devlinks -t used */
143 static char *devlinktab_file = NULL;
145 /* File and data structure to reserve enumerate IDs */
146 static char *enumerate_file = ENUMERATE_RESERVED;
147 static enumerate_file_t *enumerate_reserved = NULL;
149 /* set if /dev link is new. speeds up rm_stale_links */
150 static int linknew = TRUE;
152 /* variables for devlink.tab compat processing */
153 static devlinktab_list_t *devlinktab_list = NULL;
154 static unsigned int devlinktab_line = 0;
156 /* cache head for devfsadm_enumerate*() functions */
157 static numeral_set_t *head_numeral_set = NULL;
159 /* list list of devfsadm modules */
160 static module_t *module_head = NULL;
162 /* name_to_major list used in utility function */
163 static n2m_t *n2m_list = NULL;
165 /* cache of some links used for performance */
166 static linkhead_t *headlinkhead = NULL;
168 /* locking variables to prevent multiples writes to /dev */
169 static int hold_dev_lock = FALSE;
170 static int hold_daemon_lock = FALSE;
171 static int dev_lock_fd;
172 static int daemon_lock_fd;
173 static char dev_lockfile[PATH_MAX + 1];
174 static char daemon_lockfile[PATH_MAX + 1];
176 /* last devinfo node/minor processed. used for performance */
177 static di_node_t lnode;
178 static di_minor_t lminor;
179 static char lphy_path[PATH_MAX + 1] = {""};
181 /* Globals used by the link database */
182 static di_devlink_handle_t devlink_cache;
183 static int update_database = FALSE;
185 /* Globals used to set logindev perms */
186 static struct login_dev *login_dev_cache = NULL;
187 static int login_dev_enable = FALSE;
189 /* Global to use devinfo snapshot cache */
190 static int use_snapshot_cache = FALSE;
192 /* Global for no-further-processing hash */
193 static item_t **nfp_hash;
194 static mutex_t nfp_mutex = DEFAULTMUTEX;
197 * Directories not removed even when empty. They are packaged, or may
198 * be referred to from a non-global zone. The dirs must be listed in
199 * canonical form i.e. without leading "/dev/"
201 static char *sticky_dirs[] =
202 {"dsk", "rdsk", "term", "lofi", "rlofi", NULL};
204 /* Devname globals */
205 static int lookup_door_fd = -1;
206 static char *lookup_door_path;
208 static void load_dev_acl(void);
209 static void update_drvconf(major_t, int);
210 static void check_reconfig_state(void);
211 static int s_stat(const char *, struct stat *);
213 static int is_blank(char *);
215 /* sysevent queue related globals */
216 static mutex_t syseventq_mutex = DEFAULTMUTEX;
217 static syseventq_t *syseventq_front;
218 static syseventq_t *syseventq_back;
219 static void process_syseventq();
221 static di_node_t devi_root_node = DI_NODE_NIL;
224 main(int argc, char *argv[])
226 struct passwd *pw;
227 struct group *gp;
228 pid_t pid;
230 (void) setlocale(LC_ALL, "");
231 (void) textdomain(TEXT_DOMAIN);
233 if ((prog = strrchr(argv[0], '/')) == NULL) {
234 prog = argv[0];
235 } else {
236 prog++;
239 if (getuid() != 0) {
240 err_print(MUST_BE_ROOT);
241 devfsadm_exit(1);
242 /*NOTREACHED*/
245 if (getzoneid() != GLOBAL_ZONEID) {
246 err_print(MUST_BE_GLOBAL_ZONE);
247 devfsadm_exit(1);
251 * Close all files except stdin/stdout/stderr
253 closefrom(3);
255 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
256 root_uid = pw->pw_uid;
257 } else {
258 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
259 root_uid = (uid_t)0; /* assume 0 is root */
262 /* the default group is sys */
264 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
265 sys_gid = gp->gr_gid;
266 } else {
267 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
268 sys_gid = (gid_t)3; /* assume 3 is sys */
271 (void) umask(0);
273 parse_args(argc, argv);
275 (void) sema_init(&dev_sema, 1, USYNC_THREAD, NULL);
277 if (daemon_mode == TRUE) {
279 * Build /dev and /devices before daemonizing if
280 * reconfig booting and daemon invoked with alternate
281 * root. This is to support install.
283 if (getenv(RECONFIG_BOOT) != NULL && root_dir[0] != '\0') {
284 vprint(INFO_MID, CONFIGURING);
285 load_dev_acl();
286 update_drvconf((major_t)-1, 0);
287 process_devinfo_tree();
288 (void) modctl(MODSETMINIROOT);
292 * fork before detaching from tty in order to print error
293 * message if unable to acquire file lock. locks not preserved
294 * across forks. Even under debug we want to fork so that
295 * when executed at boot we don't hang.
297 if (fork() != 0) {
298 devfsadm_exit(0);
299 /*NOTREACHED*/
302 /* set directory to / so it coredumps there */
303 if (chdir("/") == -1) {
304 err_print(CHROOT_FAILED, strerror(errno));
307 /* only one daemon can run at a time */
308 if ((pid = enter_daemon_lock()) == getpid()) {
309 detachfromtty();
310 (void) cond_init(&cv, USYNC_THREAD, 0);
311 (void) mutex_init(&count_lock, USYNC_THREAD, 0);
312 if (thr_create(NULL, 0,
313 (void *(*)(void *))instance_flush_thread,
314 NULL, THR_DETACHED, NULL) != 0) {
315 err_print(CANT_CREATE_THREAD, "daemon",
316 strerror(errno));
317 devfsadm_exit(1);
318 /*NOTREACHED*/
321 /* start the minor_fini_thread */
322 (void) mutex_init(&minor_fini_mutex, USYNC_THREAD, 0);
323 (void) cond_init(&minor_fini_cv, USYNC_THREAD, 0);
324 if (thr_create(NULL, 0,
325 (void *(*)(void *))minor_fini_thread,
326 NULL, THR_DETACHED, NULL)) {
327 err_print(CANT_CREATE_THREAD, "minor_fini",
328 strerror(errno));
329 devfsadm_exit(1);
330 /*NOTREACHED*/
335 * logindevperms need only be set
336 * in daemon mode and when root dir is "/".
338 if (root_dir[0] == '\0')
339 login_dev_enable = TRUE;
340 daemon_update();
341 devfsadm_exit(0);
342 /*NOTREACHED*/
343 } else {
344 err_print(DAEMON_RUNNING, pid);
345 devfsadm_exit(1);
346 /*NOTREACHED*/
348 } else {
349 /* not a daemon, so just build /dev and /devices */
350 process_devinfo_tree();
352 return (0);
355 static void
356 update_drvconf(major_t major, int flags)
358 if (modctl(MODLOADDRVCONF, major, flags) != 0)
359 err_print(gettext("update_drvconf failed for major %d\n"),
360 major);
363 static void
364 load_dev_acl()
366 if (load_devpolicy() != 0)
367 err_print(gettext("device policy load failed\n"));
368 load_minor_perm_file();
372 * As devfsadm is run early in boot to provide the kernel with
373 * minor_perm info, we might as well check for reconfig at the
374 * same time to avoid running devfsadm twice. This gets invoked
375 * earlier than the env variable RECONFIG_BOOT is set up.
377 static void
378 check_reconfig_state()
380 struct stat sb;
382 if (s_stat("/reconfigure", &sb) == 0) {
383 (void) modctl(MODDEVNAME, MODDEVNAME_RECONFIG, 0);
387 static void
388 modctl_sysavail()
391 * Inform /dev that system is available, that
392 * implicit reconfig can now be performed.
394 (void) modctl(MODDEVNAME, MODDEVNAME_SYSAVAIL, 0);
397 static void
398 set_lock_root(void)
400 struct stat sb;
401 char *lock_root;
402 size_t len;
404 lock_root = attr_root ? attr_root : root_dir;
406 len = strlen(lock_root) + strlen(ETCDEV) + 1;
407 etc_dev_dir = s_malloc(len);
408 (void) snprintf(etc_dev_dir, len, "%s%s", lock_root, ETCDEV);
410 if (s_stat(etc_dev_dir, &sb) != 0) {
411 s_mkdirp(etc_dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
412 } else if (!S_ISDIR(sb.st_mode)) {
413 err_print(NOT_DIR, etc_dev_dir);
414 devfsadm_exit(1);
415 /*NOTREACHED*/
421 * Parse arguments for all 6 programs handled from devfsadm.
423 static void
424 parse_args(int argc, char *argv[])
426 char opt;
427 char get_linkcompat_opts = FALSE;
428 char *compat_class;
429 int num_aliases = 0;
430 int len;
431 int retval;
432 int config = TRUE;
433 int bind = FALSE;
434 int force_flag = FALSE;
435 struct aliases *ap = NULL;
436 struct aliases *a_head = NULL;
437 struct aliases *a_tail = NULL;
438 struct modconfig mc;
440 (void) bzero(&mc, sizeof (mc));
442 if (strcmp(prog, DISKS) == 0) {
443 compat_class = "disk";
444 get_linkcompat_opts = TRUE;
446 } else if (strcmp(prog, TAPES) == 0) {
447 compat_class = "tape";
448 get_linkcompat_opts = TRUE;
450 } else if (strcmp(prog, PORTS) == 0) {
451 compat_class = "port";
452 get_linkcompat_opts = TRUE;
454 } else if (strcmp(prog, AUDLINKS) == 0) {
455 compat_class = "audio";
456 get_linkcompat_opts = TRUE;
458 } else if (strcmp(prog, DEVLINKS) == 0) {
459 devlinktab_file = DEVLINKTAB_FILE;
461 build_devices = FALSE;
462 load_attach_drv = FALSE;
464 while ((opt = getopt(argc, argv, "dnr:st:vV:")) != EOF) {
465 switch (opt) {
466 case 'd':
467 file_mods = FALSE;
468 flush_path_to_inst_enable = FALSE;
469 devlinks_debug = TRUE;
470 break;
471 case 'n':
472 /* prevent driver loading and deferred attach */
473 load_attach_drv = FALSE;
474 break;
475 case 'r':
476 set_root_devices_dev_dir(optarg);
477 if (zone_pathcheck(root_dir) !=
478 DEVFSADM_SUCCESS)
479 devfsadm_exit(1);
480 /*NOTREACHED*/
481 break;
482 case 's':
484 * suppress. don't create/remove links/nodes
485 * useful with -v or -V
487 file_mods = FALSE;
488 flush_path_to_inst_enable = FALSE;
489 break;
490 case 't':
491 /* supply a non-default table file */
492 devlinktab_file = optarg;
493 break;
494 case 'v':
495 /* documented verbose flag */
496 add_verbose_id(VERBOSE_MID);
497 break;
498 case 'V':
499 /* undocumented for extra verbose levels */
500 add_verbose_id(optarg);
501 break;
502 default:
503 usage();
504 break;
508 if (optind < argc) {
509 usage();
512 } else if (strcmp(prog, DRVCONFIG) == 0) {
513 int update_only = 0;
514 build_dev = FALSE;
516 while ((opt =
517 getopt(argc, argv, "a:bc:dfi:m:np:R:r:suvV:x")) != EOF) {
518 switch (opt) {
519 case 'a':
520 ap = calloc(sizeof (struct aliases), 1);
521 ap->a_name = dequote(optarg);
522 len = strlen(ap->a_name) + 1;
523 if (len > MAXMODCONFNAME) {
524 err_print(ALIAS_TOO_LONG,
525 MAXMODCONFNAME, ap->a_name);
526 devfsadm_exit(1);
527 /*NOTREACHED*/
529 ap->a_len = len;
530 if (a_tail == NULL) {
531 a_head = ap;
532 } else {
533 a_tail->a_next = ap;
535 a_tail = ap;
536 num_aliases++;
537 bind = TRUE;
538 break;
539 case 'b':
540 bind = TRUE;
541 break;
542 case 'c':
543 (void) strcpy(mc.drvclass, optarg);
544 break;
545 case 'd':
547 * need to keep for compatibility, but
548 * do nothing.
550 break;
551 case 'f':
552 force_flag = TRUE;
553 break;
554 case 'i':
555 single_drv = TRUE;
556 (void) strcpy(mc.drvname, optarg);
557 driver = s_strdup(optarg);
558 break;
559 case 'm':
560 mc.major = atoi(optarg);
561 break;
562 case 'n':
563 /* prevent driver loading and deferred attach */
564 load_attach_drv = FALSE;
565 break;
566 case 'p':
567 /* specify alternate path_to_inst file */
568 inst_file = s_strdup(optarg);
569 break;
570 case 'R':
572 * Private flag for suninstall to populate
573 * device information on the installed root.
575 root_dir = s_strdup(optarg);
576 if (zone_pathcheck(root_dir) !=
577 DEVFSADM_SUCCESS)
578 devfsadm_exit(devfsadm_copy());
579 /*NOTREACHED*/
580 break;
581 case 'r':
582 devices_dir = s_strdup(optarg);
583 if (zone_pathcheck(devices_dir) !=
584 DEVFSADM_SUCCESS)
585 devfsadm_exit(1);
586 /*NOTREACHED*/
587 break;
588 case 's':
590 * suppress. don't create nodes
591 * useful with -v or -V
593 file_mods = FALSE;
594 flush_path_to_inst_enable = FALSE;
595 break;
596 case 'u':
598 * Invoked via update_drv(1m) to update
599 * the kernel's driver/alias binding
600 * when removing one or more aliases.
602 config = FALSE;
603 break;
604 case 'v':
605 /* documented verbose flag */
606 add_verbose_id(VERBOSE_MID);
607 break;
608 case 'V':
609 /* undocumented for extra verbose levels */
610 add_verbose_id(optarg);
611 break;
612 case 'x':
613 update_only = 1;
614 break;
615 default:
616 usage();
620 if (optind < argc) {
621 usage();
624 if (bind == TRUE) {
625 if ((mc.major == -1) || (mc.drvname[0] == '\0')) {
626 err_print(MAJOR_AND_B_FLAG);
627 devfsadm_exit(1);
628 /*NOTREACHED*/
630 mc.flags = 0;
631 if (force_flag)
632 mc.flags |= MOD_UNBIND_OVERRIDE;
633 if (update_only)
634 mc.flags |= MOD_ADDMAJBIND_UPDATE;
635 mc.num_aliases = num_aliases;
636 mc.ap = a_head;
637 retval = modctl((config == TRUE) ? MODADDMAJBIND :
638 MODREMDRVALIAS, NULL, (caddr_t)&mc);
639 if (retval < 0) {
640 err_print((config == TRUE) ? MODCTL_ADDMAJBIND :
641 MODCTL_REMMAJBIND);
643 devfsadm_exit(retval);
644 /*NOTREACHED*/
647 } else if ((strcmp(prog, DEVFSADM) == 0) ||
648 (strcmp(prog, DEVFSADMD) == 0)) {
649 char *zonename = NULL;
650 int init_drvconf = 0;
651 int init_perm = 0;
652 int public_mode = 0;
653 int init_sysavail = 0;
655 if (strcmp(prog, DEVFSADMD) == 0) {
656 daemon_mode = TRUE;
659 devlinktab_file = DEVLINKTAB_FILE;
661 while ((opt = getopt(argc, argv,
662 "a:Cc:Ii:l:np:PR:r:sSt:uvV:x:")) != EOF) {
663 if (opt == 'I' || opt == 'P' || opt == 'S') {
664 if (public_mode)
665 usage();
666 } else {
667 if (init_perm || init_drvconf || init_sysavail)
668 usage();
669 public_mode = 1;
671 switch (opt) {
672 case 'a':
673 attr_root = s_strdup(optarg);
674 break;
675 case 'C':
676 cleanup = TRUE;
677 break;
678 case 'c':
679 num_classes++;
680 classes = s_realloc(classes,
681 num_classes * sizeof (char *));
682 classes[num_classes - 1] = optarg;
683 break;
684 case 'I': /* update kernel driver.conf cache */
685 if (daemon_mode == TRUE)
686 usage();
687 init_drvconf = 1;
688 break;
689 case 'i':
690 single_drv = TRUE;
691 driver = s_strdup(optarg);
692 break;
693 case 'l':
694 /* specify an alternate module load path */
695 module_dirs = s_strdup(optarg);
696 break;
697 case 'n':
698 /* prevent driver loading and deferred attach */
699 load_attach_drv = FALSE;
700 break;
701 case 'p':
702 /* specify alternate path_to_inst file */
703 inst_file = s_strdup(optarg);
704 break;
705 case 'P':
706 if (daemon_mode == TRUE)
707 usage();
708 /* load minor_perm and device_policy */
709 init_perm = 1;
710 break;
711 case 'R':
713 * Private flag for suninstall to populate
714 * device information on the installed root.
716 root_dir = s_strdup(optarg);
717 devfsadm_exit(devfsadm_copy());
718 /*NOTREACHED*/
719 break;
720 case 'r':
721 set_root_devices_dev_dir(optarg);
722 break;
723 case 's':
725 * suppress. don't create/remove links/nodes
726 * useful with -v or -V
728 file_mods = FALSE;
729 flush_path_to_inst_enable = FALSE;
730 break;
731 case 'S':
732 if (daemon_mode == TRUE)
733 usage();
734 init_sysavail = 1;
735 break;
736 case 't':
737 devlinktab_file = optarg;
738 break;
739 case 'u': /* complete configuration after */
740 /* adding a driver update-only */
741 if (daemon_mode == TRUE)
742 usage();
743 update_all_drivers = TRUE;
744 break;
745 case 'v':
746 /* documented verbose flag */
747 add_verbose_id(VERBOSE_MID);
748 break;
749 case 'V':
750 /* undocumented: specify verbose lvl */
751 add_verbose_id(optarg);
752 break;
753 case 'x':
755 * x is the "private switch" option. The
756 * goal is to not suck up all the other
757 * option letters.
759 if (strcmp(optarg, "update_devlinksdb") == 0) {
760 update_database = TRUE;
761 } else if (strcmp(optarg, "no_dev") == 0) {
762 /* don't build /dev */
763 build_dev = FALSE;
764 } else if (strcmp(optarg, "no_devices") == 0) {
765 /* don't build /devices */
766 build_devices = FALSE;
767 } else if (strcmp(optarg, "no_p2i") == 0) {
768 /* don't flush path_to_inst */
769 flush_path_to_inst_enable = FALSE;
770 } else if (strcmp(optarg, "use_dicache") == 0) {
771 use_snapshot_cache = TRUE;
772 } else {
773 usage();
775 break;
776 default:
777 usage();
778 break;
781 if (optind < argc) {
782 usage();
786 * We're not in zone mode; Check to see if the rootpath
787 * collides with any zonepaths.
789 if (zonename == NULL) {
790 if (zone_pathcheck(root_dir) != DEVFSADM_SUCCESS)
791 devfsadm_exit(1);
792 /*NOTREACHED*/
795 if (init_drvconf || init_perm || init_sysavail) {
797 * Load minor perm before force-loading drivers
798 * so the correct permissions are picked up.
800 if (init_perm) {
801 check_reconfig_state();
802 load_dev_acl();
804 if (init_drvconf)
805 update_drvconf((major_t)-1, 0);
806 if (init_sysavail)
807 modctl_sysavail();
808 devfsadm_exit(0);
809 /*NOTREACHED*/
814 if (get_linkcompat_opts == TRUE) {
816 build_devices = FALSE;
817 load_attach_drv = FALSE;
818 num_classes++;
819 classes = s_realloc(classes, num_classes *
820 sizeof (char *));
821 classes[num_classes - 1] = compat_class;
823 while ((opt = getopt(argc, argv, "Cnr:svV:")) != EOF) {
824 switch (opt) {
825 case 'C':
826 cleanup = TRUE;
827 break;
828 case 'n':
829 /* prevent driver loading or deferred attach */
830 load_attach_drv = FALSE;
831 break;
832 case 'r':
833 set_root_devices_dev_dir(optarg);
834 if (zone_pathcheck(root_dir) !=
835 DEVFSADM_SUCCESS)
836 devfsadm_exit(1);
837 /*NOTREACHED*/
838 break;
839 case 's':
840 /* suppress. don't create/remove links/nodes */
841 /* useful with -v or -V */
842 file_mods = FALSE;
843 flush_path_to_inst_enable = FALSE;
844 break;
845 case 'v':
846 /* documented verbose flag */
847 add_verbose_id(VERBOSE_MID);
848 break;
849 case 'V':
850 /* undocumented for extra verbose levels */
851 add_verbose_id(optarg);
852 break;
853 default:
854 usage();
857 if (optind < argc) {
858 usage();
861 set_lock_root();
864 void
865 usage(void)
867 if (strcmp(prog, DEVLINKS) == 0) {
868 err_print(DEVLINKS_USAGE);
869 } else if (strcmp(prog, DRVCONFIG) == 0) {
870 err_print(DRVCONFIG_USAGE);
871 } else if ((strcmp(prog, DEVFSADM) == 0) ||
872 (strcmp(prog, DEVFSADMD) == 0)) {
873 err_print(DEVFSADM_USAGE);
874 } else {
875 err_print(COMPAT_LINK_USAGE);
878 devfsadm_exit(1);
879 /*NOTREACHED*/
882 static void
883 devi_tree_walk(struct dca_impl *dcip, int flags, char *ev_subclass)
885 char *msg, *name;
886 struct mlist mlist = {0};
887 di_node_t node;
889 vprint(CHATTY_MID, "devi_tree_walk: root=%s, minor=%s, driver=%s,"
890 " error=%d, flags=%u\n", dcip->dci_root,
891 dcip->dci_minor ? dcip->dci_minor : "<NULL>",
892 dcip->dci_driver ? dcip->dci_driver : "<NULL>", dcip->dci_error,
893 dcip->dci_flags);
895 assert(dcip->dci_root);
897 if (dcip->dci_flags & DCA_LOAD_DRV) {
898 node = di_init_driver(dcip->dci_driver, flags);
899 msg = DRIVER_FAILURE;
900 name = dcip->dci_driver;
901 } else {
902 node = di_init(dcip->dci_root, flags);
903 msg = DI_INIT_FAILED;
904 name = dcip->dci_root;
907 if (node == DI_NODE_NIL) {
908 dcip->dci_error = errno;
910 * Rapid hotplugging (commonly seen during USB testing),
911 * may remove a device before the create event for it
912 * has been processed. To prevent alarming users with
913 * a superfluous message, we suppress error messages
914 * for ENXIO and hotplug.
916 if (!(errno == ENXIO && (dcip->dci_flags & DCA_HOT_PLUG)))
917 err_print(msg, name, strerror(dcip->dci_error));
918 return;
921 if (dcip->dci_flags & DCA_FLUSH_PATHINST)
922 flush_path_to_inst();
924 dcip->dci_arg = &mlist;
925 devi_root_node = node; /* protected by lock_dev() */
927 vprint(CHATTY_MID, "walking device tree\n");
929 (void) di_walk_minor(node, NULL, DI_CHECK_ALIAS, dcip,
930 check_minor_type);
932 process_deferred_links(dcip, DCA_CREATE_LINK);
934 dcip->dci_arg = NULL;
937 * Finished creating devfs files and dev links.
938 * Log sysevent.
940 if (ev_subclass)
941 build_and_enq_event(EC_DEV_ADD, ev_subclass, dcip->dci_root,
942 node, dcip->dci_minor);
944 devi_root_node = DI_NODE_NIL; /* protected by lock_dev() */
945 di_fini(node);
948 static void
949 process_deferred_links(struct dca_impl *dcip, int flags)
951 struct mlist *dep;
952 struct minor *mp, *smp;
954 vprint(CHATTY_MID, "processing deferred links\n");
956 dep = dcip->dci_arg;
959 * The list head is not used during the deferred create phase
961 dcip->dci_arg = NULL;
963 assert(dep);
964 assert((dep->head == NULL) ^ (dep->tail != NULL));
965 assert(flags == DCA_FREE_LIST || flags == DCA_CREATE_LINK);
967 for (smp = NULL, mp = dep->head; mp; mp = mp->next) {
968 if (flags == DCA_CREATE_LINK)
969 (void) check_minor_type(mp->node, mp->minor, dcip);
970 free(smp);
971 smp = mp;
974 free(smp);
978 * Called in non-daemon mode to take a snap shot of the devinfo tree.
979 * Then it calls the appropriate functions to build /devices and /dev.
980 * It also flushes path_to_inst.
981 * Except in the devfsadm -i (single driver case), the flags used by devfsadm
982 * needs to match DI_CACHE_SNAPSHOT_FLAGS. That will make DINFOCACHE snapshot
983 * updated.
985 void
986 process_devinfo_tree()
988 uint_t flags;
989 struct dca_impl dci;
990 char name[MAXNAMELEN];
991 char *fcn = "process_devinfo_tree: ";
993 vprint(CHATTY_MID, "%senter\n", fcn);
995 dca_impl_init("/", NULL, &dci);
997 lock_dev();
1000 * Update kernel driver.conf cache when devfsadm/drvconfig
1001 * is invoked to build /devices and /dev.
1003 if (update_all_drivers || load_attach_drv) {
1004 update_drvconf((major_t)-1,
1005 update_all_drivers ? MOD_LOADDRVCONF_RECONF : 0);
1008 if (single_drv == TRUE) {
1010 * load a single driver, but walk the entire devinfo tree
1012 if (load_attach_drv == FALSE)
1013 err_print(DRV_LOAD_REQD);
1015 vprint(CHATTY_MID, "%sattaching driver (%s)\n", fcn, driver);
1017 dci.dci_flags |= DCA_LOAD_DRV;
1018 (void) snprintf(name, sizeof (name), "%s", driver);
1019 dci.dci_driver = name;
1020 flags = DINFOCPYALL | DINFOPATH;
1022 } else if (load_attach_drv == TRUE) {
1024 * Load and attach all drivers, then walk the entire tree.
1025 * If the cache flag is set, use DINFOCACHE to get cached
1026 * data.
1028 if (use_snapshot_cache == TRUE) {
1029 flags = DINFOCACHE;
1030 vprint(CHATTY_MID, "%susing snapshot cache\n", fcn);
1031 } else {
1032 vprint(CHATTY_MID, "%sattaching all drivers\n", fcn);
1033 flags = DI_CACHE_SNAPSHOT_FLAGS;
1034 if (cleanup) {
1036 * remove dangling entries from /etc/devices
1037 * files.
1039 flags |= DINFOCLEANUP;
1042 } else {
1044 * For devlinks, disks, ports, tapes and devfsadm -n,
1045 * just need to take a snapshot with active devices.
1047 vprint(CHATTY_MID, "%staking snapshot of active devices\n",
1048 fcn);
1049 flags = DINFOCPYALL;
1052 if (((load_attach_drv == TRUE) || (single_drv == TRUE)) &&
1053 (build_devices == TRUE)) {
1054 dci.dci_flags |= DCA_FLUSH_PATHINST;
1057 /* handle pre-cleanup operations desired by the modules. */
1058 pre_and_post_cleanup(RM_PRE);
1060 devi_tree_walk(&dci, flags, NULL);
1062 if (dci.dci_error) {
1063 devfsadm_exit(1);
1064 /*NOTREACHED*/
1067 /* handle post-cleanup operations desired by the modules. */
1068 pre_and_post_cleanup(RM_POST);
1070 unlock_dev(SYNC_STATE);
1073 /*ARGSUSED*/
1074 static void
1075 print_cache_signal(int signo)
1077 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1078 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1079 devfsadm_exit(1);
1080 /*NOTREACHED*/
1084 static void
1085 revoke_lookup_door(void)
1087 if (lookup_door_fd != -1) {
1088 if (door_revoke(lookup_door_fd) == -1) {
1089 err_print("door_revoke of %s failed - %s\n",
1090 lookup_door_path, strerror(errno));
1095 /*ARGSUSED*/
1096 static void
1097 catch_exit(int signo)
1099 revoke_lookup_door();
1103 * Register with eventd for messages. Create doors for synchronous
1104 * link creation.
1106 static void
1107 daemon_update(void)
1109 int fd;
1110 char *fcn = "daemon_update: ";
1111 char door_file[MAXPATHLEN];
1112 const char *subclass_list;
1113 sysevent_handle_t *sysevent_hp;
1114 vprint(CHATTY_MID, "%senter\n", fcn);
1116 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1117 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1118 devfsadm_exit(1);
1119 /*NOTREACHED*/
1121 if (signal(SIGTERM, catch_exit) == SIG_ERR) {
1122 err_print("signal SIGTERM failed: %s\n", strerror(errno));
1123 devfsadm_exit(1);
1124 /*NOTREACHED*/
1127 if (snprintf(door_file, sizeof (door_file),
1128 "%s%s", attr_root ? attr_root : root_dir, DEVFSADM_SERVICE_DOOR)
1129 >= sizeof (door_file)) {
1130 err_print("update_daemon failed to open sysevent service "
1131 "door\n");
1132 devfsadm_exit(1);
1133 /*NOTREACHED*/
1135 if ((sysevent_hp = sysevent_open_channel_alt(
1136 door_file)) == NULL) {
1137 err_print(CANT_CREATE_DOOR,
1138 door_file, strerror(errno));
1139 devfsadm_exit(1);
1140 /*NOTREACHED*/
1142 if (sysevent_bind_subscriber(sysevent_hp, event_handler) != 0) {
1143 err_print(CANT_CREATE_DOOR,
1144 door_file, strerror(errno));
1145 (void) sysevent_close_channel(sysevent_hp);
1146 devfsadm_exit(1);
1147 /*NOTREACHED*/
1149 subclass_list = EC_SUB_ALL;
1150 if (sysevent_register_event(sysevent_hp, EC_ALL, &subclass_list, 1)
1151 != 0) {
1152 err_print(CANT_CREATE_DOOR,
1153 door_file, strerror(errno));
1154 (void) sysevent_unbind_subscriber(sysevent_hp);
1155 (void) sysevent_close_channel(sysevent_hp);
1156 devfsadm_exit(1);
1157 /*NOTREACHED*/
1159 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1160 etc_dev_dir, DEVFSADM_SYNCH_DOOR) >= sizeof (door_file)) {
1161 err_print(CANT_CREATE_DOOR, DEVFSADM_SYNCH_DOOR,
1162 strerror(ENAMETOOLONG));
1163 devfsadm_exit(1);
1164 /*NOTREACHED*/
1167 (void) s_unlink(door_file);
1168 if ((fd = open(door_file, O_RDWR | O_CREAT, SYNCH_DOOR_PERMS)) == -1) {
1169 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1170 devfsadm_exit(1);
1171 /*NOTREACHED*/
1173 (void) close(fd);
1175 if ((fd = door_create(sync_handler, NULL,
1176 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1177 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1178 (void) s_unlink(door_file);
1179 devfsadm_exit(1);
1180 /*NOTREACHED*/
1183 if (fattach(fd, door_file) == -1) {
1184 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1185 (void) s_unlink(door_file);
1186 devfsadm_exit(1);
1187 /*NOTREACHED*/
1191 * devname_lookup_door
1193 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1194 etc_dev_dir, DEVNAME_LOOKUP_DOOR) >= sizeof (door_file)) {
1195 err_print(CANT_CREATE_DOOR, DEVNAME_LOOKUP_DOOR,
1196 strerror(ENAMETOOLONG));
1197 devfsadm_exit(1);
1198 /*NOTREACHED*/
1201 (void) s_unlink(door_file);
1202 if ((fd = open(door_file, O_RDWR | O_CREAT, S_IRUSR|S_IWUSR)) == -1) {
1203 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1204 devfsadm_exit(1);
1205 /*NOTREACHED*/
1207 (void) close(fd);
1209 if ((fd = door_create(devname_lookup_handler, NULL,
1210 DOOR_REFUSE_DESC)) == -1) {
1211 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1212 (void) s_unlink(door_file);
1213 devfsadm_exit(1);
1214 /*NOTREACHED*/
1217 (void) fdetach(door_file);
1218 lookup_door_path = s_strdup(door_file);
1219 retry:
1220 if (fattach(fd, door_file) == -1) {
1221 if (errno == EBUSY)
1222 goto retry;
1223 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1224 (void) s_unlink(door_file);
1225 devfsadm_exit(1);
1226 /*NOTREACHED*/
1228 lookup_door_fd = fd;
1230 /* pass down the door name to kernel for door_ki_open */
1231 if (devname_kcall(MODDEVNAME_LOOKUPDOOR, (void *)door_file) != 0)
1232 err_print(DEVNAME_CONTACT_FAILED, strerror(errno));
1234 vprint(CHATTY_MID, "%spausing\n", fcn);
1235 for (;;) {
1236 (void) pause();
1240 /*ARGSUSED*/
1241 static void
1242 sync_handler(void *cookie, char *ap, size_t asize,
1243 door_desc_t *dp, uint_t ndesc)
1245 door_cred_t dcred;
1246 struct dca_off *dcp, rdca;
1247 struct dca_impl dci;
1250 * Must be root to make this call
1251 * If caller is not root, don't touch its data.
1253 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
1254 dcp = &rdca;
1255 dcp->dca_error = EPERM;
1256 goto out;
1259 assert(ap);
1260 assert(asize == sizeof (*dcp));
1262 dcp = (void *)ap;
1265 * Root is always present and is the first component of "name" member
1267 assert(dcp->dca_root == 0);
1270 * The structure passed in by the door_client uses offsets
1271 * instead of pointers to work across address space boundaries.
1272 * Now copy the data into a structure (dca_impl) which uses
1273 * pointers.
1275 dci.dci_root = &dcp->dca_name[dcp->dca_root];
1276 dci.dci_minor = dcp->dca_minor ? &dcp->dca_name[dcp->dca_minor] : NULL;
1277 dci.dci_driver =
1278 dcp->dca_driver ? &dcp->dca_name[dcp->dca_driver] : NULL;
1279 dci.dci_error = 0;
1280 dci.dci_flags = dcp->dca_flags | (dci.dci_driver ? DCA_LOAD_DRV : 0);
1281 dci.dci_arg = NULL;
1283 lock_dev();
1284 devi_tree_walk(&dci, DINFOCPYALL, NULL);
1285 dcp->dca_error = dci.dci_error;
1287 if (dcp->dca_flags & DCA_DEVLINK_SYNC)
1288 unlock_dev(SYNC_STATE);
1289 else
1290 unlock_dev(CACHE_STATE);
1292 out: (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0);
1295 static void
1296 lock_dev(void)
1298 vprint(CHATTY_MID, "lock_dev(): entered\n");
1300 if (build_dev == FALSE)
1301 return;
1303 /* lockout other threads from /dev */
1304 while (sema_wait(&dev_sema) != 0)
1308 * Lock out other devfsadm processes from /dev.
1309 * If this wasn't the last process to run,
1310 * clear caches
1312 if (enter_dev_lock() != getpid()) {
1313 invalidate_enumerate_cache();
1314 rm_all_links_from_cache();
1315 (void) di_devlink_close(&devlink_cache, DI_LINK_ERROR);
1317 /* send any sysevents that were queued up. */
1318 process_syseventq();
1322 * (re)load the reverse links database if not
1323 * already cached.
1325 if (devlink_cache == NULL)
1326 devlink_cache = di_devlink_open(root_dir, 0);
1329 * If modules were unloaded, reload them. Also use module status
1330 * as an indication that we should check to see if other binding
1331 * files need to be reloaded.
1333 if (module_head == NULL) {
1334 load_modules();
1335 read_minor_perm_file();
1336 read_driver_aliases_file();
1337 read_devlinktab_file();
1338 read_logindevperm_file();
1339 read_enumerate_file();
1342 if (module_head != NULL)
1343 return;
1345 if (strcmp(prog, DEVLINKS) == 0) {
1346 if (devlinktab_list == NULL) {
1347 err_print(NO_LINKTAB, devlinktab_file);
1348 err_print(NO_MODULES, module_dirs);
1349 err_print(ABORTING);
1350 devfsadm_exit(1);
1351 /*NOTREACHED*/
1353 } else {
1354 err_print(NO_MODULES, module_dirs);
1355 if (strcmp(prog, DEVFSADM) == 0) {
1356 err_print(MODIFY_PATH);
1362 * Unlock the device. If we are processing a CACHE_STATE call, we signal a
1363 * minor_fini_thread delayed SYNC_STATE at the end of the call. If we are
1364 * processing a SYNC_STATE call, we cancel any minor_fini_thread SYNC_STATE
1365 * at both the start and end of the call since we will be doing the SYNC_STATE.
1367 static void
1368 unlock_dev(int flag)
1370 assert(flag == SYNC_STATE || flag == CACHE_STATE);
1372 vprint(CHATTY_MID, "unlock_dev(): entered\n");
1374 /* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */
1375 if (flag == SYNC_STATE) {
1376 (void) mutex_lock(&minor_fini_mutex);
1377 minor_fini_canceled = TRUE;
1378 minor_fini_delayed = FALSE;
1379 (void) mutex_unlock(&minor_fini_mutex);
1382 if (build_dev == FALSE)
1383 return;
1385 if (devlink_cache == NULL) {
1386 err_print(NO_DEVLINK_CACHE);
1388 assert(devlink_cache);
1390 if (flag == SYNC_STATE) {
1391 unload_modules();
1392 if (update_database)
1393 (void) di_devlink_update(devlink_cache);
1394 (void) di_devlink_close(&devlink_cache, 0);
1397 * now that the devlinks db cache has been flushed, it is safe
1398 * to send any sysevents that were queued up.
1400 process_syseventq();
1403 exit_dev_lock(0);
1405 (void) mutex_lock(&minor_fini_mutex);
1406 if (flag == SYNC_STATE) {
1407 /* We did a SYNC_STATE, cancel minor_fini_thread SYNC */
1408 minor_fini_canceled = TRUE;
1409 minor_fini_delayed = FALSE;
1410 } else {
1411 /* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */
1412 minor_fini_canceled = FALSE;
1413 minor_fini_delayed = TRUE;
1414 (void) cond_signal(&minor_fini_cv);
1416 (void) mutex_unlock(&minor_fini_mutex);
1418 (void) sema_post(&dev_sema);
1422 * Check that if -r is set, it is not any part of a zone--- that is, that
1423 * the zonepath is not a substring of the root path.
1425 static int
1426 zone_pathcheck(char *checkpath)
1428 void *dlhdl = NULL;
1429 char *name;
1430 char root[MAXPATHLEN]; /* resolved devfsadm root path */
1431 char zroot[MAXPATHLEN]; /* zone root path */
1432 char rzroot[MAXPATHLEN]; /* resolved zone root path */
1433 char tmp[MAXPATHLEN];
1434 FILE *cookie;
1435 int err = DEVFSADM_SUCCESS;
1437 if (checkpath[0] == '\0')
1438 return (DEVFSADM_SUCCESS);
1441 * Check if zones is available on this system.
1443 if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
1444 return (DEVFSADM_SUCCESS);
1447 bzero(root, sizeof (root));
1448 if (resolvepath(checkpath, root, sizeof (root) - 1) == -1) {
1450 * In this case the user has done "devfsadm -r" on some path
1451 * which does not yet exist, or we got some other misc. error.
1452 * We punt and don't resolve the path in this case.
1454 (void) strlcpy(root, checkpath, sizeof (root));
1457 if (strlen(root) > 0 && (root[strlen(root) - 1] != '/')) {
1458 (void) snprintf(tmp, sizeof (tmp), "%s/", root);
1459 (void) strlcpy(root, tmp, sizeof (root));
1462 cookie = setzoneent();
1463 while ((name = getzoneent(cookie)) != NULL) {
1464 /* Skip the global zone */
1465 if (strcmp(name, GLOBAL_ZONENAME) == 0) {
1466 free(name);
1467 continue;
1470 if (zone_get_zonepath(name, zroot, sizeof (zroot)) != Z_OK) {
1471 free(name);
1472 continue;
1475 bzero(rzroot, sizeof (rzroot));
1476 if (resolvepath(zroot, rzroot, sizeof (rzroot) - 1) == -1) {
1478 * Zone path doesn't exist, or other misc error,
1479 * so we try using the non-resolved pathname.
1481 (void) strlcpy(rzroot, zroot, sizeof (rzroot));
1483 if (strlen(rzroot) > 0 && (rzroot[strlen(rzroot) - 1] != '/')) {
1484 (void) snprintf(tmp, sizeof (tmp), "%s/", rzroot);
1485 (void) strlcpy(rzroot, tmp, sizeof (rzroot));
1489 * Finally, the comparison. If the zone root path is a
1490 * leading substring of the root path, fail.
1492 if (strncmp(rzroot, root, strlen(rzroot)) == 0) {
1493 err_print(ZONE_PATHCHECK, root, name);
1494 err = DEVFSADM_FAILURE;
1495 free(name);
1496 break;
1498 free(name);
1500 endzoneent(cookie);
1501 (void) dlclose(dlhdl);
1502 return (err);
1506 * Called by the daemon when it receives an event from the devfsadm SLM
1507 * to syseventd.
1509 * The devfsadm SLM uses a private event channel for communication to
1510 * devfsadmd set-up via private libsysevent interfaces. This handler is
1511 * used to bind to the devfsadmd channel for event delivery.
1512 * The devfsadmd SLM insures single calls to this routine as well as
1513 * synchronized event delivery.
1516 static void
1517 event_handler(sysevent_t *ev)
1519 char *path;
1520 char *minor;
1521 char *subclass;
1522 char *dev_ev_subclass;
1523 char *driver_name;
1524 nvlist_t *attr_list = NULL;
1525 int err = 0;
1526 int instance;
1527 int branch_event = 0;
1530 * If this is event-driven, then we cannot trust the static devlist
1531 * to be correct.
1534 event_driven = TRUE;
1535 subclass = sysevent_get_subclass_name(ev);
1536 vprint(EVENT_MID, "event_handler: %s id:0X%llx\n",
1537 subclass, sysevent_get_seq(ev));
1539 if (strcmp(subclass, ESC_DEVFS_START) == 0) {
1540 return;
1543 /* Check if event is an instance modification */
1544 if (strcmp(subclass, ESC_DEVFS_INSTANCE_MOD) == 0) {
1545 devfs_instance_mod();
1546 return;
1548 if (sysevent_get_attr_list(ev, &attr_list) != 0) {
1549 vprint(EVENT_MID, "event_handler: can not get attr list\n");
1550 return;
1553 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0 ||
1554 strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0 ||
1555 strcmp(subclass, ESC_DEVFS_MINOR_CREATE) == 0 ||
1556 strcmp(subclass, ESC_DEVFS_MINOR_REMOVE) == 0) {
1557 if ((err = nvlist_lookup_string(attr_list, DEVFS_PATHNAME,
1558 &path)) != 0)
1559 goto out;
1561 if (nvlist_lookup_string(attr_list, DEVFS_DEVI_CLASS,
1562 &dev_ev_subclass) != 0)
1563 dev_ev_subclass = NULL;
1565 if (nvlist_lookup_string(attr_list, DEVFS_DRIVER_NAME,
1566 &driver_name) != 0)
1567 driver_name = NULL;
1569 if (nvlist_lookup_int32(attr_list, DEVFS_INSTANCE,
1570 &instance) != 0)
1571 instance = -1;
1573 if (nvlist_lookup_int32(attr_list, DEVFS_BRANCH_EVENT,
1574 &branch_event) != 0)
1575 branch_event = 0;
1577 if (nvlist_lookup_string(attr_list, DEVFS_MINOR_NAME,
1578 &minor) != 0)
1579 minor = NULL;
1581 lock_dev();
1583 if (strcmp(ESC_DEVFS_DEVI_ADD, subclass) == 0) {
1584 add_minor_pathname(path, NULL, dev_ev_subclass);
1585 if (branch_event) {
1586 build_and_enq_event(EC_DEV_BRANCH,
1587 ESC_DEV_BRANCH_ADD, path, DI_NODE_NIL,
1588 NULL);
1591 } else if (strcmp(ESC_DEVFS_MINOR_CREATE, subclass) == 0) {
1592 add_minor_pathname(path, minor, dev_ev_subclass);
1594 } else if (strcmp(ESC_DEVFS_MINOR_REMOVE, subclass) == 0) {
1595 hot_cleanup(path, minor, dev_ev_subclass, driver_name,
1596 instance);
1598 } else { /* ESC_DEVFS_DEVI_REMOVE */
1599 hot_cleanup(path, NULL, dev_ev_subclass,
1600 driver_name, instance);
1601 if (branch_event) {
1602 build_and_enq_event(EC_DEV_BRANCH,
1603 ESC_DEV_BRANCH_REMOVE, path, DI_NODE_NIL,
1604 NULL);
1608 unlock_dev(CACHE_STATE);
1610 } else if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0 ||
1611 strcmp(subclass, ESC_DEVFS_BRANCH_REMOVE) == 0) {
1612 if ((err = nvlist_lookup_string(attr_list,
1613 DEVFS_PATHNAME, &path)) != 0)
1614 goto out;
1616 /* just log ESC_DEV_BRANCH... event */
1617 if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0)
1618 dev_ev_subclass = ESC_DEV_BRANCH_ADD;
1619 else
1620 dev_ev_subclass = ESC_DEV_BRANCH_REMOVE;
1622 lock_dev();
1623 build_and_enq_event(EC_DEV_BRANCH, dev_ev_subclass, path,
1624 DI_NODE_NIL, NULL);
1625 unlock_dev(CACHE_STATE);
1626 } else
1627 err_print(UNKNOWN_EVENT, subclass);
1629 out:
1630 if (err)
1631 err_print(EVENT_ATTR_LOOKUP_FAILED, strerror(err));
1632 nvlist_free(attr_list);
1635 static void
1636 dca_impl_init(char *root, char *minor, struct dca_impl *dcip)
1638 assert(root);
1640 dcip->dci_root = root;
1641 dcip->dci_minor = minor;
1642 dcip->dci_driver = NULL;
1643 dcip->dci_error = 0;
1644 dcip->dci_flags = 0;
1645 dcip->dci_arg = NULL;
1649 * Kernel logs a message when a devinfo node is attached. Try to create
1650 * /dev and /devices for each minor node. minorname can be NULL.
1652 void
1653 add_minor_pathname(char *node, char *minor, char *ev_subclass)
1655 struct dca_impl dci;
1657 vprint(CHATTY_MID, "add_minor_pathname: node_path=%s minor=%s\n",
1658 node, minor ? minor : "NULL");
1660 dca_impl_init(node, minor, &dci);
1663 * Restrict hotplug link creation if daemon
1664 * started with -i option.
1666 if (single_drv == TRUE) {
1667 dci.dci_driver = driver;
1671 * We are being invoked in response to a hotplug event.
1673 dci.dci_flags = DCA_HOT_PLUG | DCA_CHECK_TYPE;
1675 devi_tree_walk(&dci, DINFOPROP|DINFOMINOR, ev_subclass);
1678 static di_node_t
1679 find_clone_node()
1681 static di_node_t clone_node = DI_NODE_NIL;
1683 if (clone_node == DI_NODE_NIL)
1684 clone_node = di_init("/pseudo/clone@0", DINFOPROP);
1685 return (clone_node);
1688 static int
1689 is_descendent_of(di_node_t node, char *driver)
1691 while (node != DI_NODE_NIL) {
1692 char *drv = di_driver_name(node);
1693 if (strcmp(drv, driver) == 0)
1694 return (1);
1695 node = di_parent_node(node);
1697 return (0);
1701 * Checks the minor type. If it is an alias node, then lookup
1702 * the real node/minor first, then call minor_process() to
1703 * do the real work.
1705 static int
1706 check_minor_type(di_node_t node, di_minor_t minor, void *arg)
1708 ddi_minor_type minor_type;
1709 di_node_t clone_node;
1710 char *mn;
1711 char *nt;
1712 struct mlist *dep;
1713 struct dca_impl *dcip = arg;
1715 assert(dcip);
1717 dep = dcip->dci_arg;
1719 mn = di_minor_name(minor);
1722 * We match driver here instead of in minor_process
1723 * as we want the actual driver name. This check is
1724 * unnecessary during deferred processing.
1726 if (dep &&
1727 ((dcip->dci_driver && !is_descendent_of(node, dcip->dci_driver)) ||
1728 (dcip->dci_minor && strcmp(mn, dcip->dci_minor)))) {
1729 return (DI_WALK_CONTINUE);
1732 if ((dcip->dci_flags & DCA_CHECK_TYPE) &&
1733 (nt = di_minor_nodetype(minor)) &&
1734 (strcmp(nt, DDI_NT_NET) == 0)) {
1735 dcip->dci_flags &= ~DCA_CHECK_TYPE;
1738 minor_type = di_minor_type(minor);
1740 if (minor_type == DDM_MINOR) {
1741 minor_process(node, minor, dep);
1743 } else if (minor_type == DDM_ALIAS) {
1744 struct mlist *cdep, clone_del = {0};
1746 clone_node = find_clone_node();
1747 if (clone_node == DI_NODE_NIL) {
1748 err_print(DI_INIT_FAILED, "clone", strerror(errno));
1749 return (DI_WALK_CONTINUE);
1752 cdep = dep ? &clone_del : NULL;
1754 minor_process(clone_node, minor, cdep);
1757 * cache "alias" minor node and free "clone" minor
1759 if (cdep != NULL && cdep->head != NULL) {
1760 assert(cdep->tail != NULL);
1761 cache_deferred_minor(dep, node, minor);
1762 dcip->dci_arg = cdep;
1763 process_deferred_links(dcip, DCA_FREE_LIST);
1764 dcip->dci_arg = dep;
1768 return (DI_WALK_CONTINUE);
1773 * This is the entry point for each minor node, whether walking
1774 * the entire tree via di_walk_minor() or processing a hotplug event
1775 * for a single devinfo node (via hotplug ndi_devi_online()).
1777 /*ARGSUSED*/
1778 static void
1779 minor_process(di_node_t node, di_minor_t minor, struct mlist *dep)
1781 create_list_t *create;
1782 int defer;
1784 vprint(CHATTY_MID, "minor_process: node=%s, minor=%s\n",
1785 di_node_name(node), di_minor_name(minor));
1787 if (dep != NULL) {
1789 if (build_devices == TRUE) {
1790 reset_node_permissions(node, minor);
1793 if (build_dev == FALSE) {
1794 return;
1798 * This function will create any nodes for /etc/devlink.tab.
1799 * If devlink.tab handles link creation, we don't call any
1800 * devfsadm modules since that could cause duplicate caching
1801 * in the enumerate functions if different re strings are
1802 * passed that are logically identical. I'm still not
1803 * convinced this would cause any harm, but better to be safe.
1805 * Deferred processing is available only for devlinks
1806 * created through devfsadm modules.
1808 if (process_devlink_compat(minor, node) == TRUE) {
1809 return;
1811 } else {
1812 vprint(CHATTY_MID, "minor_process: deferred processing\n");
1816 * look for relevant link create rules in the modules, and
1817 * invoke the link create callback function to build a link
1818 * if there is a match.
1820 defer = 0;
1821 for (create = create_head; create != NULL; create = create->next) {
1822 if ((minor_matches_rule(node, minor, create) == TRUE) &&
1823 class_ok(create->create->device_class) ==
1824 DEVFSADM_SUCCESS) {
1825 if (call_minor_init(create->modptr) ==
1826 DEVFSADM_FAILURE) {
1827 continue;
1831 * If NOT doing the deferred creates (i.e. 1st pass) and
1832 * rule requests deferred processing cache the minor
1833 * data.
1835 * If deferred processing (2nd pass), create links
1836 * ONLY if rule requests deferred processing.
1838 if (dep && ((create->create->flags & CREATE_MASK) ==
1839 CREATE_DEFER)) {
1840 defer = 1;
1841 continue;
1842 } else if (dep == NULL &&
1843 ((create->create->flags & CREATE_MASK) !=
1844 CREATE_DEFER)) {
1845 continue;
1848 if ((*(create->create->callback_fcn))
1849 (minor, node) == DEVFSADM_TERMINATE) {
1850 break;
1855 if (defer)
1856 cache_deferred_minor(dep, node, minor);
1861 * Cache node and minor in defer list.
1863 static void
1864 cache_deferred_minor(
1865 struct mlist *dep,
1866 di_node_t node,
1867 di_minor_t minor)
1869 struct minor *mp;
1870 const char *fcn = "cache_deferred_minor";
1872 vprint(CHATTY_MID, "%s node=%s, minor=%s\n", fcn,
1873 di_node_name(node), di_minor_name(minor));
1875 if (dep == NULL) {
1876 vprint(CHATTY_MID, "%s: cannot cache during "
1877 "deferred processing. Ignoring minor\n", fcn);
1878 return;
1881 mp = (struct minor *)s_zalloc(sizeof (struct minor));
1882 mp->node = node;
1883 mp->minor = minor;
1884 mp->next = NULL;
1886 assert(dep->head == NULL || dep->tail != NULL);
1887 if (dep->head == NULL) {
1888 dep->head = mp;
1889 } else {
1890 dep->tail->next = mp;
1892 dep->tail = mp;
1896 * Check to see if "create" link creation rule matches this node/minor.
1897 * If it does, return TRUE.
1899 static int
1900 minor_matches_rule(di_node_t node, di_minor_t minor, create_list_t *create)
1902 char *m_nodetype, *m_drvname;
1904 if (create->create->node_type != NULL) {
1906 m_nodetype = di_minor_nodetype(minor);
1907 assert(m_nodetype != NULL);
1909 switch (create->create->flags & TYPE_MASK) {
1910 case TYPE_EXACT:
1911 if (strcmp(create->create->node_type, m_nodetype) !=
1912 0) {
1913 return (FALSE);
1915 break;
1916 case TYPE_PARTIAL:
1917 if (strncmp(create->create->node_type, m_nodetype,
1918 strlen(create->create->node_type)) != 0) {
1919 return (FALSE);
1921 break;
1922 case TYPE_RE:
1923 if (regexec(&(create->node_type_comp), m_nodetype,
1924 0, NULL, 0) != 0) {
1925 return (FALSE);
1927 break;
1931 if (create->create->drv_name != NULL) {
1932 m_drvname = di_driver_name(node);
1933 switch (create->create->flags & DRV_MASK) {
1934 case DRV_EXACT:
1935 if (strcmp(create->create->drv_name, m_drvname) != 0) {
1936 return (FALSE);
1938 break;
1939 case DRV_RE:
1940 if (regexec(&(create->drv_name_comp), m_drvname,
1941 0, NULL, 0) != 0) {
1942 return (FALSE);
1944 break;
1948 return (TRUE);
1952 * If no classes were given on the command line, then return DEVFSADM_SUCCESS.
1953 * Otherwise, return DEVFSADM_SUCCESS if the device "class" from the module
1954 * matches one of the device classes given on the command line,
1955 * otherwise, return DEVFSADM_FAILURE.
1957 static int
1958 class_ok(char *class)
1960 int i;
1962 if (num_classes == 0) {
1963 return (DEVFSADM_SUCCESS);
1966 for (i = 0; i < num_classes; i++) {
1967 if (strcmp(class, classes[i]) == 0) {
1968 return (DEVFSADM_SUCCESS);
1971 return (DEVFSADM_FAILURE);
1975 * call minor_fini on active modules, then unload ALL modules
1977 static void
1978 unload_modules(void)
1980 module_t *module_free;
1981 create_list_t *create_free;
1982 remove_list_t *remove_free;
1984 while (create_head != NULL) {
1985 create_free = create_head;
1986 create_head = create_head->next;
1988 if ((create_free->create->flags & TYPE_RE) == TYPE_RE) {
1989 regfree(&(create_free->node_type_comp));
1991 if ((create_free->create->flags & DRV_RE) == DRV_RE) {
1992 regfree(&(create_free->drv_name_comp));
1994 free(create_free);
1997 while (remove_head != NULL) {
1998 remove_free = remove_head;
1999 remove_head = remove_head->next;
2000 free(remove_free);
2003 while (module_head != NULL) {
2005 if ((module_head->minor_fini != NULL) &&
2006 ((module_head->flags & MODULE_ACTIVE) == MODULE_ACTIVE)) {
2007 (void) (*(module_head->minor_fini))();
2010 vprint(MODLOAD_MID, "unloading module %s\n", module_head->name);
2011 free(module_head->name);
2012 (void) dlclose(module_head->dlhandle);
2014 module_free = module_head;
2015 module_head = module_head->next;
2016 free(module_free);
2021 * Load devfsadm logical link processing modules.
2023 static void
2024 load_modules(void)
2026 DIR *mod_dir;
2027 struct dirent *entp;
2028 char cdir[PATH_MAX + 1];
2029 char *last;
2030 char *mdir = module_dirs;
2031 char *fcn = "load_modules: ";
2033 while (*mdir != '\0') {
2035 while (*mdir == ':') {
2036 mdir++;
2039 if (*mdir == '\0') {
2040 continue;
2043 last = strchr(mdir, ':');
2045 if (last == NULL) {
2046 last = mdir + strlen(mdir);
2049 (void) strncpy(cdir, mdir, last - mdir);
2050 cdir[last - mdir] = '\0';
2051 mdir += strlen(cdir);
2053 if ((mod_dir = opendir(cdir)) == NULL) {
2054 vprint(MODLOAD_MID, "%sopendir(%s): %s\n",
2055 fcn, cdir, strerror(errno));
2056 continue;
2059 while ((entp = readdir(mod_dir)) != NULL) {
2061 if ((strcmp(entp->d_name, ".") == 0) ||
2062 (strcmp(entp->d_name, "..") == 0)) {
2063 continue;
2066 load_module(entp->d_name, cdir);
2068 s_closedir(mod_dir);
2072 static void
2073 load_module(char *mname, char *cdir)
2075 _devfsadm_create_reg_t *create_reg;
2076 _devfsadm_remove_reg_V1_t *remove_reg;
2077 create_list_t *create_list_element;
2078 create_list_t **create_list_next;
2079 remove_list_t *remove_list_element;
2080 remove_list_t **remove_list_next;
2081 char epath[PATH_MAX + 1], *end;
2082 char *fcn = "load_module: ";
2083 char *dlerrstr;
2084 void *dlhandle;
2085 module_t *module;
2086 int flags;
2087 int n;
2088 int i;
2090 /* ignore any file which does not end in '.so' */
2091 if ((end = strstr(mname, MODULE_SUFFIX)) != NULL) {
2092 if (end[strlen(MODULE_SUFFIX)] != '\0') {
2093 return;
2095 } else {
2096 return;
2099 (void) snprintf(epath, sizeof (epath), "%s/%s", cdir, mname);
2101 if ((dlhandle = dlopen(epath, RTLD_LAZY)) == NULL) {
2102 dlerrstr = dlerror();
2103 err_print(DLOPEN_FAILED, epath,
2104 dlerrstr ? dlerrstr : "unknown error");
2105 return;
2108 /* dlsym the _devfsadm_create_reg structure */
2109 if (NULL == (create_reg = (_devfsadm_create_reg_t *)
2110 dlsym(dlhandle, _DEVFSADM_CREATE_REG))) {
2111 vprint(MODLOAD_MID, "dlsym(%s, %s): symbol not found\n", epath,
2112 _DEVFSADM_CREATE_REG);
2113 } else {
2114 vprint(MODLOAD_MID, "%sdlsym(%s, %s) succeeded\n",
2115 fcn, epath, _DEVFSADM_CREATE_REG);
2118 /* dlsym the _devfsadm_remove_reg structure */
2119 if (NULL == (remove_reg = (_devfsadm_remove_reg_V1_t *)
2120 dlsym(dlhandle, _DEVFSADM_REMOVE_REG))) {
2121 vprint(MODLOAD_MID, "dlsym(%s,\n\t%s): symbol not found\n",
2122 epath, _DEVFSADM_REMOVE_REG);
2123 } else {
2124 vprint(MODLOAD_MID, "dlsym(%s, %s): succeeded\n",
2125 epath, _DEVFSADM_REMOVE_REG);
2128 vprint(MODLOAD_MID, "module %s loaded\n", epath);
2130 module = (module_t *)s_malloc(sizeof (module_t));
2131 module->name = s_strdup(epath);
2132 module->dlhandle = dlhandle;
2134 /* dlsym other module functions, to be called later */
2135 module->minor_fini = (int (*)())dlsym(dlhandle, MINOR_FINI);
2136 module->minor_init = (int (*)())dlsym(dlhandle, MINOR_INIT);
2137 module->flags = 0;
2140 * put a ptr to each struct devfsadm_create on "create_head"
2141 * list sorted in interpose_lvl.
2143 if (create_reg != NULL) {
2144 for (i = 0; i < create_reg->count; i++) {
2145 int flags = create_reg->tblp[i].flags;
2147 create_list_element = (create_list_t *)
2148 s_malloc(sizeof (create_list_t));
2150 create_list_element->create = &(create_reg->tblp[i]);
2151 create_list_element->modptr = module;
2153 if (((flags & CREATE_MASK) != 0) &&
2154 ((flags & CREATE_MASK) != CREATE_DEFER)) {
2155 free(create_list_element);
2156 err_print("illegal flag combination in "
2157 "module create\n");
2158 err_print(IGNORING_ENTRY, i, epath);
2159 continue;
2162 if (((flags & TYPE_MASK) == 0) ^
2163 (create_reg->tblp[i].node_type == NULL)) {
2164 free(create_list_element);
2165 err_print("flags value incompatible with "
2166 "node_type value in module create\n");
2167 err_print(IGNORING_ENTRY, i, epath);
2168 continue;
2171 if (((flags & TYPE_MASK) != 0) &&
2172 ((flags & TYPE_MASK) != TYPE_EXACT) &&
2173 ((flags & TYPE_MASK) != TYPE_RE) &&
2174 ((flags & TYPE_MASK) != TYPE_PARTIAL)) {
2175 free(create_list_element);
2176 err_print("illegal TYPE_* flag combination in "
2177 "module create\n");
2178 err_print(IGNORING_ENTRY, i, epath);
2179 continue;
2182 /* precompile regular expression for efficiency */
2183 if ((flags & TYPE_RE) == TYPE_RE) {
2184 if ((n = regcomp(&(create_list_element->
2185 node_type_comp),
2186 create_reg->tblp[i].node_type,
2187 REG_EXTENDED)) != 0) {
2188 free(create_list_element);
2189 err_print(REGCOMP_FAILED,
2190 create_reg->tblp[i].node_type, n);
2191 err_print(IGNORING_ENTRY, i, epath);
2192 continue;
2196 if (((flags & DRV_MASK) == 0) ^
2197 (create_reg->tblp[i].drv_name == NULL)) {
2198 if ((flags & TYPE_RE) == TYPE_RE) {
2199 regfree(&(create_list_element->
2200 node_type_comp));
2202 free(create_list_element);
2203 err_print("flags value incompatible with "
2204 "drv_name value in module create\n");
2205 err_print(IGNORING_ENTRY, i, epath);
2206 continue;
2209 if (((flags & DRV_MASK) != 0) &&
2210 ((flags & DRV_MASK) != DRV_EXACT) &&
2211 ((flags & DRV_MASK) != DRV_RE)) {
2212 if ((flags & TYPE_RE) == TYPE_RE) {
2213 regfree(&(create_list_element->
2214 node_type_comp));
2216 free(create_list_element);
2217 err_print("illegal DRV_* flag combination in "
2218 "module create\n");
2219 err_print(IGNORING_ENTRY, i, epath);
2220 continue;
2223 /* precompile regular expression for efficiency */
2224 if ((create_reg->tblp[i].flags & DRV_RE) == DRV_RE) {
2225 if ((n = regcomp(&(create_list_element->
2226 drv_name_comp),
2227 create_reg->tblp[i].drv_name,
2228 REG_EXTENDED)) != 0) {
2229 if ((flags & TYPE_RE) == TYPE_RE) {
2230 regfree(&(create_list_element->
2231 node_type_comp));
2233 free(create_list_element);
2234 err_print(REGCOMP_FAILED,
2235 create_reg->tblp[i].drv_name, n);
2236 err_print(IGNORING_ENTRY, i, epath);
2237 continue;
2242 /* add to list sorted by interpose level */
2243 for (create_list_next = &(create_head);
2244 (*create_list_next != NULL) &&
2245 (*create_list_next)->create->interpose_lvl >=
2246 create_list_element->create->interpose_lvl;
2247 create_list_next = &((*create_list_next)->next))
2249 create_list_element->next = *create_list_next;
2250 *create_list_next = create_list_element;
2255 * put a ptr to each struct devfsadm_remove on "remove_head"
2256 * list sorted by interpose_lvl.
2258 flags = 0;
2259 if (remove_reg != NULL) {
2260 if (remove_reg->version < DEVFSADM_V1)
2261 flags |= RM_NOINTERPOSE;
2262 for (i = 0; i < remove_reg->count; i++) {
2264 remove_list_element = (remove_list_t *)
2265 s_malloc(sizeof (remove_list_t));
2267 remove_list_element->remove = &(remove_reg->tblp[i]);
2268 remove_list_element->remove->flags |= flags;
2269 remove_list_element->modptr = module;
2271 for (remove_list_next = &(remove_head);
2272 (*remove_list_next != NULL) &&
2273 (*remove_list_next)->remove->interpose_lvl >=
2274 remove_list_element->remove->interpose_lvl;
2275 remove_list_next = &((*remove_list_next)->next))
2277 remove_list_element->next = *remove_list_next;
2278 *remove_list_next = remove_list_element;
2282 module->next = module_head;
2283 module_head = module;
2287 * After we have completed a CACHE_STATE, if a SYNC_STATE does not occur
2288 * within 'timeout' secs the minor_fini_thread needs to do a SYNC_STATE
2289 * so that we still call the minor_fini routines.
2291 /*ARGSUSED*/
2292 static void
2293 minor_fini_thread(void *arg)
2295 timestruc_t abstime;
2297 vprint(INITFINI_MID, "minor_fini_thread starting\n");
2299 (void) mutex_lock(&minor_fini_mutex);
2300 for (;;) {
2301 /* wait the gather period, or until signaled */
2302 abstime.tv_sec = time(NULL) + minor_fini_timeout;
2303 abstime.tv_nsec = 0;
2304 (void) cond_timedwait(&minor_fini_cv,
2305 &minor_fini_mutex, &abstime);
2307 /* if minor_fini was canceled, go wait again */
2308 if (minor_fini_canceled == TRUE)
2309 continue;
2311 /* if minor_fini was delayed, go wait again */
2312 if (minor_fini_delayed == TRUE) {
2313 minor_fini_delayed = FALSE;
2314 continue;
2317 /* done with cancellations and delays, do the SYNC_STATE */
2318 (void) mutex_unlock(&minor_fini_mutex);
2320 lock_dev();
2321 unlock_dev(SYNC_STATE);
2322 vprint(INITFINI_MID, "minor_fini sync done\n");
2324 (void) mutex_lock(&minor_fini_mutex);
2330 * Attempt to initialize module, if a minor_init routine exists. Set
2331 * the active flag if the routine exists and succeeds. If it doesn't
2332 * exist, just set the active flag.
2334 static int
2335 call_minor_init(module_t *module)
2337 char *fcn = "call_minor_init: ";
2339 if ((module->flags & MODULE_ACTIVE) == MODULE_ACTIVE) {
2340 return (DEVFSADM_SUCCESS);
2343 vprint(INITFINI_MID, "%smodule %s. current state: inactive\n",
2344 fcn, module->name);
2346 if (module->minor_init == NULL) {
2347 module->flags |= MODULE_ACTIVE;
2348 vprint(INITFINI_MID, "minor_init not defined\n");
2349 return (DEVFSADM_SUCCESS);
2352 if ((*(module->minor_init))() == DEVFSADM_FAILURE) {
2353 err_print(FAILED_FOR_MODULE, MINOR_INIT, module->name);
2354 return (DEVFSADM_FAILURE);
2357 vprint(INITFINI_MID, "minor_init() returns DEVFSADM_SUCCESS. "
2358 "new state: active\n");
2360 module->flags |= MODULE_ACTIVE;
2361 return (DEVFSADM_SUCCESS);
2365 * Creates a symlink 'link' to the physical path of node:minor.
2366 * Construct link contents, then call create_link_common().
2368 /*ARGSUSED*/
2370 devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags)
2372 char rcontents[PATH_MAX];
2373 char devlink[PATH_MAX];
2374 char phy_path[PATH_MAX];
2375 char *acontents;
2376 char *dev_path;
2377 int numslashes;
2378 int rv;
2379 int i, link_exists;
2380 int last_was_slash = FALSE;
2383 * try to use devices path
2385 if ((node == lnode) && (minor == lminor)) {
2386 acontents = lphy_path;
2387 } else if (di_minor_type(minor) == DDM_ALIAS) {
2388 /* use /pseudo/clone@0:<driver> as the phys path */
2389 (void) snprintf(phy_path, sizeof (phy_path),
2390 "/pseudo/clone@0:%s",
2391 di_driver_name(di_minor_devinfo(minor)));
2392 acontents = phy_path;
2393 } else {
2394 if ((dev_path = di_devfs_path(node)) == NULL) {
2395 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2396 devfsadm_exit(1);
2397 /*NOTREACHED*/
2399 (void) snprintf(phy_path, sizeof (phy_path), "%s:%s",
2400 dev_path, di_minor_name(minor));
2401 di_devfs_path_free(dev_path);
2402 acontents = phy_path;
2405 /* prepend link with dev_dir contents */
2406 (void) strlcpy(devlink, dev_dir, sizeof (devlink));
2407 (void) strlcat(devlink, "/", sizeof (devlink));
2408 (void) strlcat(devlink, link, sizeof (devlink));
2411 * Calculate # of ../ to add. Account for double '//' in path.
2412 * Ignore all leading slashes.
2414 for (i = 0; link[i] == '/'; i++)
2416 for (numslashes = 0; link[i] != '\0'; i++) {
2417 if (link[i] == '/') {
2418 if (last_was_slash == FALSE) {
2419 numslashes++;
2420 last_was_slash = TRUE;
2422 } else {
2423 last_was_slash = FALSE;
2426 /* Don't count any trailing '/' */
2427 if (link[i-1] == '/') {
2428 numslashes--;
2431 rcontents[0] = '\0';
2432 do {
2433 (void) strlcat(rcontents, "../", sizeof (rcontents));
2434 } while (numslashes-- != 0);
2436 (void) strlcat(rcontents, "devices", sizeof (rcontents));
2437 (void) strlcat(rcontents, acontents, sizeof (rcontents));
2439 if (devlinks_debug == TRUE) {
2440 vprint(INFO_MID, "adding link %s ==> %s\n", devlink, rcontents);
2443 if ((rv = create_link_common(devlink, rcontents, &link_exists))
2444 == DEVFSADM_SUCCESS) {
2445 linknew = TRUE;
2446 add_link_to_cache(link, acontents);
2447 } else {
2448 linknew = FALSE;
2451 if (link_exists == TRUE) {
2452 /* Link exists or was just created */
2453 (void) di_devlink_add_link(devlink_cache, link, rcontents,
2454 DI_PRIMARY_LINK);
2457 return (rv);
2461 * Creates a symlink link to primary_link. Calculates relative
2462 * directory offsets, then calls link_common().
2464 /*ARGSUSED*/
2466 devfsadm_secondary_link(char *link, char *primary_link, int flags)
2468 char contents[PATH_MAX + 1];
2469 char devlink[PATH_MAX + 1];
2470 int rv, link_exists;
2471 char *fpath;
2472 char *tpath;
2473 char *op;
2475 /* prepend link with dev_dir contents */
2476 (void) strcpy(devlink, dev_dir);
2477 (void) strcat(devlink, "/");
2478 (void) strcat(devlink, link);
2480 * building extra link, so use first link as link contents, but first
2481 * make it relative.
2483 fpath = link;
2484 tpath = primary_link;
2485 op = contents;
2487 while (*fpath == *tpath && *fpath != '\0') {
2488 fpath++, tpath++;
2491 /* Count directories to go up, if any, and add "../" */
2492 while (*fpath != '\0') {
2493 if (*fpath == '/') {
2494 (void) strcpy(op, "../");
2495 op += 3;
2497 fpath++;
2501 * Back up to the start of the current path component, in
2502 * case in the middle
2504 while (tpath != primary_link && *(tpath-1) != '/') {
2505 tpath--;
2507 (void) strcpy(op, tpath);
2509 if (devlinks_debug == TRUE) {
2510 vprint(INFO_MID, "adding extra link %s ==> %s\n",
2511 devlink, contents);
2514 if ((rv = create_link_common(devlink, contents, &link_exists))
2515 == DEVFSADM_SUCCESS) {
2517 * we need to save the ultimate /devices contents, and not the
2518 * secondary link, since hotcleanup only looks at /devices path.
2519 * Since we don't have devices path here, we can try to get it
2520 * by readlink'ing the secondary link. This assumes the primary
2521 * link was created first.
2523 add_link_to_cache(link, lphy_path);
2524 linknew = TRUE;
2525 } else {
2526 linknew = FALSE;
2530 * If link exists or was just created, add it to the database
2532 if (link_exists == TRUE) {
2533 (void) di_devlink_add_link(devlink_cache, link, contents,
2534 DI_SECONDARY_LINK);
2537 return (rv);
2540 /* returns pointer to the devices directory */
2541 char *
2542 devfsadm_get_devices_dir()
2544 return (devices_dir);
2548 * Does the actual link creation. VERBOSE_MID only used if there is
2549 * a change. CHATTY_MID used otherwise.
2551 static int
2552 create_link_common(char *devlink, char *contents, int *exists)
2554 int try;
2555 int linksize;
2556 int max_tries = 0;
2557 static int prev_link_existed = TRUE;
2558 char checkcontents[PATH_MAX + 1];
2559 char *hide;
2561 *exists = FALSE;
2563 /* Database is not updated when file_mods == FALSE */
2564 if (file_mods == FALSE) {
2565 /* we want *actual* link contents so no alias redirection */
2566 linksize = readlink(devlink, checkcontents, PATH_MAX);
2567 if (linksize > 0) {
2568 checkcontents[linksize] = '\0';
2569 if (strcmp(checkcontents, contents) != 0) {
2570 vprint(CHATTY_MID, REMOVING_LINK,
2571 devlink, checkcontents);
2572 return (DEVFSADM_SUCCESS);
2573 } else {
2574 vprint(CHATTY_MID, "link exists and is correct:"
2575 " %s -> %s\n", devlink, contents);
2576 /* failure only in that the link existed */
2577 return (DEVFSADM_FAILURE);
2579 } else {
2580 vprint(VERBOSE_MID, CREATING_LINK, devlink, contents);
2581 return (DEVFSADM_SUCCESS);
2586 * systems calls are expensive, so predict whether to readlink
2587 * or symlink first, based on previous attempt
2589 if (prev_link_existed == FALSE) {
2590 try = CREATE_LINK;
2591 } else {
2592 try = READ_LINK;
2595 while (++max_tries <= 3) {
2597 switch (try) {
2598 case CREATE_LINK:
2600 if (symlink(contents, devlink) == 0) {
2601 vprint(VERBOSE_MID, CREATING_LINK, devlink,
2602 contents);
2603 prev_link_existed = FALSE;
2604 /* link successfully created */
2605 *exists = TRUE;
2606 set_logindev_perms(devlink);
2607 return (DEVFSADM_SUCCESS);
2608 } else {
2609 switch (errno) {
2611 case ENOENT:
2612 /* dirpath to node doesn't exist */
2613 hide = strrchr(devlink, '/');
2614 *hide = '\0';
2615 s_mkdirp(devlink, S_IRWXU|S_IRGRP|
2616 S_IXGRP|S_IROTH|S_IXOTH);
2617 *hide = '/';
2618 break;
2619 case EEXIST:
2620 try = READ_LINK;
2621 break;
2622 default:
2623 err_print(SYMLINK_FAILED, devlink,
2624 contents, strerror(errno));
2625 return (DEVFSADM_FAILURE);
2628 break;
2630 case READ_LINK:
2633 * If there is redirection, new phys path
2634 * and old phys path will not match and the
2635 * link will be created with new phys path
2636 * which is what we want. So we want real
2637 * contents.
2639 linksize = readlink(devlink, checkcontents, PATH_MAX);
2640 if (linksize >= 0) {
2641 checkcontents[linksize] = '\0';
2642 if (strcmp(checkcontents, contents) != 0) {
2643 s_unlink(devlink);
2644 vprint(VERBOSE_MID, REMOVING_LINK,
2645 devlink, checkcontents);
2646 try = CREATE_LINK;
2647 } else {
2648 prev_link_existed = TRUE;
2649 vprint(CHATTY_MID,
2650 "link exists and is correct:"
2651 " %s -> %s\n", devlink, contents);
2652 *exists = TRUE;
2653 /* failure in that the link existed */
2654 return (DEVFSADM_FAILURE);
2656 } else {
2657 switch (errno) {
2658 case EINVAL:
2659 /* not a symlink, remove and create */
2660 s_unlink(devlink);
2661 /* FALLTHROUGH */
2662 default:
2663 /* maybe it didn't exist at all */
2664 try = CREATE_LINK;
2665 break;
2668 break;
2671 err_print(MAX_ATTEMPTS, devlink, contents);
2672 return (DEVFSADM_FAILURE);
2675 static void
2676 set_logindev_perms(char *devlink)
2678 struct login_dev *newdev;
2679 struct passwd pwd, *resp;
2680 char pwd_buf[PATH_MAX];
2681 int rv;
2682 struct stat sb;
2683 char *devfs_path = NULL;
2686 * We only want logindev perms to be set when a device is
2687 * hotplugged or an application requests synchronous creates.
2688 * So we enable this only in daemon mode. In addition,
2689 * login(1) only fixes the std. /dev dir. So we don't
2690 * change perms if alternate root is set.
2691 * login_dev_enable is TRUE only in these cases.
2693 if (login_dev_enable != TRUE)
2694 return;
2697 * Normally, /etc/logindevperm has few (8 - 10 entries) which
2698 * may be regular expressions (globs were converted to RE).
2699 * So just do a linear search through the list.
2701 for (newdev = login_dev_cache; newdev; newdev = newdev->ldev_next) {
2702 vprint(FILES_MID, "matching %s with %s\n", devlink,
2703 newdev->ldev_device);
2705 if (regexec(&newdev->ldev_device_regex, devlink, 0,
2706 NULL, 0) == 0) {
2707 vprint(FILES_MID, "matched %s with %s\n", devlink,
2708 newdev->ldev_device);
2709 break;
2713 if (newdev == NULL)
2714 return;
2717 * we have a match, now find the driver associated with this
2718 * minor node using a snapshot on the physical path
2720 (void) resolve_link(devlink, NULL, NULL, &devfs_path, 0);
2722 * We dont need redirection here - the actual link contents
2723 * whether "alias" or "current" are fine
2725 if (devfs_path) {
2726 di_node_t node;
2727 char *drv;
2728 struct driver_list *list;
2729 char *p;
2731 /* truncate on : so we can take a snapshot */
2732 (void) strcpy(pwd_buf, devfs_path);
2733 p = strrchr(pwd_buf, ':');
2734 if (p == NULL) {
2735 free(devfs_path);
2736 return;
2738 *p = '\0';
2740 vprint(FILES_MID, "link=%s->physpath=%s\n",
2741 devlink, pwd_buf);
2743 node = di_init(pwd_buf, DINFOMINOR);
2745 drv = NULL;
2746 if (node) {
2747 drv = di_driver_name(node);
2749 if (drv) {
2750 vprint(FILES_MID, "%s: driver is %s\n",
2751 devlink, drv);
2754 /* search thru the driver list specified in logindevperm */
2755 list = newdev->ldev_driver_list;
2756 if ((drv != NULL) && (list != NULL)) {
2757 while (list) {
2758 if (strcmp(list->driver_name,
2759 drv) == 0) {
2760 vprint(FILES_MID,
2761 "driver %s match!\n", drv);
2762 break;
2764 list = list->next;
2766 if (list == NULL) {
2767 vprint(FILES_MID, "no driver match!\n");
2768 free(devfs_path);
2769 return;
2772 free(devfs_path);
2773 di_fini(node);
2774 } else {
2775 return;
2778 vprint(FILES_MID, "changing permissions of %s\n", devlink);
2781 * We have a match. We now attempt to determine the
2782 * owner and group of the console user.
2784 * stat() the console device newdev->ldev_console
2785 * which will always exist - it will have the right owner but
2786 * not the right group. Use getpwuid_r() to determine group for this
2787 * uid.
2788 * Note, it is safe to use name service here since if name services
2789 * are not available (during boot or in single-user mode), then
2790 * console owner will be root and its gid can be found in
2791 * local files.
2793 if (stat(newdev->ldev_console, &sb) == -1) {
2794 vprint(VERBOSE_MID, STAT_FAILED, newdev->ldev_console,
2795 strerror(errno));
2796 return;
2799 resp = NULL;
2800 rv = getpwuid_r(sb.st_uid, &pwd, pwd_buf, sizeof (pwd_buf), &resp);
2801 if (rv || resp == NULL) {
2802 rv = rv ? rv : EINVAL;
2803 vprint(VERBOSE_MID, GID_FAILED, sb.st_uid,
2804 strerror(rv));
2805 return;
2808 assert(&pwd == resp);
2810 sb.st_gid = resp->pw_gid;
2812 if (chmod(devlink, newdev->ldev_perms) == -1) {
2813 vprint(VERBOSE_MID, CHMOD_FAILED, devlink,
2814 strerror(errno));
2815 return;
2818 if (chown(devlink, sb.st_uid, sb.st_gid) == -1) {
2819 vprint(VERBOSE_MID, CHOWN_FAILED, devlink,
2820 strerror(errno));
2825 * Reset /devices node with appropriate permissions and
2826 * ownership as specified in /etc/minor_perm.
2828 static void
2829 reset_node_permissions(di_node_t node, di_minor_t minor)
2831 int spectype;
2832 char phy_path[PATH_MAX + 1];
2833 mode_t mode;
2834 dev_t dev;
2835 uid_t uid;
2836 gid_t gid;
2837 struct stat sb;
2838 char *dev_path, *aminor = NULL;
2840 /* lphy_path starts with / */
2841 if ((dev_path = di_devfs_path(node)) == NULL) {
2842 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2843 devfsadm_exit(1);
2844 /*NOTREACHED*/
2846 (void) strcpy(lphy_path, dev_path);
2847 di_devfs_path_free(dev_path);
2849 (void) strcat(lphy_path, ":");
2850 if (di_minor_type(minor) == DDM_ALIAS) {
2851 char *driver;
2852 aminor = di_minor_name(minor);
2853 driver = di_driver_name(di_minor_devinfo(minor));
2854 (void) strcat(lphy_path, driver);
2855 } else
2856 (void) strcat(lphy_path, di_minor_name(minor));
2858 (void) strcpy(phy_path, devices_dir);
2859 (void) strcat(phy_path, lphy_path);
2861 lnode = node;
2862 lminor = minor;
2864 vprint(CHATTY_MID, "reset_node_permissions: phy_path=%s lphy_path=%s\n",
2865 phy_path, lphy_path);
2867 dev = di_minor_devt(minor);
2868 spectype = di_minor_spectype(minor); /* block or char */
2870 getattr(phy_path, aminor, spectype, dev, &mode, &uid, &gid);
2873 * compare and set permissions and ownership
2875 * Under devfs, a quick insertion and removal of USB devices
2876 * would cause stat of physical path to fail. In this case,
2877 * we emit a verbose message, but don't print errors.
2879 if ((stat(phy_path, &sb) == -1) || (sb.st_rdev != dev)) {
2880 vprint(VERBOSE_MID, NO_DEVFS_NODE, phy_path);
2881 return;
2885 * If we are here for a new device
2886 * then
2887 * set ownership and permissions as specified in minor_perm
2888 * If we are here for an existing device
2889 * then
2890 * preserve existing/user-modified ownership and
2891 * permissions
2893 * devfs indicates a new device by faking access time to be zero.
2895 if (sb.st_atime != 0) {
2897 * Leave existing devices as they are.
2899 return;
2902 if (file_mods == FALSE) {
2903 /* Nothing more to do if simulating */
2904 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
2905 return;
2908 if (sb.st_mode != mode) {
2909 if (chmod(phy_path, mode) == -1)
2910 vprint(VERBOSE_MID, CHMOD_FAILED,
2911 phy_path, strerror(errno));
2913 if (sb.st_uid != uid || sb.st_gid != gid) {
2914 if (chown(phy_path, uid, gid) == -1)
2915 vprint(VERBOSE_MID, CHOWN_FAILED,
2916 phy_path, strerror(errno));
2919 /* Report that we actually did something */
2920 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
2924 * Removes logical link and the minor node it refers to. If file is a
2925 * link, we recurse and try to remove the minor node (or link if path is
2926 * a double link) that file's link contents refer to.
2928 static void
2929 devfsadm_rm_work(char *file, int recurse, int file_type)
2931 char *fcn = "devfsadm_rm_work: ";
2932 int linksize;
2933 char contents[PATH_MAX + 1];
2934 char nextfile[PATH_MAX + 1];
2935 char newfile[PATH_MAX + 1];
2936 char *ptr;
2938 vprint(REMOVE_MID, "%s%s\n", fcn, file);
2941 * Note: we don't remove /devices (non-links) entries because they are
2942 * covered by devfs.
2944 if (file_type != TYPE_LINK) {
2945 return;
2948 /* split into multiple if's due to excessive indentations */
2949 (void) strcpy(newfile, dev_dir);
2950 (void) strcat(newfile, "/");
2951 (void) strcat(newfile, file);
2954 * we dont care about the content of the symlink, so
2955 * redirection is not needed.
2957 if ((recurse == TRUE) &&
2958 ((linksize = readlink(newfile, contents, PATH_MAX)) > 0)) {
2959 contents[linksize] = '\0';
2962 * recurse if link points to another link
2964 if (is_minor_node(contents, &ptr) != DEVFSADM_TRUE) {
2965 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
2966 devfsadm_rm_work(&contents[strlen(DEV) + 1],
2967 TRUE, TYPE_LINK);
2968 } else {
2969 if ((ptr = strrchr(file, '/')) != NULL) {
2970 *ptr = '\0';
2971 (void) strcpy(nextfile, file);
2972 *ptr = '/';
2973 (void) strcat(nextfile, "/");
2974 } else {
2975 (void) strcpy(nextfile, "");
2977 (void) strcat(nextfile, contents);
2978 devfsadm_rm_work(nextfile, TRUE, TYPE_LINK);
2983 vprint(VERBOSE_MID, DEVFSADM_UNLINK, newfile);
2984 if (file_mods == TRUE) {
2985 rm_link_from_cache(file);
2986 s_unlink(newfile);
2987 rm_parent_dir_if_empty(newfile);
2988 invalidate_enumerate_cache();
2989 (void) di_devlink_rm_link(devlink_cache, file);
2993 void
2994 devfsadm_rm_link(char *file)
2996 devfsadm_rm_work(file, FALSE, TYPE_LINK);
2999 void
3000 devfsadm_rm_all(char *file)
3002 devfsadm_rm_work(file, TRUE, TYPE_LINK);
3005 static int
3006 s_rmdir(char *path)
3008 int i;
3009 char *rpath, *dir;
3010 const char *fcn = "s_rmdir";
3013 * Certain directories are created at install time by packages.
3014 * Some of them (listed in sticky_dirs[]) are required by apps
3015 * and need to be present even when empty.
3017 vprint(REMOVE_MID, "%s: checking if %s is sticky\n", fcn, path);
3019 rpath = path + strlen(dev_dir) + 1;
3021 for (i = 0; (dir = sticky_dirs[i]) != NULL; i++) {
3022 if (*rpath == *dir) {
3023 if (strcmp(rpath, dir) == 0) {
3024 vprint(REMOVE_MID, "%s: skipping sticky dir: "
3025 "%s\n", fcn, path);
3026 errno = EEXIST;
3027 return (-1);
3032 return (rmdir(path));
3036 * Try to remove any empty directories up the tree. It is assumed that
3037 * pathname is a file that was removed, so start with its parent, and
3038 * work up the tree.
3040 static void
3041 rm_parent_dir_if_empty(char *pathname)
3043 char *ptr, path[PATH_MAX + 1];
3044 char *fcn = "rm_parent_dir_if_empty: ";
3046 vprint(REMOVE_MID, "%schecking %s if empty\n", fcn, pathname);
3048 (void) strcpy(path, pathname);
3051 * ascend up the dir tree, deleting all empty dirs.
3052 * Return immediately if a dir is not empty.
3054 for (;;) {
3056 if ((ptr = strrchr(path, '/')) == NULL) {
3057 return;
3060 *ptr = '\0';
3062 if (finddev_emptydir(path)) {
3063 /* directory is empty */
3064 if (s_rmdir(path) == 0) {
3065 vprint(REMOVE_MID,
3066 "%sremoving empty dir %s\n", fcn, path);
3067 } else if (errno == EEXIST) {
3068 vprint(REMOVE_MID,
3069 "%sfailed to remove dir: %s\n", fcn, path);
3070 return;
3072 } else {
3073 /* some other file is here, so return */
3074 vprint(REMOVE_MID, "%sdir not empty: %s\n", fcn, path);
3075 return;
3081 * This function and all the functions it calls below were added to
3082 * handle the unique problem with world wide names (WWN). The problem is
3083 * that if a WWN device is moved to another address on the same controller
3084 * its logical link will change, while the physical node remains the same.
3085 * The result is that two logical links will point to the same physical path
3086 * in /devices, the valid link and a stale link. This function will
3087 * find all the stale nodes, though at a significant performance cost.
3089 * Caching is used to increase performance.
3090 * A cache will be built from disk if the cache tag doesn't already exist.
3091 * The cache tag is a regular expression "dir_re", which selects a
3092 * subset of disks to search from typically something like
3093 * "dev/cXt[0-9]+d[0-9]+s[0-9]+". After the cache is built, consistency must
3094 * be maintained, so entries are added as new links are created, and removed
3095 * as old links are deleted. The whole cache is flushed if we are a daemon,
3096 * and another devfsadm process ran in between.
3098 * Once the cache is built, this function finds the cache which matches
3099 * dir_re, and then it searches all links in that cache looking for
3100 * any link whose contents match "valid_link_contents" with a corresponding link
3101 * which does not match "valid_link". Any such matches are stale and removed.
3103 * This happens outside the context of a "reparenting" so we dont need
3104 * redirection.
3106 void
3107 devfsadm_rm_stale_links(char *dir_re, char *valid_link, di_node_t node,
3108 di_minor_t minor)
3110 link_t *link;
3111 linkhead_t *head;
3112 char phy_path[PATH_MAX + 1];
3113 char *valid_link_contents;
3114 char *dev_path;
3115 char rmlink[PATH_MAX + 1];
3118 * try to use devices path
3120 if ((node == lnode) && (minor == lminor)) {
3121 valid_link_contents = lphy_path;
3122 } else {
3123 if ((dev_path = di_devfs_path(node)) == NULL) {
3124 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
3125 devfsadm_exit(1);
3126 /*NOTREACHED*/
3128 (void) strcpy(phy_path, dev_path);
3129 di_devfs_path_free(dev_path);
3131 (void) strcat(phy_path, ":");
3132 (void) strcat(phy_path, di_minor_name(minor));
3133 valid_link_contents = phy_path;
3137 * As an optimization, check to make sure the corresponding
3138 * devlink was just created before continuing.
3141 if (linknew == FALSE) {
3142 return;
3145 head = get_cached_links(dir_re);
3147 assert(head->nextlink == NULL);
3149 for (link = head->link; link != NULL; link = head->nextlink) {
3151 * See hot_cleanup() for why we do this
3153 head->nextlink = link->next;
3154 if ((strcmp(link->contents, valid_link_contents) == 0) &&
3155 (strcmp(link->devlink, valid_link) != 0)) {
3156 vprint(CHATTY_MID, "removing %s -> %s\n"
3157 "valid link is: %s -> %s\n",
3158 link->devlink, link->contents,
3159 valid_link, valid_link_contents);
3161 * Use a copy of the cached link name as the
3162 * cache entry will go away during link removal
3164 (void) snprintf(rmlink, sizeof (rmlink), "%s",
3165 link->devlink);
3166 devfsadm_rm_link(rmlink);
3172 * Return previously created cache, or create cache.
3174 static linkhead_t *
3175 get_cached_links(char *dir_re)
3177 recurse_dev_t rd;
3178 linkhead_t *linkhead;
3179 int n;
3181 vprint(BUILDCACHE_MID, "get_cached_links: %s\n", dir_re);
3183 for (linkhead = headlinkhead; linkhead != NULL;
3184 linkhead = linkhead->nexthead) {
3185 if (strcmp(linkhead->dir_re, dir_re) == 0) {
3186 return (linkhead);
3191 * This tag is not in cache, so add it, along with all its
3192 * matching /dev entries. This is the only time we go to disk.
3194 linkhead = s_malloc(sizeof (linkhead_t));
3195 linkhead->nexthead = headlinkhead;
3196 headlinkhead = linkhead;
3197 linkhead->dir_re = s_strdup(dir_re);
3199 if ((n = regcomp(&(linkhead->dir_re_compiled), dir_re,
3200 REG_EXTENDED)) != 0) {
3201 err_print(REGCOMP_FAILED, dir_re, n);
3204 linkhead->nextlink = NULL;
3205 linkhead->link = NULL;
3207 rd.fcn = build_devlink_list;
3208 rd.data = (void *)linkhead;
3210 vprint(BUILDCACHE_MID, "get_cached_links: calling recurse_dev_re\n");
3212 /* call build_devlink_list for each directory in the dir_re RE */
3213 if (dir_re[0] == '/') {
3214 recurse_dev_re("/", &dir_re[1], &rd);
3215 } else {
3216 recurse_dev_re(dev_dir, dir_re, &rd);
3219 return (linkhead);
3222 static void
3223 build_devlink_list(char *devlink, void *data)
3225 char *fcn = "build_devlink_list: ";
3226 char *ptr;
3227 char *r_contents;
3228 char *r_devlink;
3229 char contents[PATH_MAX + 1];
3230 char newlink[PATH_MAX + 1];
3231 char stage_link[PATH_MAX + 1];
3232 int linksize;
3233 linkhead_t *linkhead = (linkhead_t *)data;
3234 link_t *link;
3235 int i = 0;
3237 vprint(BUILDCACHE_MID, "%scheck_link: %s\n", fcn, devlink);
3239 (void) strcpy(newlink, devlink);
3241 do {
3243 * None of the consumers of this function need redirection
3244 * so this readlink gets the "current" contents
3246 linksize = readlink(newlink, contents, PATH_MAX);
3247 if (linksize <= 0) {
3249 * The first pass through the do loop we may readlink()
3250 * non-symlink files(EINVAL) from false regexec matches.
3251 * Suppress error messages in those cases or if the link
3252 * content is the empty string.
3254 if (linksize < 0 && (i || errno != EINVAL))
3255 err_print(READLINK_FAILED, "build_devlink_list",
3256 newlink, strerror(errno));
3257 return;
3259 contents[linksize] = '\0';
3260 i = 1;
3262 if (is_minor_node(contents, &r_contents) == DEVFSADM_FALSE) {
3264 * assume that link contents is really a pointer to
3265 * another link, so recurse and read its link contents.
3267 * some link contents are absolute:
3268 * /dev/audio -> /dev/sound/0
3270 if (strncmp(contents, DEV "/",
3271 strlen(DEV) + strlen("/")) != 0) {
3273 if ((ptr = strrchr(newlink, '/')) == NULL) {
3274 vprint(REMOVE_MID, "%s%s -> %s invalid "
3275 "link. missing '/'\n", fcn,
3276 newlink, contents);
3277 return;
3279 *ptr = '\0';
3280 (void) strcpy(stage_link, newlink);
3281 *ptr = '/';
3282 (void) strcat(stage_link, "/");
3283 (void) strcat(stage_link, contents);
3284 (void) strcpy(newlink, stage_link);
3285 } else {
3286 (void) strcpy(newlink, dev_dir);
3287 (void) strcat(newlink, "/");
3288 (void) strcat(newlink,
3289 &contents[strlen(DEV) + strlen("/")]);
3292 } else {
3293 newlink[0] = '\0';
3295 } while (newlink[0] != '\0');
3297 if (strncmp(devlink, dev_dir, strlen(dev_dir)) != 0) {
3298 vprint(BUILDCACHE_MID, "%sinvalid link: %s\n", fcn, devlink);
3299 return;
3302 r_devlink = devlink + strlen(dev_dir);
3304 if (r_devlink[0] != '/')
3305 return;
3307 link = s_malloc(sizeof (link_t));
3309 /* don't store the '/' after rootdir/dev */
3310 r_devlink += 1;
3312 vprint(BUILDCACHE_MID, "%scaching link: %s\n", fcn, r_devlink);
3313 link->devlink = s_strdup(r_devlink);
3315 link->contents = s_strdup(r_contents);
3317 link->next = linkhead->link;
3318 linkhead->link = link;
3322 * to be consistent, devlink must not begin with / and must be
3323 * relative to /dev/, whereas physpath must contain / and be
3324 * relative to /devices.
3326 static void
3327 add_link_to_cache(char *devlink, char *physpath)
3329 linkhead_t *linkhead;
3330 link_t *link;
3331 int added = 0;
3333 if (file_mods == FALSE) {
3334 return;
3337 vprint(CACHE_MID, "add_link_to_cache: %s -> %s ",
3338 devlink, physpath);
3340 for (linkhead = headlinkhead; linkhead != NULL;
3341 linkhead = linkhead->nexthead) {
3342 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3343 == 0) {
3344 added++;
3345 link = s_malloc(sizeof (link_t));
3346 link->devlink = s_strdup(devlink);
3347 link->contents = s_strdup(physpath);
3348 link->next = linkhead->link;
3349 linkhead->link = link;
3353 vprint(CACHE_MID,
3354 " %d %s\n", added, added == 0 ? "NOT ADDED" : "ADDED");
3358 * Remove devlink from cache. Devlink must be relative to /dev/ and not start
3359 * with /.
3361 static void
3362 rm_link_from_cache(char *devlink)
3364 linkhead_t *linkhead;
3365 link_t **linkp;
3366 link_t *save;
3368 vprint(CACHE_MID, "rm_link_from_cache enter: %s\n", devlink);
3370 for (linkhead = headlinkhead; linkhead != NULL;
3371 linkhead = linkhead->nexthead) {
3372 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3373 == 0) {
3375 for (linkp = &(linkhead->link); *linkp != NULL; ) {
3376 if ((strcmp((*linkp)->devlink, devlink) == 0)) {
3377 save = *linkp;
3378 *linkp = (*linkp)->next;
3380 * We are removing our caller's
3381 * "next" link. Update the nextlink
3382 * field in the head so that our
3383 * callers accesses the next valid
3384 * link
3386 if (linkhead->nextlink == save)
3387 linkhead->nextlink = *linkp;
3388 free(save->devlink);
3389 free(save->contents);
3390 free(save);
3391 vprint(CACHE_MID, " %s FREED FROM "
3392 "CACHE\n", devlink);
3393 } else {
3394 linkp = &((*linkp)->next);
3401 static void
3402 rm_all_links_from_cache()
3404 linkhead_t *linkhead;
3405 linkhead_t *nextlinkhead;
3406 link_t *link;
3407 link_t *nextlink;
3409 vprint(CACHE_MID, "rm_all_links_from_cache\n");
3411 for (linkhead = headlinkhead; linkhead != NULL;
3412 linkhead = nextlinkhead) {
3414 nextlinkhead = linkhead->nexthead;
3415 assert(linkhead->nextlink == NULL);
3416 for (link = linkhead->link; link != NULL; link = nextlink) {
3417 nextlink = link->next;
3418 free(link->devlink);
3419 free(link->contents);
3420 free(link);
3422 regfree(&(linkhead->dir_re_compiled));
3423 free(linkhead->dir_re);
3424 free(linkhead);
3426 headlinkhead = NULL;
3430 * Called when the kernel has modified the incore path_to_inst data. This
3431 * function will schedule a flush of the data to the filesystem.
3433 static void
3434 devfs_instance_mod(void)
3436 char *fcn = "devfs_instance_mod: ";
3437 vprint(PATH2INST_MID, "%senter\n", fcn);
3439 /* signal instance thread */
3440 (void) mutex_lock(&count_lock);
3441 inst_count++;
3442 (void) cond_signal(&cv);
3443 (void) mutex_unlock(&count_lock);
3446 static void
3447 instance_flush_thread(void)
3449 int i;
3450 int idle;
3452 for (;;) {
3454 (void) mutex_lock(&count_lock);
3455 while (inst_count == 0) {
3456 (void) cond_wait(&cv, &count_lock);
3458 inst_count = 0;
3460 vprint(PATH2INST_MID, "signaled to flush path_to_inst."
3461 " Enter delay loop\n");
3463 * Wait MAX_IDLE_DELAY seconds after getting the last flush
3464 * path_to_inst event before invoking a flush, but never wait
3465 * more than MAX_DELAY seconds after getting the first event.
3467 for (idle = 0, i = 0; i < MAX_DELAY; i++) {
3469 (void) mutex_unlock(&count_lock);
3470 (void) sleep(1);
3471 (void) mutex_lock(&count_lock);
3473 /* shorten the delay if we are idle */
3474 if (inst_count == 0) {
3475 idle++;
3476 if (idle > MAX_IDLE_DELAY) {
3477 break;
3479 } else {
3480 inst_count = idle = 0;
3484 (void) mutex_unlock(&count_lock);
3486 flush_path_to_inst();
3491 * Helper function for flush_path_to_inst() below; this routine calls the
3492 * inst_sync syscall to flush the path_to_inst database to the given file.
3494 static int
3495 do_inst_sync(char *filename, char *instfilename)
3497 void (*sigsaved)(int);
3498 int err = 0, flags = INST_SYNC_IF_REQUIRED;
3499 struct stat sb;
3501 if (stat(instfilename, &sb) == -1 && errno == ENOENT)
3502 flags = INST_SYNC_ALWAYS;
3504 vprint(INSTSYNC_MID, "do_inst_sync: about to flush %s\n", filename);
3505 sigsaved = sigset(SIGSYS, SIG_IGN);
3506 if (inst_sync(filename, flags) == -1)
3507 err = errno;
3508 (void) sigset(SIGSYS, sigsaved);
3510 switch (err) {
3511 case 0:
3512 return (DEVFSADM_SUCCESS);
3513 case EALREADY: /* no-op, path_to_inst already up to date */
3514 return (EALREADY);
3515 case ENOSYS:
3516 err_print(CANT_LOAD_SYSCALL);
3517 break;
3518 case EPERM:
3519 err_print(SUPER_TO_SYNC);
3520 break;
3521 default:
3522 err_print(INSTSYNC_FAILED, filename, strerror(err));
3523 break;
3525 return (DEVFSADM_FAILURE);
3529 * Flush the kernel's path_to_inst database to /etc/path_to_inst. To do so
3530 * safely, the database is flushed to a temporary file, then moved into place.
3532 * The following files are used during this process:
3533 * /etc/path_to_inst: The path_to_inst file
3534 * /etc/path_to_inst.<pid>: Contains data flushed from the kernel
3535 * /etc/path_to_inst.old: The backup file
3536 * /etc/path_to_inst.old.<pid>: Temp file for creating backup
3539 static void
3540 flush_path_to_inst(void)
3542 char *new_inst_file = NULL;
3543 char *old_inst_file = NULL;
3544 char *old_inst_file_npid = NULL;
3545 FILE *inst_file_fp = NULL;
3546 FILE *old_inst_file_fp = NULL;
3547 struct stat sb;
3548 int err = 0;
3549 int c;
3550 int inst_strlen;
3552 vprint(PATH2INST_MID, "flush_path_to_inst: %s\n",
3553 (flush_path_to_inst_enable == TRUE) ? "ENABLED" : "DISABLED");
3555 if (flush_path_to_inst_enable == FALSE) {
3556 return;
3559 inst_strlen = strlen(inst_file);
3560 new_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 2);
3561 old_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 6);
3562 old_inst_file_npid = s_malloc(inst_strlen +
3563 sizeof (INSTANCE_FILE_SUFFIX));
3565 (void) snprintf(new_inst_file, inst_strlen + PID_STR_LEN + 2,
3566 "%s.%ld", inst_file, getpid());
3568 if (stat(new_inst_file, &sb) == 0) {
3569 s_unlink(new_inst_file);
3572 err = do_inst_sync(new_inst_file, inst_file);
3573 if (err != DEVFSADM_SUCCESS) {
3574 goto out;
3575 /*NOTREACHED*/
3579 * Now we deal with the somewhat tricky updating and renaming
3580 * of this critical piece of kernel state.
3584 * Copy the current instance file into a temporary file.
3585 * Then rename the temporary file into the backup (.old)
3586 * file and rename the newly flushed kernel data into
3587 * the instance file.
3588 * Of course if 'inst_file' doesn't exist, there's much
3589 * less for us to do .. tee hee.
3591 if ((inst_file_fp = fopen(inst_file, "r")) == NULL) {
3593 * No such file. Rename the new onto the old
3595 if ((err = rename(new_inst_file, inst_file)) != 0)
3596 err_print(RENAME_FAILED, inst_file, strerror(errno));
3597 goto out;
3598 /*NOTREACHED*/
3601 (void) snprintf(old_inst_file, inst_strlen + PID_STR_LEN + 6,
3602 "%s.old.%ld", inst_file, getpid());
3604 if (stat(old_inst_file, &sb) == 0) {
3605 s_unlink(old_inst_file);
3608 if ((old_inst_file_fp = fopen(old_inst_file, "w")) == NULL) {
3610 * Can't open the 'old_inst_file' file for writing.
3611 * This is somewhat strange given that the syscall
3612 * just succeeded to write a file out.. hmm.. maybe
3613 * the fs just filled up or something nasty.
3615 * Anyway, abort what we've done so far.
3617 err_print(CANT_UPDATE, old_inst_file);
3618 err = DEVFSADM_FAILURE;
3619 goto out;
3620 /*NOTREACHED*/
3624 * Copy current instance file into the temporary file
3626 err = 0;
3627 while ((c = getc(inst_file_fp)) != EOF) {
3628 if ((err = putc(c, old_inst_file_fp)) == EOF) {
3629 break;
3633 if (fclose(old_inst_file_fp) == EOF || err == EOF) {
3634 vprint(INFO_MID, CANT_UPDATE, old_inst_file);
3635 err = DEVFSADM_FAILURE;
3636 goto out;
3637 /* NOTREACHED */
3641 * Set permissions to be the same on the backup as
3642 * /etc/path_to_inst.
3644 (void) chmod(old_inst_file, 0444);
3647 * So far, everything we've done is more or less reversible.
3648 * But now we're going to commit ourselves.
3651 (void) snprintf(old_inst_file_npid,
3652 inst_strlen + sizeof (INSTANCE_FILE_SUFFIX),
3653 "%s%s", inst_file, INSTANCE_FILE_SUFFIX);
3655 if ((err = rename(old_inst_file, old_inst_file_npid)) != 0) {
3656 err_print(RENAME_FAILED, old_inst_file_npid,
3657 strerror(errno));
3658 } else if ((err = rename(new_inst_file, inst_file)) != 0) {
3659 err_print(RENAME_FAILED, inst_file, strerror(errno));
3662 out:
3663 if (inst_file_fp != NULL) {
3664 if (fclose(inst_file_fp) == EOF) {
3665 err_print(FCLOSE_FAILED, inst_file, strerror(errno));
3669 if (stat(new_inst_file, &sb) == 0) {
3670 s_unlink(new_inst_file);
3672 free(new_inst_file);
3674 if (stat(old_inst_file, &sb) == 0) {
3675 s_unlink(old_inst_file);
3677 free(old_inst_file);
3679 free(old_inst_file_npid);
3681 if (err != 0 && err != EALREADY) {
3682 err_print(FAILED_TO_UPDATE, inst_file);
3687 * detach from tty. For daemon mode.
3689 void
3690 detachfromtty()
3692 (void) setsid();
3693 if (DEVFSADM_DEBUG_ON == TRUE) {
3694 return;
3697 (void) close(0);
3698 (void) close(1);
3699 (void) close(2);
3700 (void) open("/dev/null", O_RDWR, 0);
3701 (void) dup(0);
3702 (void) dup(0);
3703 openlog(DEVFSADMD, LOG_PID, LOG_DAEMON);
3704 (void) setlogmask(LOG_UPTO(LOG_INFO));
3705 logflag = TRUE;
3709 * Use an advisory lock to synchronize updates to /dev. If the lock is
3710 * held by another process, block in the fcntl() system call until that
3711 * process drops the lock or exits. The lock file itself is
3712 * DEV_LOCK_FILE. The process id of the current and last process owning
3713 * the lock is kept in the lock file. After acquiring the lock, read the
3714 * process id and return it. It is the process ID which last owned the
3715 * lock, and will be used to determine if caches need to be flushed.
3717 * NOTE: if the devlink database is held open by the caller, it may
3718 * be closed by this routine. This is to enforce the following lock ordering:
3719 * 1) /dev lock 2) database open
3721 pid_t
3722 enter_dev_lock()
3724 struct flock lock;
3725 int n;
3726 pid_t pid;
3727 pid_t last_owner_pid;
3729 if (file_mods == FALSE) {
3730 return (0);
3733 (void) snprintf(dev_lockfile, sizeof (dev_lockfile),
3734 "%s/%s", etc_dev_dir, DEV_LOCK_FILE);
3736 vprint(LOCK_MID, "enter_dev_lock: lock file %s\n", dev_lockfile);
3738 dev_lock_fd = open(dev_lockfile, O_CREAT|O_RDWR, 0644);
3739 if (dev_lock_fd < 0) {
3740 err_print(OPEN_FAILED, dev_lockfile, strerror(errno));
3741 devfsadm_exit(1);
3742 /*NOTREACHED*/
3745 lock.l_type = F_WRLCK;
3746 lock.l_whence = SEEK_SET;
3747 lock.l_start = 0;
3748 lock.l_len = 0;
3750 /* try for the lock, but don't wait */
3751 if (fcntl(dev_lock_fd, F_SETLK, &lock) == -1) {
3752 if ((errno == EACCES) || (errno == EAGAIN)) {
3753 pid = 0;
3754 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3755 vprint(LOCK_MID, "waiting for PID %d to complete\n",
3756 (int)pid);
3757 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3758 err_print(LSEEK_FAILED, dev_lockfile,
3759 strerror(errno));
3760 devfsadm_exit(1);
3761 /*NOTREACHED*/
3764 * wait for the dev lock. If we have the database open,
3765 * close it first - the order of lock acquisition should
3766 * always be: 1) dev_lock 2) database
3767 * This is to prevent deadlocks with any locks the
3768 * database code may hold.
3770 (void) di_devlink_close(&devlink_cache, 0);
3772 /* send any sysevents that were queued up. */
3773 process_syseventq();
3775 if (fcntl(dev_lock_fd, F_SETLKW, &lock) == -1) {
3776 err_print(LOCK_FAILED, dev_lockfile,
3777 strerror(errno));
3778 devfsadm_exit(1);
3779 /*NOTREACHED*/
3784 hold_dev_lock = TRUE;
3785 pid = 0;
3786 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3787 if (n == sizeof (pid_t) && pid == getpid()) {
3788 return (pid);
3791 last_owner_pid = pid;
3793 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3794 err_print(LSEEK_FAILED, dev_lockfile, strerror(errno));
3795 devfsadm_exit(1);
3796 /*NOTREACHED*/
3798 pid = getpid();
3799 n = write(dev_lock_fd, &pid, sizeof (pid_t));
3800 if (n != sizeof (pid_t)) {
3801 err_print(WRITE_FAILED, dev_lockfile, strerror(errno));
3802 devfsadm_exit(1);
3803 /*NOTREACHED*/
3806 return (last_owner_pid);
3810 * Drop the advisory /dev lock, close lock file. Close and re-open the
3811 * file every time so to ensure a resync if for some reason the lock file
3812 * gets removed.
3814 void
3815 exit_dev_lock(int exiting)
3817 struct flock unlock;
3819 if (hold_dev_lock == FALSE) {
3820 return;
3823 vprint(LOCK_MID, "exit_dev_lock: lock file %s, exiting = %d\n",
3824 dev_lockfile, exiting);
3826 unlock.l_type = F_UNLCK;
3827 unlock.l_whence = SEEK_SET;
3828 unlock.l_start = 0;
3829 unlock.l_len = 0;
3831 if (fcntl(dev_lock_fd, F_SETLK, &unlock) == -1) {
3832 err_print(UNLOCK_FAILED, dev_lockfile, strerror(errno));
3835 hold_dev_lock = FALSE;
3837 if (close(dev_lock_fd) == -1) {
3838 err_print(CLOSE_FAILED, dev_lockfile, strerror(errno));
3839 if (!exiting)
3840 devfsadm_exit(1);
3841 /*NOTREACHED*/
3847 * Use an advisory lock to ensure that only one daemon process is active
3848 * in the system at any point in time. If the lock is held by another
3849 * process, do not block but return the pid owner of the lock to the
3850 * caller immediately. The lock is cleared if the holding daemon process
3851 * exits for any reason even if the lock file remains, so the daemon can
3852 * be restarted if necessary. The lock file is DAEMON_LOCK_FILE.
3854 pid_t
3855 enter_daemon_lock(void)
3857 struct flock lock;
3859 (void) snprintf(daemon_lockfile, sizeof (daemon_lockfile),
3860 "%s/%s", etc_dev_dir, DAEMON_LOCK_FILE);
3862 vprint(LOCK_MID, "enter_daemon_lock: lock file %s\n", daemon_lockfile);
3864 daemon_lock_fd = open(daemon_lockfile, O_CREAT|O_RDWR, 0644);
3865 if (daemon_lock_fd < 0) {
3866 err_print(OPEN_FAILED, daemon_lockfile, strerror(errno));
3867 devfsadm_exit(1);
3868 /*NOTREACHED*/
3871 lock.l_type = F_WRLCK;
3872 lock.l_whence = SEEK_SET;
3873 lock.l_start = 0;
3874 lock.l_len = 0;
3876 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
3878 if (errno == EAGAIN || errno == EDEADLK) {
3879 if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) {
3880 err_print(LOCK_FAILED, daemon_lockfile,
3881 strerror(errno));
3882 devfsadm_exit(1);
3883 /*NOTREACHED*/
3885 return (lock.l_pid);
3888 hold_daemon_lock = TRUE;
3889 return (getpid());
3893 * Drop the advisory daemon lock, close lock file
3895 void
3896 exit_daemon_lock(int exiting)
3898 struct flock lock;
3900 if (hold_daemon_lock == FALSE) {
3901 return;
3904 vprint(LOCK_MID, "exit_daemon_lock: lock file %s, exiting = %d\n",
3905 daemon_lockfile, exiting);
3907 lock.l_type = F_UNLCK;
3908 lock.l_whence = SEEK_SET;
3909 lock.l_start = 0;
3910 lock.l_len = 0;
3912 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
3913 err_print(UNLOCK_FAILED, daemon_lockfile, strerror(errno));
3916 if (close(daemon_lock_fd) == -1) {
3917 err_print(CLOSE_FAILED, daemon_lockfile, strerror(errno));
3918 if (!exiting)
3919 devfsadm_exit(1);
3920 /*NOTREACHED*/
3925 * Called to removed danging nodes in two different modes: RM_PRE, RM_POST.
3926 * RM_PRE mode is called before processing the entire devinfo tree, and RM_POST
3927 * is called after processing the entire devinfo tree.
3929 static void
3930 pre_and_post_cleanup(int flags)
3932 remove_list_t *rm;
3933 recurse_dev_t rd;
3934 cleanup_data_t cleanup_data;
3935 char *fcn = "pre_and_post_cleanup: ";
3937 if (build_dev == FALSE)
3938 return;
3940 vprint(CHATTY_MID, "attempting %s-cleanup\n",
3941 flags == RM_PRE ? "pre" : "post");
3942 vprint(REMOVE_MID, "%sflags = %d\n", fcn, flags);
3945 * the generic function recurse_dev_re is shared among different
3946 * functions, so set the method and data that it should use for
3947 * matches.
3949 rd.fcn = matching_dev;
3950 rd.data = (void *)&cleanup_data;
3951 cleanup_data.flags = flags;
3953 (void) mutex_lock(&nfp_mutex);
3954 nfphash_create();
3956 for (rm = remove_head; rm != NULL; rm = rm->next) {
3957 if ((flags & rm->remove->flags) == flags) {
3958 cleanup_data.rm = rm;
3960 * If reached this point, RM_PRE or RM_POST cleanup is
3961 * desired. clean_ok() decides whether to clean
3962 * under the given circumstances.
3964 vprint(REMOVE_MID, "%scleanup: PRE or POST\n", fcn);
3965 if (clean_ok(rm->remove) == DEVFSADM_SUCCESS) {
3966 vprint(REMOVE_MID, "cleanup: cleanup OK\n");
3967 recurse_dev_re(dev_dir,
3968 rm->remove->dev_dirs_re, &rd);
3972 nfphash_destroy();
3973 (void) mutex_unlock(&nfp_mutex);
3977 * clean_ok() determines whether cleanup should be done according
3978 * to the following matrix:
3980 * command line arguments RM_PRE RM_POST RM_PRE && RM_POST &&
3981 * RM_ALWAYS RM_ALWAYS
3982 * ---------------------- ------ ----- --------- ----------
3984 * <neither -c nor -C> - - pre-clean post-clean
3986 * -C pre-clean post-clean pre-clean post-clean
3988 * -C -c class pre-clean post-clean pre-clean post-clean
3989 * if class if class if class if class
3990 * matches matches matches matches
3992 * -c class - - pre-clean post-clean
3993 * if class if class
3994 * matches matches
3997 static int
3998 clean_ok(devfsadm_remove_V1_t *remove)
4000 int i;
4002 if (single_drv == TRUE) {
4003 /* no cleanup at all when using -i option */
4004 return (DEVFSADM_FAILURE);
4008 * no cleanup if drivers are not loaded. We make an exception
4009 * for the "disks" program however, since disks has a public
4010 * cleanup flag (-C) and disk drivers are usually never
4011 * unloaded.
4013 if (load_attach_drv == FALSE && strcmp(prog, DISKS) != 0) {
4014 return (DEVFSADM_FAILURE);
4017 /* if the cleanup flag was not specified, return false */
4018 if ((cleanup == FALSE) && ((remove->flags & RM_ALWAYS) == 0)) {
4019 return (DEVFSADM_FAILURE);
4022 if (num_classes == 0) {
4023 return (DEVFSADM_SUCCESS);
4027 * if reached this point, check to see if the class in the given
4028 * remove structure matches a class given on the command line
4031 for (i = 0; i < num_classes; i++) {
4032 if (strcmp(remove->device_class, classes[i]) == 0) {
4033 return (DEVFSADM_SUCCESS);
4037 return (DEVFSADM_FAILURE);
4041 * Called to remove dangling nodes after receiving a hotplug event
4042 * containing the physical node pathname to be removed.
4044 void
4045 hot_cleanup(char *node_path, char *minor_name, char *ev_subclass,
4046 char *driver_name, int instance)
4048 link_t *link;
4049 linkhead_t *head;
4050 remove_list_t *rm;
4051 char *fcn = "hot_cleanup: ";
4052 char path[PATH_MAX + 1];
4053 int path_len;
4054 char rmlink[PATH_MAX + 1];
4055 nvlist_t *nvl = NULL;
4056 int skip;
4057 int ret;
4060 * dev links can go away as part of hot cleanup.
4061 * So first build event attributes in order capture dev links.
4063 if (ev_subclass != NULL)
4064 nvl = build_event_attributes(EC_DEV_REMOVE, ev_subclass,
4065 node_path, DI_NODE_NIL, driver_name, instance, minor_name);
4067 (void) strcpy(path, node_path);
4068 (void) strcat(path, ":");
4069 (void) strcat(path, minor_name == NULL ? "" : minor_name);
4071 path_len = strlen(path);
4073 vprint(REMOVE_MID, "%spath=%s\n", fcn, path);
4075 (void) mutex_lock(&nfp_mutex);
4076 nfphash_create();
4078 for (rm = remove_head; rm != NULL; rm = rm->next) {
4079 if ((RM_HOT & rm->remove->flags) == RM_HOT) {
4080 head = get_cached_links(rm->remove->dev_dirs_re);
4081 assert(head->nextlink == NULL);
4082 for (link = head->link;
4083 link != NULL; link = head->nextlink) {
4085 * The remove callback below may remove
4086 * the current and/or any or all of the
4087 * subsequent links in the list.
4088 * Save the next link in the head. If
4089 * the callback removes the next link
4090 * the saved pointer in the head will be
4091 * updated by the callback to point at
4092 * the next valid link.
4094 head->nextlink = link->next;
4097 * if devlink is in no-further-process hash,
4098 * skip its remove
4100 if (nfphash_lookup(link->devlink) != NULL)
4101 continue;
4103 if (minor_name)
4104 skip = strcmp(link->contents, path);
4105 else
4106 skip = strncmp(link->contents, path,
4107 path_len);
4108 if (skip ||
4109 (call_minor_init(rm->modptr) ==
4110 DEVFSADM_FAILURE))
4111 continue;
4113 vprint(REMOVE_MID,
4114 "%sremoving %s -> %s\n", fcn,
4115 link->devlink, link->contents);
4117 * Use a copy of the cached link name
4118 * as the cache entry will go away
4119 * during link removal
4121 (void) snprintf(rmlink, sizeof (rmlink),
4122 "%s", link->devlink);
4123 if (rm->remove->flags & RM_NOINTERPOSE) {
4124 ((void (*)(char *))
4125 (rm->remove->callback_fcn))(rmlink);
4126 } else {
4127 ret = ((int (*)(char *))
4128 (rm->remove->callback_fcn))(rmlink);
4129 if (ret == DEVFSADM_TERMINATE)
4130 nfphash_insert(rmlink);
4136 nfphash_destroy();
4137 (void) mutex_unlock(&nfp_mutex);
4139 /* now log an event */
4140 if (nvl) {
4141 log_event(EC_DEV_REMOVE, ev_subclass, nvl);
4142 free(nvl);
4147 * Open the dir current_dir. For every file which matches the first dir
4148 * component of path_re, recurse. If there are no more *dir* path
4149 * components left in path_re (ie no more /), then call function rd->fcn.
4151 static void
4152 recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
4154 regex_t re1;
4155 char *slash;
4156 char new_path[PATH_MAX + 1];
4157 char *anchored_path_re;
4158 size_t len;
4159 finddevhdl_t fhandle;
4160 const char *fp;
4162 vprint(RECURSEDEV_MID, "recurse_dev_re: curr = %s path=%s\n",
4163 current_dir, path_re);
4165 if (finddev_readdir(current_dir, &fhandle) != 0)
4166 return;
4168 len = strlen(path_re);
4169 if ((slash = strchr(path_re, '/')) != NULL) {
4170 len = (slash - path_re);
4173 anchored_path_re = s_malloc(len + 3);
4174 (void) sprintf(anchored_path_re, "^%.*s$", len, path_re);
4176 if (regcomp(&re1, anchored_path_re, REG_EXTENDED) != 0) {
4177 free(anchored_path_re);
4178 goto out;
4181 free(anchored_path_re);
4183 while ((fp = finddev_next(fhandle)) != NULL) {
4185 if (regexec(&re1, fp, 0, NULL, 0) == 0) {
4186 /* match */
4187 (void) strcpy(new_path, current_dir);
4188 (void) strcat(new_path, "/");
4189 (void) strcat(new_path, fp);
4191 vprint(RECURSEDEV_MID, "recurse_dev_re: match, new "
4192 "path = %s\n", new_path);
4194 if (slash != NULL) {
4195 recurse_dev_re(new_path, slash + 1, rd);
4196 } else {
4197 /* reached the leaf component of path_re */
4198 vprint(RECURSEDEV_MID,
4199 "recurse_dev_re: calling fcn\n");
4200 (*(rd->fcn))(new_path, rd->data);
4205 regfree(&re1);
4207 out:
4208 finddev_close(fhandle);
4212 * Found a devpath which matches a RE in the remove structure.
4213 * Now check to see if it is dangling.
4215 static void
4216 matching_dev(char *devpath, void *data)
4218 cleanup_data_t *cleanup_data = data;
4219 int norm_len = strlen(dev_dir) + strlen("/");
4220 int ret;
4221 char *fcn = "matching_dev: ";
4223 vprint(RECURSEDEV_MID, "%sexamining devpath = '%s'\n", fcn,
4224 devpath);
4227 * If the link is in the no-further-process hash
4228 * don't do any remove operation on it.
4230 if (nfphash_lookup(devpath + norm_len) != NULL)
4231 return;
4234 * Dangling check will work whether "alias" or "current"
4235 * so no need to redirect.
4237 if (resolve_link(devpath, NULL, NULL, NULL, 1) == TRUE) {
4238 if (call_minor_init(cleanup_data->rm->modptr) ==
4239 DEVFSADM_FAILURE) {
4240 return;
4243 devpath += norm_len;
4245 vprint(RECURSEDEV_MID, "%scalling callback %s\n", fcn, devpath);
4246 if (cleanup_data->rm->remove->flags & RM_NOINTERPOSE)
4247 ((void (*)(char *))
4248 (cleanup_data->rm->remove->callback_fcn))(devpath);
4249 else {
4250 ret = ((int (*)(char *))
4251 (cleanup_data->rm->remove->callback_fcn))(devpath);
4252 if (ret == DEVFSADM_TERMINATE) {
4254 * We want no further remove processing for
4255 * this link. Add it to the nfp_hash;
4257 nfphash_insert(devpath);
4264 devfsadm_read_link(di_node_t anynode, char *link, char **devfs_path)
4266 char devlink[PATH_MAX];
4267 char *path;
4269 *devfs_path = NULL;
4271 /* prepend link with dev_dir contents */
4272 (void) strcpy(devlink, dev_dir);
4273 (void) strcat(devlink, "/");
4274 (void) strcat(devlink, link);
4276 /* We *don't* want a stat of the /devices node */
4277 path = NULL;
4278 (void) resolve_link(devlink, NULL, NULL, &path, 0);
4279 if (path != NULL) {
4280 /* redirect if alias to current */
4281 *devfs_path = di_alias2curr(anynode, path);
4282 free(path);
4284 return (*devfs_path ? DEVFSADM_SUCCESS : DEVFSADM_FAILURE);
4288 devfsadm_link_valid(di_node_t anynode, char *link)
4290 struct stat sb;
4291 char devlink[PATH_MAX + 1], *contents, *raw_contents;
4292 int rv, type;
4294 /* prepend link with dev_dir contents */
4295 (void) strcpy(devlink, dev_dir);
4296 (void) strcat(devlink, "/");
4297 (void) strcat(devlink, link);
4299 if (!device_exists(devlink) || lstat(devlink, &sb) != 0) {
4300 return (DEVFSADM_FALSE);
4303 raw_contents = NULL;
4304 type = 0;
4305 if (resolve_link(devlink, &raw_contents, &type, NULL, 1) == TRUE) {
4306 rv = DEVFSADM_FALSE;
4307 } else {
4308 rv = DEVFSADM_TRUE;
4312 * resolve alias paths for primary links
4314 contents = raw_contents;
4315 if (type == DI_PRIMARY_LINK) {
4316 contents = di_alias2curr(anynode, raw_contents);
4317 free(raw_contents);
4321 * The link exists. Add it to the database
4323 (void) di_devlink_add_link(devlink_cache, link, contents, type);
4324 free(contents);
4326 return (rv);
4330 * devpath: Absolute path to /dev link
4331 * content_p: Returns malloced string (link content)
4332 * type_p: Returns link type: primary or secondary
4333 * devfs_path: Returns malloced string: /devices path w/out "/devices"
4334 * dangle: if set, check if link is dangling
4335 * Returns:
4336 * TRUE if dangling
4337 * FALSE if not or if caller doesn't care
4338 * Caller is assumed to have initialized pointer contents to NULL
4341 static int
4342 resolve_link(char *devpath, char **content_p, int *type_p, char **devfs_path,
4343 int dangle)
4345 char contents[PATH_MAX + 1];
4346 char stage_link[PATH_MAX + 1];
4347 char *fcn = "resolve_link: ";
4348 char *ptr;
4349 int linksize;
4350 int rv = TRUE;
4351 struct stat sb;
4354 * This routine will return the "raw" contents. It is upto the
4355 * the caller to redirect "alias" to "current" (or vice versa)
4357 linksize = readlink(devpath, contents, PATH_MAX);
4359 if (linksize <= 0) {
4360 return (FALSE);
4361 } else {
4362 contents[linksize] = '\0';
4364 vprint(REMOVE_MID, "%s %s -> %s\n", fcn, devpath, contents);
4366 if (content_p) {
4367 *content_p = s_strdup(contents);
4371 * Check to see if this is a link pointing to another link in /dev. The
4372 * cheap way to do this is to look for a lack of ../devices/.
4375 if (is_minor_node(contents, &ptr) == DEVFSADM_FALSE) {
4377 if (type_p) {
4378 *type_p = DI_SECONDARY_LINK;
4382 * assume that linkcontents is really a pointer to another
4383 * link, and if so recurse and read its link contents.
4385 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
4386 (void) strcpy(stage_link, dev_dir);
4387 (void) strcat(stage_link, "/");
4388 (void) strcpy(stage_link,
4389 &contents[strlen(DEV) + strlen("/")]);
4390 } else {
4391 if ((ptr = strrchr(devpath, '/')) == NULL) {
4392 vprint(REMOVE_MID, "%s%s -> %s invalid link. "
4393 "missing '/'\n", fcn, devpath, contents);
4394 return (TRUE);
4396 *ptr = '\0';
4397 (void) strcpy(stage_link, devpath);
4398 *ptr = '/';
4399 (void) strcat(stage_link, "/");
4400 (void) strcat(stage_link, contents);
4402 return (resolve_link(stage_link, NULL, NULL, devfs_path,
4403 dangle));
4406 /* Current link points at a /devices minor node */
4407 if (type_p) {
4408 *type_p = DI_PRIMARY_LINK;
4411 if (devfs_path)
4412 *devfs_path = s_strdup(ptr);
4414 rv = FALSE;
4415 if (dangle)
4416 rv = (stat(ptr - strlen(DEVICES), &sb) == -1);
4418 vprint(REMOVE_MID, "%slink=%s, returning %s\n", fcn,
4419 devpath, ((rv == TRUE) ? "TRUE" : "FALSE"));
4421 return (rv);
4425 * Returns the substring of interest, given a path.
4427 static char *
4428 alloc_cmp_str(const char *path, devfsadm_enumerate_t *dep)
4430 uint_t match;
4431 char *np, *ap, *mp;
4432 char *cmp_str = NULL;
4433 char at[] = "@";
4434 char *fcn = "alloc_cmp_str";
4436 np = ap = mp = NULL;
4439 * extract match flags from the flags argument.
4441 match = (dep->flags & MATCH_MASK);
4443 vprint(ENUM_MID, "%s: enumeration match type: 0x%x"
4444 " path: %s\n", fcn, match, path);
4447 * MATCH_CALLBACK and MATCH_ALL are the only flags
4448 * which may be used if "path" is a /dev path
4450 if (match == MATCH_CALLBACK) {
4451 if (dep->sel_fcn == NULL) {
4452 vprint(ENUM_MID, "%s: invalid enumerate"
4453 " callback: path: %s\n", fcn, path);
4454 return (NULL);
4456 cmp_str = dep->sel_fcn(path, dep->cb_arg);
4457 return (cmp_str);
4460 cmp_str = s_strdup(path);
4462 if (match == MATCH_ALL) {
4463 return (cmp_str);
4467 * The remaining flags make sense only for /devices
4468 * paths
4470 if ((mp = strrchr(cmp_str, ':')) == NULL) {
4471 vprint(ENUM_MID, "%s: invalid path: %s\n",
4472 fcn, path);
4473 goto err;
4476 if (match == MATCH_MINOR) {
4477 /* A NULL "match_arg" values implies entire minor */
4478 if (get_component(mp + 1, dep->match_arg) == NULL) {
4479 vprint(ENUM_MID, "%s: invalid minor component:"
4480 " path: %s\n", fcn, path);
4481 goto err;
4483 return (cmp_str);
4486 if ((np = strrchr(cmp_str, '/')) == NULL) {
4487 vprint(ENUM_MID, "%s: invalid path: %s\n", fcn, path);
4488 goto err;
4491 if (match == MATCH_PARENT) {
4492 if (strcmp(cmp_str, "/") == 0) {
4493 vprint(ENUM_MID, "%s: invalid path: %s\n",
4494 fcn, path);
4495 goto err;
4498 if (np == cmp_str) {
4499 *(np + 1) = '\0';
4500 } else {
4501 *np = '\0';
4503 return (cmp_str);
4506 /* ap can be NULL - Leaf address may not exist or be empty string */
4507 ap = strchr(np+1, '@');
4509 /* minor is no longer of interest */
4510 *mp = '\0';
4512 if (match == MATCH_NODE) {
4513 if (ap)
4514 *ap = '\0';
4515 return (cmp_str);
4516 } else if (match == MATCH_ADDR) {
4518 * The empty string is a valid address. The only MATCH_ADDR
4519 * allowed in this case is against the whole address or
4520 * the first component of the address (match_arg=NULL/"0"/"1")
4521 * Note that in this case, the path won't have an "@"
4522 * As a result ap will be NULL. We fake up an ap = @'\0'
4523 * so that get_component() will work correctly.
4525 if (ap == NULL) {
4526 ap = at;
4529 if (get_component(ap + 1, dep->match_arg) == NULL) {
4530 vprint(ENUM_MID, "%s: invalid leaf addr. component:"
4531 " path: %s\n", fcn, path);
4532 goto err;
4534 return (cmp_str);
4537 vprint(ENUM_MID, "%s: invalid enumeration flags: 0x%x"
4538 " path: %s\n", fcn, dep->flags, path);
4540 /*FALLTHRU*/
4541 err:
4542 free(cmp_str);
4543 return (NULL);
4548 * "str" is expected to be a string with components separated by ','
4549 * The terminating null char is considered a separator.
4550 * get_component() will remove the portion of the string beyond
4551 * the component indicated.
4552 * If comp_str is NULL, the entire "str" is returned.
4554 static char *
4555 get_component(char *str, const char *comp_str)
4557 long comp;
4558 char *cp;
4560 if (str == NULL) {
4561 return (NULL);
4564 if (comp_str == NULL) {
4565 return (str);
4568 errno = 0;
4569 comp = strtol(comp_str, &cp, 10);
4570 if (errno != 0 || *cp != '\0' || comp < 0) {
4571 return (NULL);
4574 if (comp == 0)
4575 return (str);
4577 for (cp = str; ; cp++) {
4578 if (*cp == ',' || *cp == '\0')
4579 comp--;
4580 if (*cp == '\0' || comp <= 0) {
4581 break;
4585 if (comp == 0) {
4586 *cp = '\0';
4587 } else {
4588 str = NULL;
4591 return (str);
4596 * Enumerate serves as a generic counter as well as a means to determine
4597 * logical unit/controller numbers for such items as disk and tape
4598 * drives.
4600 * rules[] is an array of devfsadm_enumerate_t structures which defines
4601 * the enumeration rules to be used for a specified set of links in /dev.
4602 * The set of links is specified through regular expressions (of the flavor
4603 * described in regex(5)). These regular expressions are used to determine
4604 * the set of links in /dev to examine. The last path component in these
4605 * regular expressions MUST contain a parenthesized subexpression surrounding
4606 * the RE which is to be considered the enumerating component. The subexp
4607 * member in a rule is the subexpression number of the enumerating
4608 * component. Subexpressions in the last path component are numbered starting
4609 * from 1.
4611 * A cache of current id assignments is built up from existing symlinks and
4612 * new assignments use the lowest unused id. Assignments are based on a
4613 * match of a specified substring of a symlink's contents. If the specified
4614 * component for the devfs_path argument matches the corresponding substring
4615 * for a existing symlink's contents, the cached id is returned. Else, a new
4616 * id is created and returned in *buf. *buf must be freed by the caller.
4618 * An id assignment may be governed by a combination of rules, each rule
4619 * applicable to a different subset of links in /dev. For example, controller
4620 * numbers may be determined by a combination of disk symlinks in /dev/[r]dsk
4621 * and controller symlinks in /dev/cfg, with the two sets requiring different
4622 * rules to derive the "substring of interest". In such cases, the rules
4623 * array will have more than one element.
4626 devfsadm_enumerate_int(char *devfs_path, int index, char **buf,
4627 devfsadm_enumerate_t rules[], int nrules)
4629 return (find_enum_id(rules, nrules,
4630 devfs_path, index, "0", INTEGER, buf, 0));
4634 disk_enumerate_int(char *devfs_path, int index, char **buf,
4635 devfsadm_enumerate_t rules[], int nrules)
4637 return (find_enum_id(rules, nrules,
4638 devfs_path, index, "0", INTEGER, buf, 1));
4642 * Same as above, but allows a starting value to be specified.
4643 * Private to devfsadm.... used by devlinks.
4645 static int
4646 devfsadm_enumerate_int_start(char *devfs_path, int index, char **buf,
4647 devfsadm_enumerate_t rules[], int nrules, char *start)
4649 return (find_enum_id(rules, nrules,
4650 devfs_path, index, start, INTEGER, buf, 0));
4654 * devfsadm_enumerate_char serves as a generic counter returning
4655 * a single letter.
4658 devfsadm_enumerate_char(char *devfs_path, int index, char **buf,
4659 devfsadm_enumerate_t rules[], int nrules)
4661 return (find_enum_id(rules, nrules,
4662 devfs_path, index, "a", LETTER, buf, 0));
4666 * Same as above, but allows a starting char to be specified.
4667 * Private to devfsadm - used by ports module (port_link.c)
4670 devfsadm_enumerate_char_start(char *devfs_path, int index, char **buf,
4671 devfsadm_enumerate_t rules[], int nrules, char *start)
4673 return (find_enum_id(rules, nrules,
4674 devfs_path, index, start, LETTER, buf, 0));
4679 * For a given numeral_set (see get_cached_set for desc of numeral_set),
4680 * search all cached entries looking for matches on a specified substring
4681 * of devfs_path. The substring is derived from devfs_path based on the
4682 * rule specified by "index". If a match is found on a cached entry,
4683 * return the enumerated id in buf. Otherwise, create a new id by calling
4684 * new_id, then cache and return that entry.
4686 static int
4687 find_enum_id(devfsadm_enumerate_t rules[], int nrules,
4688 char *devfs_path, int index, char *min, int type, char **buf,
4689 int multiple)
4691 numeral_t *matchnp;
4692 numeral_t *numeral;
4693 int matchcount = 0;
4694 char *cmp_str;
4695 char *fcn = "find_enum_id";
4696 numeral_set_t *set;
4698 if (rules == NULL) {
4699 vprint(ENUM_MID, "%s: no rules. path: %s\n",
4700 fcn, devfs_path ? devfs_path : "<NULL path>");
4701 return (DEVFSADM_FAILURE);
4704 if (devfs_path == NULL) {
4705 vprint(ENUM_MID, "%s: NULL path\n", fcn);
4706 return (DEVFSADM_FAILURE);
4709 if (nrules <= 0 || index < 0 || index >= nrules || buf == NULL) {
4710 vprint(ENUM_MID, "%s: invalid arguments. path: %s\n",
4711 fcn, devfs_path);
4712 return (DEVFSADM_FAILURE);
4715 *buf = NULL;
4718 cmp_str = alloc_cmp_str(devfs_path, &rules[index]);
4719 if (cmp_str == NULL) {
4720 return (DEVFSADM_FAILURE);
4723 if ((set = get_enum_cache(rules, nrules)) == NULL) {
4724 free(cmp_str);
4725 return (DEVFSADM_FAILURE);
4728 assert(nrules == set->re_count);
4731 * Check and see if a matching entry is already cached.
4733 matchcount = lookup_enum_cache(set, cmp_str, rules, index,
4734 &matchnp);
4736 if (matchcount < 0 || matchcount > 1) {
4737 free(cmp_str);
4738 if (multiple && matchcount > 1)
4739 return (DEVFSADM_MULTIPLE);
4740 else
4741 return (DEVFSADM_FAILURE);
4744 /* if matching entry already cached, return it */
4745 if (matchcount == 1) {
4746 /* should never create a link with a reserved ID */
4747 vprint(ENUM_MID, "%s: 1 match w/ ID: %s\n", fcn, matchnp->id);
4748 assert(matchnp->flags == 0);
4749 *buf = s_strdup(matchnp->id);
4750 free(cmp_str);
4751 return (DEVFSADM_SUCCESS);
4755 * no cached entry, initialize a numeral struct
4756 * by calling new_id() and cache onto the numeral_set
4758 numeral = s_malloc(sizeof (numeral_t));
4759 numeral->id = new_id(set->headnumeral, type, min);
4760 numeral->full_path = s_strdup(devfs_path);
4761 numeral->rule_index = index;
4762 numeral->cmp_str = cmp_str;
4763 cmp_str = NULL;
4764 numeral->flags = 0;
4765 vprint(RSRV_MID, "%s: alloc new_id: %s numeral flags = %d\n",
4766 fcn, numeral->id, numeral->flags);
4769 /* insert to head of list for fast lookups */
4770 numeral->next = set->headnumeral;
4771 set->headnumeral = numeral;
4773 *buf = s_strdup(numeral->id);
4774 return (DEVFSADM_SUCCESS);
4779 * Looks up the specified cache for a match with a specified string
4780 * Returns:
4781 * -1 : on error.
4782 * 0/1/2 : Number of matches.
4783 * Returns the matching element only if there is a single match.
4784 * If the "uncached" flag is set, derives the "cmp_str" afresh
4785 * for the match instead of using cached values.
4787 static int
4788 lookup_enum_cache(numeral_set_t *set, char *cmp_str,
4789 devfsadm_enumerate_t rules[], int index, numeral_t **matchnpp)
4791 int matchcount = 0, rv = -1;
4792 int uncached;
4793 numeral_t *np;
4794 char *fcn = "lookup_enum_cache";
4795 char *cp;
4797 *matchnpp = NULL;
4799 assert(index < set->re_count);
4801 if (cmp_str == NULL) {
4802 return (-1);
4805 uncached = 0;
4806 if ((rules[index].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
4807 uncached = 1;
4811 * Check and see if a matching entry is already cached.
4813 for (np = set->headnumeral; np != NULL; np = np->next) {
4816 * Skip reserved IDs
4818 if (np->flags & NUMERAL_RESERVED) {
4819 vprint(RSRV_MID, "lookup_enum_cache: "
4820 "Cannot Match with reserved ID (%s), "
4821 "skipping\n", np->id);
4822 assert(np->flags == NUMERAL_RESERVED);
4823 continue;
4824 } else {
4825 vprint(RSRV_MID, "lookup_enum_cache: "
4826 "Attempting match with numeral ID: %s"
4827 " numeral flags = %d\n", np->id, np->flags);
4828 assert(np->flags == 0);
4831 if (np->cmp_str == NULL) {
4832 vprint(ENUM_MID, "%s: invalid entry in enumerate"
4833 " cache. path: %s\n", fcn, np->full_path);
4834 return (-1);
4837 if (uncached) {
4838 vprint(CHATTY_MID, "%s: bypassing enumerate cache."
4839 " path: %s\n", fcn, cmp_str);
4840 cp = alloc_cmp_str(np->full_path,
4841 &rules[np->rule_index]);
4842 if (cp == NULL)
4843 return (-1);
4844 rv = strcmp(cmp_str, cp);
4845 free(cp);
4846 } else {
4847 rv = strcmp(cmp_str, np->cmp_str);
4850 if (rv == 0) {
4851 if (matchcount++ != 0) {
4852 break; /* more than 1 match. */
4854 *matchnpp = np;
4858 return (matchcount);
4861 #ifdef DEBUG
4862 static void
4863 dump_enum_cache(numeral_set_t *setp)
4865 int i;
4866 numeral_t *np;
4867 char *fcn = "dump_enum_cache";
4869 vprint(ENUM_MID, "%s: re_count = %d\n", fcn, setp->re_count);
4870 for (i = 0; i < setp->re_count; i++) {
4871 vprint(ENUM_MID, "%s: re[%d] = %s\n", fcn, i, setp->re[i]);
4874 for (np = setp->headnumeral; np != NULL; np = np->next) {
4875 vprint(ENUM_MID, "%s: id: %s\n", fcn, np->id);
4876 vprint(ENUM_MID, "%s: full_path: %s\n", fcn, np->full_path);
4877 vprint(ENUM_MID, "%s: rule_index: %d\n", fcn, np->rule_index);
4878 vprint(ENUM_MID, "%s: cmp_str: %s\n", fcn, np->cmp_str);
4879 vprint(ENUM_MID, "%s: flags: %d\n", fcn, np->flags);
4882 #endif
4885 * For a given set of regular expressions in rules[], this function returns
4886 * either a previously cached struct numeral_set or it will create and
4887 * cache a new struct numeral_set. There is only one struct numeral_set
4888 * for the combination of REs present in rules[]. Each numeral_set contains
4889 * the regular expressions in rules[] used for cache selection AND a linked
4890 * list of struct numerals, ONE FOR EACH *UNIQUE* numeral or character ID
4891 * selected by the grouping parenthesized subexpression found in the last
4892 * path component of each rules[].re. For example, the RE: "rmt/([0-9]+)"
4893 * selects all the logical nodes of the correct form in dev/rmt/.
4894 * Each rmt/X will store a *single* struct numeral... ie 0, 1, 2 each get a
4895 * single struct numeral. There is no need to store more than a single logical
4896 * node matching X since the information desired in the devfspath would be
4897 * identical for the portion of the devfspath of interest. (the part up to,
4898 * but not including the minor name in this example.)
4900 * If the given numeral_set is not yet cached, call enumerate_recurse to
4901 * create it.
4903 static numeral_set_t *
4904 get_enum_cache(devfsadm_enumerate_t rules[], int nrules)
4906 /* linked list of numeral sets */
4907 numeral_set_t *setp;
4908 int i;
4909 int ret;
4910 char *path_left;
4911 enumerate_file_t *entry;
4912 char *fcn = "get_enum_cache";
4915 * See if we've already cached this numeral set.
4917 for (setp = head_numeral_set; setp != NULL; setp = setp->next) {
4919 * check all regexp's passed in function against
4920 * those in cached set.
4922 if (nrules != setp->re_count) {
4923 continue;
4926 for (i = 0; i < nrules; i++) {
4927 if (strcmp(setp->re[i], rules[i].re) != 0) {
4928 break;
4932 if (i == nrules) {
4933 return (setp);
4938 * If the MATCH_UNCACHED flag is set, we should not be here.
4940 for (i = 0; i < nrules; i++) {
4941 if ((rules[i].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
4942 vprint(ENUM_MID, "%s: invalid enumeration flags: "
4943 "0x%x\n", fcn, rules[i].flags);
4944 return (NULL);
4949 * Since we made it here, we have not yet cached the given set of
4950 * logical nodes matching the passed re. Create a cached entry
4951 * struct numeral_set and populate it with a minimal set of
4952 * logical nodes from /dev.
4955 setp = s_malloc(sizeof (numeral_set_t));
4956 setp->re = s_malloc(sizeof (char *) * nrules);
4957 for (i = 0; i < nrules; i++) {
4958 setp->re[i] = s_strdup(rules[i].re);
4960 setp->re_count = nrules;
4961 setp->headnumeral = NULL;
4963 /* put this new cached set on the cached set list */
4964 setp->next = head_numeral_set;
4965 head_numeral_set = setp;
4968 * For each RE, search the "reserved" list to create numeral IDs that
4969 * are reserved.
4971 for (entry = enumerate_reserved; entry; entry = entry->er_next) {
4973 vprint(RSRV_MID, "parsing rstring: %s\n", entry->er_file);
4975 for (i = 0; i < nrules; i++) {
4976 path_left = s_strdup(setp->re[i]);
4977 vprint(RSRV_MID, "parsing rule RE: %s\n", path_left);
4978 ret = enumerate_parse(entry->er_file, path_left,
4979 setp, rules, i);
4980 free(path_left);
4981 if (ret == 1) {
4983 * We found the reserved ID for this entry.
4984 * We still keep the entry since it is needed
4985 * by the new link bypass code in disks
4987 vprint(RSRV_MID, "found rsv ID: rstring: %s "
4988 "rule RE: %s\n", entry->er_file, path_left);
4989 break;
4995 * For each RE, search disk and cache any matches on the
4996 * numeral list.
4998 for (i = 0; i < nrules; i++) {
4999 path_left = s_strdup(setp->re[i]);
5000 enumerate_recurse(dev_dir, path_left, setp, rules, i);
5001 free(path_left);
5004 #ifdef DEBUG
5005 dump_enum_cache(setp);
5006 #endif
5008 return (setp);
5013 * This function stats the pathname namebuf. If this is a directory
5014 * entry, we recurse down dname/fname until we find the first symbolic
5015 * link, and then stat and return it. This is valid for the same reason
5016 * that we only need to read a single pathname for multiple matching
5017 * logical ID's... ie, all the logical nodes should contain identical
5018 * physical paths for the parts we are interested.
5021 get_stat_info(char *namebuf, struct stat *sb)
5023 char *cp;
5024 finddevhdl_t fhandle;
5025 const char *fp;
5027 if (lstat(namebuf, sb) < 0) {
5028 (void) err_print(LSTAT_FAILED, namebuf, strerror(errno));
5029 return (DEVFSADM_FAILURE);
5032 if ((sb->st_mode & S_IFMT) == S_IFLNK) {
5033 return (DEVFSADM_SUCCESS);
5037 * If it is a dir, recurse down until we find a link and
5038 * then use the link.
5040 if ((sb->st_mode & S_IFMT) == S_IFDIR) {
5042 if (finddev_readdir(namebuf, &fhandle) != 0) {
5043 return (DEVFSADM_FAILURE);
5047 * Search each dir entry looking for a symlink. Return
5048 * the first symlink found in namebuf. Recurse dirs.
5050 while ((fp = finddev_next(fhandle)) != NULL) {
5051 cp = namebuf + strlen(namebuf);
5052 if ((strlcat(namebuf, "/", PATH_MAX) >= PATH_MAX) ||
5053 (strlcat(namebuf, fp, PATH_MAX) >= PATH_MAX)) {
5054 *cp = '\0';
5055 finddev_close(fhandle);
5056 return (DEVFSADM_FAILURE);
5058 if (get_stat_info(namebuf, sb) == DEVFSADM_SUCCESS) {
5059 finddev_close(fhandle);
5060 return (DEVFSADM_SUCCESS);
5062 *cp = '\0';
5064 finddev_close(fhandle);
5067 /* no symlink found, so return error */
5068 return (DEVFSADM_FAILURE);
5072 * An existing matching ID was not found, so this function is called to
5073 * create the next lowest ID. In the INTEGER case, return the next
5074 * lowest unused integer. In the case of LETTER, return the next lowest
5075 * unused letter. Return empty string if all 26 are used.
5076 * Only IDs >= min will be returned.
5078 char *
5079 new_id(numeral_t *numeral, int type, char *min)
5081 int imin;
5082 temp_t *temp;
5083 temp_t *ptr;
5084 temp_t **previous;
5085 temp_t *head = NULL;
5086 char *retval;
5087 static char tempbuff[8];
5088 numeral_t *np;
5090 if (type == LETTER) {
5092 char letter[26], i;
5094 if (numeral == NULL) {
5095 return (s_strdup(min));
5098 for (i = 0; i < 26; i++) {
5099 letter[i] = 0;
5102 for (np = numeral; np != NULL; np = np->next) {
5103 assert(np->flags == 0 ||
5104 np->flags == NUMERAL_RESERVED);
5105 letter[*np->id - 'a']++;
5108 imin = *min - 'a';
5110 for (i = imin; i < 26; i++) {
5111 if (letter[i] == 0) {
5112 retval = s_malloc(2);
5113 retval[0] = 'a' + i;
5114 retval[1] = '\0';
5115 return (retval);
5119 return (s_strdup(""));
5122 if (type == INTEGER) {
5124 if (numeral == NULL) {
5125 return (s_strdup(min));
5128 imin = atoi(min);
5130 /* sort list */
5131 for (np = numeral; np != NULL; np = np->next) {
5132 assert(np->flags == 0 ||
5133 np->flags == NUMERAL_RESERVED);
5134 temp = s_malloc(sizeof (temp_t));
5135 temp->integer = atoi(np->id);
5136 temp->next = NULL;
5138 previous = &head;
5139 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5140 if (temp->integer < ptr->integer) {
5141 temp->next = ptr;
5142 *previous = temp;
5143 break;
5145 previous = &(ptr->next);
5147 if (ptr == NULL) {
5148 *previous = temp;
5152 /* now search sorted list for first hole >= imin */
5153 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5154 if (imin == ptr->integer) {
5155 imin++;
5156 } else {
5157 if (imin < ptr->integer) {
5158 break;
5164 /* free temp list */
5165 for (ptr = head; ptr != NULL; ) {
5166 temp = ptr;
5167 ptr = ptr->next;
5168 free(temp);
5171 (void) sprintf(tempbuff, "%d", imin);
5172 return (s_strdup(tempbuff));
5175 return (s_strdup(""));
5178 static int
5179 enumerate_parse(char *rsvstr, char *path_left, numeral_set_t *setp,
5180 devfsadm_enumerate_t rules[], int index)
5182 char *slash1 = NULL;
5183 char *slash2 = NULL;
5184 char *numeral_id;
5185 char *path_left_save;
5186 char *rsvstr_save;
5187 int ret = 0;
5188 static int warned = 0;
5190 rsvstr_save = rsvstr;
5191 path_left_save = path_left;
5193 if (rsvstr == NULL || rsvstr[0] == '\0' || rsvstr[0] == '/') {
5194 if (!warned) {
5195 err_print("invalid reserved filepath: %s\n",
5196 rsvstr ? rsvstr : "<NULL>");
5197 warned = 1;
5199 return (0);
5202 vprint(RSRV_MID, "processing rule: %s, rstring: %s\n",
5203 path_left, rsvstr);
5206 for (;;) {
5207 /* get rid of any extra '/' in the reserve string */
5208 while (*rsvstr == '/') {
5209 rsvstr++;
5212 /* get rid of any extra '/' in the RE */
5213 while (*path_left == '/') {
5214 path_left++;
5217 if (slash1 = strchr(path_left, '/')) {
5218 *slash1 = '\0';
5220 if (slash2 = strchr(rsvstr, '/')) {
5221 *slash2 = '\0';
5224 if ((slash1 != NULL) ^ (slash2 != NULL)) {
5225 ret = 0;
5226 vprint(RSRV_MID, "mismatch in # of path components\n");
5227 goto out;
5231 * Returns true if path_left matches the list entry.
5232 * If it is the last path component, pass subexp
5233 * so that it will return the corresponding ID in
5234 * numeral_id.
5236 numeral_id = NULL;
5237 if (match_path_component(path_left, rsvstr, &numeral_id,
5238 slash1 ? 0 : rules[index].subexp)) {
5240 /* We have a match. */
5241 if (slash1 == NULL) {
5242 /* Is last path component */
5243 vprint(RSRV_MID, "match and last component\n");
5244 create_reserved_numeral(setp, numeral_id);
5245 if (numeral_id != NULL) {
5246 free(numeral_id);
5248 ret = 1;
5249 goto out;
5250 } else {
5251 /* Not last path component. Continue parsing */
5252 *slash1 = '/';
5253 *slash2 = '/';
5254 path_left = slash1 + 1;
5255 rsvstr = slash2 + 1;
5256 vprint(RSRV_MID,
5257 "match and NOT last component\n");
5258 continue;
5260 } else {
5261 /* No match */
5262 ret = 0;
5263 vprint(RSRV_MID, "No match: rule RE = %s, "
5264 "rstring = %s\n", path_left, rsvstr);
5265 goto out;
5269 out:
5270 if (slash1)
5271 *slash1 = '/';
5272 if (slash2)
5273 *slash2 = '/';
5275 if (ret == 1) {
5276 vprint(RSRV_MID, "match: rule RE: %s, rstring: %s\n",
5277 path_left_save, rsvstr_save);
5278 } else {
5279 vprint(RSRV_MID, "NO match: rule RE: %s, rstring: %s\n",
5280 path_left_save, rsvstr_save);
5283 return (ret);
5287 * Search current_dir for all files which match the first path component
5288 * of path_left, which is an RE. If a match is found, but there are more
5289 * components of path_left, then recurse, otherwise, if we have reached
5290 * the last component of path_left, call create_cached_numerals for each
5291 * file. At some point, recurse_dev_re() should be rewritten so that this
5292 * function can be eliminated.
5294 static void
5295 enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
5296 devfsadm_enumerate_t rules[], int index)
5298 char *slash;
5299 char *new_path;
5300 char *numeral_id;
5301 finddevhdl_t fhandle;
5302 const char *fp;
5304 if (finddev_readdir(current_dir, &fhandle) != 0) {
5305 return;
5308 /* get rid of any extra '/' */
5309 while (*path_left == '/') {
5310 path_left++;
5313 if (slash = strchr(path_left, '/')) {
5314 *slash = '\0';
5317 while ((fp = finddev_next(fhandle)) != NULL) {
5320 * Returns true if path_left matches the list entry.
5321 * If it is the last path component, pass subexp
5322 * so that it will return the corresponding ID in
5323 * numeral_id.
5325 numeral_id = NULL;
5326 if (match_path_component(path_left, (char *)fp, &numeral_id,
5327 slash ? 0 : rules[index].subexp)) {
5329 new_path = s_malloc(strlen(current_dir) +
5330 strlen(fp) + 2);
5332 (void) strcpy(new_path, current_dir);
5333 (void) strcat(new_path, "/");
5334 (void) strcat(new_path, fp);
5336 if (slash != NULL) {
5337 enumerate_recurse(new_path, slash + 1,
5338 setp, rules, index);
5339 } else {
5340 create_cached_numeral(new_path, setp,
5341 numeral_id, rules, index);
5342 if (numeral_id != NULL) {
5343 free(numeral_id);
5346 free(new_path);
5350 if (slash != NULL) {
5351 *slash = '/';
5353 finddev_close(fhandle);
5358 * Returns true if file matches file_re. If subexp is non-zero, it means
5359 * we are searching the last path component and need to return the
5360 * parenthesized subexpression subexp in id.
5363 static int
5364 match_path_component(char *file_re, char *file, char **id, int subexp)
5366 regex_t re1;
5367 int match = 0;
5368 int nelements;
5369 regmatch_t *pmatch;
5371 if (subexp != 0) {
5372 nelements = subexp + 1;
5373 pmatch =
5374 (regmatch_t *)s_malloc(sizeof (regmatch_t) * nelements);
5375 } else {
5376 pmatch = NULL;
5377 nelements = 0;
5380 if (regcomp(&re1, file_re, REG_EXTENDED) != 0) {
5381 if (pmatch != NULL) {
5382 free(pmatch);
5384 return (0);
5387 if (regexec(&re1, file, nelements, pmatch, 0) == 0) {
5388 match = 1;
5391 if ((match != 0) && (subexp != 0)) {
5392 int size = pmatch[subexp].rm_eo - pmatch[subexp].rm_so;
5393 *id = s_malloc(size + 1);
5394 (void) strncpy(*id, &file[pmatch[subexp].rm_so], size);
5395 (*id)[size] = '\0';
5398 if (pmatch != NULL) {
5399 free(pmatch);
5401 regfree(&re1);
5402 return (match);
5405 static void
5406 create_reserved_numeral(numeral_set_t *setp, char *numeral_id)
5408 numeral_t *np;
5410 vprint(RSRV_MID, "Attempting to create reserved numeral: %s\n",
5411 numeral_id);
5414 * We found a numeral_id from an entry in the enumerate_reserved file
5415 * which matched the re passed in from devfsadm_enumerate. We only
5416 * need to make sure ONE copy of numeral_id exists on the numeral list.
5417 * We only need to store /dev/dsk/cNtod0s0 and no other entries
5418 * hanging off of controller N.
5420 for (np = setp->headnumeral; np != NULL; np = np->next) {
5421 if (strcmp(numeral_id, np->id) == 0) {
5422 vprint(RSRV_MID, "ID: %s, already reserved\n", np->id);
5423 assert(np->flags == NUMERAL_RESERVED);
5424 return;
5425 } else {
5426 assert(np->flags == 0 ||
5427 np->flags == NUMERAL_RESERVED);
5431 /* NOT on list, so add it */
5432 np = s_malloc(sizeof (numeral_t));
5433 np->id = s_strdup(numeral_id);
5434 np->full_path = NULL;
5435 np->rule_index = 0;
5436 np->cmp_str = NULL;
5437 np->flags = NUMERAL_RESERVED;
5438 np->next = setp->headnumeral;
5439 setp->headnumeral = np;
5441 vprint(RSRV_MID, "Reserved numeral ID: %s\n", np->id);
5445 * This function is called for every file which matched the leaf
5446 * component of the RE. If the "numeral_id" is not already on the
5447 * numeral set's numeral list, add it and its physical path.
5449 static void
5450 create_cached_numeral(char *path, numeral_set_t *setp, char *numeral_id,
5451 devfsadm_enumerate_t rules[], int index)
5453 char linkbuf[PATH_MAX + 1];
5454 char lpath[PATH_MAX + 1];
5455 char *linkptr, *cmp_str;
5456 numeral_t *np;
5457 int linksize;
5458 struct stat sb;
5459 char *contents;
5460 const char *fcn = "create_cached_numeral";
5462 assert(index >= 0 && index < setp->re_count);
5463 assert(strcmp(rules[index].re, setp->re[index]) == 0);
5466 * We found a numeral_id from an entry in /dev which matched
5467 * the re passed in from devfsadm_enumerate. We only need to make sure
5468 * ONE copy of numeral_id exists on the numeral list. We only need
5469 * to store /dev/dsk/cNtod0s0 and no other entries hanging off
5470 * of controller N.
5472 for (np = setp->headnumeral; np != NULL; np = np->next) {
5473 assert(np->flags == 0 || np->flags == NUMERAL_RESERVED);
5474 if (strcmp(numeral_id, np->id) == 0) {
5476 * Note that we can't assert that the flags field
5477 * of the numeral is 0, since both reserved and
5478 * unreserved links in /dev come here
5480 if (np->flags == NUMERAL_RESERVED) {
5481 vprint(RSRV_MID, "ID derived from /dev link is"
5482 " reserved: %s\n", np->id);
5483 } else {
5484 vprint(RSRV_MID, "ID derived from /dev link is"
5485 " NOT reserved: %s\n", np->id);
5487 return;
5491 /* NOT on list, so add it */
5493 (void) strcpy(lpath, path);
5495 * If path is a dir, it is changed to the first symbolic link it find
5496 * if it finds one.
5498 if (get_stat_info(lpath, &sb) == DEVFSADM_FAILURE) {
5499 return;
5502 /* If we get here, we found a symlink */
5503 linksize = readlink(lpath, linkbuf, PATH_MAX);
5505 if (linksize <= 0) {
5506 err_print(READLINK_FAILED, fcn, lpath, strerror(errno));
5507 return;
5510 linkbuf[linksize] = '\0';
5513 * redirect alias path to current path
5514 * devi_root_node is protected by lock_dev()
5516 contents = di_alias2curr(devi_root_node, linkbuf);
5519 * the following just points linkptr to the root of the /devices
5520 * node if it is a minor node, otherwise, to the first char of
5521 * linkbuf if it is a link.
5523 (void) is_minor_node(contents, &linkptr);
5525 cmp_str = alloc_cmp_str(linkptr, &rules[index]);
5526 if (cmp_str == NULL) {
5527 free(contents);
5528 return;
5531 np = s_malloc(sizeof (numeral_t));
5533 np->id = s_strdup(numeral_id);
5534 np->full_path = s_strdup(linkptr);
5535 np->rule_index = index;
5536 np->cmp_str = cmp_str;
5537 np->flags = 0;
5539 np->next = setp->headnumeral;
5540 setp->headnumeral = np;
5542 free(contents);
5547 * This should be called either before or after granting access to a
5548 * command line version of devfsadm running, since it may have changed
5549 * the state of /dev. It forces future enumerate calls to re-build
5550 * cached information from /dev.
5552 void
5553 invalidate_enumerate_cache(void)
5555 numeral_set_t *setp;
5556 numeral_set_t *savedsetp;
5557 numeral_t *savednumset;
5558 numeral_t *numset;
5559 int i;
5561 for (setp = head_numeral_set; setp != NULL; ) {
5563 * check all regexp's passed in function against
5564 * those in cached set.
5567 savedsetp = setp;
5568 setp = setp->next;
5570 for (i = 0; i < savedsetp->re_count; i++) {
5571 free(savedsetp->re[i]);
5573 free(savedsetp->re);
5575 for (numset = savedsetp->headnumeral; numset != NULL; ) {
5576 savednumset = numset;
5577 numset = numset->next;
5578 assert(savednumset->rule_index < savedsetp->re_count);
5579 free(savednumset->id);
5580 free(savednumset->full_path);
5581 free(savednumset->cmp_str);
5582 free(savednumset);
5584 free(savedsetp);
5586 head_numeral_set = NULL;
5590 * Copies over links from /dev to <root>/dev and device special files in
5591 * /devices to <root>/devices, preserving the existing file modes. If
5592 * the link or special file already exists on <root>, skip the copy. (it
5593 * would exist only if a package hard coded it there, so assume package
5594 * knows best?). Use /etc/name_to_major and <root>/etc/name_to_major to
5595 * make translations for major numbers on device special files. No need to
5596 * make a translation on minor_perm since if the file was created in the
5597 * miniroot then it would presumably have the same minor_perm entry in
5598 * <root>/etc/minor_perm. To be used only by install.
5601 devfsadm_copy(void)
5603 char filename[PATH_MAX + 1];
5605 /* load the installed root's name_to_major for translations */
5606 (void) snprintf(filename, sizeof (filename), "%s%s", root_dir,
5607 NAME_TO_MAJOR);
5608 if (load_n2m_table(filename) == DEVFSADM_FAILURE) {
5609 return (DEVFSADM_FAILURE);
5612 /* Copy /dev to target disk. No need to copy /devices with devfs */
5613 (void) nftw(DEV, devfsadm_copy_file, 20, FTW_PHYS);
5615 /* Let install handle copying over path_to_inst */
5617 return (DEVFSADM_SUCCESS);
5621 * This function copies links, dirs, and device special files.
5622 * Note that it always returns DEVFSADM_SUCCESS, so that nftw doesn't
5623 * abort.
5625 /*ARGSUSED*/
5626 static int
5627 devfsadm_copy_file(const char *file, const struct stat *stat,
5628 int flags, struct FTW *ftw)
5630 struct stat sp;
5631 dev_t newdev;
5632 char newfile[PATH_MAX + 1];
5633 char linkcontents[PATH_MAX + 1];
5634 int bytes;
5635 const char *fcn = "devfsadm_copy_file";
5637 (void) strcpy(newfile, root_dir);
5638 (void) strcat(newfile, "/");
5639 (void) strcat(newfile, file);
5641 if (lstat(newfile, &sp) == 0) {
5642 /* newfile already exists, so no need to continue */
5643 return (DEVFSADM_SUCCESS);
5646 if (((stat->st_mode & S_IFMT) == S_IFBLK) ||
5647 ((stat->st_mode & S_IFMT) == S_IFCHR)) {
5648 if (translate_major(stat->st_rdev, &newdev) ==
5649 DEVFSADM_FAILURE) {
5650 return (DEVFSADM_SUCCESS);
5652 if (mknod(newfile, stat->st_mode, newdev) == -1) {
5653 err_print(MKNOD_FAILED, newfile, strerror(errno));
5654 return (DEVFSADM_SUCCESS);
5656 } else if ((stat->st_mode & S_IFMT) == S_IFDIR) {
5657 if (mknod(newfile, stat->st_mode, 0) == -1) {
5658 err_print(MKNOD_FAILED, newfile, strerror(errno));
5659 return (DEVFSADM_SUCCESS);
5661 } else if ((stat->st_mode & S_IFMT) == S_IFLNK) {
5663 * No need to redirect alias paths. We want a
5664 * true copy. The system on first boot after install
5665 * will redirect paths
5667 if ((bytes = readlink(file, linkcontents, PATH_MAX)) == -1) {
5668 err_print(READLINK_FAILED, fcn, file, strerror(errno));
5669 return (DEVFSADM_SUCCESS);
5671 linkcontents[bytes] = '\0';
5672 if (symlink(linkcontents, newfile) == -1) {
5673 err_print(SYMLINK_FAILED, newfile, newfile,
5674 strerror(errno));
5675 return (DEVFSADM_SUCCESS);
5679 (void) lchown(newfile, stat->st_uid, stat->st_gid);
5680 return (DEVFSADM_SUCCESS);
5684 * Given a dev_t from the running kernel, return the new_dev_t
5685 * by translating to the major number found on the installed
5686 * target's root name_to_major file.
5688 static int
5689 translate_major(dev_t old_dev, dev_t *new_dev)
5691 major_t oldmajor;
5692 major_t newmajor;
5693 minor_t oldminor;
5694 minor_t newminor;
5695 char cdriver[FILENAME_MAX + 1];
5696 char driver[FILENAME_MAX + 1];
5697 char *fcn = "translate_major: ";
5699 oldmajor = major(old_dev);
5700 if (modctl(MODGETNAME, driver, sizeof (driver), &oldmajor) != 0) {
5701 return (DEVFSADM_FAILURE);
5704 if (strcmp(driver, "clone") != 0) {
5705 /* non-clone case */
5707 /* look up major number is target's name2major */
5708 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5709 return (DEVFSADM_FAILURE);
5712 *new_dev = makedev(newmajor, minor(old_dev));
5713 if (old_dev != *new_dev) {
5714 vprint(CHATTY_MID, "%sdriver: %s old: %lu,%lu "
5715 "new: %lu,%lu\n", fcn, driver, major(old_dev),
5716 minor(old_dev), major(*new_dev), minor(*new_dev));
5718 return (DEVFSADM_SUCCESS);
5719 } else {
5721 * The clone is a special case. Look at its minor
5722 * number since it is the major number of the real driver.
5724 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5725 return (DEVFSADM_FAILURE);
5728 oldminor = minor(old_dev);
5729 if (modctl(MODGETNAME, cdriver, sizeof (cdriver),
5730 &oldminor) != 0) {
5731 err_print(MODGETNAME_FAILED, oldminor);
5732 return (DEVFSADM_FAILURE);
5735 if (get_major_no(cdriver, &newminor) == DEVFSADM_FAILURE) {
5736 return (DEVFSADM_FAILURE);
5739 *new_dev = makedev(newmajor, newminor);
5740 if (old_dev != *new_dev) {
5741 vprint(CHATTY_MID, "%sdriver: %s old: "
5742 "%lu,%lu new: %lu,%lu\n", fcn, driver,
5743 major(old_dev), minor(old_dev),
5744 major(*new_dev), minor(*new_dev));
5746 return (DEVFSADM_SUCCESS);
5752 * Find the major number for driver, searching the n2m_list that was
5753 * built in load_n2m_table().
5755 static int
5756 get_major_no(char *driver, major_t *major)
5758 n2m_t *ptr;
5760 for (ptr = n2m_list; ptr != NULL; ptr = ptr->next) {
5761 if (strcmp(ptr->driver, driver) == 0) {
5762 *major = ptr->major;
5763 return (DEVFSADM_SUCCESS);
5766 err_print(FIND_MAJOR_FAILED, driver);
5767 return (DEVFSADM_FAILURE);
5771 * Loads a name_to_major table into memory. Used only for suninstall's
5772 * private -R option to devfsadm, to translate major numbers from the
5773 * running to the installed target disk.
5775 static int
5776 load_n2m_table(char *file)
5778 FILE *fp;
5779 char line[1024], *cp;
5780 char driver[PATH_MAX + 1];
5781 major_t major;
5782 n2m_t *ptr;
5783 int ln = 0;
5785 if ((fp = fopen(file, "r")) == NULL) {
5786 err_print(FOPEN_FAILED, file, strerror(errno));
5787 return (DEVFSADM_FAILURE);
5790 while (fgets(line, sizeof (line), fp) != NULL) {
5791 ln++;
5792 /* cut off comments starting with '#' */
5793 if ((cp = strchr(line, '#')) != NULL)
5794 *cp = '\0';
5795 /* ignore comment or blank lines */
5796 if (is_blank(line))
5797 continue;
5798 /* sanity-check */
5799 if (sscanf(line, "%1024s%lu", driver, &major) != 2) {
5800 err_print(IGNORING_LINE_IN, ln, file);
5801 continue;
5803 ptr = (n2m_t *)s_malloc(sizeof (n2m_t));
5804 ptr->major = major;
5805 ptr->driver = s_strdup(driver);
5806 ptr->next = n2m_list;
5807 n2m_list = ptr;
5809 if (fclose(fp) == EOF) {
5810 err_print(FCLOSE_FAILED, file, strerror(errno));
5812 return (DEVFSADM_SUCCESS);
5816 * Called at devfsadm startup to read the file /etc/dev/enumerate_reserved
5817 * Creates a linked list of devlinks from which reserved IDs can be derived
5819 static void
5820 read_enumerate_file(void)
5822 FILE *fp;
5823 int linenum;
5824 char line[PATH_MAX+1];
5825 enumerate_file_t *entry;
5826 struct stat current_sb;
5827 static struct stat cached_sb;
5828 static int cached = FALSE;
5830 assert(enumerate_file);
5832 if (stat(enumerate_file, &current_sb) == -1) {
5833 vprint(RSRV_MID, "No reserved file: %s\n", enumerate_file);
5834 cached = FALSE;
5835 if (enumerate_reserved != NULL) {
5836 vprint(RSRV_MID, "invalidating %s cache\n",
5837 enumerate_file);
5839 while (enumerate_reserved != NULL) {
5840 entry = enumerate_reserved;
5841 enumerate_reserved = entry->er_next;
5842 free(entry->er_file);
5843 free(entry->er_id);
5844 free(entry);
5846 return;
5849 /* if already cached, check to see if it is still valid */
5850 if (cached == TRUE) {
5852 if (current_sb.st_mtime == cached_sb.st_mtime) {
5853 vprint(RSRV_MID, "%s cache valid\n", enumerate_file);
5854 vprint(FILES_MID, "%s cache valid\n", enumerate_file);
5855 return;
5858 vprint(RSRV_MID, "invalidating %s cache\n", enumerate_file);
5859 vprint(FILES_MID, "invalidating %s cache\n", enumerate_file);
5861 while (enumerate_reserved != NULL) {
5862 entry = enumerate_reserved;
5863 enumerate_reserved = entry->er_next;
5864 free(entry->er_file);
5865 free(entry->er_id);
5866 free(entry);
5868 vprint(RSRV_MID, "Recaching file: %s\n", enumerate_file);
5869 } else {
5870 vprint(RSRV_MID, "Caching file (first time): %s\n",
5871 enumerate_file);
5872 cached = TRUE;
5875 (void) stat(enumerate_file, &cached_sb);
5877 if ((fp = fopen(enumerate_file, "r")) == NULL) {
5878 err_print(FOPEN_FAILED, enumerate_file, strerror(errno));
5879 return;
5882 vprint(RSRV_MID, "Reading reserve file: %s\n", enumerate_file);
5883 linenum = 0;
5884 while (fgets(line, sizeof (line), fp) != NULL) {
5885 char *cp, *ncp;
5887 linenum++;
5889 /* remove newline */
5890 cp = strchr(line, '\n');
5891 if (cp)
5892 *cp = '\0';
5894 vprint(RSRV_MID, "Reserve file: line %d: %s\n", linenum, line);
5896 /* skip over space and tab */
5897 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
5900 if (*cp == '\0' || *cp == '#') {
5901 vprint(RSRV_MID, "Skipping line: '%s'\n", line);
5902 continue; /* blank line or comment line */
5905 ncp = cp;
5907 /* delete trailing blanks */
5908 for (; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
5910 *cp = '\0';
5912 entry = s_zalloc(sizeof (enumerate_file_t));
5913 entry->er_file = s_strdup(ncp);
5914 entry->er_id = NULL;
5915 entry->er_next = enumerate_reserved;
5916 enumerate_reserved = entry;
5919 if (fclose(fp) == EOF) {
5920 err_print(FCLOSE_FAILED, enumerate_file, strerror(errno));
5925 * Called at devfsadm startup to read in the devlink.tab file. Creates
5926 * a linked list of devlinktab_list structures which will be
5927 * searched for every minor node.
5929 static void
5930 read_devlinktab_file(void)
5932 devlinktab_list_t *headp = NULL;
5933 devlinktab_list_t *entryp;
5934 devlinktab_list_t **previous;
5935 devlinktab_list_t *save;
5936 char line[MAX_DEVLINK_LINE], *cp;
5937 char *selector;
5938 char *p_link;
5939 char *s_link;
5940 FILE *fp;
5941 int i;
5942 static struct stat cached_sb;
5943 struct stat current_sb;
5944 static int cached = FALSE;
5946 if (devlinktab_file == NULL) {
5947 return;
5950 (void) stat(devlinktab_file, &current_sb);
5952 /* if already cached, check to see if it is still valid */
5953 if (cached == TRUE) {
5955 if (current_sb.st_mtime == cached_sb.st_mtime) {
5956 vprint(FILES_MID, "%s cache valid\n", devlinktab_file);
5957 return;
5960 vprint(FILES_MID, "invalidating %s cache\n", devlinktab_file);
5962 while (devlinktab_list != NULL) {
5963 free_link_list(devlinktab_list->p_link);
5964 free_link_list(devlinktab_list->s_link);
5965 free_selector_list(devlinktab_list->selector);
5966 free(devlinktab_list->selector_pattern);
5967 free(devlinktab_list->p_link_pattern);
5968 if (devlinktab_list->s_link_pattern != NULL) {
5969 free(devlinktab_list->s_link_pattern);
5971 save = devlinktab_list;
5972 devlinktab_list = devlinktab_list->next;
5973 free(save);
5975 } else {
5976 cached = TRUE;
5979 (void) stat(devlinktab_file, &cached_sb);
5981 if ((fp = fopen(devlinktab_file, "r")) == NULL) {
5982 err_print(FOPEN_FAILED, devlinktab_file, strerror(errno));
5983 return;
5986 previous = &headp;
5988 while (fgets(line, sizeof (line), fp) != NULL) {
5989 devlinktab_line++;
5990 i = strlen(line);
5991 if (line[i-1] == NEWLINE) {
5992 line[i-1] = '\0';
5993 } else if (i == sizeof (line-1)) {
5994 err_print(LINE_TOO_LONG, devlinktab_line,
5995 devlinktab_file, sizeof (line)-1);
5996 while (((i = getc(fp)) != '\n') && (i != EOF))
5998 continue;
6001 /* cut off comments starting with '#' */
6002 if ((cp = strchr(line, '#')) != NULL)
6003 *cp = '\0';
6004 /* ignore comment or blank lines */
6005 if (is_blank(line))
6006 continue;
6008 vprint(DEVLINK_MID, "table: %s line %d: '%s'\n",
6009 devlinktab_file, devlinktab_line, line);
6011 /* break each entry into fields. s_link may be NULL */
6012 if (split_devlinktab_entry(line, &selector, &p_link,
6013 &s_link) == DEVFSADM_FAILURE) {
6014 vprint(DEVLINK_MID, "split_entry returns failure\n");
6015 continue;
6016 } else {
6017 vprint(DEVLINK_MID, "split_entry selector='%s' "
6018 "p_link='%s' s_link='%s'\n\n", selector,
6019 p_link, (s_link == NULL) ? "" : s_link);
6022 entryp =
6023 (devlinktab_list_t *)s_malloc(sizeof (devlinktab_list_t));
6025 entryp->line_number = devlinktab_line;
6027 if ((entryp->selector = create_selector_list(selector))
6028 == NULL) {
6029 free(entryp);
6030 continue;
6032 entryp->selector_pattern = s_strdup(selector);
6034 if ((entryp->p_link = create_link_list(p_link)) == NULL) {
6035 free_selector_list(entryp->selector);
6036 free(entryp->selector_pattern);
6037 free(entryp);
6038 continue;
6041 entryp->p_link_pattern = s_strdup(p_link);
6043 if (s_link != NULL) {
6044 if ((entryp->s_link =
6045 create_link_list(s_link)) == NULL) {
6046 free_selector_list(entryp->selector);
6047 free_link_list(entryp->p_link);
6048 free(entryp->selector_pattern);
6049 free(entryp->p_link_pattern);
6050 free(entryp);
6051 continue;
6053 entryp->s_link_pattern = s_strdup(s_link);
6054 } else {
6055 entryp->s_link = NULL;
6056 entryp->s_link_pattern = NULL;
6060 /* append to end of list */
6062 entryp->next = NULL;
6063 *previous = entryp;
6064 previous = &(entryp->next);
6066 if (fclose(fp) == EOF) {
6067 err_print(FCLOSE_FAILED, devlinktab_file, strerror(errno));
6069 devlinktab_list = headp;
6074 * For a single line entry in devlink.tab, split the line into fields
6075 * selector, p_link, and an optionally s_link. If s_link field is not
6076 * present, then return NULL in s_link (not NULL string).
6078 static int
6079 split_devlinktab_entry(char *entry, char **selector, char **p_link,
6080 char **s_link)
6082 char *tab;
6084 *selector = entry;
6086 if ((tab = strchr(entry, TAB)) != NULL) {
6087 *tab = '\0';
6088 *p_link = ++tab;
6089 } else {
6090 err_print(MISSING_TAB, devlinktab_line, devlinktab_file);
6091 return (DEVFSADM_FAILURE);
6094 if (**p_link == '\0') {
6095 err_print(MISSING_DEVNAME, devlinktab_line, devlinktab_file);
6096 return (DEVFSADM_FAILURE);
6099 if ((tab = strchr(*p_link, TAB)) != NULL) {
6100 *tab = '\0';
6101 *s_link = ++tab;
6102 if (strchr(*s_link, TAB) != NULL) {
6103 err_print(TOO_MANY_FIELDS, devlinktab_line,
6104 devlinktab_file);
6105 return (DEVFSADM_FAILURE);
6107 } else {
6108 *s_link = NULL;
6111 return (DEVFSADM_SUCCESS);
6115 * For a given devfs_spec field, for each element in the field, add it to
6116 * a linked list of devfs_spec structures. Return the linked list in
6117 * devfs_spec_list.
6119 static selector_list_t *
6120 create_selector_list(char *selector)
6122 char *key;
6123 char *val;
6124 int error = FALSE;
6125 selector_list_t *head_selector_list = NULL;
6126 selector_list_t *selector_list;
6128 /* parse_devfs_spec splits the next field into keyword & value */
6129 while ((*selector != '\0') && (error == FALSE)) {
6130 if (parse_selector(&selector, &key, &val) == DEVFSADM_FAILURE) {
6131 error = TRUE;
6132 break;
6133 } else {
6134 selector_list = (selector_list_t *)
6135 s_malloc(sizeof (selector_list_t));
6136 if (strcmp(NAME_S, key) == 0) {
6137 selector_list->key = NAME;
6138 } else if (strcmp(TYPE_S, key) == 0) {
6139 selector_list->key = TYPE;
6140 } else if (strncmp(ADDR_S, key, ADDR_S_LEN) == 0) {
6141 selector_list->key = ADDR;
6142 if (key[ADDR_S_LEN] == '\0') {
6143 selector_list->arg = 0;
6144 } else if (isdigit(key[ADDR_S_LEN]) != FALSE) {
6145 selector_list->arg =
6146 atoi(&key[ADDR_S_LEN]);
6147 } else {
6148 error = TRUE;
6149 free(selector_list);
6150 err_print(BADKEYWORD, key,
6151 devlinktab_line, devlinktab_file);
6152 break;
6154 } else if (strncmp(MINOR_S, key, MINOR_S_LEN) == 0) {
6155 selector_list->key = MINOR;
6156 if (key[MINOR_S_LEN] == '\0') {
6157 selector_list->arg = 0;
6158 } else if (isdigit(key[MINOR_S_LEN]) != FALSE) {
6159 selector_list->arg =
6160 atoi(&key[MINOR_S_LEN]);
6161 } else {
6162 error = TRUE;
6163 free(selector_list);
6164 err_print(BADKEYWORD, key,
6165 devlinktab_line, devlinktab_file);
6166 break;
6168 vprint(DEVLINK_MID, "MINOR = %s\n", val);
6169 } else {
6170 err_print(UNRECOGNIZED_KEY, key,
6171 devlinktab_line, devlinktab_file);
6172 error = TRUE;
6173 free(selector_list);
6174 break;
6176 selector_list->val = s_strdup(val);
6177 selector_list->next = head_selector_list;
6178 head_selector_list = selector_list;
6179 vprint(DEVLINK_MID, "key='%s' val='%s' arg=%d\n",
6180 key, val, selector_list->arg);
6184 if ((error == FALSE) && (head_selector_list != NULL)) {
6185 return (head_selector_list);
6186 } else {
6187 /* parse failed. Free any allocated structs */
6188 free_selector_list(head_selector_list);
6189 return (NULL);
6194 * Takes a semicolon separated list of selector elements and breaks up
6195 * into a keyword-value pair. semicolon and equal characters are
6196 * replaced with NULL's. On success, selector is updated to point to the
6197 * terminating NULL character terminating the keyword-value pair, and the
6198 * function returns DEVFSADM_SUCCESS. If there is a syntax error,
6199 * devfs_spec is not modified and function returns DEVFSADM_FAILURE.
6201 static int
6202 parse_selector(char **selector, char **key, char **val)
6204 char *equal;
6205 char *semi_colon;
6207 *key = *selector;
6209 if ((equal = strchr(*key, '=')) != NULL) {
6210 *equal = '\0';
6211 } else {
6212 err_print(MISSING_EQUAL, devlinktab_line, devlinktab_file);
6213 return (DEVFSADM_FAILURE);
6216 *val = ++equal;
6217 if ((semi_colon = strchr(equal, ';')) != NULL) {
6218 *semi_colon = '\0';
6219 *selector = semi_colon + 1;
6220 } else {
6221 *selector = equal + strlen(equal);
6223 return (DEVFSADM_SUCCESS);
6227 * link is either the second or third field of devlink.tab. Parse link
6228 * into a linked list of devlink structures and return ptr to list. Each
6229 * list element is either a constant string, or one of the following
6230 * escape sequences: \M, \A, \N, or \D. The first three escape sequences
6231 * take a numerical argument.
6233 static link_list_t *
6234 create_link_list(char *link)
6236 int x = 0;
6237 int error = FALSE;
6238 int counter_found = FALSE;
6239 link_list_t *head = NULL;
6240 link_list_t **ptr;
6241 link_list_t *link_list;
6242 char constant[MAX_DEVLINK_LINE];
6243 char *error_str;
6245 if (link == NULL) {
6246 return (NULL);
6249 while ((*link != '\0') && (error == FALSE)) {
6250 link_list = (link_list_t *)s_malloc(sizeof (link_list_t));
6251 link_list->next = NULL;
6253 while ((*link != '\0') && (*link != '\\')) {
6254 /* a non-escaped string */
6255 constant[x++] = *(link++);
6257 if (x != 0) {
6258 constant[x] = '\0';
6259 link_list->type = CONSTANT;
6260 link_list->constant = s_strdup(constant);
6261 x = 0;
6262 vprint(DEVLINK_MID, "CONSTANT FOUND %s\n", constant);
6263 } else {
6264 switch (*(++link)) {
6265 case 'M':
6266 link_list->type = MINOR;
6267 break;
6268 case 'A':
6269 link_list->type = ADDR;
6270 break;
6271 case 'N':
6272 if (counter_found == TRUE) {
6273 error = TRUE;
6274 error_str =
6275 "multiple counters not permitted";
6276 free(link_list);
6277 } else {
6278 counter_found = TRUE;
6279 link_list->type = COUNTER;
6281 break;
6282 case 'D':
6283 link_list->type = NAME;
6284 break;
6285 default:
6286 error = TRUE;
6287 free(link_list);
6288 error_str = "unrecognized escape sequence";
6289 break;
6291 if (*(link++) != 'D') {
6292 if (isdigit(*link) == FALSE) {
6293 error_str = "escape sequence must be "
6294 "followed by a digit\n";
6295 error = TRUE;
6296 free(link_list);
6297 } else {
6298 link_list->arg =
6299 (int)strtoul(link, &link, 10);
6300 vprint(DEVLINK_MID, "link_list->arg = "
6301 "%d\n", link_list->arg);
6305 /* append link_list struct to end of list */
6306 if (error == FALSE) {
6307 for (ptr = &head; *ptr != NULL; ptr = &((*ptr)->next))
6309 *ptr = link_list;
6313 if (error == FALSE) {
6314 return (head);
6315 } else {
6316 err_print(CONFIG_INCORRECT, devlinktab_line, devlinktab_file,
6317 error_str);
6318 free_link_list(head);
6319 return (NULL);
6324 * Called for each minor node devfsadm processes; for each minor node,
6325 * look for matches in the devlinktab_list list which was created on
6326 * startup read_devlinktab_file(). If there is a match, call build_links()
6327 * to build a logical devlink and a possible extra devlink.
6329 static int
6330 process_devlink_compat(di_minor_t minor, di_node_t node)
6332 int link_built = FALSE;
6333 devlinktab_list_t *entry;
6334 char *nodetype;
6335 char *dev_path;
6337 if (devlinks_debug == TRUE) {
6338 nodetype = di_minor_nodetype(minor);
6339 assert(nodetype != NULL);
6340 if ((dev_path = di_devfs_path(node)) != NULL) {
6341 vprint(INFO_MID, "'%s' entry: %s:%s\n",
6342 nodetype, dev_path,
6343 di_minor_name(minor) ? di_minor_name(minor) : "");
6344 di_devfs_path_free(dev_path);
6350 /* don't process devlink.tab if devfsadm invoked with -c <class> */
6351 if (num_classes > 0) {
6352 return (FALSE);
6355 for (entry = devlinktab_list; entry != NULL; entry = entry->next) {
6356 if (devlink_matches(entry, minor, node) == DEVFSADM_SUCCESS) {
6357 link_built = TRUE;
6358 (void) build_links(entry, minor, node);
6361 return (link_built);
6365 * For a given devlink.tab devlinktab_list entry, see if the selector
6366 * field matches this minor node. If it does, return DEVFSADM_SUCCESS,
6367 * otherwise DEVFSADM_FAILURE.
6369 static int
6370 devlink_matches(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6372 selector_list_t *selector = entry->selector;
6373 char *addr;
6374 char *minor_name;
6375 char *node_type;
6377 for (; selector != NULL; selector = selector->next) {
6378 switch (selector->key) {
6379 case NAME:
6380 if (strcmp(di_node_name(node), selector->val) != 0) {
6381 return (DEVFSADM_FAILURE);
6383 break;
6384 case TYPE:
6385 node_type = di_minor_nodetype(minor);
6386 assert(node_type != NULL);
6387 if (strcmp(node_type, selector->val) != 0) {
6388 return (DEVFSADM_FAILURE);
6390 break;
6391 case ADDR:
6392 if ((addr = di_bus_addr(node)) == NULL) {
6393 return (DEVFSADM_FAILURE);
6395 if (selector->arg == 0) {
6396 if (strcmp(addr, selector->val) != 0) {
6397 return (DEVFSADM_FAILURE);
6399 } else {
6400 if (compare_field(addr, selector->val,
6401 selector->arg) == DEVFSADM_FAILURE) {
6402 return (DEVFSADM_FAILURE);
6405 break;
6406 case MINOR:
6407 if ((minor_name = di_minor_name(minor)) == NULL) {
6408 return (DEVFSADM_FAILURE);
6410 if (selector->arg == 0) {
6411 if (strcmp(minor_name, selector->val) != 0) {
6412 return (DEVFSADM_FAILURE);
6414 } else {
6415 if (compare_field(minor_name, selector->val,
6416 selector->arg) == DEVFSADM_FAILURE) {
6417 return (DEVFSADM_FAILURE);
6420 break;
6421 default:
6422 return (DEVFSADM_FAILURE);
6426 return (DEVFSADM_SUCCESS);
6430 * For the given minor node and devlinktab_list entry from devlink.tab,
6431 * build a logical dev link and a possible extra devlink.
6432 * Return DEVFSADM_SUCCESS if link is created, otherwise DEVFSADM_FAILURE.
6434 static int
6435 build_links(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6437 char secondary_link[PATH_MAX + 1];
6438 char primary_link[PATH_MAX + 1];
6439 char contents[PATH_MAX + 1];
6440 char *dev_path;
6442 if ((dev_path = di_devfs_path(node)) == NULL) {
6443 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
6444 devfsadm_exit(1);
6445 /*NOTREACHED*/
6447 (void) strcpy(contents, dev_path);
6448 di_devfs_path_free(dev_path);
6450 (void) strcat(contents, ":");
6451 (void) strcat(contents, di_minor_name(minor));
6453 if (construct_devlink(primary_link, entry->p_link, contents,
6454 minor, node, entry->p_link_pattern) == DEVFSADM_FAILURE) {
6455 return (DEVFSADM_FAILURE);
6457 (void) devfsadm_mklink(primary_link, node, minor, 0);
6459 if (entry->s_link == NULL) {
6460 return (DEVFSADM_SUCCESS);
6463 if (construct_devlink(secondary_link, entry->s_link, primary_link,
6464 minor, node, entry->s_link_pattern) == DEVFSADM_FAILURE) {
6465 return (DEVFSADM_FAILURE);
6468 (void) devfsadm_secondary_link(secondary_link, primary_link, 0);
6470 return (DEVFSADM_SUCCESS);
6474 * The counter rule for devlink.tab entries is implemented via
6475 * devfsadm_enumerate_int_start(). One of the arguments to this function
6476 * is a path, where each path component is treated as a regular expression.
6477 * For devlink.tab entries, this path regular expression is derived from
6478 * the devlink spec. get_anchored_re() accepts path regular expressions derived
6479 * from devlink.tab entries and inserts the anchors '^' and '$' at the beginning
6480 * and end respectively of each path component. This is done to prevent
6481 * false matches. For example, without anchors, "a/([0-9]+)" will match "ab/c9"
6482 * and incorrect links will be generated.
6484 static int
6485 get_anchored_re(char *link, char *anchored_re, char *pattern)
6487 if (*link == '/' || *link == '\0') {
6488 err_print(INVALID_DEVLINK_SPEC, pattern);
6489 return (DEVFSADM_FAILURE);
6492 *anchored_re++ = '^';
6493 for (; *link != '\0'; ) {
6494 if (*link == '/') {
6495 while (*link == '/')
6496 link++;
6497 *anchored_re++ = '$';
6498 *anchored_re++ = '/';
6499 if (*link != '\0') {
6500 *anchored_re++ = '^';
6502 } else {
6503 *anchored_re++ = *link++;
6504 if (*link == '\0') {
6505 *anchored_re++ = '$';
6509 *anchored_re = '\0';
6511 return (DEVFSADM_SUCCESS);
6514 static int
6515 construct_devlink(char *link, link_list_t *link_build, char *contents,
6516 di_minor_t minor, di_node_t node, char *pattern)
6518 int counter_offset = -1;
6519 devfsadm_enumerate_t rules[1] = {NULL};
6520 char templink[PATH_MAX + 1];
6521 char *buff;
6522 char start[10];
6523 char *node_path;
6524 char anchored_re[PATH_MAX + 1];
6526 link[0] = '\0';
6528 for (; link_build != NULL; link_build = link_build->next) {
6529 switch (link_build->type) {
6530 case NAME:
6531 (void) strcat(link, di_node_name(node));
6532 break;
6533 case CONSTANT:
6534 (void) strcat(link, link_build->constant);
6535 break;
6536 case ADDR:
6537 if (component_cat(link, di_bus_addr(node),
6538 link_build->arg) == DEVFSADM_FAILURE) {
6539 node_path = di_devfs_path(node);
6540 err_print(CANNOT_BE_USED, pattern, node_path,
6541 di_minor_name(minor));
6542 di_devfs_path_free(node_path);
6543 return (DEVFSADM_FAILURE);
6545 break;
6546 case MINOR:
6547 if (component_cat(link, di_minor_name(minor),
6548 link_build->arg) == DEVFSADM_FAILURE) {
6549 node_path = di_devfs_path(node);
6550 err_print(CANNOT_BE_USED, pattern, node_path,
6551 di_minor_name(minor));
6552 di_devfs_path_free(node_path);
6553 return (DEVFSADM_FAILURE);
6555 break;
6556 case COUNTER:
6557 counter_offset = strlen(link);
6558 (void) strcat(link, "([0-9]+)");
6559 (void) sprintf(start, "%d", link_build->arg);
6560 break;
6561 default:
6562 return (DEVFSADM_FAILURE);
6566 if (counter_offset != -1) {
6568 * copy anything appended after "([0-9]+)" into
6569 * templink
6572 (void) strcpy(templink,
6573 &link[counter_offset + strlen("([0-9]+)")]);
6574 if (get_anchored_re(link, anchored_re, pattern)
6575 != DEVFSADM_SUCCESS) {
6576 return (DEVFSADM_FAILURE);
6578 rules[0].re = anchored_re;
6579 rules[0].subexp = 1;
6580 rules[0].flags = MATCH_ALL;
6581 if (devfsadm_enumerate_int_start(contents, 0, &buff,
6582 rules, 1, start) == DEVFSADM_FAILURE) {
6583 return (DEVFSADM_FAILURE);
6585 (void) strcpy(&link[counter_offset], buff);
6586 free(buff);
6587 (void) strcat(link, templink);
6588 vprint(DEVLINK_MID, "COUNTER is %s\n", link);
6590 return (DEVFSADM_SUCCESS);
6594 * Compares "field" number of the comma separated list "full_name" with
6595 * field_item. Returns DEVFSADM_SUCCESS for match,
6596 * DEVFSADM_FAILURE for no match.
6598 static int
6599 compare_field(char *full_name, char *field_item, int field)
6601 --field;
6602 while ((*full_name != '\0') && (field != 0)) {
6603 if (*(full_name++) == ',') {
6604 field--;
6608 if (field != 0) {
6609 return (DEVFSADM_FAILURE);
6612 while ((*full_name != '\0') && (*field_item != '\0') &&
6613 (*full_name != ',')) {
6614 if (*(full_name++) != *(field_item++)) {
6615 return (DEVFSADM_FAILURE);
6619 if (*field_item != '\0') {
6620 return (DEVFSADM_FAILURE);
6623 if ((*full_name == '\0') || (*full_name == ','))
6624 return (DEVFSADM_SUCCESS);
6626 return (DEVFSADM_FAILURE);
6630 * strcat() field # "field" of comma separated list "name" to "link".
6631 * Field 0 is the entire name.
6632 * Return DEVFSADM_SUCCESS or DEVFSADM_FAILURE.
6634 static int
6635 component_cat(char *link, char *name, int field)
6638 if (name == NULL) {
6639 return (DEVFSADM_FAILURE);
6642 if (field == 0) {
6643 (void) strcat(link, name);
6644 return (DEVFSADM_SUCCESS);
6647 while (*link != '\0') {
6648 link++;
6651 --field;
6652 while ((*name != '\0') && (field != 0)) {
6653 if (*(name++) == ',') {
6654 --field;
6658 if (field != 0) {
6659 return (DEVFSADM_FAILURE);
6662 while ((*name != '\0') && (*name != ',')) {
6663 *(link++) = *(name++);
6666 *link = '\0';
6667 return (DEVFSADM_SUCCESS);
6670 static void
6671 free_selector_list(selector_list_t *head)
6673 selector_list_t *temp;
6675 while (head != NULL) {
6676 temp = head;
6677 head = head->next;
6678 free(temp->val);
6679 free(temp);
6683 static void
6684 free_link_list(link_list_t *head)
6686 link_list_t *temp;
6688 while (head != NULL) {
6689 temp = head;
6690 head = head->next;
6691 if (temp->type == CONSTANT) {
6692 free(temp->constant);
6694 free(temp);
6699 * Prints only if level matches one of the debug levels
6700 * given on command line. INFO_MID is always printed.
6702 * See devfsadm.h for a listing of globally defined levels and
6703 * meanings. Modules should prefix the level with their
6704 * module name to prevent collisions.
6706 /*PRINTFLIKE2*/
6707 void
6708 devfsadm_print(char *msgid, char *message, ...)
6710 va_list ap;
6711 static int newline = TRUE;
6712 int x;
6714 if (msgid != NULL) {
6715 for (x = 0; x < num_verbose; x++) {
6716 if (strcmp(verbose[x], msgid) == 0) {
6717 break;
6719 if (strcmp(verbose[x], ALL_MID) == 0) {
6720 break;
6723 if (x == num_verbose) {
6724 return;
6728 va_start(ap, message);
6730 if (msgid == NULL) {
6731 if (logflag == TRUE) {
6732 (void) vsyslog(LOG_NOTICE, message, ap);
6733 } else {
6734 (void) vfprintf(stdout, message, ap);
6737 } else {
6738 if (logflag == TRUE) {
6739 (void) syslog(LOG_DEBUG, "%s[%ld]: %s: ",
6740 prog, getpid(), msgid);
6741 (void) vsyslog(LOG_DEBUG, message, ap);
6742 } else {
6743 if (newline == TRUE) {
6744 (void) fprintf(stdout, "%s[%ld]: %s: ",
6745 prog, getpid(), msgid);
6747 (void) vfprintf(stdout, message, ap);
6751 if (message[strlen(message) - 1] == '\n') {
6752 newline = TRUE;
6753 } else {
6754 newline = FALSE;
6756 va_end(ap);
6760 * print error messages to the terminal or to syslog
6762 /*PRINTFLIKE1*/
6763 void
6764 devfsadm_errprint(char *message, ...)
6766 va_list ap;
6768 va_start(ap, message);
6770 if (logflag == TRUE) {
6771 (void) vsyslog(LOG_ERR, message, ap);
6772 } else {
6773 (void) fprintf(stderr, "%s: ", prog);
6774 (void) vfprintf(stderr, message, ap);
6776 va_end(ap);
6780 * return noupdate state (-s)
6783 devfsadm_noupdate(void)
6785 return (file_mods == TRUE ? DEVFSADM_TRUE : DEVFSADM_FALSE);
6789 * return current root update path (-r)
6791 const char *
6792 devfsadm_root_path(void)
6794 if (root_dir[0] == '\0') {
6795 return ("/");
6796 } else {
6797 return ((const char *)root_dir);
6801 void
6802 devfsadm_free_dev_names(char **dev_names, int len)
6804 int i;
6806 for (i = 0; i < len; i++)
6807 free(dev_names[i]);
6808 free(dev_names);
6812 * Return all devlinks corresponding to phys_path as an array of strings.
6813 * The number of entries in the array is returned through lenp.
6814 * devfsadm_free_dev_names() is used to free the returned array.
6815 * NULL is returned on failure or when there are no matching devlinks.
6817 * re is an extended regular expression in regex(5) format used to further
6818 * match devlinks pointing to phys_path; it may be NULL to match all
6820 char **
6821 devfsadm_lookup_dev_names(char *phys_path, char *re, int *lenp)
6823 struct devlink_cb_arg cb_arg;
6824 char **dev_names = NULL;
6825 int i;
6827 *lenp = 0;
6828 cb_arg.count = 0;
6829 cb_arg.rv = 0;
6830 (void) di_devlink_cache_walk(devlink_cache, re, phys_path,
6831 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
6833 if (cb_arg.rv == -1 || cb_arg.count <= 0)
6834 return (NULL);
6836 dev_names = s_malloc(cb_arg.count * sizeof (char *));
6837 if (dev_names == NULL)
6838 goto out;
6840 for (i = 0; i < cb_arg.count; i++) {
6841 dev_names[i] = s_strdup(cb_arg.dev_names[i]);
6842 if (dev_names[i] == NULL) {
6843 devfsadm_free_dev_names(dev_names, i);
6844 dev_names = NULL;
6845 goto out;
6848 *lenp = cb_arg.count;
6850 out:
6851 free_dev_names(&cb_arg);
6852 return (dev_names);
6855 /* common exit function which ensures releasing locks */
6856 static void
6857 devfsadm_exit(int status)
6859 if (DEVFSADM_DEBUG_ON) {
6860 vprint(INFO_MID, "exit status = %d\n", status);
6863 exit_dev_lock(1);
6864 exit_daemon_lock(1);
6866 if (logflag == TRUE) {
6867 closelog();
6870 exit(status);
6871 /*NOTREACHED*/
6875 * set root_dir, devices_dir, dev_dir using optarg.
6877 static void
6878 set_root_devices_dev_dir(char *dir)
6880 size_t len;
6882 root_dir = s_strdup(dir);
6883 len = strlen(dir) + strlen(DEVICES) + 1;
6884 devices_dir = s_malloc(len);
6885 (void) snprintf(devices_dir, len, "%s%s", root_dir, DEVICES);
6886 len = strlen(root_dir) + strlen(DEV) + 1;
6887 dev_dir = s_malloc(len);
6888 (void) snprintf(dev_dir, len, "%s%s", root_dir, DEV);
6892 * Removes quotes.
6894 static char *
6895 dequote(char *src)
6897 char *dst;
6898 int len;
6900 len = strlen(src);
6901 dst = s_malloc(len + 1);
6902 if (src[0] == '\"' && src[len - 1] == '\"') {
6903 len -= 2;
6904 (void) strncpy(dst, &src[1], len);
6905 dst[len] = '\0';
6906 } else {
6907 (void) strcpy(dst, src);
6909 return (dst);
6913 * For a given physical device pathname and spectype, return the
6914 * ownership and permissions attributes by looking in data from
6915 * /etc/minor_perm. If currently in installation mode, check for
6916 * possible major number translations from the miniroot to the installed
6917 * root's name_to_major table. Note that there can be multiple matches,
6918 * but the last match takes effect. pts seems to rely on this
6919 * implementation behavior.
6921 static void
6922 getattr(char *phy_path, char *aminor, int spectype, dev_t dev, mode_t *mode,
6923 uid_t *uid, gid_t *gid)
6925 char devname[PATH_MAX + 1];
6926 char *node_name;
6927 char *minor_name;
6928 int match = FALSE;
6929 int is_clone;
6930 int mp_drvname_matches_node_name;
6931 int mp_drvname_matches_minor_name;
6932 int mp_drvname_is_clone;
6933 int mp_drvname_matches_drvname;
6934 struct mperm *mp;
6935 major_t major_no;
6936 char driver[PATH_MAX + 1];
6939 * Get the driver name based on the major number since the name
6940 * in /devices may be generic. Could be running with more major
6941 * numbers than are in /etc/name_to_major, so get it from the kernel
6943 major_no = major(dev);
6945 if (modctl(MODGETNAME, driver, sizeof (driver), &major_no) != 0) {
6946 /* return default values */
6947 goto use_defaults;
6950 (void) strcpy(devname, phy_path);
6952 node_name = strrchr(devname, '/'); /* node name is the last */
6953 /* component */
6954 if (node_name == NULL) {
6955 err_print(NO_NODE, devname);
6956 goto use_defaults;
6959 minor_name = strchr(++node_name, '@'); /* see if it has address part */
6961 if (minor_name != NULL) {
6962 *minor_name++ = '\0';
6963 } else {
6964 minor_name = node_name;
6967 minor_name = strchr(minor_name, ':'); /* look for minor name */
6969 if (minor_name == NULL) {
6970 err_print(NO_MINOR, devname);
6971 goto use_defaults;
6973 *minor_name++ = '\0';
6976 * mp->mp_drvname = device name from minor_perm
6977 * mp->mp_minorname = minor part of device name from
6978 * minor_perm
6979 * drvname = name of driver for this device
6982 is_clone = (strcmp(node_name, "clone") == 0 ? TRUE : FALSE);
6983 for (mp = minor_perms; mp != NULL; mp = mp->mp_next) {
6984 mp_drvname_matches_node_name =
6985 (strcmp(mp->mp_drvname, node_name) == 0 ? TRUE : FALSE);
6986 mp_drvname_matches_minor_name =
6987 (strcmp(mp->mp_drvname, minor_name) == 0 ? TRUE:FALSE);
6988 mp_drvname_is_clone =
6989 (strcmp(mp->mp_drvname, "clone") == 0 ? TRUE : FALSE);
6990 mp_drvname_matches_drvname =
6991 (strcmp(mp->mp_drvname, driver) == 0 ? TRUE : FALSE);
6994 * If one of the following cases is true, then we try to change
6995 * the permissions if a "shell global pattern match" of
6996 * mp_>mp_minorname matches minor_name.
6998 * 1. mp->mp_drvname matches driver.
7000 * OR
7002 * 2. mp->mp_drvname matches node_name and this
7003 * name is an alias of the driver name
7005 * OR
7007 * 3. /devices entry is the clone device and either
7008 * minor_perm entry is the clone device or matches
7009 * the minor part of the clone device.
7012 if ((mp_drvname_matches_drvname == TRUE)||
7013 ((mp_drvname_matches_node_name == TRUE) &&
7014 (alias(driver, node_name) == TRUE)) ||
7015 ((is_clone == TRUE) &&
7016 ((mp_drvname_is_clone == TRUE) ||
7017 (mp_drvname_matches_minor_name == TRUE)))) {
7019 * Check that the minor part of the
7020 * device name from the minor_perm
7021 * entry matches and if so, set the
7022 * permissions.
7024 * Under real devfs, clone minor name is changed
7025 * to match the driver name, but minor_perm may
7026 * not match. We reconcile it here.
7028 if (aminor != NULL)
7029 minor_name = aminor;
7031 if (gmatch(minor_name, mp->mp_minorname) != 0) {
7032 *uid = mp->mp_uid;
7033 *gid = mp->mp_gid;
7034 *mode = spectype | mp->mp_mode;
7035 match = TRUE;
7040 if (match == TRUE) {
7041 return;
7044 use_defaults:
7045 /* not found in minor_perm, so just use default values */
7046 *uid = root_uid;
7047 *gid = sys_gid;
7048 *mode = (spectype | 0600);
7052 * Called by devfs_read_minor_perm() to report errors
7053 * key is:
7054 * line number: ignoring line number error
7055 * errno: open/close errors
7056 * size: alloc errors
7058 static void
7059 minorperm_err_cb(minorperm_err_t mp_err, int key)
7061 switch (mp_err) {
7062 case MP_FOPEN_ERR:
7063 err_print(FOPEN_FAILED, MINOR_PERM_FILE, strerror(key));
7064 break;
7065 case MP_FCLOSE_ERR:
7066 err_print(FCLOSE_FAILED, MINOR_PERM_FILE, strerror(key));
7067 break;
7068 case MP_IGNORING_LINE_ERR:
7069 err_print(IGNORING_LINE_IN, key, MINOR_PERM_FILE);
7070 break;
7071 case MP_ALLOC_ERR:
7072 err_print(MALLOC_FAILED, key);
7073 break;
7074 case MP_NVLIST_ERR:
7075 err_print(NVLIST_ERROR, MINOR_PERM_FILE, strerror(key));
7076 break;
7077 case MP_CANT_FIND_USER_ERR:
7078 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
7079 break;
7080 case MP_CANT_FIND_GROUP_ERR:
7081 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
7082 break;
7086 static void
7087 read_minor_perm_file(void)
7089 static int cached = FALSE;
7090 static struct stat cached_sb;
7091 struct stat current_sb;
7093 (void) stat(MINOR_PERM_FILE, &current_sb);
7095 /* If already cached, check to see if it is still valid */
7096 if (cached == TRUE) {
7098 if (current_sb.st_mtime == cached_sb.st_mtime) {
7099 vprint(FILES_MID, "%s cache valid\n", MINOR_PERM_FILE);
7100 return;
7102 devfs_free_minor_perm(minor_perms);
7103 minor_perms = NULL;
7104 } else {
7105 cached = TRUE;
7108 (void) stat(MINOR_PERM_FILE, &cached_sb);
7110 vprint(FILES_MID, "loading binding file: %s\n", MINOR_PERM_FILE);
7112 minor_perms = devfs_read_minor_perm(minorperm_err_cb);
7115 static void
7116 load_minor_perm_file(void)
7118 read_minor_perm_file();
7119 if (devfs_load_minor_perm(minor_perms, minorperm_err_cb) != 0)
7120 err_print(gettext("minor_perm load failed\n"));
7123 static char *
7124 convert_to_re(char *dev)
7126 char *p, *l, *out;
7127 int i;
7129 out = s_malloc(PATH_MAX);
7131 for (l = p = dev, i = 0; (*p != '\0') && (i < (PATH_MAX - 1));
7132 ++p, i++) {
7133 if ((*p == '*') && ((l != p) && (*l == '/'))) {
7134 out[i++] = '.';
7135 out[i] = '+';
7136 } else {
7137 out[i] = *p;
7139 l = p;
7141 out[i] = '\0';
7142 p = (char *)s_malloc(strlen(out) + 1);
7143 (void) strlcpy(p, out, strlen(out) + 1);
7144 free(out);
7146 vprint(FILES_MID, "converted %s -> %s\n", dev, p);
7148 return (p);
7151 static void
7152 read_logindevperm_file(void)
7154 static int cached = FALSE;
7155 static struct stat cached_sb;
7156 struct stat current_sb;
7157 struct login_dev *ldev;
7158 FILE *fp;
7159 char line[MAX_LDEV_LINE];
7160 int ln, perm, rv;
7161 char *cp, *console, *dlist, *dev;
7162 char *lasts, *devlasts, *permstr, *drv;
7163 struct driver_list *list, *next;
7165 /* Read logindevperm only when enabled */
7166 if (login_dev_enable != TRUE)
7167 return;
7169 if (cached == TRUE) {
7170 if (stat(LDEV_FILE, &current_sb) == 0 &&
7171 current_sb.st_mtime == cached_sb.st_mtime) {
7172 vprint(FILES_MID, "%s cache valid\n", LDEV_FILE);
7173 return;
7175 vprint(FILES_MID, "invalidating %s cache\n", LDEV_FILE);
7176 while (login_dev_cache != NULL) {
7178 ldev = login_dev_cache;
7179 login_dev_cache = ldev->ldev_next;
7180 free(ldev->ldev_console);
7181 free(ldev->ldev_device);
7182 regfree(&ldev->ldev_device_regex);
7183 list = ldev->ldev_driver_list;
7184 while (list) {
7185 next = list->next;
7186 free(list);
7187 list = next;
7189 free(ldev);
7191 } else {
7192 cached = TRUE;
7195 assert(login_dev_cache == NULL);
7197 if (stat(LDEV_FILE, &cached_sb) != 0) {
7198 cached = FALSE;
7199 return;
7202 vprint(FILES_MID, "loading file: %s\n", LDEV_FILE);
7204 if ((fp = fopen(LDEV_FILE, "r")) == NULL) {
7205 /* Not fatal to devfsadm */
7206 cached = FALSE;
7207 err_print(FOPEN_FAILED, LDEV_FILE, strerror(errno));
7208 return;
7211 ln = 0;
7212 while (fgets(line, MAX_LDEV_LINE, fp) != NULL) {
7213 ln++;
7215 /* Remove comments */
7216 if ((cp = strchr(line, '#')) != NULL)
7217 *cp = '\0';
7219 if ((console = strtok_r(line, LDEV_DELIMS, &lasts)) == NULL)
7220 continue; /* Blank line */
7222 if ((permstr = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7223 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7224 continue; /* Malformed line */
7228 * permstr is string in octal format. Convert to int
7230 cp = NULL;
7231 errno = 0;
7232 perm = strtol(permstr, &cp, 8);
7233 if (errno || perm < 0 || perm > 0777 || *cp != '\0') {
7234 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7235 continue;
7238 if ((dlist = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7239 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7240 continue;
7243 dev = strtok_r(dlist, LDEV_DEV_DELIM, &devlasts);
7244 while (dev) {
7246 ldev = (struct login_dev *)s_zalloc(
7247 sizeof (struct login_dev));
7248 ldev->ldev_console = s_strdup(console);
7249 ldev->ldev_perms = perm;
7252 * the logical device name may contain '*' which
7253 * we convert to a regular expression
7255 ldev->ldev_device = convert_to_re(dev);
7256 if (ldev->ldev_device &&
7257 (rv = regcomp(&ldev->ldev_device_regex,
7258 ldev->ldev_device, REG_EXTENDED))) {
7259 bzero(&ldev->ldev_device_regex,
7260 sizeof (ldev->ldev_device_regex));
7261 err_print(REGCOMP_FAILED,
7262 ldev->ldev_device, rv);
7264 ldev->ldev_next = login_dev_cache;
7265 login_dev_cache = ldev;
7266 dev = strtok_r(NULL, LDEV_DEV_DELIM, &devlasts);
7269 drv = strtok_r(NULL, LDEV_DRVLIST_DELIMS, &lasts);
7270 if (drv) {
7271 if (strcmp(drv, LDEV_DRVLIST_NAME) == 0) {
7273 drv = strtok_r(NULL, LDEV_DRV_DELIMS, &lasts);
7275 while (drv) {
7276 vprint(FILES_MID,
7277 "logindevperm driver=%s\n", drv);
7280 * create a linked list of driver
7281 * names
7283 list = (struct driver_list *)
7284 s_zalloc(
7285 sizeof (struct driver_list));
7286 (void) strlcpy(list->driver_name, drv,
7287 sizeof (list->driver_name));
7288 list->next = ldev->ldev_driver_list;
7289 ldev->ldev_driver_list = list;
7290 drv = strtok_r(NULL, LDEV_DRV_DELIMS,
7291 &lasts);
7296 (void) fclose(fp);
7300 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
7302 * Returns DEVFSADM_SUCCESS if token found, DEVFSADM_FAILURE otherwise.
7304 static int
7305 getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar)
7307 char *cp;
7308 char *cp1;
7309 char *tokenp;
7311 cp = next;
7312 while (*cp == ' ' || *cp == '\t') {
7313 cp++; /* skip leading spaces */
7315 tokenp = cp; /* start of token */
7316 while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' &&
7317 *cp != ':' && *cp != '=' && *cp != '&' &&
7318 *cp != '|' && *cp != ';') {
7319 cp++; /* point to next character */
7322 * If terminating character is a space or tab, look ahead to see if
7323 * there's another terminator that's not a space or a tab.
7324 * (This code handles trailing spaces.)
7326 if (*cp == ' ' || *cp == '\t') {
7327 cp1 = cp;
7328 while (*++cp1 == ' ' || *cp1 == '\t')
7330 if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' ||
7331 *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') {
7332 *cp = '\0'; /* terminate token */
7333 cp = cp1;
7336 if (tchar != NULL) {
7337 *tchar = *cp; /* save terminating character */
7338 if (*tchar == '\0') {
7339 *tchar = '\n';
7342 *cp++ = '\0'; /* terminate token, point to next */
7343 *nextp = cp; /* set pointer to next character */
7344 if (cp - tokenp - 1 == 0) {
7345 return (DEVFSADM_FAILURE);
7347 *tokenpp = tokenp;
7348 return (DEVFSADM_SUCCESS);
7352 * read or reread the driver aliases file
7354 static void
7355 read_driver_aliases_file(void)
7358 driver_alias_t *save;
7359 driver_alias_t *lst_tail;
7360 driver_alias_t *ap;
7361 static int cached = FALSE;
7362 FILE *afd;
7363 char line[256];
7364 char *cp;
7365 char *p;
7366 char t;
7367 int ln = 0;
7368 static struct stat cached_sb;
7369 struct stat current_sb;
7371 (void) stat(ALIASFILE, &current_sb);
7373 /* If already cached, check to see if it is still valid */
7374 if (cached == TRUE) {
7376 if (current_sb.st_mtime == cached_sb.st_mtime) {
7377 vprint(FILES_MID, "%s cache valid\n", ALIASFILE);
7378 return;
7381 vprint(FILES_MID, "invalidating %s cache\n", ALIASFILE);
7382 while (driver_aliases != NULL) {
7383 free(driver_aliases->alias_name);
7384 free(driver_aliases->driver_name);
7385 save = driver_aliases;
7386 driver_aliases = driver_aliases->next;
7387 free(save);
7389 } else {
7390 cached = TRUE;
7393 (void) stat(ALIASFILE, &cached_sb);
7395 vprint(FILES_MID, "loading binding file: %s\n", ALIASFILE);
7397 if ((afd = fopen(ALIASFILE, "r")) == NULL) {
7398 err_print(FOPEN_FAILED, ALIASFILE, strerror(errno));
7399 devfsadm_exit(1);
7400 /*NOTREACHED*/
7403 while (fgets(line, sizeof (line), afd) != NULL) {
7404 ln++;
7405 /* cut off comments starting with '#' */
7406 if ((cp = strchr(line, '#')) != NULL)
7407 *cp = '\0';
7408 /* ignore comment or blank lines */
7409 if (is_blank(line))
7410 continue;
7411 cp = line;
7412 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7413 err_print(IGNORING_LINE_IN, ln, ALIASFILE);
7414 continue;
7416 if (t == '\n' || t == '\0') {
7417 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7418 continue;
7420 ap = (struct driver_alias *)
7421 s_zalloc(sizeof (struct driver_alias));
7422 ap->driver_name = s_strdup(p);
7423 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7424 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7425 free(ap->driver_name);
7426 free(ap);
7427 continue;
7429 if (*p == '"') {
7430 if (p[strlen(p) - 1] == '"') {
7431 p[strlen(p) - 1] = '\0';
7432 p++;
7435 ap->alias_name = s_strdup(p);
7436 if (driver_aliases == NULL) {
7437 driver_aliases = ap;
7438 lst_tail = ap;
7439 } else {
7440 lst_tail->next = ap;
7441 lst_tail = ap;
7444 if (fclose(afd) == EOF) {
7445 err_print(FCLOSE_FAILED, ALIASFILE, strerror(errno));
7450 * return TRUE if alias_name is an alias for driver_name, otherwise
7451 * return FALSE.
7453 static int
7454 alias(char *driver_name, char *alias_name)
7456 driver_alias_t *alias;
7459 * check for a match
7461 for (alias = driver_aliases; alias != NULL; alias = alias->next) {
7462 if ((strcmp(alias->driver_name, driver_name) == 0) &&
7463 (strcmp(alias->alias_name, alias_name) == 0)) {
7464 return (TRUE);
7467 return (FALSE);
7471 * convenience functions
7473 static int
7474 s_stat(const char *path, struct stat *sbufp)
7476 int rv;
7477 retry:
7478 if ((rv = stat(path, sbufp)) == -1) {
7479 if (errno == EINTR)
7480 goto retry;
7482 return (rv);
7485 static void *
7486 s_malloc(const size_t size)
7488 void *rp;
7490 rp = malloc(size);
7491 if (rp == NULL) {
7492 err_print(MALLOC_FAILED, size);
7493 devfsadm_exit(1);
7494 /*NOTREACHED*/
7496 return (rp);
7500 * convenience functions
7502 static void *
7503 s_realloc(void *ptr, const size_t size)
7505 ptr = realloc(ptr, size);
7506 if (ptr == NULL) {
7507 err_print(REALLOC_FAILED, size);
7508 devfsadm_exit(1);
7509 /*NOTREACHED*/
7511 return (ptr);
7514 static void *
7515 s_zalloc(const size_t size)
7517 void *rp;
7519 rp = calloc(1, size);
7520 if (rp == NULL) {
7521 err_print(CALLOC_FAILED, size);
7522 devfsadm_exit(1);
7523 /*NOTREACHED*/
7525 return (rp);
7528 char *
7529 s_strdup(const char *ptr)
7531 void *rp;
7533 rp = strdup(ptr);
7534 if (rp == NULL) {
7535 err_print(STRDUP_FAILED, ptr);
7536 devfsadm_exit(1);
7537 /*NOTREACHED*/
7539 return (rp);
7542 static void
7543 s_closedir(DIR *dirp)
7545 retry:
7546 if (closedir(dirp) != 0) {
7547 if (errno == EINTR)
7548 goto retry;
7549 err_print(CLOSEDIR_FAILED, strerror(errno));
7553 static void
7554 s_mkdirp(const char *path, const mode_t mode)
7556 vprint(CHATTY_MID, "mkdirp(%s, 0x%lx)\n", path, mode);
7557 if (mkdirp(path, mode) == -1) {
7558 if (errno != EEXIST) {
7559 err_print(MKDIR_FAILED, path, mode, strerror(errno));
7564 static void
7565 s_unlink(const char *file)
7567 retry:
7568 if (unlink(file) == -1) {
7569 if (errno == EINTR || errno == EAGAIN)
7570 goto retry;
7571 if (errno != ENOENT) {
7572 err_print(UNLINK_FAILED, file, strerror(errno));
7577 static void
7578 add_verbose_id(char *mid)
7580 num_verbose++;
7581 verbose = s_realloc(verbose, num_verbose * sizeof (char *));
7582 verbose[num_verbose - 1] = mid;
7586 * returns DEVFSADM_TRUE if contents is a minor node in /devices.
7587 * If mn_root is not NULL, mn_root is set to:
7588 * if contents is a /dev node, mn_root = contents
7589 * OR
7590 * if contents is a /devices node, mn_root set to the '/'
7591 * following /devices.
7593 static int
7594 is_minor_node(char *contents, char **mn_root)
7596 char *ptr;
7597 char device_prefix[100];
7599 (void) snprintf(device_prefix, sizeof (device_prefix), "../devices/");
7601 if ((ptr = strstr(contents, device_prefix)) != NULL) {
7602 if (mn_root != NULL) {
7603 /* mn_root should point to the / following /devices */
7604 *mn_root = ptr += strlen(device_prefix) - 1;
7606 return (DEVFSADM_TRUE);
7609 (void) snprintf(device_prefix, sizeof (device_prefix), "/devices/");
7611 if (strncmp(contents, device_prefix, strlen(device_prefix)) == 0) {
7612 if (mn_root != NULL) {
7613 /* mn_root should point to the / following /devices */
7614 *mn_root = contents + strlen(device_prefix) - 1;
7616 return (DEVFSADM_TRUE);
7619 if (mn_root != NULL) {
7620 *mn_root = contents;
7622 return (DEVFSADM_FALSE);
7626 * Add the specified property to nvl.
7627 * Returns:
7628 * 0 successfully added
7629 * -1 an error occurred
7630 * 1 could not add the property for reasons not due to errors.
7632 static int
7633 add_property(nvlist_t *nvl, di_prop_t prop)
7635 char *name;
7636 char *attr_name;
7637 int n, len;
7638 int32_t *int32p;
7639 int64_t *int64p;
7640 char *str;
7641 char **strarray;
7642 uchar_t *bytep;
7643 int rv = 0;
7644 int i;
7646 if ((name = di_prop_name(prop)) == NULL)
7647 return (-1);
7649 len = sizeof (DEV_PROP_PREFIX) + strlen(name);
7650 if ((attr_name = malloc(len)) == NULL)
7651 return (-1);
7653 (void) strlcpy(attr_name, DEV_PROP_PREFIX, len);
7654 (void) strlcat(attr_name, name, len);
7656 switch (di_prop_type(prop)) {
7657 case DI_PROP_TYPE_BOOLEAN:
7658 if (nvlist_add_boolean(nvl, attr_name) != 0)
7659 goto out;
7660 break;
7662 case DI_PROP_TYPE_INT:
7663 if ((n = di_prop_ints(prop, &int32p)) < 1)
7664 goto out;
7666 if (n <= (PROP_LEN_LIMIT / sizeof (int32_t))) {
7667 if (nvlist_add_int32_array(nvl, attr_name, int32p,
7668 n) != 0)
7669 goto out;
7670 } else
7671 rv = 1;
7672 break;
7674 case DI_PROP_TYPE_INT64:
7675 if ((n = di_prop_int64(prop, &int64p)) < 1)
7676 goto out;
7678 if (n <= (PROP_LEN_LIMIT / sizeof (int64_t))) {
7679 if (nvlist_add_int64_array(nvl, attr_name, int64p,
7680 n) != 0)
7681 goto out;
7682 } else
7683 rv = 1;
7684 break;
7686 case DI_PROP_TYPE_BYTE:
7687 case DI_PROP_TYPE_UNKNOWN:
7688 if ((n = di_prop_bytes(prop, &bytep)) < 1)
7689 goto out;
7691 if (n <= PROP_LEN_LIMIT) {
7692 if (nvlist_add_byte_array(nvl, attr_name, bytep, n)
7693 != 0)
7694 goto out;
7695 } else
7696 rv = 1;
7697 break;
7699 case DI_PROP_TYPE_STRING:
7700 if ((n = di_prop_strings(prop, &str)) < 1)
7701 goto out;
7703 if ((strarray = malloc(n * sizeof (char *))) == NULL)
7704 goto out;
7706 len = 0;
7707 for (i = 0; i < n; i++) {
7708 strarray[i] = str + len;
7709 len += strlen(strarray[i]) + 1;
7712 if (len <= PROP_LEN_LIMIT) {
7713 if (nvlist_add_string_array(nvl, attr_name, strarray,
7714 n) != 0) {
7715 free(strarray);
7716 goto out;
7718 } else
7719 rv = 1;
7720 free(strarray);
7721 break;
7723 default:
7724 rv = 1;
7725 break;
7728 free(attr_name);
7729 return (rv);
7731 out:
7732 free(attr_name);
7733 return (-1);
7736 static void
7737 free_dev_names(struct devlink_cb_arg *x)
7739 int i;
7741 for (i = 0; i < x->count; i++) {
7742 free(x->dev_names[i]);
7743 free(x->link_contents[i]);
7747 /* callback function for di_devlink_cache_walk */
7748 static int
7749 devlink_cb(di_devlink_t dl, void *arg)
7751 struct devlink_cb_arg *x = (struct devlink_cb_arg *)arg;
7752 const char *path;
7753 const char *content;
7755 if ((path = di_devlink_path(dl)) == NULL ||
7756 (content = di_devlink_content(dl)) == NULL ||
7757 (x->dev_names[x->count] = s_strdup(path)) == NULL)
7758 goto out;
7760 if ((x->link_contents[x->count] = s_strdup(content)) == NULL) {
7761 free(x->dev_names[x->count]);
7762 goto out;
7765 x->count++;
7766 if (x->count >= MAX_DEV_NAME_COUNT)
7767 return (DI_WALK_TERMINATE);
7769 return (DI_WALK_CONTINUE);
7771 out:
7772 x->rv = -1;
7773 free_dev_names(x);
7774 return (DI_WALK_TERMINATE);
7778 * Lookup dev name corresponding to the phys_path.
7779 * phys_path is path to a node or minor node.
7780 * Returns:
7781 * 0 with *dev_name set to the dev name
7782 * Lookup succeeded and dev_name found
7783 * 0 with *dev_name set to NULL
7784 * Lookup encountered no errors but dev name not found
7785 * -1
7786 * Lookup failed
7788 static int
7789 lookup_dev_name(char *phys_path, char **dev_name)
7791 struct devlink_cb_arg cb_arg;
7793 *dev_name = NULL;
7795 cb_arg.count = 0;
7796 cb_arg.rv = 0;
7797 (void) di_devlink_cache_walk(devlink_cache, NULL, phys_path,
7798 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7800 if (cb_arg.rv == -1)
7801 return (-1);
7803 if (cb_arg.count > 0) {
7804 *dev_name = s_strdup(cb_arg.dev_names[0]);
7805 free_dev_names(&cb_arg);
7806 if (*dev_name == NULL)
7807 return (-1);
7810 return (0);
7813 static char *
7814 lookup_disk_dev_name(char *node_path)
7816 struct devlink_cb_arg cb_arg;
7817 char *dev_name = NULL;
7818 int i;
7819 char *p;
7820 int len1, len2;
7822 #define DEV_RDSK "/dev/rdsk/"
7823 #define DISK_RAW_MINOR ",raw"
7825 cb_arg.count = 0;
7826 cb_arg.rv = 0;
7827 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
7828 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7830 if (cb_arg.rv == -1 || cb_arg.count == 0)
7831 return (NULL);
7833 /* first try lookup based on /dev/rdsk name */
7834 for (i = 0; i < cb_arg.count; i++) {
7835 if (strncmp(cb_arg.dev_names[i], DEV_RDSK,
7836 sizeof (DEV_RDSK) - 1) == 0) {
7837 dev_name = s_strdup(cb_arg.dev_names[i]);
7838 break;
7842 if (dev_name == NULL) {
7843 /* now try lookup based on a minor name ending with ",raw" */
7844 len1 = sizeof (DISK_RAW_MINOR) - 1;
7845 for (i = 0; i < cb_arg.count; i++) {
7846 len2 = strlen(cb_arg.link_contents[i]);
7847 if (len2 >= len1 &&
7848 strcmp(cb_arg.link_contents[i] + len2 - len1,
7849 DISK_RAW_MINOR) == 0) {
7850 dev_name = s_strdup(cb_arg.dev_names[i]);
7851 break;
7856 free_dev_names(&cb_arg);
7858 if (dev_name == NULL)
7859 return (NULL);
7860 if (strlen(dev_name) == 0) {
7861 free(dev_name);
7862 return (NULL);
7865 /* if the name contains slice or partition number strip it */
7866 p = dev_name + strlen(dev_name) - 1;
7867 if (isdigit(*p)) {
7868 while (p != dev_name && isdigit(*p))
7869 p--;
7870 if (*p == 's' || *p == 'p')
7871 *p = '\0';
7874 return (dev_name);
7877 static char *
7878 lookup_lofi_dev_name(char *node_path, char *minor)
7880 struct devlink_cb_arg cb_arg;
7881 char *dev_name = NULL;
7882 int i;
7883 int len1, len2;
7885 cb_arg.count = 0;
7886 cb_arg.rv = 0;
7887 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
7888 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7890 if (cb_arg.rv == -1 || cb_arg.count == 0)
7891 return (NULL);
7893 /* lookup based on a minor name ending with ",raw" */
7894 len1 = strlen(minor);
7895 for (i = 0; i < cb_arg.count; i++) {
7896 len2 = strlen(cb_arg.link_contents[i]);
7897 if (len2 >= len1 &&
7898 strcmp(cb_arg.link_contents[i] + len2 - len1,
7899 minor) == 0) {
7900 dev_name = s_strdup(cb_arg.dev_names[i]);
7901 break;
7905 free_dev_names(&cb_arg);
7907 if (dev_name == NULL)
7908 return (NULL);
7909 if (strlen(dev_name) == 0) {
7910 free(dev_name);
7911 return (NULL);
7914 return (dev_name);
7917 static char *
7918 lookup_network_dev_name(char *node_path, char *driver_name)
7920 char *dev_name = NULL;
7921 char phys_path[MAXPATHLEN];
7923 if (lookup_dev_name(node_path, &dev_name) == -1)
7924 return (NULL);
7926 if (dev_name == NULL) {
7927 /* dlpi style-2 only interface */
7928 (void) snprintf(phys_path, sizeof (phys_path),
7929 "/pseudo/clone@0:%s", driver_name);
7930 if (lookup_dev_name(phys_path, &dev_name) == -1 ||
7931 dev_name == NULL)
7932 return (NULL);
7935 return (dev_name);
7938 static char *
7939 lookup_printer_dev_name(char *node_path)
7941 struct devlink_cb_arg cb_arg;
7942 char *dev_name = NULL;
7943 int i;
7945 #define DEV_PRINTERS "/dev/printers/"
7947 cb_arg.count = 0;
7948 cb_arg.rv = 0;
7949 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
7950 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7952 if (cb_arg.rv == -1 || cb_arg.count == 0)
7953 return (NULL);
7955 /* first try lookup based on /dev/printers name */
7956 for (i = 0; i < cb_arg.count; i++) {
7957 if (strncmp(cb_arg.dev_names[i], DEV_PRINTERS,
7958 sizeof (DEV_PRINTERS) - 1) == 0) {
7959 dev_name = s_strdup(cb_arg.dev_names[i]);
7960 break;
7964 /* fallback to the first name */
7965 if ((dev_name == NULL) && (cb_arg.count > 0))
7966 dev_name = s_strdup(cb_arg.dev_names[0]);
7968 free_dev_names(&cb_arg);
7970 return (dev_name);
7974 * Build an nvlist containing all attributes for devfs events.
7975 * Returns nvlist pointer on success, NULL on failure.
7977 static nvlist_t *
7978 build_event_attributes(char *class, char *subclass, char *node_path,
7979 di_node_t node, char *driver_name, int instance, char *minor)
7981 nvlist_t *nvl;
7982 int err = 0;
7983 di_prop_t prop;
7984 int count;
7985 char *prop_name;
7986 int x;
7987 char *dev_name = NULL;
7988 int dev_name_lookup_err = 0;
7990 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) {
7991 nvl = NULL;
7992 goto out;
7995 if ((err = nvlist_add_int32(nvl, EV_VERSION, EV_V1)) != 0)
7996 goto out;
7998 if ((err = nvlist_add_string(nvl, DEV_PHYS_PATH, node_path)) != 0)
7999 goto out;
8001 if (strcmp(class, EC_DEV_ADD) != 0 &&
8002 strcmp(class, EC_DEV_REMOVE) != 0)
8003 return (nvl);
8005 if (driver_name == NULL || instance == -1)
8006 goto out;
8008 if (strcmp(subclass, ESC_DISK) == 0) {
8010 * While we're removing labeled lofi device, we will receive
8011 * event for every registered minor device and lastly,
8012 * an event with minor set to NULL, as in following example:
8013 * class: EC_dev_remove subclass: disk
8014 * node_path: /pseudo/lofi@1 driver: lofi minor: u,raw
8015 * class: EC_dev_remove subclass: disk
8016 * node_path: /pseudo/lofi@1 driver: lofi minor: NULL
8018 * When we receive this last event with minor set to NULL,
8019 * all lofi minor devices are already removed and the call to
8020 * lookup_disk_dev_name() would result in error.
8021 * To prevent name lookup error messages for this case, we
8022 * need to filter out that last event.
8024 if (strcmp(class, EC_DEV_REMOVE) == 0 &&
8025 strcmp(driver_name, "lofi") == 0 && minor == NULL) {
8026 nvlist_free(nvl);
8027 return (NULL);
8029 if ((dev_name = lookup_disk_dev_name(node_path)) == NULL) {
8030 dev_name_lookup_err = 1;
8031 goto out;
8033 } else if (strcmp(subclass, ESC_NETWORK) == 0) {
8034 if ((dev_name = lookup_network_dev_name(node_path, driver_name))
8035 == NULL) {
8036 dev_name_lookup_err = 1;
8037 goto out;
8039 } else if (strcmp(subclass, ESC_PRINTER) == 0) {
8040 if ((dev_name = lookup_printer_dev_name(node_path)) == NULL) {
8041 dev_name_lookup_err = 1;
8042 goto out;
8044 } else if (strcmp(subclass, ESC_LOFI) == 0) {
8046 * The raw minor node is created or removed after the block
8047 * node. Lofi devfs events are dependent on this behavior.
8048 * Generate the sysevent only for the raw minor node.
8050 * If the lofi mapping is created, we will receive the following
8051 * event: class: EC_dev_add subclass: lofi minor: NULL
8053 * As in case of EC_dev_add, the minor is NULL pointer,
8054 * to get device links created, we will need to provide the
8055 * type of minor node for lookup_lofi_dev_name()
8057 * If the lofi device is unmapped, we will receive following
8058 * events:
8059 * class: EC_dev_remove subclass: lofi minor: disk
8060 * class: EC_dev_remove subclass: lofi minor: disk,raw
8061 * class: EC_dev_remove subclass: lofi minor: NULL
8064 if (strcmp(class, EC_DEV_ADD) == 0 && minor == NULL)
8065 minor = "disk,raw";
8067 if (minor == NULL || strstr(minor, "raw") == NULL) {
8068 nvlist_free(nvl);
8069 return (NULL);
8071 if ((dev_name = lookup_lofi_dev_name(node_path, minor)) ==
8072 NULL) {
8073 dev_name_lookup_err = 1;
8074 goto out;
8078 if (dev_name) {
8079 if ((err = nvlist_add_string(nvl, DEV_NAME, dev_name)) != 0)
8080 goto out;
8081 free(dev_name);
8082 dev_name = NULL;
8085 if ((err = nvlist_add_string(nvl, DEV_DRIVER_NAME, driver_name)) != 0)
8086 goto out;
8088 if ((err = nvlist_add_int32(nvl, DEV_INSTANCE, instance)) != 0)
8089 goto out;
8091 if (strcmp(class, EC_DEV_ADD) == 0) {
8092 /* add properties */
8093 count = 0;
8094 for (prop = di_prop_next(node, DI_PROP_NIL);
8095 prop != DI_PROP_NIL && count < MAX_PROP_COUNT;
8096 prop = di_prop_next(node, prop)) {
8098 if (di_prop_devt(prop) != DDI_DEV_T_NONE)
8099 continue;
8101 if ((x = add_property(nvl, prop)) == 0)
8102 count++;
8103 else if (x == -1) {
8104 if ((prop_name = di_prop_name(prop)) == NULL)
8105 prop_name = "";
8106 err_print(PROP_ADD_FAILED, prop_name);
8107 goto out;
8112 return (nvl);
8114 out:
8115 nvlist_free(nvl);
8117 free(dev_name);
8119 if (dev_name_lookup_err) {
8121 * If a lofi mount fails, the /devices node may well have
8122 * disappeared by the time we run, so let's not complain.
8124 if (strcmp(subclass, ESC_LOFI) != 0)
8125 err_print(DEV_NAME_LOOKUP_FAILED, node_path);
8126 } else {
8127 err_print(BUILD_EVENT_ATTR_FAILED, (err) ? strerror(err) : "");
8129 return (NULL);
8132 static void
8133 log_event(char *class, char *subclass, nvlist_t *nvl)
8135 sysevent_id_t eid;
8137 if (sysevent_post_event(class, subclass, "SUNW", DEVFSADMD,
8138 nvl, &eid) != 0) {
8139 err_print(LOG_EVENT_FAILED, strerror(errno));
8144 * When devfsadmd needs to generate sysevents, they are queued for later
8145 * delivery this allows them to be delivered after the devlinks db cache has
8146 * been flushed guaranteeing that applications consuming these events have
8147 * access to an accurate devlinks db. The queue is a FIFO, sysevents to be
8148 * inserted in the front of the queue and consumed off the back.
8150 static void
8151 enqueue_sysevent(char *class, char *subclass, nvlist_t *nvl)
8153 syseventq_t *tmp;
8155 if ((tmp = s_zalloc(sizeof (*tmp))) == NULL)
8156 return;
8158 tmp->class = s_strdup(class);
8159 tmp->subclass = s_strdup(subclass);
8160 tmp->nvl = nvl;
8162 (void) mutex_lock(&syseventq_mutex);
8163 if (syseventq_front != NULL)
8164 syseventq_front->next = tmp;
8165 else
8166 syseventq_back = tmp;
8167 syseventq_front = tmp;
8168 (void) mutex_unlock(&syseventq_mutex);
8171 static void
8172 process_syseventq()
8174 (void) mutex_lock(&syseventq_mutex);
8175 while (syseventq_back != NULL) {
8176 syseventq_t *tmp = syseventq_back;
8178 vprint(CHATTY_MID, "sending queued event: %s, %s\n",
8179 tmp->class, tmp->subclass);
8181 log_event(tmp->class, tmp->subclass, tmp->nvl);
8183 free(tmp->class);
8184 free(tmp->subclass);
8185 nvlist_free(tmp->nvl);
8186 syseventq_back = syseventq_back->next;
8187 if (syseventq_back == NULL)
8188 syseventq_front = NULL;
8189 free(tmp);
8191 (void) mutex_unlock(&syseventq_mutex);
8194 static void
8195 build_and_enq_event(char *class, char *subclass, char *node_path,
8196 di_node_t node, char *minor)
8198 nvlist_t *nvl;
8200 vprint(CHATTY_MID, "build_and_enq_event(%s, %s, %s, 0x%8.8x)\n",
8201 class, subclass, node_path, (int)node);
8203 if (node != DI_NODE_NIL)
8204 nvl = build_event_attributes(class, subclass, node_path, node,
8205 di_driver_name(node), di_instance(node), minor);
8206 else
8207 nvl = build_event_attributes(class, subclass, node_path, node,
8208 NULL, -1, minor);
8210 if (nvl) {
8211 enqueue_sysevent(class, subclass, nvl);
8216 * is_blank() returns 1 (true) if a line specified is composed of
8217 * whitespace characters only. otherwise, it returns 0 (false).
8219 * Note. the argument (line) must be null-terminated.
8221 static int
8222 is_blank(char *line)
8224 for (/* nothing */; *line != '\0'; line++)
8225 if (!isspace(*line))
8226 return (0);
8227 return (1);
8231 * Functions to deal with the no-further-processing hash
8234 static void
8235 nfphash_create(void)
8237 assert(nfp_hash == NULL);
8238 nfp_hash = s_zalloc(NFP_HASH_SZ * sizeof (item_t *));
8241 static int
8242 nfphash_fcn(char *key)
8244 int i;
8245 uint64_t sum = 0;
8247 for (i = 0; key[i] != '\0'; i++) {
8248 sum += (uchar_t)key[i];
8251 return (sum % NFP_HASH_SZ);
8254 static item_t *
8255 nfphash_lookup(char *key)
8257 int index;
8258 item_t *ip;
8260 index = nfphash_fcn(key);
8262 assert(index >= 0);
8264 for (ip = nfp_hash[index]; ip; ip = ip->i_next) {
8265 if (strcmp(ip->i_key, key) == 0)
8266 return (ip);
8269 return (NULL);
8272 static void
8273 nfphash_insert(char *key)
8275 item_t *ip;
8276 int index;
8278 index = nfphash_fcn(key);
8280 assert(index >= 0);
8282 ip = s_zalloc(sizeof (item_t));
8283 ip->i_key = s_strdup(key);
8285 ip->i_next = nfp_hash[index];
8286 nfp_hash[index] = ip;
8289 static void
8290 nfphash_destroy(void)
8292 int i;
8293 item_t *ip;
8295 for (i = 0; i < NFP_HASH_SZ; i++) {
8296 /*LINTED*/
8297 while (ip = nfp_hash[i]) {
8298 nfp_hash[i] = ip->i_next;
8299 free(ip->i_key);
8300 free(ip);
8304 free(nfp_hash);
8305 nfp_hash = NULL;
8308 static int
8309 devname_kcall(int subcmd, void *args)
8311 int error = 0;
8313 switch (subcmd) {
8314 case MODDEVNAME_LOOKUPDOOR:
8315 error = modctl(MODDEVNAME, subcmd, (uintptr_t)args);
8316 if (error) {
8317 vprint(INFO_MID, "modctl(MODDEVNAME, "
8318 "MODDEVNAME_LOOKUPDOOR) failed - %s\n",
8319 strerror(errno));
8321 break;
8322 default:
8323 error = EINVAL;
8324 break;
8326 return (error);
8329 /* ARGSUSED */
8330 static void
8331 devname_lookup_handler(void *cookie, char *argp, size_t arg_size,
8332 door_desc_t *dp, uint_t n_desc)
8334 int32_t error = 0;
8335 door_cred_t dcred;
8336 struct dca_impl dci;
8337 uint8_t cmd;
8338 sdev_door_res_t res;
8339 sdev_door_arg_t *args;
8341 if (argp == NULL || arg_size == 0) {
8342 vprint(DEVNAME_MID, "devname_lookup_handler: argp wrong\n");
8343 error = DEVFSADM_RUN_INVALID;
8344 goto done;
8346 vprint(DEVNAME_MID, "devname_lookup_handler\n");
8348 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
8349 vprint(DEVNAME_MID, "devname_lookup_handler: cred wrong\n");
8350 error = DEVFSADM_RUN_EPERM;
8351 goto done;
8354 args = (sdev_door_arg_t *)argp;
8355 cmd = args->devfsadm_cmd;
8357 vprint(DEVNAME_MID, "devname_lookup_handler: cmd %d\n", cmd);
8358 switch (cmd) {
8359 case DEVFSADMD_RUN_ALL:
8361 * run "devfsadm"
8363 dci.dci_root = "/";
8364 dci.dci_minor = NULL;
8365 dci.dci_driver = NULL;
8366 dci.dci_error = 0;
8367 dci.dci_flags = 0;
8368 dci.dci_arg = NULL;
8370 lock_dev();
8371 update_drvconf((major_t)-1, 0);
8372 dci.dci_flags |= DCA_FLUSH_PATHINST;
8374 pre_and_post_cleanup(RM_PRE);
8375 devi_tree_walk(&dci, DI_CACHE_SNAPSHOT_FLAGS, NULL);
8376 error = (int32_t)dci.dci_error;
8377 if (!error) {
8378 pre_and_post_cleanup(RM_POST);
8379 update_database = TRUE;
8380 unlock_dev(SYNC_STATE);
8381 update_database = FALSE;
8382 } else {
8383 if (DEVFSADM_DEBUG_ON) {
8384 vprint(INFO_MID, "devname_lookup_handler: "
8385 "DEVFSADMD_RUN_ALL failed\n");
8388 unlock_dev(SYNC_STATE);
8390 break;
8391 default:
8392 /* log an error here? */
8393 error = DEVFSADM_RUN_NOTSUP;
8394 break;
8397 done:
8398 vprint(DEVNAME_MID, "devname_lookup_handler: error %d\n", error);
8399 res.devfsadm_error = error;
8400 (void) door_return((char *)&res, sizeof (struct sdev_door_res),
8401 NULL, 0);
8405 di_devlink_handle_t
8406 devfsadm_devlink_cache(void)
8408 return (devlink_cache);
8412 devfsadm_reserve_id_cache(devlink_re_t re_array[], enumerate_file_t *head)
8414 enumerate_file_t *entry;
8415 int nelem;
8416 int i;
8417 int subex;
8418 char *re;
8419 size_t size;
8420 regmatch_t *pmch;
8423 * Check the <RE, subexp> array passed in and compile it.
8425 for (i = 0; re_array[i].d_re; i++) {
8426 if (re_array[i].d_subexp == 0) {
8427 err_print("bad subexp value in RE: %s\n",
8428 re_array[i].d_re);
8429 goto bad_re;
8432 re = re_array[i].d_re;
8433 if (regcomp(&re_array[i].d_rcomp, re, REG_EXTENDED) != 0) {
8434 err_print("reg. exp. failed to compile: %s\n", re);
8435 goto bad_re;
8437 subex = re_array[i].d_subexp;
8438 nelem = subex + 1;
8439 re_array[i].d_pmatch = s_malloc(sizeof (regmatch_t) * nelem);
8442 entry = head ? head : enumerate_reserved;
8443 for (; entry; entry = entry->er_next) {
8444 if (entry->er_id) {
8445 vprint(RSBY_MID, "entry %s already has ID %s\n",
8446 entry->er_file, entry->er_id);
8447 continue;
8449 for (i = 0; re_array[i].d_re; i++) {
8450 subex = re_array[i].d_subexp;
8451 pmch = re_array[i].d_pmatch;
8452 if (regexec(&re_array[i].d_rcomp, entry->er_file,
8453 subex + 1, pmch, 0) != 0) {
8454 /* No match */
8455 continue;
8457 size = pmch[subex].rm_eo - pmch[subex].rm_so;
8458 entry->er_id = s_malloc(size + 1);
8459 (void) strncpy(entry->er_id,
8460 &entry->er_file[pmch[subex].rm_so], size);
8461 entry->er_id[size] = '\0';
8462 if (head) {
8463 vprint(RSBY_MID, "devlink(%s) matches RE(%s). "
8464 "ID is %s\n", entry->er_file,
8465 re_array[i].d_re, entry->er_id);
8466 } else {
8467 vprint(RSBY_MID, "rsrv entry(%s) matches "
8468 "RE(%s) ID is %s\n", entry->er_file,
8469 re_array[i].d_re, entry->er_id);
8471 break;
8475 for (i = 0; re_array[i].d_re; i++) {
8476 regfree(&re_array[i].d_rcomp);
8477 assert(re_array[i].d_pmatch);
8478 free(re_array[i].d_pmatch);
8481 entry = head ? head : enumerate_reserved;
8482 for (; entry; entry = entry->er_next) {
8483 if (entry->er_id == NULL)
8484 continue;
8485 if (head) {
8486 vprint(RSBY_MID, "devlink: %s\n", entry->er_file);
8487 vprint(RSBY_MID, "ID: %s\n", entry->er_id);
8488 } else {
8489 vprint(RSBY_MID, "reserve file entry: %s\n",
8490 entry->er_file);
8491 vprint(RSBY_MID, "reserve file id: %s\n",
8492 entry->er_id);
8496 return (DEVFSADM_SUCCESS);
8498 bad_re:
8499 for (i = i-1; i >= 0; i--) {
8500 regfree(&re_array[i].d_rcomp);
8501 assert(re_array[i].d_pmatch);
8502 free(re_array[i].d_pmatch);
8504 return (DEVFSADM_FAILURE);
8508 * Return 1 if we have reserved links.
8511 devfsadm_have_reserved()
8513 return (enumerate_reserved ? 1 : 0);
8517 * This functions errs on the side of caution. If there is any error
8518 * we assume that the devlink is *not* reserved
8521 devfsadm_is_reserved(devlink_re_t re_array[], char *devlink)
8523 int match;
8524 enumerate_file_t estruct = {NULL};
8525 enumerate_file_t *entry;
8527 match = 0;
8528 estruct.er_file = devlink;
8529 estruct.er_id = NULL;
8530 estruct.er_next = NULL;
8532 if (devfsadm_reserve_id_cache(re_array, &estruct) != DEVFSADM_SUCCESS) {
8533 err_print("devfsadm_is_reserved: devlink (%s) does not "
8534 "match RE\n", devlink);
8535 return (0);
8537 if (estruct.er_id == NULL) {
8538 err_print("devfsadm_is_reserved: ID derived from devlink %s "
8539 "is NULL\n", devlink);
8540 return (0);
8543 entry = enumerate_reserved;
8544 for (; entry; entry = entry->er_next) {
8545 if (entry->er_id == NULL)
8546 continue;
8547 if (strcmp(entry->er_id, estruct.er_id) != 0)
8548 continue;
8549 match = 1;
8550 vprint(RSBY_MID, "reserve file entry (%s) and devlink (%s) "
8551 "match\n", entry->er_file, devlink);
8552 break;
8555 free(estruct.er_id);
8556 return (match);