1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2016 Toomas Soome <tsoome@me.com>
24 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
25 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright 2019, Joyent, Inc.
27 * Copyright 2026 Oxide Computer Company
28 */
29
30 /*
31 * Devfsadm replaces drvconfig, audlinks, disks, tapes, ports, devlinks
32 * as a general purpose device administrative utility. It creates
33 * devices special files in /devices and logical links in /dev, and
34 * coordinates updates to /etc/path_to_instance with the kernel. It
35 * operates in both command line mode to handle user or script invoked
36 * reconfiguration updates, and operates in daemon mode to handle dynamic
37 * reconfiguration for hotplugging support.
38 */
39
40 #include <string.h>
41 #include <deflt.h>
42 #include <tsol/label.h>
43 #include <bsm/devices.h>
44 #include <bsm/devalloc.h>
45 #include <utime.h>
46 #include <sys/param.h>
47 #include <bsm/libbsm.h>
48 #include <zone.h>
49 #include "devfsadm_impl.h"
50
51 /* externs from devalloc.c */
52 extern void _reset_devalloc(int);
53 extern void _update_devalloc_db(devlist_t *, int, int, char *, char *);
54 extern int _da_check_for_usb(char *, char *);
55
56 /* create or remove nodes or links. unset with -n */
57 static int file_mods = TRUE;
58
59 /* cleanup mode. Set with -C */
60 static int cleanup = FALSE;
61
62 /* devlinks -d compatibility */
63 static int devlinks_debug = FALSE;
64
65 /* flag to check if system is labeled */
66 int system_labeled = FALSE;
67
68 /* flag to enable/disable device allocation with -e/-d */
69 static int devalloc_flag = 0;
70
71 /* flag that indicates if device allocation is on or not */
72 static int devalloc_is_on = 0;
73
74 /* flag to update device allocation database for this device type */
75 static int update_devdb = 0;
76
77 /*
78 * devices to be deallocated with -d :
79 * audio, floppy, cd, floppy, tape, rmdisk.
80 */
81 static char *devalloc_list[10] = {DDI_NT_AUDIO, DDI_NT_CD, DDI_NT_CD_CHAN,
82 DDI_NT_FD, DDI_NT_TAPE, DDI_NT_BLOCK_CHAN,
83 DDI_NT_UGEN, DDI_NT_USB_ATTACHMENT_POINT,
84 DDI_NT_SCSI_NEXUS, NULL};
85
86 /* list of allocatable devices */
87 static devlist_t devlist;
88
89 /* load a single driver only. set with -i */
90 static int single_drv = FALSE;
91 static char *driver = NULL;
92
93 /* attempt to load drivers or defer attach nodes */
94 static int load_attach_drv = TRUE;
95
96 /* reload all driver.conf files */
97 static int update_all_drivers = FALSE;
98
99 /* set if invoked via /usr/lib/devfsadm/devfsadmd */
100 static int daemon_mode = FALSE;
101
102 /* set if event_handler triggered */
103 int event_driven = FALSE;
104
105 /* output directed to syslog during daemon mode if set */
106 static int logflag = FALSE;
107
108 /* build links in /dev. -x to turn off */
109 static int build_dev = TRUE;
110
111 /* build nodes in /devices. -y to turn off */
112 static int build_devices = TRUE;
113
114 /* -z to turn off */
115 static int flush_path_to_inst_enable = TRUE;
116
117 /* variables used for path_to_inst flushing */
118 static int inst_count = 0;
119 static mutex_t count_lock;
120 static cond_t cv;
121
122 /* variables for minor_fini thread */
123 static mutex_t minor_fini_mutex;
124 static int minor_fini_canceled = TRUE;
125 static int minor_fini_delayed = FALSE;
126 static cond_t minor_fini_cv;
127 static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
128
129 /* single-threads /dev modification */
130 static sema_t dev_sema;
131
132 /* the program we were invoked as; ie argv[0] */
133 static char *prog;
134
135 /* pointers to create/remove link lists */
136 static create_list_t *create_head = NULL;
137 static remove_list_t *remove_head = NULL;
138
139 /* supports the class -c option */
140 static char **classes = NULL;
141 static int num_classes = 0;
142
143 /* used with verbose option -v or -V */
144 static int num_verbose = 0;
145 static char **verbose = NULL;
146
147 static struct mperm *minor_perms = NULL;
148 static driver_alias_t *driver_aliases = NULL;
149
150 /* set if -r alternate root given */
151 static char *root_dir = "";
152
153 /* /devices or <rootdir>/devices */
154 static char *devices_dir = DEVICES;
155
156 /* /dev or <rootdir>/dev */
157 static char *dev_dir = DEV;
158
159 /* /etc/dev or <rootdir>/etc/dev */
160 static char *etc_dev_dir = ETCDEV;
161
162 /*
163 * writable root (for lock files and doors during install).
164 * This is also root dir for /dev attr dir during install.
165 */
166 static char *attr_root = NULL;
167
168 /* /etc/path_to_inst unless -p used */
169 static char *inst_file = INSTANCE_FILE;
170
171 /* /usr/lib/devfsadm/linkmods unless -l used */
172 static char *module_dirs = MODULE_DIRS;
173
174 /* default uid/gid used if /etc/minor_perm entry not found */
175 static uid_t root_uid;
176 static gid_t sys_gid;
177
178 /* /etc/devlink.tab unless devlinks -t used */
179 static char *devlinktab_file = NULL;
180
181 /* File and data structure to reserve enumerate IDs */
182 static char *enumerate_file = ENUMERATE_RESERVED;
183 static enumerate_file_t *enumerate_reserved = NULL;
184
185 /* set if /dev link is new. speeds up rm_stale_links */
186 static int linknew = TRUE;
187
188 /* variables for devlink.tab compat processing */
189 static devlinktab_list_t *devlinktab_list = NULL;
190 static unsigned int devlinktab_line = 0;
191
192 /* cache head for devfsadm_enumerate*() functions */
193 static numeral_set_t *head_numeral_set = NULL;
194
195 /* list list of devfsadm modules */
196 static module_t *module_head = NULL;
197
198 /* name_to_major list used in utility function */
199 static n2m_t *n2m_list = NULL;
200
201 /* cache of some links used for performance */
202 static linkhead_t *headlinkhead = NULL;
203
204 /* locking variables to prevent multiples writes to /dev */
205 static int hold_dev_lock = FALSE;
206 static int hold_daemon_lock = FALSE;
207 static int dev_lock_fd;
208 static int daemon_lock_fd;
209 static char dev_lockfile[PATH_MAX + 1];
210 static char daemon_lockfile[PATH_MAX + 1];
211
212 /* last devinfo node/minor processed. used for performance */
213 static di_node_t lnode;
214 static di_minor_t lminor;
215 static char lphy_path[PATH_MAX + 1] = {""};
216
217 /* Globals used by the link database */
218 static di_devlink_handle_t devlink_cache;
219 static int update_database = FALSE;
220
221 /* Globals used to set logindev perms */
222 static struct login_dev *login_dev_cache = NULL;
223 static int login_dev_enable = FALSE;
224
225 /* Global to use devinfo snapshot cache */
226 static int use_snapshot_cache = FALSE;
227
228 /* Global for no-further-processing hash */
229 static item_t **nfp_hash;
230 static mutex_t nfp_mutex = DEFAULTMUTEX;
231
232 /*
233 * Directories not removed even when empty. They are packaged, or may
234 * be referred to from a non-global zone. The dirs must be listed in
235 * canonical form i.e. without leading "/dev/"
236 */
237 static char *sticky_dirs[] =
238 {"dsk", "rdsk", "term", "lofi", "rlofi", NULL};
239
240 /* Devname globals */
241 static int lookup_door_fd = -1;
242 static char *lookup_door_path;
243
244 static void load_dev_acl(void);
245 static void update_drvconf(major_t, int);
246 static void check_reconfig_state(void);
247 static int s_stat(const char *, struct stat *);
248
249 static int is_blank(char *);
250
251 /* sysevent queue related globals */
252 static mutex_t syseventq_mutex = DEFAULTMUTEX;
253 static syseventq_t *syseventq_front;
254 static syseventq_t *syseventq_back;
255 static void process_syseventq();
256
257 static di_node_t devi_root_node = DI_NODE_NIL;
258
259 int
main(int argc,char * argv[])260 main(int argc, char *argv[])
261 {
262 struct passwd *pw;
263 struct group *gp;
264 pid_t pid;
265
266 (void) setlocale(LC_ALL, "");
267 (void) textdomain(TEXT_DOMAIN);
268
269 if ((prog = strrchr(argv[0], '/')) == NULL) {
270 prog = argv[0];
271 } else {
272 prog++;
273 }
274
275 if (getuid() != 0) {
276 err_print(MUST_BE_ROOT);
277 devfsadm_exit(1);
278 /*NOTREACHED*/
279 }
280
281 if (getzoneid() != GLOBAL_ZONEID) {
282 err_print(MUST_BE_GLOBAL_ZONE);
283 devfsadm_exit(1);
284 }
285
286 /*
287 * Close all files except stdin/stdout/stderr
288 */
289 closefrom(3);
290
291 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
292 root_uid = pw->pw_uid;
293 } else {
294 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
295 root_uid = (uid_t)0; /* assume 0 is root */
296 }
297
298 /* the default group is sys */
299
300 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
301 sys_gid = gp->gr_gid;
302 } else {
303 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
304 sys_gid = (gid_t)3; /* assume 3 is sys */
305 }
306
307 (void) umask(0);
308
309 system_labeled = is_system_labeled();
310 if (system_labeled == FALSE) {
311 /*
312 * is_system_labeled() will return false in case we are
313 * starting before the first reboot after Trusted Extensions
314 * is enabled. Check the setting in /etc/system to see if
315 * TX is enabled (even if not yet booted).
316 */
317 if (defopen("/etc/system") == 0) {
318 if (defread("set sys_labeling=1") != NULL)
319 system_labeled = TRUE;
320
321 /* close defaults file */
322 (void) defopen(NULL);
323 }
324 }
325 /*
326 * Check if device allocation is enabled.
327 */
328 devalloc_is_on = (da_is_on() == 1) ? 1 : 0;
329
330 #ifdef DEBUG
331 if (system_labeled == FALSE) {
332 struct stat tx_stat;
333
334 /* test hook: see also mkdevalloc.c and allocate.c */
335 system_labeled = is_system_labeled_debug(&tx_stat);
336 }
337 #endif
338
339 parse_args(argc, argv);
340
341 (void) sema_init(&dev_sema, 1, USYNC_THREAD, NULL);
342
343 /* Initialize device allocation list */
344 devlist.audio = devlist.cd = devlist.floppy = devlist.tape =
345 devlist.rmdisk = NULL;
346
347 if (daemon_mode == TRUE) {
348 /*
349 * Build /dev and /devices before daemonizing if
350 * reconfig booting and daemon invoked with alternate
351 * root. This is to support install.
352 */
353 if (getenv(RECONFIG_BOOT) != NULL && root_dir[0] != '\0') {
354 vprint(INFO_MID, CONFIGURING);
355 load_dev_acl();
356 update_drvconf((major_t)-1, 0);
357 process_devinfo_tree();
358 (void) modctl(MODSETMINIROOT);
359 }
360
361 /*
362 * fork before detaching from tty in order to print error
363 * message if unable to acquire file lock. locks not preserved
364 * across forks. Even under debug we want to fork so that
365 * when executed at boot we don't hang.
366 */
367 if (fork() != 0) {
368 devfsadm_exit(0);
369 /*NOTREACHED*/
370 }
371
372 /* set directory to / so it coredumps there */
373 if (chdir("/") == -1) {
374 err_print(CHROOT_FAILED, strerror(errno));
375 }
376
377 /* only one daemon can run at a time */
378 if ((pid = enter_daemon_lock()) == getpid()) {
379 detachfromtty();
380 (void) cond_init(&cv, USYNC_THREAD, 0);
381 (void) mutex_init(&count_lock, USYNC_THREAD, 0);
382 if (thr_create(NULL, 0,
383 (void *(*)(void *))instance_flush_thread,
384 NULL, THR_DETACHED, NULL) != 0) {
385 err_print(CANT_CREATE_THREAD, "daemon",
386 strerror(errno));
387 devfsadm_exit(1);
388 /*NOTREACHED*/
389 }
390
391 /* start the minor_fini_thread */
392 (void) mutex_init(&minor_fini_mutex, USYNC_THREAD, 0);
393 (void) cond_init(&minor_fini_cv, USYNC_THREAD, 0);
394 if (thr_create(NULL, 0, minor_fini_thread,
395 NULL, THR_DETACHED, NULL)) {
396 err_print(CANT_CREATE_THREAD, "minor_fini",
397 strerror(errno));
398 devfsadm_exit(1);
399 /*NOTREACHED*/
400 }
401
402
403 /*
404 * logindevperms need only be set
405 * in daemon mode and when root dir is "/".
406 */
407 if (root_dir[0] == '\0')
408 login_dev_enable = TRUE;
409 daemon_update();
410 devfsadm_exit(0);
411 /*NOTREACHED*/
412 } else {
413 err_print(DAEMON_RUNNING, pid);
414 devfsadm_exit(1);
415 /*NOTREACHED*/
416 }
417 } else {
418 /* not a daemon, so just build /dev and /devices */
419
420 /*
421 * If turning off device allocation, load the
422 * minor_perm file because process_devinfo_tree() will
423 * need this in order to reset the permissions of the
424 * device files.
425 */
426 if (devalloc_flag == DA_OFF) {
427 read_minor_perm_file();
428 }
429
430 process_devinfo_tree();
431 if (devalloc_flag != 0)
432 /* Enable/disable device allocation */
433 _reset_devalloc(devalloc_flag);
434 }
435 return (0);
436 }
437
438 static void
update_drvconf(major_t major,int flags)439 update_drvconf(major_t major, int flags)
440 {
441 if (modctl(MODLOADDRVCONF, major, flags) != 0)
442 err_print(gettext("update_drvconf failed for major %d\n"),
443 major);
444 }
445
446 static void
load_dev_acl()447 load_dev_acl()
448 {
449 if (load_devpolicy() != 0)
450 err_print(gettext("device policy load failed\n"));
451 load_minor_perm_file();
452 }
453
454 /*
455 * As devfsadm is run early in boot to provide the kernel with
456 * minor_perm info, we might as well check for reconfig at the
457 * same time to avoid running devfsadm twice. This gets invoked
458 * earlier than the env variable RECONFIG_BOOT is set up.
459 */
460 static void
check_reconfig_state()461 check_reconfig_state()
462 {
463 struct stat sb;
464
465 if (s_stat("/reconfigure", &sb) == 0) {
466 (void) modctl(MODDEVNAME, MODDEVNAME_RECONFIG, 0);
467 }
468 }
469
470 static void
modctl_sysavail()471 modctl_sysavail()
472 {
473 /*
474 * Inform /dev that system is available, that
475 * implicit reconfig can now be performed.
476 */
477 (void) modctl(MODDEVNAME, MODDEVNAME_SYSAVAIL, 0);
478 }
479
480 static void
set_lock_root(void)481 set_lock_root(void)
482 {
483 struct stat sb;
484 char *lock_root;
485 size_t len;
486
487 lock_root = attr_root ? attr_root : root_dir;
488
489 len = strlen(lock_root) + strlen(ETCDEV) + 1;
490 etc_dev_dir = s_malloc(len);
491 (void) snprintf(etc_dev_dir, len, "%s%s", lock_root, ETCDEV);
492
493 if (s_stat(etc_dev_dir, &sb) != 0) {
494 s_mkdirp(etc_dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
495 } else if (!S_ISDIR(sb.st_mode)) {
496 err_print(NOT_DIR, etc_dev_dir);
497 devfsadm_exit(1);
498 /*NOTREACHED*/
499 }
500 }
501
502
503 /*
504 * Parse arguments for all 6 programs handled from devfsadm.
505 */
506 static void
parse_args(int argc,char * argv[])507 parse_args(int argc, char *argv[])
508 {
509 int opt;
510 char get_linkcompat_opts = FALSE;
511 char *compat_class;
512 int num_aliases = 0;
513 int len;
514 int retval;
515 int config = TRUE;
516 int bind = FALSE;
517 int force_flag = FALSE;
518 struct aliases *ap = NULL;
519 struct aliases *a_head = NULL;
520 struct aliases *a_tail = NULL;
521 struct modconfig mc;
522
523 (void) bzero(&mc, sizeof (mc));
524
525 if (strcmp(prog, DISKS) == 0) {
526 compat_class = "disk";
527 get_linkcompat_opts = TRUE;
528
529 } else if (strcmp(prog, TAPES) == 0) {
530 compat_class = "tape";
531 get_linkcompat_opts = TRUE;
532
533 } else if (strcmp(prog, PORTS) == 0) {
534 compat_class = "port";
535 get_linkcompat_opts = TRUE;
536
537 } else if (strcmp(prog, AUDLINKS) == 0) {
538 compat_class = "audio";
539 get_linkcompat_opts = TRUE;
540
541 } else if (strcmp(prog, DEVLINKS) == 0) {
542 devlinktab_file = DEVLINKTAB_FILE;
543
544 build_devices = FALSE;
545 load_attach_drv = FALSE;
546
547 while ((opt = getopt(argc, argv, "dnr:st:vV:")) != EOF) {
548 switch (opt) {
549 case 'd':
550 file_mods = FALSE;
551 flush_path_to_inst_enable = FALSE;
552 devlinks_debug = TRUE;
553 break;
554 case 'n':
555 /* prevent driver loading and deferred attach */
556 load_attach_drv = FALSE;
557 break;
558 case 'r':
559 set_root_devices_dev_dir(optarg);
560 if (zone_pathcheck(root_dir) !=
561 DEVFSADM_SUCCESS)
562 devfsadm_exit(1);
563 /*NOTREACHED*/
564 break;
565 case 's':
566 /*
567 * suppress. don't create/remove links/nodes
568 * useful with -v or -V
569 */
570 file_mods = FALSE;
571 flush_path_to_inst_enable = FALSE;
572 break;
573 case 't':
574 /* supply a non-default table file */
575 devlinktab_file = optarg;
576 break;
577 case 'v':
578 /* documented verbose flag */
579 add_verbose_id(VERBOSE_MID);
580 break;
581 case 'V':
582 /* undocumented for extra verbose levels */
583 add_verbose_id(optarg);
584 break;
585 default:
586 usage();
587 break;
588 }
589 }
590
591 if (optind < argc) {
592 usage();
593 }
594
595 } else if (strcmp(prog, DRVCONFIG) == 0) {
596 int update_only = 0;
597 build_dev = FALSE;
598
599 while ((opt =
600 getopt(argc, argv, "a:bc:dfi:m:np:R:r:suvV:x")) != EOF) {
601 switch (opt) {
602 case 'a':
603 ap = calloc(1, sizeof (struct aliases));
604 ap->a_name = dequote(optarg);
605 len = strlen(ap->a_name) + 1;
606 if (len > MAXMODCONFNAME) {
607 err_print(ALIAS_TOO_LONG,
608 MAXMODCONFNAME, ap->a_name);
609 devfsadm_exit(1);
610 /*NOTREACHED*/
611 }
612 ap->a_len = len;
613 if (a_tail == NULL) {
614 a_head = ap;
615 } else {
616 a_tail->a_next = ap;
617 }
618 a_tail = ap;
619 num_aliases++;
620 bind = TRUE;
621 break;
622 case 'b':
623 bind = TRUE;
624 break;
625 case 'c':
626 (void) strcpy(mc.drvclass, optarg);
627 break;
628 case 'd':
629 /*
630 * need to keep for compatibility, but
631 * do nothing.
632 */
633 break;
634 case 'f':
635 force_flag = TRUE;
636 break;
637 case 'i':
638 single_drv = TRUE;
639 (void) strcpy(mc.drvname, optarg);
640 driver = s_strdup(optarg);
641 break;
642 case 'm':
643 mc.major = atoi(optarg);
644 break;
645 case 'n':
646 /* prevent driver loading and deferred attach */
647 load_attach_drv = FALSE;
648 break;
649 case 'p':
650 /* specify alternate path_to_inst file */
651 inst_file = s_strdup(optarg);
652 break;
653 case 'R':
654 /*
655 * Private flag for suninstall to populate
656 * device information on the installed root.
657 */
658 root_dir = s_strdup(optarg);
659 if (zone_pathcheck(root_dir) !=
660 DEVFSADM_SUCCESS) {
661 devfsadm_exit(devfsadm_copy());
662 }
663 break;
664 case 'r':
665 devices_dir = s_strdup(optarg);
666 if (zone_pathcheck(devices_dir) !=
667 DEVFSADM_SUCCESS) {
668 devfsadm_exit(1);
669 }
670 break;
671 case 's':
672 /*
673 * suppress. don't create nodes
674 * useful with -v or -V
675 */
676 file_mods = FALSE;
677 flush_path_to_inst_enable = FALSE;
678 break;
679 case 'u':
680 /*
681 * Invoked via update_drv(8) to update
682 * the kernel's driver/alias binding
683 * when removing one or more aliases.
684 */
685 config = FALSE;
686 break;
687 case 'v':
688 /* documented verbose flag */
689 add_verbose_id(VERBOSE_MID);
690 break;
691 case 'V':
692 /* undocumented for extra verbose levels */
693 add_verbose_id(optarg);
694 break;
695 case 'x':
696 update_only = 1;
697 break;
698 default:
699 usage();
700 }
701 }
702
703 if (optind < argc) {
704 usage();
705 }
706
707 if (bind == TRUE) {
708 if ((mc.major == -1) || (mc.drvname[0] == '\0')) {
709 err_print(MAJOR_AND_B_FLAG);
710 devfsadm_exit(1);
711 /*NOTREACHED*/
712 }
713 mc.flags = 0;
714 if (force_flag)
715 mc.flags |= MOD_UNBIND_OVERRIDE;
716 if (update_only)
717 mc.flags |= MOD_ADDMAJBIND_UPDATE;
718 mc.num_aliases = num_aliases;
719 mc.ap = a_head;
720 retval = modctl((config == TRUE) ? MODADDMAJBIND :
721 MODREMDRVALIAS, NULL, (caddr_t)&mc);
722 if (retval < 0) {
723 err_print((config == TRUE) ? MODCTL_ADDMAJBIND :
724 MODCTL_REMMAJBIND);
725 }
726 devfsadm_exit(retval);
727 /*NOTREACHED*/
728 }
729
730 } else if ((strcmp(prog, DEVFSADM) == 0) ||
731 (strcmp(prog, DEVFSADMD) == 0)) {
732 char *zonename = NULL;
733 int init_drvconf = 0;
734 int init_perm = 0;
735 int public_mode = 0;
736 int init_sysavail = 0;
737
738 if (strcmp(prog, DEVFSADMD) == 0) {
739 daemon_mode = TRUE;
740 }
741
742 devlinktab_file = DEVLINKTAB_FILE;
743
744 while ((opt = getopt(argc, argv,
745 "a:Cc:deIi:l:np:PR:r:sSt:uvV:x:")) != EOF) {
746 if (opt == 'I' || opt == 'P' || opt == 'S') {
747 if (public_mode)
748 usage();
749 } else {
750 if (init_perm || init_drvconf || init_sysavail)
751 usage();
752 public_mode = 1;
753 }
754 switch (opt) {
755 case 'a':
756 attr_root = s_strdup(optarg);
757 break;
758 case 'C':
759 cleanup = TRUE;
760 break;
761 case 'c':
762 num_classes++;
763 classes = s_realloc(classes,
764 num_classes * sizeof (char *));
765 classes[num_classes - 1] = optarg;
766 break;
767 case 'd':
768 if (daemon_mode == FALSE) {
769 /*
770 * Device allocation to be disabled.
771 */
772 devalloc_flag = DA_OFF;
773 build_dev = FALSE;
774 }
775 break;
776 case 'e':
777 if (daemon_mode == FALSE) {
778 /*
779 * Device allocation to be enabled.
780 */
781 devalloc_flag = DA_ON;
782 build_dev = FALSE;
783 }
784 break;
785 case 'I': /* update kernel driver.conf cache */
786 if (daemon_mode == TRUE)
787 usage();
788 init_drvconf = 1;
789 break;
790 case 'i':
791 single_drv = TRUE;
792 driver = s_strdup(optarg);
793 break;
794 case 'l':
795 /* specify an alternate module load path */
796 module_dirs = s_strdup(optarg);
797 break;
798 case 'n':
799 /* prevent driver loading and deferred attach */
800 load_attach_drv = FALSE;
801 break;
802 case 'p':
803 /* specify alternate path_to_inst file */
804 inst_file = s_strdup(optarg);
805 break;
806 case 'P':
807 if (daemon_mode == TRUE)
808 usage();
809 /* load minor_perm and device_policy */
810 init_perm = 1;
811 break;
812 case 'R':
813 /*
814 * Private flag for suninstall to populate
815 * device information on the installed root.
816 */
817 root_dir = s_strdup(optarg);
818 devfsadm_exit(devfsadm_copy());
819 /*NOTREACHED*/
820 break;
821 case 'r':
822 set_root_devices_dev_dir(optarg);
823 break;
824 case 's':
825 /*
826 * suppress. don't create/remove links/nodes
827 * useful with -v or -V
828 */
829 file_mods = FALSE;
830 flush_path_to_inst_enable = FALSE;
831 break;
832 case 'S':
833 if (daemon_mode == TRUE)
834 usage();
835 init_sysavail = 1;
836 break;
837 case 't':
838 devlinktab_file = optarg;
839 break;
840 case 'u': /* complete configuration after */
841 /* adding a driver update-only */
842 if (daemon_mode == TRUE)
843 usage();
844 update_all_drivers = TRUE;
845 break;
846 case 'v':
847 /* documented verbose flag */
848 add_verbose_id(VERBOSE_MID);
849 break;
850 case 'V':
851 /* undocumented: specify verbose lvl */
852 add_verbose_id(optarg);
853 break;
854 case 'x':
855 /*
856 * x is the "private switch" option. The
857 * goal is to not suck up all the other
858 * option letters.
859 */
860 if (strcmp(optarg, "update_devlinksdb") == 0) {
861 update_database = TRUE;
862 } else if (strcmp(optarg, "no_dev") == 0) {
863 /* don't build /dev */
864 build_dev = FALSE;
865 } else if (strcmp(optarg, "no_devices") == 0) {
866 /* don't build /devices */
867 build_devices = FALSE;
868 } else if (strcmp(optarg, "no_p2i") == 0) {
869 /* don't flush path_to_inst */
870 flush_path_to_inst_enable = FALSE;
871 } else if (strcmp(optarg, "use_dicache") == 0) {
872 use_snapshot_cache = TRUE;
873 } else {
874 usage();
875 }
876 break;
877 default:
878 usage();
879 break;
880 }
881 }
882 if (optind < argc) {
883 usage();
884 }
885
886 /*
887 * We're not in zone mode; Check to see if the rootpath
888 * collides with any zonepaths.
889 */
890 if (zonename == NULL) {
891 if (zone_pathcheck(root_dir) != DEVFSADM_SUCCESS)
892 devfsadm_exit(1);
893 /*NOTREACHED*/
894 }
895
896 if (init_drvconf || init_perm || init_sysavail) {
897 /*
898 * Load minor perm before force-loading drivers
899 * so the correct permissions are picked up.
900 */
901 if (init_perm) {
902 check_reconfig_state();
903 load_dev_acl();
904 }
905 if (init_drvconf)
906 update_drvconf((major_t)-1, 0);
907 if (init_sysavail)
908 modctl_sysavail();
909 devfsadm_exit(0);
910 /*NOTREACHED*/
911 }
912 }
913
914
915 if (get_linkcompat_opts == TRUE) {
916
917 build_devices = FALSE;
918 load_attach_drv = FALSE;
919 num_classes++;
920 classes = s_realloc(classes, num_classes *
921 sizeof (char *));
922 classes[num_classes - 1] = compat_class;
923
924 while ((opt = getopt(argc, argv, "Cnr:svV:")) != EOF) {
925 switch (opt) {
926 case 'C':
927 cleanup = TRUE;
928 break;
929 case 'n':
930 /* prevent driver loading or deferred attach */
931 load_attach_drv = FALSE;
932 break;
933 case 'r':
934 set_root_devices_dev_dir(optarg);
935 if (zone_pathcheck(root_dir) !=
936 DEVFSADM_SUCCESS)
937 devfsadm_exit(1);
938 /*NOTREACHED*/
939 break;
940 case 's':
941 /* suppress. don't create/remove links/nodes */
942 /* useful with -v or -V */
943 file_mods = FALSE;
944 flush_path_to_inst_enable = FALSE;
945 break;
946 case 'v':
947 /* documented verbose flag */
948 add_verbose_id(VERBOSE_MID);
949 break;
950 case 'V':
951 /* undocumented for extra verbose levels */
952 add_verbose_id(optarg);
953 break;
954 default:
955 usage();
956 }
957 }
958 if (optind < argc) {
959 usage();
960 }
961 }
962 set_lock_root();
963 }
964
965 void
usage(void)966 usage(void)
967 {
968 if (strcmp(prog, DEVLINKS) == 0) {
969 err_print(DEVLINKS_USAGE);
970 } else if (strcmp(prog, DRVCONFIG) == 0) {
971 err_print(DRVCONFIG_USAGE);
972 } else if ((strcmp(prog, DEVFSADM) == 0) ||
973 (strcmp(prog, DEVFSADMD) == 0)) {
974 err_print(DEVFSADM_USAGE);
975 } else {
976 err_print(COMPAT_LINK_USAGE);
977 }
978
979 devfsadm_exit(1);
980 /*NOTREACHED*/
981 }
982
983 static void
devi_tree_walk(struct dca_impl * dcip,int flags,char * ev_subclass)984 devi_tree_walk(struct dca_impl *dcip, int flags, char *ev_subclass)
985 {
986 char *msg, *name;
987 struct mlist mlist = {0};
988 di_node_t node;
989
990 vprint(CHATTY_MID, "devi_tree_walk: root=%s, minor=%s, driver=%s,"
991 " error=%d, flags=%u\n", dcip->dci_root,
992 dcip->dci_minor ? dcip->dci_minor : "<NULL>",
993 dcip->dci_driver ? dcip->dci_driver : "<NULL>", dcip->dci_error,
994 dcip->dci_flags);
995
996 assert(dcip->dci_root);
997
998 if (dcip->dci_flags & DCA_LOAD_DRV) {
999 node = di_init_driver(dcip->dci_driver, flags);
1000 msg = DRIVER_FAILURE;
1001 name = dcip->dci_driver;
1002 } else {
1003 node = di_init(dcip->dci_root, flags);
1004 msg = DI_INIT_FAILED;
1005 name = dcip->dci_root;
1006 }
1007
1008 if (node == DI_NODE_NIL) {
1009 dcip->dci_error = errno;
1010 /*
1011 * Rapid hotplugging (commonly seen during USB testing),
1012 * may remove a device before the create event for it
1013 * has been processed. To prevent alarming users with
1014 * a superfluous message, we suppress error messages
1015 * for ENXIO and hotplug.
1016 */
1017 if (!(errno == ENXIO && (dcip->dci_flags & DCA_HOT_PLUG)))
1018 err_print(msg, name, strerror(dcip->dci_error));
1019 return;
1020 }
1021
1022 if (dcip->dci_flags & DCA_FLUSH_PATHINST)
1023 flush_path_to_inst();
1024
1025 dcip->dci_arg = &mlist;
1026 devi_root_node = node; /* protected by lock_dev() */
1027
1028 vprint(CHATTY_MID, "walking device tree\n");
1029
1030 (void) di_walk_minor(node, NULL, DI_CHECK_ALIAS, dcip,
1031 check_minor_type);
1032
1033 process_deferred_links(dcip, DCA_CREATE_LINK);
1034
1035 dcip->dci_arg = NULL;
1036
1037 /*
1038 * Finished creating devfs files and dev links.
1039 * Log sysevent.
1040 */
1041 if (ev_subclass)
1042 build_and_enq_event(EC_DEV_ADD, ev_subclass, dcip->dci_root,
1043 node, dcip->dci_minor);
1044
1045 /* Add new device to device allocation database */
1046 if (system_labeled && update_devdb) {
1047 _update_devalloc_db(&devlist, 0, DA_ADD, NULL, root_dir);
1048 update_devdb = 0;
1049 }
1050
1051 devi_root_node = DI_NODE_NIL; /* protected by lock_dev() */
1052 di_fini(node);
1053 }
1054
1055 static void
process_deferred_links(struct dca_impl * dcip,int flags)1056 process_deferred_links(struct dca_impl *dcip, int flags)
1057 {
1058 struct mlist *dep;
1059 struct minor *mp, *smp;
1060
1061 vprint(CHATTY_MID, "processing deferred links\n");
1062
1063 dep = dcip->dci_arg;
1064
1065 /*
1066 * The list head is not used during the deferred create phase
1067 */
1068 dcip->dci_arg = NULL;
1069
1070 assert(dep);
1071 assert((dep->head == NULL) ^ (dep->tail != NULL));
1072 assert(flags == DCA_FREE_LIST || flags == DCA_CREATE_LINK);
1073
1074 for (smp = NULL, mp = dep->head; mp; mp = mp->next) {
1075 if (flags == DCA_CREATE_LINK)
1076 (void) check_minor_type(mp->node, mp->minor, dcip);
1077 free(smp);
1078 smp = mp;
1079 }
1080
1081 free(smp);
1082 }
1083
1084 /*
1085 * Called in non-daemon mode to take a snap shot of the devinfo tree.
1086 * Then it calls the appropriate functions to build /devices and /dev.
1087 * It also flushes path_to_inst.
1088 * Except in the devfsadm -i (single driver case), the flags used by devfsadm
1089 * needs to match DI_CACHE_SNAPSHOT_FLAGS. That will make DINFOCACHE snapshot
1090 * updated.
1091 */
1092 void
process_devinfo_tree()1093 process_devinfo_tree()
1094 {
1095 uint_t flags;
1096 struct dca_impl dci;
1097 char name[MAXNAMELEN];
1098 char *fcn = "process_devinfo_tree: ";
1099
1100 vprint(CHATTY_MID, "%senter\n", fcn);
1101
1102 dca_impl_init("/", NULL, &dci);
1103
1104 lock_dev();
1105
1106 /*
1107 * Update kernel driver.conf cache when devfsadm/drvconfig
1108 * is invoked to build /devices and /dev.
1109 */
1110 if (update_all_drivers || load_attach_drv) {
1111 update_drvconf((major_t)-1,
1112 update_all_drivers ? MOD_LOADDRVCONF_RECONF : 0);
1113 }
1114
1115 if (single_drv == TRUE) {
1116 /*
1117 * load a single driver, but walk the entire devinfo tree
1118 */
1119 if (load_attach_drv == FALSE)
1120 err_print(DRV_LOAD_REQD);
1121
1122 vprint(CHATTY_MID, "%sattaching driver (%s)\n", fcn, driver);
1123
1124 dci.dci_flags |= DCA_LOAD_DRV;
1125 (void) snprintf(name, sizeof (name), "%s", driver);
1126 dci.dci_driver = name;
1127 flags = DINFOCPYALL | DINFOPATH;
1128
1129 } else if (load_attach_drv == TRUE) {
1130 /*
1131 * Load and attach all drivers, then walk the entire tree.
1132 * If the cache flag is set, use DINFOCACHE to get cached
1133 * data.
1134 */
1135 if (use_snapshot_cache == TRUE) {
1136 flags = DINFOCACHE;
1137 vprint(CHATTY_MID, "%susing snapshot cache\n", fcn);
1138 } else {
1139 vprint(CHATTY_MID, "%sattaching all drivers\n", fcn);
1140 flags = DI_CACHE_SNAPSHOT_FLAGS;
1141 if (cleanup) {
1142 /*
1143 * remove dangling entries from /etc/devices
1144 * files.
1145 */
1146 flags |= DINFOCLEANUP;
1147 }
1148 }
1149 } else {
1150 /*
1151 * For devlinks, disks, ports, tapes and devfsadm -n,
1152 * just need to take a snapshot with active devices.
1153 */
1154 vprint(CHATTY_MID, "%staking snapshot of active devices\n",
1155 fcn);
1156 flags = DINFOCPYALL;
1157 }
1158
1159 if (((load_attach_drv == TRUE) || (single_drv == TRUE)) &&
1160 (build_devices == TRUE)) {
1161 dci.dci_flags |= DCA_FLUSH_PATHINST;
1162 }
1163
1164 /* handle pre-cleanup operations desired by the modules. */
1165 pre_and_post_cleanup(RM_PRE);
1166
1167 devi_tree_walk(&dci, flags, NULL);
1168
1169 if (dci.dci_error) {
1170 devfsadm_exit(1);
1171 /*NOTREACHED*/
1172 }
1173
1174 /* handle post-cleanup operations desired by the modules. */
1175 pre_and_post_cleanup(RM_POST);
1176
1177 unlock_dev(SYNC_STATE);
1178 }
1179
1180 /*ARGSUSED*/
1181 static void
print_cache_signal(int signo)1182 print_cache_signal(int signo)
1183 {
1184 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1185 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1186 devfsadm_exit(1);
1187 /*NOTREACHED*/
1188 }
1189 }
1190
1191 static void
revoke_lookup_door(void)1192 revoke_lookup_door(void)
1193 {
1194 if (lookup_door_fd != -1) {
1195 if (door_revoke(lookup_door_fd) == -1) {
1196 err_print("door_revoke of %s failed - %s\n",
1197 lookup_door_path, strerror(errno));
1198 }
1199 }
1200 }
1201
1202 /*ARGSUSED*/
1203 static void
catch_exit(int signo)1204 catch_exit(int signo)
1205 {
1206 revoke_lookup_door();
1207 }
1208
1209 /*
1210 * Register with eventd for messages. Create doors for synchronous
1211 * link creation.
1212 */
1213 static void
daemon_update(void)1214 daemon_update(void)
1215 {
1216 int fd;
1217 char *fcn = "daemon_update: ";
1218 char door_file[MAXPATHLEN];
1219 const char *subclass_list;
1220 sysevent_handle_t *sysevent_hp;
1221 vprint(CHATTY_MID, "%senter\n", fcn);
1222
1223 if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1224 err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1225 devfsadm_exit(1);
1226 /*NOTREACHED*/
1227 }
1228 if (signal(SIGTERM, catch_exit) == SIG_ERR) {
1229 err_print("signal SIGTERM failed: %s\n", strerror(errno));
1230 devfsadm_exit(1);
1231 /*NOTREACHED*/
1232 }
1233
1234 if (snprintf(door_file, sizeof (door_file),
1235 "%s%s", attr_root ? attr_root : root_dir, DEVFSADM_SERVICE_DOOR)
1236 >= sizeof (door_file)) {
1237 err_print("update_daemon failed to open sysevent service "
1238 "door\n");
1239 devfsadm_exit(1);
1240 /*NOTREACHED*/
1241 }
1242 if ((sysevent_hp = sysevent_open_channel_alt(
1243 door_file)) == NULL) {
1244 err_print(CANT_CREATE_DOOR,
1245 door_file, strerror(errno));
1246 devfsadm_exit(1);
1247 /*NOTREACHED*/
1248 }
1249 if (sysevent_bind_subscriber(sysevent_hp, event_handler) != 0) {
1250 err_print(CANT_CREATE_DOOR,
1251 door_file, strerror(errno));
1252 (void) sysevent_close_channel(sysevent_hp);
1253 devfsadm_exit(1);
1254 /*NOTREACHED*/
1255 }
1256 subclass_list = EC_SUB_ALL;
1257 if (sysevent_register_event(sysevent_hp, EC_ALL, &subclass_list, 1)
1258 != 0) {
1259 err_print(CANT_CREATE_DOOR,
1260 door_file, strerror(errno));
1261 (void) sysevent_unbind_subscriber(sysevent_hp);
1262 (void) sysevent_close_channel(sysevent_hp);
1263 devfsadm_exit(1);
1264 /*NOTREACHED*/
1265 }
1266 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1267 etc_dev_dir, DEVFSADM_SYNCH_DOOR) >= sizeof (door_file)) {
1268 err_print(CANT_CREATE_DOOR, DEVFSADM_SYNCH_DOOR,
1269 strerror(ENAMETOOLONG));
1270 devfsadm_exit(1);
1271 /*NOTREACHED*/
1272 }
1273
1274 (void) s_unlink(door_file);
1275 if ((fd = open(door_file, O_RDWR | O_CREAT, SYNCH_DOOR_PERMS)) == -1) {
1276 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1277 devfsadm_exit(1);
1278 /*NOTREACHED*/
1279 }
1280 (void) close(fd);
1281
1282 if ((fd = door_create(sync_handler, NULL,
1283 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1284 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1285 (void) s_unlink(door_file);
1286 devfsadm_exit(1);
1287 /*NOTREACHED*/
1288 }
1289
1290 if (fattach(fd, door_file) == -1) {
1291 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1292 (void) s_unlink(door_file);
1293 devfsadm_exit(1);
1294 /*NOTREACHED*/
1295 }
1296
1297 /*
1298 * devname_lookup_door
1299 */
1300 if (snprintf(door_file, sizeof (door_file), "%s/%s",
1301 etc_dev_dir, DEVNAME_LOOKUP_DOOR) >= sizeof (door_file)) {
1302 err_print(CANT_CREATE_DOOR, DEVNAME_LOOKUP_DOOR,
1303 strerror(ENAMETOOLONG));
1304 devfsadm_exit(1);
1305 /*NOTREACHED*/
1306 }
1307
1308 (void) s_unlink(door_file);
1309 if ((fd = open(door_file, O_RDWR | O_CREAT, S_IRUSR|S_IWUSR)) == -1) {
1310 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1311 devfsadm_exit(1);
1312 /*NOTREACHED*/
1313 }
1314 (void) close(fd);
1315
1316 if ((fd = door_create(devname_lookup_handler, NULL,
1317 DOOR_REFUSE_DESC)) == -1) {
1318 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1319 (void) s_unlink(door_file);
1320 devfsadm_exit(1);
1321 /*NOTREACHED*/
1322 }
1323
1324 (void) fdetach(door_file);
1325 lookup_door_path = s_strdup(door_file);
1326 retry:
1327 if (fattach(fd, door_file) == -1) {
1328 if (errno == EBUSY)
1329 goto retry;
1330 err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1331 (void) s_unlink(door_file);
1332 devfsadm_exit(1);
1333 /*NOTREACHED*/
1334 }
1335 lookup_door_fd = fd;
1336
1337 /* pass down the door name to kernel for door_ki_open */
1338 if (devname_kcall(MODDEVNAME_LOOKUPDOOR, (void *)door_file) != 0)
1339 err_print(DEVNAME_CONTACT_FAILED, strerror(errno));
1340
1341 vprint(CHATTY_MID, "%spausing\n", fcn);
1342 for (;;) {
1343 (void) pause();
1344 }
1345 }
1346
1347 /*ARGSUSED*/
1348 static void
sync_handler(void * cookie,char * ap,size_t asize,door_desc_t * dp,uint_t ndesc)1349 sync_handler(void *cookie, char *ap, size_t asize,
1350 door_desc_t *dp, uint_t ndesc)
1351 {
1352 door_cred_t dcred;
1353 struct dca_off *dcp, rdca;
1354 struct dca_impl dci;
1355
1356 /*
1357 * Must be root to make this call
1358 * If caller is not root, don't touch its data.
1359 */
1360 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
1361 dcp = ⤷
1362 dcp->dca_error = EPERM;
1363 goto out;
1364 }
1365
1366 assert(ap);
1367 assert(asize == sizeof (*dcp));
1368
1369 dcp = (void *)ap;
1370
1371 /*
1372 * Root is always present and is the first component of "name" member
1373 */
1374 assert(dcp->dca_root == 0);
1375
1376 /*
1377 * The structure passed in by the door_client uses offsets
1378 * instead of pointers to work across address space boundaries.
1379 * Now copy the data into a structure (dca_impl) which uses
1380 * pointers.
1381 */
1382 dci.dci_root = &dcp->dca_name[dcp->dca_root];
1383 dci.dci_minor = dcp->dca_minor ? &dcp->dca_name[dcp->dca_minor] : NULL;
1384 dci.dci_driver =
1385 dcp->dca_driver ? &dcp->dca_name[dcp->dca_driver] : NULL;
1386 dci.dci_error = 0;
1387 dci.dci_flags = dcp->dca_flags | (dci.dci_driver ? DCA_LOAD_DRV : 0);
1388 dci.dci_arg = NULL;
1389
1390 lock_dev();
1391 devi_tree_walk(&dci, DINFOCPYALL, NULL);
1392 dcp->dca_error = dci.dci_error;
1393
1394 if (dcp->dca_flags & DCA_DEVLINK_SYNC)
1395 unlock_dev(SYNC_STATE);
1396 else
1397 unlock_dev(CACHE_STATE);
1398
1399 out: (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0);
1400 }
1401
1402 static void
lock_dev(void)1403 lock_dev(void)
1404 {
1405 vprint(CHATTY_MID, "lock_dev(): entered\n");
1406
1407 if (build_dev == FALSE)
1408 return;
1409
1410 /* lockout other threads from /dev */
1411 while (sema_wait(&dev_sema) != 0)
1412 ;
1413
1414 /*
1415 * Lock out other devfsadm processes from /dev.
1416 * If this wasn't the last process to run,
1417 * clear caches
1418 */
1419 if (enter_dev_lock() != getpid()) {
1420 invalidate_enumerate_cache();
1421 rm_all_links_from_cache();
1422 (void) di_devlink_close(&devlink_cache, DI_LINK_ERROR);
1423
1424 /* send any sysevents that were queued up. */
1425 process_syseventq();
1426 }
1427
1428 /*
1429 * (re)load the reverse links database if not
1430 * already cached.
1431 */
1432 if (devlink_cache == NULL)
1433 devlink_cache = di_devlink_open(root_dir, 0);
1434
1435 /*
1436 * If modules were unloaded, reload them. Also use module status
1437 * as an indication that we should check to see if other binding
1438 * files need to be reloaded.
1439 */
1440 if (module_head == NULL) {
1441 load_modules();
1442 read_minor_perm_file();
1443 read_driver_aliases_file();
1444 read_devlinktab_file();
1445 read_logindevperm_file();
1446 read_enumerate_file();
1447 }
1448
1449 if (module_head != NULL)
1450 return;
1451
1452 if (strcmp(prog, DEVLINKS) == 0) {
1453 if (devlinktab_list == NULL) {
1454 err_print(NO_LINKTAB, devlinktab_file);
1455 err_print(NO_MODULES, module_dirs);
1456 err_print(ABORTING);
1457 devfsadm_exit(1);
1458 /*NOTREACHED*/
1459 }
1460 } else {
1461 err_print(NO_MODULES, module_dirs);
1462 if (strcmp(prog, DEVFSADM) == 0) {
1463 err_print(MODIFY_PATH);
1464 }
1465 }
1466 }
1467
1468 /*
1469 * Unlock the device. If we are processing a CACHE_STATE call, we signal a
1470 * minor_fini_thread delayed SYNC_STATE at the end of the call. If we are
1471 * processing a SYNC_STATE call, we cancel any minor_fini_thread SYNC_STATE
1472 * at both the start and end of the call since we will be doing the SYNC_STATE.
1473 */
1474 static void
unlock_dev(int flag)1475 unlock_dev(int flag)
1476 {
1477 assert(flag == SYNC_STATE || flag == CACHE_STATE);
1478
1479 vprint(CHATTY_MID, "unlock_dev(): entered\n");
1480
1481 /* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */
1482 if (flag == SYNC_STATE) {
1483 (void) mutex_lock(&minor_fini_mutex);
1484 minor_fini_canceled = TRUE;
1485 minor_fini_delayed = FALSE;
1486 (void) mutex_unlock(&minor_fini_mutex);
1487 }
1488
1489 if (build_dev == FALSE)
1490 return;
1491
1492 if (devlink_cache == NULL) {
1493 err_print(NO_DEVLINK_CACHE);
1494 }
1495 assert(devlink_cache);
1496
1497 if (flag == SYNC_STATE) {
1498 unload_modules();
1499 if (update_database)
1500 (void) di_devlink_update(devlink_cache);
1501 (void) di_devlink_close(&devlink_cache, 0);
1502
1503 /*
1504 * now that the devlinks db cache has been flushed, it is safe
1505 * to send any sysevents that were queued up.
1506 */
1507 process_syseventq();
1508 }
1509
1510 exit_dev_lock(0);
1511
1512 (void) mutex_lock(&minor_fini_mutex);
1513 if (flag == SYNC_STATE) {
1514 /* We did a SYNC_STATE, cancel minor_fini_thread SYNC */
1515 minor_fini_canceled = TRUE;
1516 minor_fini_delayed = FALSE;
1517 } else {
1518 /* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */
1519 minor_fini_canceled = FALSE;
1520 minor_fini_delayed = TRUE;
1521 (void) cond_signal(&minor_fini_cv);
1522 }
1523 (void) mutex_unlock(&minor_fini_mutex);
1524
1525 (void) sema_post(&dev_sema);
1526 }
1527
1528 /*
1529 * Check that if -r is set, it is not any part of a zone--- that is, that
1530 * the zonepath is not a substring of the root path.
1531 */
1532 static int
zone_pathcheck(char * checkpath)1533 zone_pathcheck(char *checkpath)
1534 {
1535 void *dlhdl = NULL;
1536 char *name;
1537 char root[MAXPATHLEN]; /* resolved devfsadm root path */
1538 char zroot[MAXPATHLEN]; /* zone root path */
1539 char rzroot[MAXPATHLEN]; /* resolved zone root path */
1540 char tmp[MAXPATHLEN];
1541 FILE *cookie;
1542 int err = DEVFSADM_SUCCESS;
1543
1544 if (checkpath[0] == '\0')
1545 return (DEVFSADM_SUCCESS);
1546
1547 /*
1548 * Check if zones is available on this system.
1549 */
1550 if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
1551 return (DEVFSADM_SUCCESS);
1552 }
1553
1554 bzero(root, sizeof (root));
1555 if (resolvepath(checkpath, root, sizeof (root) - 1) == -1) {
1556 /*
1557 * In this case the user has done "devfsadm -r" on some path
1558 * which does not yet exist, or we got some other misc. error.
1559 * We punt and don't resolve the path in this case.
1560 */
1561 (void) strlcpy(root, checkpath, sizeof (root));
1562 }
1563
1564 if (strlen(root) > 0 && (root[strlen(root) - 1] != '/')) {
1565 (void) snprintf(tmp, sizeof (tmp), "%s/", root);
1566 (void) strlcpy(root, tmp, sizeof (root));
1567 }
1568
1569 cookie = setzoneent();
1570 while ((name = getzoneent(cookie)) != NULL) {
1571 /* Skip the global zone */
1572 if (strcmp(name, GLOBAL_ZONENAME) == 0) {
1573 free(name);
1574 continue;
1575 }
1576
1577 if (zone_get_zonepath(name, zroot, sizeof (zroot)) != Z_OK) {
1578 free(name);
1579 continue;
1580 }
1581
1582 bzero(rzroot, sizeof (rzroot));
1583 if (resolvepath(zroot, rzroot, sizeof (rzroot) - 1) == -1) {
1584 /*
1585 * Zone path doesn't exist, or other misc error,
1586 * so we try using the non-resolved pathname.
1587 */
1588 (void) strlcpy(rzroot, zroot, sizeof (rzroot));
1589 }
1590 if (strlen(rzroot) > 0 && (rzroot[strlen(rzroot) - 1] != '/')) {
1591 (void) snprintf(tmp, sizeof (tmp), "%s/", rzroot);
1592 (void) strlcpy(rzroot, tmp, sizeof (rzroot));
1593 }
1594
1595 /*
1596 * Finally, the comparison. If the zone root path is a
1597 * leading substring of the root path, fail.
1598 */
1599 if (strncmp(rzroot, root, strlen(rzroot)) == 0) {
1600 err_print(ZONE_PATHCHECK, root, name);
1601 err = DEVFSADM_FAILURE;
1602 free(name);
1603 break;
1604 }
1605 free(name);
1606 }
1607 endzoneent(cookie);
1608 (void) dlclose(dlhdl);
1609 return (err);
1610 }
1611
1612 /*
1613 * Called by the daemon when it receives an event from the devfsadm SLM
1614 * to syseventd.
1615 *
1616 * The devfsadm SLM uses a private event channel for communication to
1617 * devfsadmd set-up via private libsysevent interfaces. This handler is
1618 * used to bind to the devfsadmd channel for event delivery.
1619 * The devfsadmd SLM insures single calls to this routine as well as
1620 * synchronized event delivery.
1621 *
1622 */
1623 static void
event_handler(sysevent_t * ev)1624 event_handler(sysevent_t *ev)
1625 {
1626 char *path;
1627 char *minor;
1628 char *subclass;
1629 char *dev_ev_subclass;
1630 char *driver_name;
1631 nvlist_t *attr_list = NULL;
1632 int err = 0;
1633 int instance;
1634 int branch_event = 0;
1635
1636 /*
1637 * If this is event-driven, then we cannot trust the static devlist
1638 * to be correct.
1639 */
1640
1641 event_driven = TRUE;
1642 subclass = sysevent_get_subclass_name(ev);
1643 vprint(EVENT_MID, "event_handler: %s id:0X%llx\n",
1644 subclass, sysevent_get_seq(ev));
1645
1646 if (strcmp(subclass, ESC_DEVFS_START) == 0) {
1647 return;
1648 }
1649
1650 /* Check if event is an instance modification */
1651 if (strcmp(subclass, ESC_DEVFS_INSTANCE_MOD) == 0) {
1652 devfs_instance_mod();
1653 return;
1654 }
1655 if (sysevent_get_attr_list(ev, &attr_list) != 0) {
1656 vprint(EVENT_MID, "event_handler: can not get attr list\n");
1657 return;
1658 }
1659
1660 if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0 ||
1661 strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0 ||
1662 strcmp(subclass, ESC_DEVFS_MINOR_CREATE) == 0 ||
1663 strcmp(subclass, ESC_DEVFS_MINOR_REMOVE) == 0) {
1664 if ((err = nvlist_lookup_string(attr_list, DEVFS_PATHNAME,
1665 &path)) != 0)
1666 goto out;
1667
1668 if (nvlist_lookup_string(attr_list, DEVFS_DEVI_CLASS,
1669 &dev_ev_subclass) != 0)
1670 dev_ev_subclass = NULL;
1671
1672 if (nvlist_lookup_string(attr_list, DEVFS_DRIVER_NAME,
1673 &driver_name) != 0)
1674 driver_name = NULL;
1675
1676 if (nvlist_lookup_int32(attr_list, DEVFS_INSTANCE,
1677 &instance) != 0)
1678 instance = -1;
1679
1680 if (nvlist_lookup_int32(attr_list, DEVFS_BRANCH_EVENT,
1681 &branch_event) != 0)
1682 branch_event = 0;
1683
1684 if (nvlist_lookup_string(attr_list, DEVFS_MINOR_NAME,
1685 &minor) != 0)
1686 minor = NULL;
1687
1688 lock_dev();
1689
1690 if (strcmp(ESC_DEVFS_DEVI_ADD, subclass) == 0) {
1691 add_minor_pathname(path, NULL, dev_ev_subclass);
1692 if (branch_event) {
1693 build_and_enq_event(EC_DEV_BRANCH,
1694 ESC_DEV_BRANCH_ADD, path, DI_NODE_NIL,
1695 NULL);
1696 }
1697
1698 } else if (strcmp(ESC_DEVFS_MINOR_CREATE, subclass) == 0) {
1699 add_minor_pathname(path, minor, dev_ev_subclass);
1700
1701 } else if (strcmp(ESC_DEVFS_MINOR_REMOVE, subclass) == 0) {
1702 hot_cleanup(path, minor, dev_ev_subclass, driver_name,
1703 instance);
1704
1705 } else { /* ESC_DEVFS_DEVI_REMOVE */
1706 hot_cleanup(path, NULL, dev_ev_subclass,
1707 driver_name, instance);
1708 if (branch_event) {
1709 build_and_enq_event(EC_DEV_BRANCH,
1710 ESC_DEV_BRANCH_REMOVE, path, DI_NODE_NIL,
1711 NULL);
1712 }
1713 }
1714
1715 unlock_dev(CACHE_STATE);
1716
1717 } else if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0 ||
1718 strcmp(subclass, ESC_DEVFS_BRANCH_REMOVE) == 0) {
1719 if ((err = nvlist_lookup_string(attr_list,
1720 DEVFS_PATHNAME, &path)) != 0)
1721 goto out;
1722
1723 /* just log ESC_DEV_BRANCH... event */
1724 if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0)
1725 dev_ev_subclass = ESC_DEV_BRANCH_ADD;
1726 else
1727 dev_ev_subclass = ESC_DEV_BRANCH_REMOVE;
1728
1729 lock_dev();
1730 build_and_enq_event(EC_DEV_BRANCH, dev_ev_subclass, path,
1731 DI_NODE_NIL, NULL);
1732 unlock_dev(CACHE_STATE);
1733 } else
1734 err_print(UNKNOWN_EVENT, subclass);
1735
1736 out:
1737 if (err)
1738 err_print(EVENT_ATTR_LOOKUP_FAILED, strerror(err));
1739 nvlist_free(attr_list);
1740 }
1741
1742 static void
dca_impl_init(char * root,char * minor,struct dca_impl * dcip)1743 dca_impl_init(char *root, char *minor, struct dca_impl *dcip)
1744 {
1745 assert(root);
1746
1747 dcip->dci_root = root;
1748 dcip->dci_minor = minor;
1749 dcip->dci_driver = NULL;
1750 dcip->dci_error = 0;
1751 dcip->dci_flags = 0;
1752 dcip->dci_arg = NULL;
1753 }
1754
1755 /*
1756 * Kernel logs a message when a devinfo node is attached. Try to create
1757 * /dev and /devices for each minor node. minorname can be NULL.
1758 */
1759 void
add_minor_pathname(char * node,char * minor,char * ev_subclass)1760 add_minor_pathname(char *node, char *minor, char *ev_subclass)
1761 {
1762 struct dca_impl dci;
1763
1764 vprint(CHATTY_MID, "add_minor_pathname: node_path=%s minor=%s\n",
1765 node, minor ? minor : "NULL");
1766
1767 dca_impl_init(node, minor, &dci);
1768
1769 /*
1770 * Restrict hotplug link creation if daemon
1771 * started with -i option.
1772 */
1773 if (single_drv == TRUE) {
1774 dci.dci_driver = driver;
1775 }
1776
1777 /*
1778 * We are being invoked in response to a hotplug event.
1779 */
1780 dci.dci_flags = DCA_HOT_PLUG | DCA_CHECK_TYPE;
1781
1782 devi_tree_walk(&dci, DINFOPROP|DINFOMINOR, ev_subclass);
1783 }
1784
1785 static di_node_t
find_clone_node()1786 find_clone_node()
1787 {
1788 static di_node_t clone_node = DI_NODE_NIL;
1789
1790 if (clone_node == DI_NODE_NIL)
1791 clone_node = di_init("/pseudo/clone@0", DINFOPROP);
1792 return (clone_node);
1793 }
1794
1795 static int
is_descendent_of(di_node_t node,char * driver)1796 is_descendent_of(di_node_t node, char *driver)
1797 {
1798 while (node != DI_NODE_NIL) {
1799 char *drv = di_driver_name(node);
1800 if (strcmp(drv, driver) == 0)
1801 return (1);
1802 node = di_parent_node(node);
1803 }
1804 return (0);
1805 }
1806
1807 /*
1808 * Checks the minor type. If it is an alias node, then lookup
1809 * the real node/minor first, then call minor_process() to
1810 * do the real work.
1811 */
1812 static int
check_minor_type(di_node_t node,di_minor_t minor,void * arg)1813 check_minor_type(di_node_t node, di_minor_t minor, void *arg)
1814 {
1815 ddi_minor_type minor_type;
1816 di_node_t clone_node;
1817 char *mn;
1818 char *nt;
1819 struct mlist *dep;
1820 struct dca_impl *dcip = arg;
1821
1822 assert(dcip);
1823
1824 dep = dcip->dci_arg;
1825
1826 mn = di_minor_name(minor);
1827
1828 /*
1829 * We match driver here instead of in minor_process
1830 * as we want the actual driver name. This check is
1831 * unnecessary during deferred processing.
1832 */
1833 if (dep &&
1834 ((dcip->dci_driver && !is_descendent_of(node, dcip->dci_driver)) ||
1835 (dcip->dci_minor && strcmp(mn, dcip->dci_minor)))) {
1836 return (DI_WALK_CONTINUE);
1837 }
1838
1839 if ((dcip->dci_flags & DCA_CHECK_TYPE) &&
1840 (nt = di_minor_nodetype(minor)) &&
1841 (strcmp(nt, DDI_NT_NET) == 0)) {
1842 dcip->dci_flags &= ~DCA_CHECK_TYPE;
1843 }
1844
1845 minor_type = di_minor_type(minor);
1846
1847 if (minor_type == DDM_MINOR) {
1848 minor_process(node, minor, dep);
1849
1850 } else if (minor_type == DDM_ALIAS) {
1851 struct mlist *cdep, clone_del = {0};
1852
1853 clone_node = find_clone_node();
1854 if (clone_node == DI_NODE_NIL) {
1855 err_print(DI_INIT_FAILED, "clone", strerror(errno));
1856 return (DI_WALK_CONTINUE);
1857 }
1858
1859 cdep = dep ? &clone_del : NULL;
1860
1861 minor_process(clone_node, minor, cdep);
1862
1863 /*
1864 * cache "alias" minor node and free "clone" minor
1865 */
1866 if (cdep != NULL && cdep->head != NULL) {
1867 assert(cdep->tail != NULL);
1868 cache_deferred_minor(dep, node, minor);
1869 dcip->dci_arg = cdep;
1870 process_deferred_links(dcip, DCA_FREE_LIST);
1871 dcip->dci_arg = dep;
1872 }
1873 }
1874
1875 return (DI_WALK_CONTINUE);
1876 }
1877
1878
1879 /*
1880 * This is the entry point for each minor node, whether walking
1881 * the entire tree via di_walk_minor() or processing a hotplug event
1882 * for a single devinfo node (via hotplug ndi_devi_online()).
1883 */
1884 /*ARGSUSED*/
1885 static void
minor_process(di_node_t node,di_minor_t minor,struct mlist * dep)1886 minor_process(di_node_t node, di_minor_t minor, struct mlist *dep)
1887 {
1888 create_list_t *create;
1889 int defer;
1890
1891 vprint(CHATTY_MID, "minor_process: node=%s, minor=%s\n",
1892 di_node_name(node), di_minor_name(minor));
1893
1894 if (dep != NULL) {
1895
1896 /*
1897 * Reset /devices node to minor_perm perm/ownership
1898 * if we are here to deactivate device allocation
1899 */
1900 if (build_devices == TRUE) {
1901 reset_node_permissions(node, minor);
1902 }
1903
1904 if (build_dev == FALSE) {
1905 return;
1906 }
1907
1908 /*
1909 * This function will create any nodes for /etc/devlink.tab.
1910 * If devlink.tab handles link creation, we don't call any
1911 * devfsadm modules since that could cause duplicate caching
1912 * in the enumerate functions if different re strings are
1913 * passed that are logically identical. I'm still not
1914 * convinced this would cause any harm, but better to be safe.
1915 *
1916 * Deferred processing is available only for devlinks
1917 * created through devfsadm modules.
1918 */
1919 if (process_devlink_compat(minor, node) == TRUE) {
1920 return;
1921 }
1922 } else {
1923 vprint(CHATTY_MID, "minor_process: deferred processing\n");
1924 }
1925
1926 /*
1927 * look for relevant link create rules in the modules, and
1928 * invoke the link create callback function to build a link
1929 * if there is a match.
1930 */
1931 defer = 0;
1932 for (create = create_head; create != NULL; create = create->next) {
1933 if ((minor_matches_rule(node, minor, create) == TRUE) &&
1934 class_ok(create->create->device_class) ==
1935 DEVFSADM_SUCCESS) {
1936 if (call_minor_init(create->modptr) ==
1937 DEVFSADM_FAILURE) {
1938 continue;
1939 }
1940
1941 /*
1942 * If NOT doing the deferred creates (i.e. 1st pass) and
1943 * rule requests deferred processing cache the minor
1944 * data.
1945 *
1946 * If deferred processing (2nd pass), create links
1947 * ONLY if rule requests deferred processing.
1948 */
1949 if (dep && ((create->create->flags & CREATE_MASK) ==
1950 CREATE_DEFER)) {
1951 defer = 1;
1952 continue;
1953 } else if (dep == NULL &&
1954 ((create->create->flags & CREATE_MASK) !=
1955 CREATE_DEFER)) {
1956 continue;
1957 }
1958
1959 if ((*(create->create->callback_fcn))
1960 (minor, node) == DEVFSADM_TERMINATE) {
1961 break;
1962 }
1963 }
1964 }
1965
1966 if (defer)
1967 cache_deferred_minor(dep, node, minor);
1968 }
1969
1970
1971 /*
1972 * Cache node and minor in defer list.
1973 */
1974 static void
cache_deferred_minor(struct mlist * dep,di_node_t node,di_minor_t minor)1975 cache_deferred_minor(
1976 struct mlist *dep,
1977 di_node_t node,
1978 di_minor_t minor)
1979 {
1980 struct minor *mp;
1981 const char *fcn = "cache_deferred_minor";
1982
1983 vprint(CHATTY_MID, "%s node=%s, minor=%s\n", fcn,
1984 di_node_name(node), di_minor_name(minor));
1985
1986 if (dep == NULL) {
1987 vprint(CHATTY_MID, "%s: cannot cache during "
1988 "deferred processing. Ignoring minor\n", fcn);
1989 return;
1990 }
1991
1992 mp = (struct minor *)s_zalloc(sizeof (struct minor));
1993 mp->node = node;
1994 mp->minor = minor;
1995 mp->next = NULL;
1996
1997 assert(dep->head == NULL || dep->tail != NULL);
1998 if (dep->head == NULL) {
1999 dep->head = mp;
2000 } else {
2001 dep->tail->next = mp;
2002 }
2003 dep->tail = mp;
2004 }
2005
2006 /*
2007 * Check to see if "create" link creation rule matches this node/minor.
2008 * If it does, return TRUE.
2009 */
2010 static int
minor_matches_rule(di_node_t node,di_minor_t minor,create_list_t * create)2011 minor_matches_rule(di_node_t node, di_minor_t minor, create_list_t *create)
2012 {
2013 char *m_nodetype, *m_drvname;
2014
2015 if (create->create->node_type != NULL) {
2016
2017 m_nodetype = di_minor_nodetype(minor);
2018 assert(m_nodetype != NULL);
2019
2020 switch (create->create->flags & TYPE_MASK) {
2021 case TYPE_EXACT:
2022 if (strcmp(create->create->node_type, m_nodetype) !=
2023 0) {
2024 return (FALSE);
2025 }
2026 break;
2027 case TYPE_PARTIAL:
2028 if (strncmp(create->create->node_type, m_nodetype,
2029 strlen(create->create->node_type)) != 0) {
2030 return (FALSE);
2031 }
2032 break;
2033 case TYPE_RE:
2034 if (regexec(&(create->node_type_comp), m_nodetype,
2035 0, NULL, 0) != 0) {
2036 return (FALSE);
2037 }
2038 break;
2039 }
2040 }
2041
2042 if (create->create->drv_name != NULL) {
2043 m_drvname = di_driver_name(node);
2044 switch (create->create->flags & DRV_MASK) {
2045 case DRV_EXACT:
2046 if (strcmp(create->create->drv_name, m_drvname) != 0) {
2047 return (FALSE);
2048 }
2049 break;
2050 case DRV_RE:
2051 if (regexec(&(create->drv_name_comp), m_drvname,
2052 0, NULL, 0) != 0) {
2053 return (FALSE);
2054 }
2055 break;
2056 }
2057 }
2058
2059 return (TRUE);
2060 }
2061
2062 /*
2063 * If no classes were given on the command line, then return DEVFSADM_SUCCESS.
2064 * Otherwise, return DEVFSADM_SUCCESS if the device "class" from the module
2065 * matches one of the device classes given on the command line,
2066 * otherwise, return DEVFSADM_FAILURE.
2067 */
2068 static int
class_ok(char * class)2069 class_ok(char *class)
2070 {
2071 int i;
2072
2073 if (num_classes == 0) {
2074 return (DEVFSADM_SUCCESS);
2075 }
2076
2077 /*
2078 * Some create tabs operate on multiple classes of devices because the
2079 * kernel doesn't have a good way for a driver to indicate that a
2080 * particular minor's class is different from that of the dev_info_t
2081 * it belongs to. As such, we'll always fail to match those here.
2082 */
2083 if (class == NULL) {
2084 return (DEVFSADM_FAILURE);
2085 }
2086
2087 for (i = 0; i < num_classes; i++) {
2088 if (strcmp(class, classes[i]) == 0) {
2089 return (DEVFSADM_SUCCESS);
2090 }
2091 }
2092 return (DEVFSADM_FAILURE);
2093 }
2094
2095 /*
2096 * call minor_fini on active modules, then unload ALL modules
2097 */
2098 static void
unload_modules(void)2099 unload_modules(void)
2100 {
2101 module_t *module_free;
2102 create_list_t *create_free;
2103 remove_list_t *remove_free;
2104
2105 while (create_head != NULL) {
2106 create_free = create_head;
2107 create_head = create_head->next;
2108
2109 if ((create_free->create->flags & TYPE_RE) == TYPE_RE) {
2110 regfree(&(create_free->node_type_comp));
2111 }
2112 if ((create_free->create->flags & DRV_RE) == DRV_RE) {
2113 regfree(&(create_free->drv_name_comp));
2114 }
2115 free(create_free);
2116 }
2117
2118 while (remove_head != NULL) {
2119 remove_free = remove_head;
2120 remove_head = remove_head->next;
2121 free(remove_free);
2122 }
2123
2124 while (module_head != NULL) {
2125
2126 if ((module_head->minor_fini != NULL) &&
2127 ((module_head->flags & MODULE_ACTIVE) == MODULE_ACTIVE)) {
2128 (void) (*(module_head->minor_fini))();
2129 }
2130
2131 vprint(MODLOAD_MID, "unloading module %s\n", module_head->name);
2132 free(module_head->name);
2133 (void) dlclose(module_head->dlhandle);
2134
2135 module_free = module_head;
2136 module_head = module_head->next;
2137 free(module_free);
2138 }
2139 }
2140
2141 /*
2142 * Load devfsadm logical link processing modules.
2143 */
2144 static void
load_modules(void)2145 load_modules(void)
2146 {
2147 DIR *mod_dir;
2148 struct dirent *entp;
2149 char cdir[PATH_MAX + 1];
2150 char *last;
2151 char *mdir = module_dirs;
2152 char *fcn = "load_modules: ";
2153
2154 while (*mdir != '\0') {
2155
2156 while (*mdir == ':') {
2157 mdir++;
2158 }
2159
2160 if (*mdir == '\0') {
2161 continue;
2162 }
2163
2164 last = strchr(mdir, ':');
2165
2166 if (last == NULL) {
2167 last = mdir + strlen(mdir);
2168 }
2169
2170 (void) strncpy(cdir, mdir, last - mdir);
2171 cdir[last - mdir] = '\0';
2172 mdir += strlen(cdir);
2173
2174 if ((mod_dir = opendir(cdir)) == NULL) {
2175 vprint(MODLOAD_MID, "%sopendir(%s): %s\n",
2176 fcn, cdir, strerror(errno));
2177 continue;
2178 }
2179
2180 while ((entp = readdir(mod_dir)) != NULL) {
2181
2182 if ((strcmp(entp->d_name, ".") == 0) ||
2183 (strcmp(entp->d_name, "..") == 0)) {
2184 continue;
2185 }
2186
2187 load_module(entp->d_name, cdir);
2188 }
2189 s_closedir(mod_dir);
2190 }
2191 }
2192
2193 static void
load_module(char * mname,char * cdir)2194 load_module(char *mname, char *cdir)
2195 {
2196 _devfsadm_create_reg_t *create_reg;
2197 _devfsadm_remove_reg_V1_t *remove_reg;
2198 create_list_t *create_list_element;
2199 create_list_t **create_list_next;
2200 remove_list_t *remove_list_element;
2201 remove_list_t **remove_list_next;
2202 char epath[PATH_MAX + 1], *end;
2203 char *fcn = "load_module: ";
2204 char *dlerrstr;
2205 void *dlhandle;
2206 module_t *module;
2207 int flags;
2208 int n;
2209 int i;
2210
2211 /* ignore any file which does not end in '.so' */
2212 if ((end = strstr(mname, MODULE_SUFFIX)) != NULL) {
2213 if (end[strlen(MODULE_SUFFIX)] != '\0') {
2214 return;
2215 }
2216 } else {
2217 return;
2218 }
2219
2220 (void) snprintf(epath, sizeof (epath), "%s/%s", cdir, mname);
2221
2222 if ((dlhandle = dlopen(epath, RTLD_LAZY)) == NULL) {
2223 dlerrstr = dlerror();
2224 err_print(DLOPEN_FAILED, epath,
2225 dlerrstr ? dlerrstr : "unknown error");
2226 return;
2227 }
2228
2229 /* dlsym the _devfsadm_create_reg structure */
2230 if (NULL == (create_reg = (_devfsadm_create_reg_t *)
2231 dlsym(dlhandle, _DEVFSADM_CREATE_REG))) {
2232 vprint(MODLOAD_MID, "dlsym(%s, %s): symbol not found\n", epath,
2233 _DEVFSADM_CREATE_REG);
2234 } else {
2235 vprint(MODLOAD_MID, "%sdlsym(%s, %s) succeeded\n",
2236 fcn, epath, _DEVFSADM_CREATE_REG);
2237 }
2238
2239 /* dlsym the _devfsadm_remove_reg structure */
2240 if (NULL == (remove_reg = (_devfsadm_remove_reg_V1_t *)
2241 dlsym(dlhandle, _DEVFSADM_REMOVE_REG))) {
2242 vprint(MODLOAD_MID, "dlsym(%s,\n\t%s): symbol not found\n",
2243 epath, _DEVFSADM_REMOVE_REG);
2244 } else {
2245 vprint(MODLOAD_MID, "dlsym(%s, %s): succeeded\n",
2246 epath, _DEVFSADM_REMOVE_REG);
2247 }
2248
2249 vprint(MODLOAD_MID, "module %s loaded\n", epath);
2250
2251 module = (module_t *)s_malloc(sizeof (module_t));
2252 module->name = s_strdup(epath);
2253 module->dlhandle = dlhandle;
2254
2255 /* dlsym other module functions, to be called later */
2256 module->minor_fini = (int (*)())dlsym(dlhandle, MINOR_FINI);
2257 module->minor_init = (int (*)())dlsym(dlhandle, MINOR_INIT);
2258 module->flags = 0;
2259
2260 /*
2261 * put a ptr to each struct devfsadm_create on "create_head"
2262 * list sorted in interpose_lvl.
2263 */
2264 if (create_reg != NULL) {
2265 for (i = 0; i < create_reg->count; i++) {
2266 int flags = create_reg->tblp[i].flags;
2267
2268 create_list_element = (create_list_t *)
2269 s_malloc(sizeof (create_list_t));
2270
2271 create_list_element->create = &(create_reg->tblp[i]);
2272 create_list_element->modptr = module;
2273
2274 if (((flags & CREATE_MASK) != 0) &&
2275 ((flags & CREATE_MASK) != CREATE_DEFER)) {
2276 free(create_list_element);
2277 err_print("illegal flag combination in "
2278 "module create\n");
2279 err_print(IGNORING_ENTRY, i, epath);
2280 continue;
2281 }
2282
2283 if (((flags & TYPE_MASK) == 0) ^
2284 (create_reg->tblp[i].node_type == NULL)) {
2285 free(create_list_element);
2286 err_print("flags value incompatible with "
2287 "node_type value in module create\n");
2288 err_print(IGNORING_ENTRY, i, epath);
2289 continue;
2290 }
2291
2292 if (((flags & TYPE_MASK) != 0) &&
2293 ((flags & TYPE_MASK) != TYPE_EXACT) &&
2294 ((flags & TYPE_MASK) != TYPE_RE) &&
2295 ((flags & TYPE_MASK) != TYPE_PARTIAL)) {
2296 free(create_list_element);
2297 err_print("illegal TYPE_* flag combination in "
2298 "module create\n");
2299 err_print(IGNORING_ENTRY, i, epath);
2300 continue;
2301 }
2302
2303 /* precompile regular expression for efficiency */
2304 if ((flags & TYPE_RE) == TYPE_RE) {
2305 if ((n = regcomp(&(create_list_element->
2306 node_type_comp),
2307 create_reg->tblp[i].node_type,
2308 REG_EXTENDED)) != 0) {
2309 free(create_list_element);
2310 err_print(REGCOMP_FAILED,
2311 create_reg->tblp[i].node_type, n);
2312 err_print(IGNORING_ENTRY, i, epath);
2313 continue;
2314 }
2315 }
2316
2317 if (((flags & DRV_MASK) == 0) ^
2318 (create_reg->tblp[i].drv_name == NULL)) {
2319 if ((flags & TYPE_RE) == TYPE_RE) {
2320 regfree(&(create_list_element->
2321 node_type_comp));
2322 }
2323 free(create_list_element);
2324 err_print("flags value incompatible with "
2325 "drv_name value in module create\n");
2326 err_print(IGNORING_ENTRY, i, epath);
2327 continue;
2328 }
2329
2330 if (((flags & DRV_MASK) != 0) &&
2331 ((flags & DRV_MASK) != DRV_EXACT) &&
2332 ((flags & DRV_MASK) != DRV_RE)) {
2333 if ((flags & TYPE_RE) == TYPE_RE) {
2334 regfree(&(create_list_element->
2335 node_type_comp));
2336 }
2337 free(create_list_element);
2338 err_print("illegal DRV_* flag combination in "
2339 "module create\n");
2340 err_print(IGNORING_ENTRY, i, epath);
2341 continue;
2342 }
2343
2344 /* precompile regular expression for efficiency */
2345 if ((create_reg->tblp[i].flags & DRV_RE) == DRV_RE) {
2346 if ((n = regcomp(&(create_list_element->
2347 drv_name_comp),
2348 create_reg->tblp[i].drv_name,
2349 REG_EXTENDED)) != 0) {
2350 if ((flags & TYPE_RE) == TYPE_RE) {
2351 regfree(&(create_list_element->
2352 node_type_comp));
2353 }
2354 free(create_list_element);
2355 err_print(REGCOMP_FAILED,
2356 create_reg->tblp[i].drv_name, n);
2357 err_print(IGNORING_ENTRY, i, epath);
2358 continue;
2359 }
2360 }
2361
2362
2363 /* add to list sorted by interpose level */
2364 for (create_list_next = &(create_head);
2365 (*create_list_next != NULL) &&
2366 (*create_list_next)->create->interpose_lvl >=
2367 create_list_element->create->interpose_lvl;
2368 create_list_next = &((*create_list_next)->next))
2369 ;
2370 create_list_element->next = *create_list_next;
2371 *create_list_next = create_list_element;
2372 }
2373 }
2374
2375 /*
2376 * put a ptr to each struct devfsadm_remove on "remove_head"
2377 * list sorted by interpose_lvl.
2378 */
2379 flags = 0;
2380 if (remove_reg != NULL) {
2381 if (remove_reg->version < DEVFSADM_V1)
2382 flags |= RM_NOINTERPOSE;
2383 for (i = 0; i < remove_reg->count; i++) {
2384
2385 remove_list_element = (remove_list_t *)
2386 s_malloc(sizeof (remove_list_t));
2387
2388 remove_list_element->remove = &(remove_reg->tblp[i]);
2389 remove_list_element->remove->flags |= flags;
2390 remove_list_element->modptr = module;
2391
2392 for (remove_list_next = &(remove_head);
2393 (*remove_list_next != NULL) &&
2394 (*remove_list_next)->remove->interpose_lvl >=
2395 remove_list_element->remove->interpose_lvl;
2396 remove_list_next = &((*remove_list_next)->next))
2397 ;
2398 remove_list_element->next = *remove_list_next;
2399 *remove_list_next = remove_list_element;
2400 }
2401 }
2402
2403 module->next = module_head;
2404 module_head = module;
2405 }
2406
2407 /*
2408 * After we have completed a CACHE_STATE, if a SYNC_STATE does not occur
2409 * within 'timeout' secs the minor_fini_thread needs to do a SYNC_STATE
2410 * so that we still call the minor_fini routines.
2411 */
2412 static void *
minor_fini_thread(void * arg __unused)2413 minor_fini_thread(void *arg __unused)
2414 {
2415 timestruc_t abstime;
2416
2417 vprint(INITFINI_MID, "minor_fini_thread starting\n");
2418
2419 (void) mutex_lock(&minor_fini_mutex);
2420 for (;;) {
2421 /* wait the gather period, or until signaled */
2422 abstime.tv_sec = time(NULL) + minor_fini_timeout;
2423 abstime.tv_nsec = 0;
2424 (void) cond_timedwait(&minor_fini_cv,
2425 &minor_fini_mutex, &abstime);
2426
2427 /* if minor_fini was canceled, go wait again */
2428 if (minor_fini_canceled == TRUE)
2429 continue;
2430
2431 /* if minor_fini was delayed, go wait again */
2432 if (minor_fini_delayed == TRUE) {
2433 minor_fini_delayed = FALSE;
2434 continue;
2435 }
2436
2437 /* done with cancellations and delays, do the SYNC_STATE */
2438 (void) mutex_unlock(&minor_fini_mutex);
2439
2440 lock_dev();
2441 unlock_dev(SYNC_STATE);
2442 vprint(INITFINI_MID, "minor_fini sync done\n");
2443
2444 (void) mutex_lock(&minor_fini_mutex);
2445 }
2446 return (NULL);
2447 }
2448
2449
2450 /*
2451 * Attempt to initialize module, if a minor_init routine exists. Set
2452 * the active flag if the routine exists and succeeds. If it doesn't
2453 * exist, just set the active flag.
2454 */
2455 static int
call_minor_init(module_t * module)2456 call_minor_init(module_t *module)
2457 {
2458 char *fcn = "call_minor_init: ";
2459
2460 if ((module->flags & MODULE_ACTIVE) == MODULE_ACTIVE) {
2461 return (DEVFSADM_SUCCESS);
2462 }
2463
2464 vprint(INITFINI_MID, "%smodule %s. current state: inactive\n",
2465 fcn, module->name);
2466
2467 if (module->minor_init == NULL) {
2468 module->flags |= MODULE_ACTIVE;
2469 vprint(INITFINI_MID, "minor_init not defined\n");
2470 return (DEVFSADM_SUCCESS);
2471 }
2472
2473 if ((*(module->minor_init))() == DEVFSADM_FAILURE) {
2474 err_print(FAILED_FOR_MODULE, MINOR_INIT, module->name);
2475 return (DEVFSADM_FAILURE);
2476 }
2477
2478 vprint(INITFINI_MID, "minor_init() returns DEVFSADM_SUCCESS. "
2479 "new state: active\n");
2480
2481 module->flags |= MODULE_ACTIVE;
2482 return (DEVFSADM_SUCCESS);
2483 }
2484
2485 /*
2486 * Creates a symlink 'link' to the physical path of node:minor.
2487 * Construct link contents, then call create_link_common().
2488 */
2489 /*ARGSUSED*/
2490 int
devfsadm_mklink(char * link,di_node_t node,di_minor_t minor,int flags)2491 devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags)
2492 {
2493 char rcontents[PATH_MAX];
2494 char devlink[PATH_MAX];
2495 char phy_path[PATH_MAX];
2496 char *acontents;
2497 char *dev_path;
2498 int numslashes;
2499 int rv;
2500 int i, link_exists;
2501 int last_was_slash = FALSE;
2502
2503 /*
2504 * try to use devices path
2505 */
2506 if ((node == lnode) && (minor == lminor)) {
2507 acontents = lphy_path;
2508 } else if (di_minor_type(minor) == DDM_ALIAS) {
2509 /* use /pseudo/clone@0:<driver> as the phys path */
2510 (void) snprintf(phy_path, sizeof (phy_path),
2511 "/pseudo/clone@0:%s",
2512 di_driver_name(di_minor_devinfo(minor)));
2513 acontents = phy_path;
2514 } else {
2515 if ((dev_path = di_devfs_path(node)) == NULL) {
2516 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2517 devfsadm_exit(1);
2518 /*NOTREACHED*/
2519 }
2520 (void) snprintf(phy_path, sizeof (phy_path), "%s:%s",
2521 dev_path, di_minor_name(minor));
2522 di_devfs_path_free(dev_path);
2523 acontents = phy_path;
2524 }
2525
2526 /* prepend link with dev_dir contents */
2527 (void) strlcpy(devlink, dev_dir, sizeof (devlink));
2528 (void) strlcat(devlink, "/", sizeof (devlink));
2529 (void) strlcat(devlink, link, sizeof (devlink));
2530
2531 /*
2532 * Calculate # of ../ to add. Account for double '//' in path.
2533 * Ignore all leading slashes.
2534 */
2535 for (i = 0; link[i] == '/'; i++)
2536 ;
2537 for (numslashes = 0; link[i] != '\0'; i++) {
2538 if (link[i] == '/') {
2539 if (last_was_slash == FALSE) {
2540 numslashes++;
2541 last_was_slash = TRUE;
2542 }
2543 } else {
2544 last_was_slash = FALSE;
2545 }
2546 }
2547 /* Don't count any trailing '/' */
2548 if (link[i-1] == '/') {
2549 numslashes--;
2550 }
2551
2552 rcontents[0] = '\0';
2553 do {
2554 (void) strlcat(rcontents, "../", sizeof (rcontents));
2555 } while (numslashes-- != 0);
2556
2557 (void) strlcat(rcontents, "devices", sizeof (rcontents));
2558 (void) strlcat(rcontents, acontents, sizeof (rcontents));
2559
2560 if (devlinks_debug == TRUE) {
2561 vprint(INFO_MID, "adding link %s ==> %s\n", devlink, rcontents);
2562 }
2563
2564 if ((rv = create_link_common(devlink, rcontents, &link_exists))
2565 == DEVFSADM_SUCCESS) {
2566 linknew = TRUE;
2567 add_link_to_cache(link, acontents);
2568 } else {
2569 linknew = FALSE;
2570 }
2571
2572 if (link_exists == TRUE) {
2573 /* Link exists or was just created */
2574 (void) di_devlink_add_link(devlink_cache, link, rcontents,
2575 DI_PRIMARY_LINK);
2576
2577 if (system_labeled && (flags & DA_ADD)) {
2578 /*
2579 * Add this to the list of allocatable devices. If this
2580 * is a hotplugged, removable disk, add it as rmdisk.
2581 */
2582 int instance = di_instance(node);
2583
2584 if ((flags & DA_CD) &&
2585 (_da_check_for_usb(devlink, root_dir) == 1)) {
2586 (void) da_add_list(&devlist, devlink, instance,
2587 DA_ADD|DA_RMDISK);
2588 update_devdb = DA_RMDISK;
2589 } else if (linknew == TRUE) {
2590 (void) da_add_list(&devlist, devlink, instance,
2591 flags);
2592 update_devdb = flags;
2593 }
2594 }
2595 }
2596
2597 return (rv);
2598 }
2599
2600 /*
2601 * Creates a symlink link to primary_link. Calculates relative
2602 * directory offsets, then calls link_common().
2603 */
2604 /*ARGSUSED*/
2605 int
devfsadm_secondary_link(char * link,char * primary_link,int flags)2606 devfsadm_secondary_link(char *link, char *primary_link, int flags)
2607 {
2608 char contents[PATH_MAX + 1];
2609 char devlink[PATH_MAX + 1];
2610 int rv, link_exists;
2611 char *fpath;
2612 char *tpath;
2613 char *op;
2614
2615 /* prepend link with dev_dir contents */
2616 (void) strcpy(devlink, dev_dir);
2617 (void) strcat(devlink, "/");
2618 (void) strcat(devlink, link);
2619 /*
2620 * building extra link, so use first link as link contents, but first
2621 * make it relative.
2622 */
2623 fpath = link;
2624 tpath = primary_link;
2625 op = contents;
2626
2627 while (*fpath == *tpath && *fpath != '\0') {
2628 fpath++, tpath++;
2629 }
2630
2631 /* Count directories to go up, if any, and add "../" */
2632 while (*fpath != '\0') {
2633 if (*fpath == '/') {
2634 (void) strcpy(op, "../");
2635 op += 3;
2636 }
2637 fpath++;
2638 }
2639
2640 /*
2641 * Back up to the start of the current path component, in
2642 * case in the middle
2643 */
2644 while (tpath != primary_link && *(tpath-1) != '/') {
2645 tpath--;
2646 }
2647 (void) strcpy(op, tpath);
2648
2649 if (devlinks_debug == TRUE) {
2650 vprint(INFO_MID, "adding extra link %s ==> %s\n",
2651 devlink, contents);
2652 }
2653
2654 if ((rv = create_link_common(devlink, contents, &link_exists))
2655 == DEVFSADM_SUCCESS) {
2656 /*
2657 * we need to save the ultimate /devices contents, and not the
2658 * secondary link, since hotcleanup only looks at /devices path.
2659 * Since we don't have devices path here, we can try to get it
2660 * by readlink'ing the secondary link. This assumes the primary
2661 * link was created first.
2662 */
2663 add_link_to_cache(link, lphy_path);
2664 linknew = TRUE;
2665 if (system_labeled &&
2666 ((flags & DA_AUDIO) && (flags & DA_ADD))) {
2667 /*
2668 * Add this device to the list of allocatable devices.
2669 */
2670 int instance = 0;
2671
2672 op = strrchr(contents, '/');
2673 op++;
2674 (void) sscanf(op, "%d", &instance);
2675 (void) da_add_list(&devlist, devlink, instance, flags);
2676 update_devdb = flags;
2677 }
2678 } else {
2679 linknew = FALSE;
2680 }
2681
2682 /*
2683 * If link exists or was just created, add it to the database
2684 */
2685 if (link_exists == TRUE) {
2686 (void) di_devlink_add_link(devlink_cache, link, contents,
2687 DI_SECONDARY_LINK);
2688 }
2689
2690 return (rv);
2691 }
2692
2693 /* returns pointer to the devices directory */
2694 char *
devfsadm_get_devices_dir()2695 devfsadm_get_devices_dir()
2696 {
2697 return (devices_dir);
2698 }
2699
2700 /*
2701 * Does the actual link creation. VERBOSE_MID only used if there is
2702 * a change. CHATTY_MID used otherwise.
2703 */
2704 static int
create_link_common(char * devlink,char * contents,int * exists)2705 create_link_common(char *devlink, char *contents, int *exists)
2706 {
2707 int try;
2708 int linksize;
2709 int max_tries = 0;
2710 static int prev_link_existed = TRUE;
2711 char checkcontents[PATH_MAX + 1];
2712 char *hide;
2713
2714 *exists = FALSE;
2715
2716 /* Database is not updated when file_mods == FALSE */
2717 if (file_mods == FALSE) {
2718 /* we want *actual* link contents so no alias redirection */
2719 linksize = readlink(devlink, checkcontents, PATH_MAX);
2720 if (linksize > 0) {
2721 checkcontents[linksize] = '\0';
2722 if (strcmp(checkcontents, contents) != 0) {
2723 vprint(CHATTY_MID, REMOVING_LINK,
2724 devlink, checkcontents);
2725 return (DEVFSADM_SUCCESS);
2726 } else {
2727 vprint(CHATTY_MID, "link exists and is correct:"
2728 " %s -> %s\n", devlink, contents);
2729 /* failure only in that the link existed */
2730 return (DEVFSADM_FAILURE);
2731 }
2732 } else {
2733 vprint(VERBOSE_MID, CREATING_LINK, devlink, contents);
2734 return (DEVFSADM_SUCCESS);
2735 }
2736 }
2737
2738 /*
2739 * systems calls are expensive, so predict whether to readlink
2740 * or symlink first, based on previous attempt
2741 */
2742 if (prev_link_existed == FALSE) {
2743 try = CREATE_LINK;
2744 } else {
2745 try = READ_LINK;
2746 }
2747
2748 while (++max_tries <= 3) {
2749
2750 switch (try) {
2751 case CREATE_LINK:
2752
2753 if (symlink(contents, devlink) == 0) {
2754 vprint(VERBOSE_MID, CREATING_LINK, devlink,
2755 contents);
2756 prev_link_existed = FALSE;
2757 /* link successfully created */
2758 *exists = TRUE;
2759 set_logindev_perms(devlink);
2760 return (DEVFSADM_SUCCESS);
2761 } else {
2762 switch (errno) {
2763
2764 case ENOENT:
2765 /* dirpath to node doesn't exist */
2766 hide = strrchr(devlink, '/');
2767 *hide = '\0';
2768 s_mkdirp(devlink, S_IRWXU|S_IRGRP|
2769 S_IXGRP|S_IROTH|S_IXOTH);
2770 *hide = '/';
2771 break;
2772 case EEXIST:
2773 try = READ_LINK;
2774 break;
2775 default:
2776 err_print(SYMLINK_FAILED, devlink,
2777 contents, strerror(errno));
2778 return (DEVFSADM_FAILURE);
2779 }
2780 }
2781 break;
2782
2783 case READ_LINK:
2784
2785 /*
2786 * If there is redirection, new phys path
2787 * and old phys path will not match and the
2788 * link will be created with new phys path
2789 * which is what we want. So we want real
2790 * contents.
2791 */
2792 linksize = readlink(devlink, checkcontents, PATH_MAX);
2793 if (linksize >= 0) {
2794 checkcontents[linksize] = '\0';
2795 if (strcmp(checkcontents, contents) != 0) {
2796 s_unlink(devlink);
2797 vprint(VERBOSE_MID, REMOVING_LINK,
2798 devlink, checkcontents);
2799 try = CREATE_LINK;
2800 } else {
2801 prev_link_existed = TRUE;
2802 vprint(CHATTY_MID,
2803 "link exists and is correct:"
2804 " %s -> %s\n", devlink, contents);
2805 *exists = TRUE;
2806 /* failure in that the link existed */
2807 return (DEVFSADM_FAILURE);
2808 }
2809 } else {
2810 switch (errno) {
2811 case EINVAL:
2812 /* not a symlink, remove and create */
2813 s_unlink(devlink);
2814 /* FALLTHROUGH */
2815 default:
2816 /* maybe it didn't exist at all */
2817 try = CREATE_LINK;
2818 break;
2819 }
2820 }
2821 break;
2822 }
2823 }
2824 err_print(MAX_ATTEMPTS, devlink, contents);
2825 return (DEVFSADM_FAILURE);
2826 }
2827
2828 static void
set_logindev_perms(char * devlink)2829 set_logindev_perms(char *devlink)
2830 {
2831 struct login_dev *newdev;
2832 struct passwd pwd, *resp;
2833 char pwd_buf[PATH_MAX];
2834 int rv;
2835 struct stat sb;
2836 char *devfs_path = NULL;
2837
2838 /*
2839 * We only want logindev perms to be set when a device is
2840 * hotplugged or an application requests synchronous creates.
2841 * So we enable this only in daemon mode. In addition,
2842 * login(1) only fixes the std. /dev dir. So we don't
2843 * change perms if alternate root is set.
2844 * login_dev_enable is TRUE only in these cases.
2845 */
2846 if (login_dev_enable != TRUE)
2847 return;
2848
2849 /*
2850 * Normally, /etc/logindevperm has few (8 - 10 entries) which
2851 * may be regular expressions (globs were converted to RE).
2852 * So just do a linear search through the list.
2853 */
2854 for (newdev = login_dev_cache; newdev; newdev = newdev->ldev_next) {
2855 vprint(FILES_MID, "matching %s with %s\n", devlink,
2856 newdev->ldev_device);
2857
2858 if (regexec(&newdev->ldev_device_regex, devlink, 0,
2859 NULL, 0) == 0) {
2860 vprint(FILES_MID, "matched %s with %s\n", devlink,
2861 newdev->ldev_device);
2862 break;
2863 }
2864 }
2865
2866 if (newdev == NULL)
2867 return;
2868
2869 /*
2870 * we have a match, now find the driver associated with this
2871 * minor node using a snapshot on the physical path
2872 */
2873 (void) resolve_link(devlink, NULL, NULL, &devfs_path, 0);
2874 /*
2875 * We dont need redirection here - the actual link contents
2876 * whether "alias" or "current" are fine
2877 */
2878 if (devfs_path) {
2879 di_node_t node;
2880 char *drv;
2881 struct driver_list *list;
2882 char *p;
2883
2884 /* truncate on : so we can take a snapshot */
2885 (void) strcpy(pwd_buf, devfs_path);
2886 p = strrchr(pwd_buf, ':');
2887 if (p == NULL) {
2888 free(devfs_path);
2889 return;
2890 }
2891 *p = '\0';
2892
2893 vprint(FILES_MID, "link=%s->physpath=%s\n",
2894 devlink, pwd_buf);
2895
2896 node = di_init(pwd_buf, DINFOMINOR);
2897
2898 drv = NULL;
2899 if (node) {
2900 drv = di_driver_name(node);
2901
2902 if (drv) {
2903 vprint(FILES_MID, "%s: driver is %s\n",
2904 devlink, drv);
2905 }
2906 }
2907 /* search thru the driver list specified in logindevperm */
2908 list = newdev->ldev_driver_list;
2909 if ((drv != NULL) && (list != NULL)) {
2910 while (list) {
2911 if (strcmp(list->driver_name,
2912 drv) == 0) {
2913 vprint(FILES_MID,
2914 "driver %s match!\n", drv);
2915 break;
2916 }
2917 list = list->next;
2918 }
2919 if (list == NULL) {
2920 vprint(FILES_MID, "no driver match!\n");
2921 free(devfs_path);
2922 return;
2923 }
2924 }
2925 free(devfs_path);
2926 di_fini(node);
2927 } else {
2928 return;
2929 }
2930
2931 vprint(FILES_MID, "changing permissions of %s\n", devlink);
2932
2933 /*
2934 * We have a match. We now attempt to determine the
2935 * owner and group of the console user.
2936 *
2937 * stat() the console device newdev->ldev_console
2938 * which will always exist - it will have the right owner but
2939 * not the right group. Use getpwuid_r() to determine group for this
2940 * uid.
2941 * Note, it is safe to use name service here since if name services
2942 * are not available (during boot or in single-user mode), then
2943 * console owner will be root and its gid can be found in
2944 * local files.
2945 */
2946 if (stat(newdev->ldev_console, &sb) == -1) {
2947 vprint(VERBOSE_MID, STAT_FAILED, newdev->ldev_console,
2948 strerror(errno));
2949 return;
2950 }
2951
2952 resp = NULL;
2953 rv = getpwuid_r(sb.st_uid, &pwd, pwd_buf, sizeof (pwd_buf), &resp);
2954 if (rv || resp == NULL) {
2955 rv = rv ? rv : EINVAL;
2956 vprint(VERBOSE_MID, GID_FAILED, sb.st_uid,
2957 strerror(rv));
2958 return;
2959 }
2960
2961 assert(&pwd == resp);
2962
2963 sb.st_gid = resp->pw_gid;
2964
2965 if (chmod(devlink, newdev->ldev_perms) == -1) {
2966 vprint(VERBOSE_MID, CHMOD_FAILED, devlink,
2967 strerror(errno));
2968 return;
2969 }
2970
2971 if (chown(devlink, sb.st_uid, sb.st_gid) == -1) {
2972 vprint(VERBOSE_MID, CHOWN_FAILED, devlink,
2973 strerror(errno));
2974 }
2975 }
2976
2977 /*
2978 * Reset /devices node with appropriate permissions and
2979 * ownership as specified in /etc/minor_perm.
2980 */
2981 static void
reset_node_permissions(di_node_t node,di_minor_t minor)2982 reset_node_permissions(di_node_t node, di_minor_t minor)
2983 {
2984 int spectype;
2985 char phy_path[PATH_MAX + 1];
2986 mode_t mode;
2987 dev_t dev;
2988 uid_t uid;
2989 gid_t gid;
2990 struct stat sb;
2991 char *dev_path, *aminor = NULL;
2992
2993 /* lphy_path starts with / */
2994 if ((dev_path = di_devfs_path(node)) == NULL) {
2995 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2996 devfsadm_exit(1);
2997 /*NOTREACHED*/
2998 }
2999 (void) strcpy(lphy_path, dev_path);
3000 di_devfs_path_free(dev_path);
3001
3002 (void) strcat(lphy_path, ":");
3003 if (di_minor_type(minor) == DDM_ALIAS) {
3004 char *driver;
3005 aminor = di_minor_name(minor);
3006 driver = di_driver_name(di_minor_devinfo(minor));
3007 (void) strcat(lphy_path, driver);
3008 } else
3009 (void) strcat(lphy_path, di_minor_name(minor));
3010
3011 (void) strcpy(phy_path, devices_dir);
3012 (void) strcat(phy_path, lphy_path);
3013
3014 lnode = node;
3015 lminor = minor;
3016
3017 vprint(CHATTY_MID, "reset_node_permissions: phy_path=%s lphy_path=%s\n",
3018 phy_path, lphy_path);
3019
3020 dev = di_minor_devt(minor);
3021 spectype = di_minor_spectype(minor); /* block or char */
3022
3023 getattr(phy_path, aminor, spectype, dev, &mode, &uid, &gid);
3024
3025 /*
3026 * compare and set permissions and ownership
3027 *
3028 * Under devfs, a quick insertion and removal of USB devices
3029 * would cause stat of physical path to fail. In this case,
3030 * we emit a verbose message, but don't print errors.
3031 */
3032 if ((stat(phy_path, &sb) == -1) || (sb.st_rdev != dev)) {
3033 vprint(VERBOSE_MID, NO_DEVFS_NODE, phy_path);
3034 return;
3035 }
3036
3037 /*
3038 * If we are here for a new device
3039 * If device allocation is on
3040 * then
3041 * set ownership to root:other and permissions to 0000
3042 * else
3043 * set ownership and permissions as specified in minor_perm
3044 * If we are here for an existing device
3045 * If device allocation is to be turned on
3046 * then
3047 * reset ownership to root:other and permissions to 0000
3048 * else if device allocation is to be turned off
3049 * reset ownership and permissions to those specified in
3050 * minor_perm
3051 * else
3052 * preserve existing/user-modified ownership and
3053 * permissions
3054 *
3055 * devfs indicates a new device by faking access time to be zero.
3056 */
3057 if (sb.st_atime != 0) {
3058 int i;
3059 char *nt;
3060
3061 if ((devalloc_flag == 0) && (devalloc_is_on != 1))
3062 /*
3063 * Leave existing devices as they are if we are not
3064 * turning device allocation on/off.
3065 */
3066 return;
3067
3068 nt = di_minor_nodetype(minor);
3069
3070 if (nt == NULL)
3071 return;
3072
3073 for (i = 0; devalloc_list[i]; i++) {
3074 if (strcmp(nt, devalloc_list[i]) == 0)
3075 /*
3076 * One of the types recognized by devalloc,
3077 * reset attrs.
3078 */
3079 break;
3080 }
3081 if (devalloc_list[i] == NULL)
3082 return;
3083 }
3084
3085 if (file_mods == FALSE) {
3086 /* Nothing more to do if simulating */
3087 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3088 return;
3089 }
3090
3091 if ((devalloc_flag == DA_ON) ||
3092 ((devalloc_is_on == 1) && (devalloc_flag != DA_OFF))) {
3093 /*
3094 * we are here either to turn device allocation on or
3095 * to add a new device while device allocation is on
3096 * (and we've confirmed that we're not turning it
3097 * off).
3098 */
3099 mode = DEALLOC_MODE;
3100 uid = DA_UID;
3101 gid = DA_GID;
3102 }
3103
3104 if ((devalloc_is_on == 1) || (devalloc_flag == DA_ON) ||
3105 (sb.st_mode != mode)) {
3106 if (chmod(phy_path, mode) == -1)
3107 vprint(VERBOSE_MID, CHMOD_FAILED,
3108 phy_path, strerror(errno));
3109 }
3110 if ((devalloc_is_on == 1) || (devalloc_flag == DA_ON) ||
3111 (sb.st_uid != uid || sb.st_gid != gid)) {
3112 if (chown(phy_path, uid, gid) == -1)
3113 vprint(VERBOSE_MID, CHOWN_FAILED,
3114 phy_path, strerror(errno));
3115 }
3116
3117 /* Report that we actually did something */
3118 vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3119 }
3120
3121 /*
3122 * Removes logical link and the minor node it refers to. If file is a
3123 * link, we recurse and try to remove the minor node (or link if path is
3124 * a double link) that file's link contents refer to.
3125 */
3126 static void
devfsadm_rm_work(char * file,int recurse,int file_type)3127 devfsadm_rm_work(char *file, int recurse, int file_type)
3128 {
3129 char *fcn = "devfsadm_rm_work: ";
3130 int linksize;
3131 char contents[PATH_MAX + 1];
3132 char nextfile[PATH_MAX + 1];
3133 char newfile[PATH_MAX + 1];
3134 char *ptr;
3135
3136 vprint(REMOVE_MID, "%s%s\n", fcn, file);
3137
3138 /*
3139 * Note: we don't remove /devices (non-links) entries because they are
3140 * covered by devfs.
3141 */
3142 if (file_type != TYPE_LINK) {
3143 return;
3144 }
3145
3146 /* split into multiple if's due to excessive indentations */
3147 (void) strcpy(newfile, dev_dir);
3148 (void) strcat(newfile, "/");
3149 (void) strcat(newfile, file);
3150
3151 /*
3152 * we dont care about the content of the symlink, so
3153 * redirection is not needed.
3154 */
3155 if ((recurse == TRUE) &&
3156 ((linksize = readlink(newfile, contents, PATH_MAX)) > 0)) {
3157 contents[linksize] = '\0';
3158
3159 /*
3160 * recurse if link points to another link
3161 */
3162 if (is_minor_node(contents, &ptr) != DEVFSADM_TRUE) {
3163 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
3164 devfsadm_rm_work(&contents[strlen(DEV) + 1],
3165 TRUE, TYPE_LINK);
3166 } else {
3167 if ((ptr = strrchr(file, '/')) != NULL) {
3168 *ptr = '\0';
3169 (void) strcpy(nextfile, file);
3170 *ptr = '/';
3171 (void) strcat(nextfile, "/");
3172 } else {
3173 (void) strcpy(nextfile, "");
3174 }
3175 (void) strcat(nextfile, contents);
3176 devfsadm_rm_work(nextfile, TRUE, TYPE_LINK);
3177 }
3178 }
3179 }
3180
3181 vprint(VERBOSE_MID, DEVFSADM_UNLINK, newfile);
3182 if (file_mods == TRUE) {
3183 rm_link_from_cache(file);
3184 s_unlink(newfile);
3185 rm_parent_dir_if_empty(newfile);
3186 invalidate_enumerate_cache();
3187 (void) di_devlink_rm_link(devlink_cache, file);
3188 }
3189 }
3190
3191 void
devfsadm_rm_link(char * file)3192 devfsadm_rm_link(char *file)
3193 {
3194 devfsadm_rm_work(file, FALSE, TYPE_LINK);
3195 }
3196
3197 void
devfsadm_rm_all(char * file)3198 devfsadm_rm_all(char *file)
3199 {
3200 devfsadm_rm_work(file, TRUE, TYPE_LINK);
3201 }
3202
3203 static int
s_rmdir(char * path)3204 s_rmdir(char *path)
3205 {
3206 int i;
3207 char *rpath, *dir;
3208 const char *fcn = "s_rmdir";
3209
3210 /*
3211 * Certain directories are created at install time by packages.
3212 * Some of them (listed in sticky_dirs[]) are required by apps
3213 * and need to be present even when empty.
3214 */
3215 vprint(REMOVE_MID, "%s: checking if %s is sticky\n", fcn, path);
3216
3217 rpath = path + strlen(dev_dir) + 1;
3218
3219 for (i = 0; (dir = sticky_dirs[i]) != NULL; i++) {
3220 if (*rpath == *dir) {
3221 if (strcmp(rpath, dir) == 0) {
3222 vprint(REMOVE_MID, "%s: skipping sticky dir: "
3223 "%s\n", fcn, path);
3224 errno = EEXIST;
3225 return (-1);
3226 }
3227 }
3228 }
3229
3230 return (rmdir(path));
3231 }
3232
3233 /*
3234 * Try to remove any empty directories up the tree. It is assumed that
3235 * pathname is a file that was removed, so start with its parent, and
3236 * work up the tree.
3237 */
3238 static void
rm_parent_dir_if_empty(char * pathname)3239 rm_parent_dir_if_empty(char *pathname)
3240 {
3241 char *ptr, path[PATH_MAX + 1];
3242 char *fcn = "rm_parent_dir_if_empty: ";
3243
3244 vprint(REMOVE_MID, "%schecking %s if empty\n", fcn, pathname);
3245
3246 (void) strcpy(path, pathname);
3247
3248 /*
3249 * ascend up the dir tree, deleting all empty dirs.
3250 * Return immediately if a dir is not empty.
3251 */
3252 for (;;) {
3253
3254 if ((ptr = strrchr(path, '/')) == NULL) {
3255 return;
3256 }
3257
3258 *ptr = '\0';
3259
3260 if (finddev_emptydir(path)) {
3261 /* directory is empty */
3262 if (s_rmdir(path) == 0) {
3263 vprint(REMOVE_MID,
3264 "%sremoving empty dir %s\n", fcn, path);
3265 } else if (errno == EEXIST) {
3266 vprint(REMOVE_MID,
3267 "%sfailed to remove dir: %s\n", fcn, path);
3268 return;
3269 }
3270 } else {
3271 /* some other file is here, so return */
3272 vprint(REMOVE_MID, "%sdir not empty: %s\n", fcn, path);
3273 return;
3274 }
3275 }
3276 }
3277
3278 /*
3279 * This function and all the functions it calls below were added to
3280 * handle the unique problem with world wide names (WWN). The problem is
3281 * that if a WWN device is moved to another address on the same controller
3282 * its logical link will change, while the physical node remains the same.
3283 * The result is that two logical links will point to the same physical path
3284 * in /devices, the valid link and a stale link. This function will
3285 * find all the stale nodes, though at a significant performance cost.
3286 *
3287 * Caching is used to increase performance.
3288 * A cache will be built from disk if the cache tag doesn't already exist.
3289 * The cache tag is a regular expression "dir_re", which selects a
3290 * subset of disks to search from typically something like
3291 * "dev/cXt[0-9]+d[0-9]+s[0-9]+". After the cache is built, consistency must
3292 * be maintained, so entries are added as new links are created, and removed
3293 * as old links are deleted. The whole cache is flushed if we are a daemon,
3294 * and another devfsadm process ran in between.
3295 *
3296 * Once the cache is built, this function finds the cache which matches
3297 * dir_re, and then it searches all links in that cache looking for
3298 * any link whose contents match "valid_link_contents" with a corresponding link
3299 * which does not match "valid_link". Any such matches are stale and removed.
3300 *
3301 * This happens outside the context of a "reparenting" so we dont need
3302 * redirection.
3303 */
3304 void
devfsadm_rm_stale_links(char * dir_re,char * valid_link,di_node_t node,di_minor_t minor)3305 devfsadm_rm_stale_links(char *dir_re, char *valid_link, di_node_t node,
3306 di_minor_t minor)
3307 {
3308 link_t *link;
3309 linkhead_t *head;
3310 char phy_path[PATH_MAX + 1];
3311 char *valid_link_contents;
3312 char *dev_path;
3313 char rmlink[PATH_MAX + 1];
3314
3315 /*
3316 * try to use devices path
3317 */
3318 if ((node == lnode) && (minor == lminor)) {
3319 valid_link_contents = lphy_path;
3320 } else {
3321 if ((dev_path = di_devfs_path(node)) == NULL) {
3322 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
3323 devfsadm_exit(1);
3324 /*NOTREACHED*/
3325 }
3326 (void) strcpy(phy_path, dev_path);
3327 di_devfs_path_free(dev_path);
3328
3329 (void) strcat(phy_path, ":");
3330 (void) strcat(phy_path, di_minor_name(minor));
3331 valid_link_contents = phy_path;
3332 }
3333
3334 /*
3335 * As an optimization, check to make sure the corresponding
3336 * devlink was just created before continuing.
3337 */
3338
3339 if (linknew == FALSE) {
3340 return;
3341 }
3342
3343 head = get_cached_links(dir_re);
3344
3345 assert(head->nextlink == NULL);
3346
3347 for (link = head->link; link != NULL; link = head->nextlink) {
3348 /*
3349 * See hot_cleanup() for why we do this
3350 */
3351 head->nextlink = link->next;
3352 if ((strcmp(link->contents, valid_link_contents) == 0) &&
3353 (strcmp(link->devlink, valid_link) != 0)) {
3354 vprint(CHATTY_MID, "removing %s -> %s\n"
3355 "valid link is: %s -> %s\n",
3356 link->devlink, link->contents,
3357 valid_link, valid_link_contents);
3358 /*
3359 * Use a copy of the cached link name as the
3360 * cache entry will go away during link removal
3361 */
3362 (void) snprintf(rmlink, sizeof (rmlink), "%s",
3363 link->devlink);
3364 devfsadm_rm_link(rmlink);
3365 }
3366 }
3367 }
3368
3369 /*
3370 * Return previously created cache, or create cache.
3371 */
3372 static linkhead_t *
get_cached_links(char * dir_re)3373 get_cached_links(char *dir_re)
3374 {
3375 recurse_dev_t rd;
3376 linkhead_t *linkhead;
3377 int n;
3378
3379 vprint(BUILDCACHE_MID, "get_cached_links: %s\n", dir_re);
3380
3381 for (linkhead = headlinkhead; linkhead != NULL;
3382 linkhead = linkhead->nexthead) {
3383 if (strcmp(linkhead->dir_re, dir_re) == 0) {
3384 return (linkhead);
3385 }
3386 }
3387
3388 /*
3389 * This tag is not in cache, so add it, along with all its
3390 * matching /dev entries. This is the only time we go to disk.
3391 */
3392 linkhead = s_malloc(sizeof (linkhead_t));
3393 linkhead->nexthead = headlinkhead;
3394 headlinkhead = linkhead;
3395 linkhead->dir_re = s_strdup(dir_re);
3396
3397 if ((n = regcomp(&(linkhead->dir_re_compiled), dir_re,
3398 REG_EXTENDED)) != 0) {
3399 err_print(REGCOMP_FAILED, dir_re, n);
3400 }
3401
3402 linkhead->nextlink = NULL;
3403 linkhead->link = NULL;
3404
3405 rd.fcn = build_devlink_list;
3406 rd.data = (void *)linkhead;
3407
3408 vprint(BUILDCACHE_MID, "get_cached_links: calling recurse_dev_re\n");
3409
3410 /* call build_devlink_list for each directory in the dir_re RE */
3411 if (dir_re[0] == '/') {
3412 recurse_dev_re("/", &dir_re[1], &rd);
3413 } else {
3414 recurse_dev_re(dev_dir, dir_re, &rd);
3415 }
3416
3417 return (linkhead);
3418 }
3419
3420 static void
build_devlink_list(char * devlink,void * data)3421 build_devlink_list(char *devlink, void *data)
3422 {
3423 char *fcn = "build_devlink_list: ";
3424 char *ptr;
3425 char *r_contents;
3426 char *r_devlink;
3427 char contents[PATH_MAX + 1];
3428 char newlink[PATH_MAX + 1];
3429 char stage_link[PATH_MAX + 1];
3430 int linksize;
3431 linkhead_t *linkhead = (linkhead_t *)data;
3432 link_t *link;
3433 int i = 0;
3434
3435 vprint(BUILDCACHE_MID, "%scheck_link: %s\n", fcn, devlink);
3436
3437 (void) strcpy(newlink, devlink);
3438
3439 do {
3440 /*
3441 * None of the consumers of this function need redirection
3442 * so this readlink gets the "current" contents
3443 */
3444 linksize = readlink(newlink, contents, PATH_MAX);
3445 if (linksize <= 0) {
3446 /*
3447 * The first pass through the do loop we may readlink()
3448 * non-symlink files(EINVAL) from false regexec matches.
3449 * Suppress error messages in those cases or if the link
3450 * content is the empty string.
3451 */
3452 if (linksize < 0 && (i || errno != EINVAL))
3453 err_print(READLINK_FAILED, "build_devlink_list",
3454 newlink, strerror(errno));
3455 return;
3456 }
3457 contents[linksize] = '\0';
3458 i = 1;
3459
3460 if (is_minor_node(contents, &r_contents) == DEVFSADM_FALSE) {
3461 /*
3462 * assume that link contents is really a pointer to
3463 * another link, so recurse and read its link contents.
3464 *
3465 * some link contents are absolute:
3466 * /dev/audio -> /dev/sound/0
3467 */
3468 if (strncmp(contents, DEV "/",
3469 strlen(DEV) + strlen("/")) != 0) {
3470
3471 if ((ptr = strrchr(newlink, '/')) == NULL) {
3472 vprint(REMOVE_MID, "%s%s -> %s invalid "
3473 "link. missing '/'\n", fcn,
3474 newlink, contents);
3475 return;
3476 }
3477 *ptr = '\0';
3478 (void) strcpy(stage_link, newlink);
3479 *ptr = '/';
3480 (void) strcat(stage_link, "/");
3481 (void) strcat(stage_link, contents);
3482 (void) strcpy(newlink, stage_link);
3483 } else {
3484 (void) strcpy(newlink, dev_dir);
3485 (void) strcat(newlink, "/");
3486 (void) strcat(newlink,
3487 &contents[strlen(DEV) + strlen("/")]);
3488 }
3489
3490 } else {
3491 newlink[0] = '\0';
3492 }
3493 } while (newlink[0] != '\0');
3494
3495 if (strncmp(devlink, dev_dir, strlen(dev_dir)) != 0) {
3496 vprint(BUILDCACHE_MID, "%sinvalid link: %s\n", fcn, devlink);
3497 return;
3498 }
3499
3500 r_devlink = devlink + strlen(dev_dir);
3501
3502 if (r_devlink[0] != '/')
3503 return;
3504
3505 link = s_malloc(sizeof (link_t));
3506
3507 /* don't store the '/' after rootdir/dev */
3508 r_devlink += 1;
3509
3510 vprint(BUILDCACHE_MID, "%scaching link: %s\n", fcn, r_devlink);
3511 link->devlink = s_strdup(r_devlink);
3512
3513 link->contents = s_strdup(r_contents);
3514
3515 link->next = linkhead->link;
3516 linkhead->link = link;
3517 }
3518
3519 /*
3520 * to be consistent, devlink must not begin with / and must be
3521 * relative to /dev/, whereas physpath must contain / and be
3522 * relative to /devices.
3523 */
3524 static void
add_link_to_cache(char * devlink,char * physpath)3525 add_link_to_cache(char *devlink, char *physpath)
3526 {
3527 linkhead_t *linkhead;
3528 link_t *link;
3529 int added = 0;
3530
3531 if (file_mods == FALSE) {
3532 return;
3533 }
3534
3535 vprint(CACHE_MID, "add_link_to_cache: %s -> %s ",
3536 devlink, physpath);
3537
3538 for (linkhead = headlinkhead; linkhead != NULL;
3539 linkhead = linkhead->nexthead) {
3540 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3541 == 0) {
3542 added++;
3543 link = s_malloc(sizeof (link_t));
3544 link->devlink = s_strdup(devlink);
3545 link->contents = s_strdup(physpath);
3546 link->next = linkhead->link;
3547 linkhead->link = link;
3548 }
3549 }
3550
3551 vprint(CACHE_MID,
3552 " %d %s\n", added, added == 0 ? "NOT ADDED" : "ADDED");
3553 }
3554
3555 /*
3556 * Remove devlink from cache. Devlink must be relative to /dev/ and not start
3557 * with /.
3558 */
3559 static void
rm_link_from_cache(char * devlink)3560 rm_link_from_cache(char *devlink)
3561 {
3562 linkhead_t *linkhead;
3563 link_t **linkp;
3564 link_t *save;
3565
3566 vprint(CACHE_MID, "rm_link_from_cache enter: %s\n", devlink);
3567
3568 for (linkhead = headlinkhead; linkhead != NULL;
3569 linkhead = linkhead->nexthead) {
3570 if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL, 0)
3571 == 0) {
3572
3573 for (linkp = &(linkhead->link); *linkp != NULL; ) {
3574 if ((strcmp((*linkp)->devlink, devlink) == 0)) {
3575 save = *linkp;
3576 *linkp = (*linkp)->next;
3577 /*
3578 * We are removing our caller's
3579 * "next" link. Update the nextlink
3580 * field in the head so that our
3581 * callers accesses the next valid
3582 * link
3583 */
3584 if (linkhead->nextlink == save)
3585 linkhead->nextlink = *linkp;
3586 free(save->devlink);
3587 free(save->contents);
3588 free(save);
3589 vprint(CACHE_MID, " %s FREED FROM "
3590 "CACHE\n", devlink);
3591 } else {
3592 linkp = &((*linkp)->next);
3593 }
3594 }
3595 }
3596 }
3597 }
3598
3599 static void
rm_all_links_from_cache()3600 rm_all_links_from_cache()
3601 {
3602 linkhead_t *linkhead;
3603 linkhead_t *nextlinkhead;
3604 link_t *link;
3605 link_t *nextlink;
3606
3607 vprint(CACHE_MID, "rm_all_links_from_cache\n");
3608
3609 for (linkhead = headlinkhead; linkhead != NULL;
3610 linkhead = nextlinkhead) {
3611
3612 nextlinkhead = linkhead->nexthead;
3613 assert(linkhead->nextlink == NULL);
3614 for (link = linkhead->link; link != NULL; link = nextlink) {
3615 nextlink = link->next;
3616 free(link->devlink);
3617 free(link->contents);
3618 free(link);
3619 }
3620 regfree(&(linkhead->dir_re_compiled));
3621 free(linkhead->dir_re);
3622 free(linkhead);
3623 }
3624 headlinkhead = NULL;
3625 }
3626
3627 /*
3628 * Called when the kernel has modified the incore path_to_inst data. This
3629 * function will schedule a flush of the data to the filesystem.
3630 */
3631 static void
devfs_instance_mod(void)3632 devfs_instance_mod(void)
3633 {
3634 char *fcn = "devfs_instance_mod: ";
3635 vprint(PATH2INST_MID, "%senter\n", fcn);
3636
3637 /* signal instance thread */
3638 (void) mutex_lock(&count_lock);
3639 inst_count++;
3640 (void) cond_signal(&cv);
3641 (void) mutex_unlock(&count_lock);
3642 }
3643
3644 static void
instance_flush_thread(void)3645 instance_flush_thread(void)
3646 {
3647 int i;
3648 int idle;
3649
3650 for (;;) {
3651
3652 (void) mutex_lock(&count_lock);
3653 while (inst_count == 0) {
3654 (void) cond_wait(&cv, &count_lock);
3655 }
3656 inst_count = 0;
3657
3658 vprint(PATH2INST_MID, "signaled to flush path_to_inst."
3659 " Enter delay loop\n");
3660 /*
3661 * Wait MAX_IDLE_DELAY seconds after getting the last flush
3662 * path_to_inst event before invoking a flush, but never wait
3663 * more than MAX_DELAY seconds after getting the first event.
3664 */
3665 for (idle = 0, i = 0; i < MAX_DELAY; i++) {
3666
3667 (void) mutex_unlock(&count_lock);
3668 (void) sleep(1);
3669 (void) mutex_lock(&count_lock);
3670
3671 /* shorten the delay if we are idle */
3672 if (inst_count == 0) {
3673 idle++;
3674 if (idle > MAX_IDLE_DELAY) {
3675 break;
3676 }
3677 } else {
3678 inst_count = idle = 0;
3679 }
3680 }
3681
3682 (void) mutex_unlock(&count_lock);
3683
3684 flush_path_to_inst();
3685 }
3686 }
3687
3688 /*
3689 * Helper function for flush_path_to_inst() below; this routine calls the
3690 * inst_sync syscall to flush the path_to_inst database to the given file.
3691 */
3692 static int
do_inst_sync(char * filename,char * instfilename)3693 do_inst_sync(char *filename, char *instfilename)
3694 {
3695 void (*sigsaved)(int);
3696 int err = 0, flags = INST_SYNC_IF_REQUIRED;
3697 struct stat sb;
3698
3699 if (stat(instfilename, &sb) == -1 && errno == ENOENT)
3700 flags = INST_SYNC_ALWAYS;
3701
3702 vprint(INSTSYNC_MID, "do_inst_sync: about to flush %s\n", filename);
3703 sigsaved = sigset(SIGSYS, SIG_IGN);
3704 if (inst_sync(filename, flags) == -1)
3705 err = errno;
3706 (void) sigset(SIGSYS, sigsaved);
3707
3708 switch (err) {
3709 case 0:
3710 return (DEVFSADM_SUCCESS);
3711 case EALREADY: /* no-op, path_to_inst already up to date */
3712 return (EALREADY);
3713 case ENOSYS:
3714 err_print(CANT_LOAD_SYSCALL);
3715 break;
3716 case EPERM:
3717 err_print(SUPER_TO_SYNC);
3718 break;
3719 default:
3720 err_print(INSTSYNC_FAILED, filename, strerror(err));
3721 break;
3722 }
3723 return (DEVFSADM_FAILURE);
3724 }
3725
3726 /*
3727 * Flush the kernel's path_to_inst database to /etc/path_to_inst. To do so
3728 * safely, the database is flushed to a temporary file, then moved into place.
3729 *
3730 * The following files are used during this process:
3731 * /etc/path_to_inst: The path_to_inst file
3732 * /etc/path_to_inst.<pid>: Contains data flushed from the kernel
3733 * /etc/path_to_inst.old: The backup file
3734 * /etc/path_to_inst.old.<pid>: Temp file for creating backup
3735 *
3736 */
3737 static void
flush_path_to_inst(void)3738 flush_path_to_inst(void)
3739 {
3740 char *new_inst_file = NULL;
3741 char *old_inst_file = NULL;
3742 char *old_inst_file_npid = NULL;
3743 FILE *inst_file_fp = NULL;
3744 FILE *old_inst_file_fp = NULL;
3745 struct stat sb;
3746 int err = 0;
3747 int c;
3748 int inst_strlen;
3749
3750 vprint(PATH2INST_MID, "flush_path_to_inst: %s\n",
3751 (flush_path_to_inst_enable == TRUE) ? "ENABLED" : "DISABLED");
3752
3753 if (flush_path_to_inst_enable == FALSE) {
3754 return;
3755 }
3756
3757 inst_strlen = strlen(inst_file);
3758 new_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 2);
3759 old_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 6);
3760 old_inst_file_npid = s_malloc(inst_strlen +
3761 sizeof (INSTANCE_FILE_SUFFIX));
3762
3763 (void) snprintf(new_inst_file, inst_strlen + PID_STR_LEN + 2,
3764 "%s.%ld", inst_file, getpid());
3765
3766 if (stat(new_inst_file, &sb) == 0) {
3767 s_unlink(new_inst_file);
3768 }
3769
3770 err = do_inst_sync(new_inst_file, inst_file);
3771 if (err != DEVFSADM_SUCCESS) {
3772 goto out;
3773 /*NOTREACHED*/
3774 }
3775
3776 /*
3777 * Now we deal with the somewhat tricky updating and renaming
3778 * of this critical piece of kernel state.
3779 */
3780
3781 /*
3782 * Copy the current instance file into a temporary file.
3783 * Then rename the temporary file into the backup (.old)
3784 * file and rename the newly flushed kernel data into
3785 * the instance file.
3786 * Of course if 'inst_file' doesn't exist, there's much
3787 * less for us to do .. tee hee.
3788 */
3789 if ((inst_file_fp = fopen(inst_file, "r")) == NULL) {
3790 /*
3791 * No such file. Rename the new onto the old
3792 */
3793 if ((err = rename(new_inst_file, inst_file)) != 0)
3794 err_print(RENAME_FAILED, inst_file, strerror(errno));
3795 goto out;
3796 /*NOTREACHED*/
3797 }
3798
3799 (void) snprintf(old_inst_file, inst_strlen + PID_STR_LEN + 6,
3800 "%s.old.%ld", inst_file, getpid());
3801
3802 if (stat(old_inst_file, &sb) == 0) {
3803 s_unlink(old_inst_file);
3804 }
3805
3806 if ((old_inst_file_fp = fopen(old_inst_file, "w")) == NULL) {
3807 /*
3808 * Can't open the 'old_inst_file' file for writing.
3809 * This is somewhat strange given that the syscall
3810 * just succeeded to write a file out.. hmm.. maybe
3811 * the fs just filled up or something nasty.
3812 *
3813 * Anyway, abort what we've done so far.
3814 */
3815 err_print(CANT_UPDATE, old_inst_file);
3816 err = DEVFSADM_FAILURE;
3817 goto out;
3818 /*NOTREACHED*/
3819 }
3820
3821 /*
3822 * Copy current instance file into the temporary file
3823 */
3824 err = 0;
3825 while ((c = getc(inst_file_fp)) != EOF) {
3826 if ((err = putc(c, old_inst_file_fp)) == EOF) {
3827 break;
3828 }
3829 }
3830
3831 if (fclose(old_inst_file_fp) == EOF || err == EOF) {
3832 vprint(INFO_MID, CANT_UPDATE, old_inst_file);
3833 err = DEVFSADM_FAILURE;
3834 goto out;
3835 /* NOTREACHED */
3836 }
3837
3838 /*
3839 * Set permissions to be the same on the backup as
3840 * /etc/path_to_inst.
3841 */
3842 (void) chmod(old_inst_file, 0444);
3843
3844 /*
3845 * So far, everything we've done is more or less reversible.
3846 * But now we're going to commit ourselves.
3847 */
3848
3849 (void) snprintf(old_inst_file_npid,
3850 inst_strlen + sizeof (INSTANCE_FILE_SUFFIX),
3851 "%s%s", inst_file, INSTANCE_FILE_SUFFIX);
3852
3853 if ((err = rename(old_inst_file, old_inst_file_npid)) != 0) {
3854 err_print(RENAME_FAILED, old_inst_file_npid,
3855 strerror(errno));
3856 } else if ((err = rename(new_inst_file, inst_file)) != 0) {
3857 err_print(RENAME_FAILED, inst_file, strerror(errno));
3858 }
3859
3860 out:
3861 if (inst_file_fp != NULL) {
3862 if (fclose(inst_file_fp) == EOF) {
3863 err_print(FCLOSE_FAILED, inst_file, strerror(errno));
3864 }
3865 }
3866
3867 if (stat(new_inst_file, &sb) == 0) {
3868 s_unlink(new_inst_file);
3869 }
3870 free(new_inst_file);
3871
3872 if (stat(old_inst_file, &sb) == 0) {
3873 s_unlink(old_inst_file);
3874 }
3875 free(old_inst_file);
3876
3877 free(old_inst_file_npid);
3878
3879 if (err != 0 && err != EALREADY) {
3880 err_print(FAILED_TO_UPDATE, inst_file);
3881 }
3882 }
3883
3884 /*
3885 * detach from tty. For daemon mode.
3886 */
3887 void
detachfromtty()3888 detachfromtty()
3889 {
3890 (void) setsid();
3891 if (DEVFSADM_DEBUG_ON == TRUE) {
3892 return;
3893 }
3894
3895 (void) close(0);
3896 (void) close(1);
3897 (void) close(2);
3898 (void) open("/dev/null", O_RDWR, 0);
3899 (void) dup(0);
3900 (void) dup(0);
3901 openlog(DEVFSADMD, LOG_PID, LOG_DAEMON);
3902 (void) setlogmask(LOG_UPTO(LOG_INFO));
3903 logflag = TRUE;
3904 }
3905
3906 /*
3907 * Use an advisory lock to synchronize updates to /dev. If the lock is
3908 * held by another process, block in the fcntl() system call until that
3909 * process drops the lock or exits. The lock file itself is
3910 * DEV_LOCK_FILE. The process id of the current and last process owning
3911 * the lock is kept in the lock file. After acquiring the lock, read the
3912 * process id and return it. It is the process ID which last owned the
3913 * lock, and will be used to determine if caches need to be flushed.
3914 *
3915 * NOTE: if the devlink database is held open by the caller, it may
3916 * be closed by this routine. This is to enforce the following lock ordering:
3917 * 1) /dev lock 2) database open
3918 */
3919 pid_t
enter_dev_lock()3920 enter_dev_lock()
3921 {
3922 struct flock lock;
3923 int n;
3924 pid_t pid;
3925 pid_t last_owner_pid;
3926
3927 if (file_mods == FALSE) {
3928 return (0);
3929 }
3930
3931 (void) snprintf(dev_lockfile, sizeof (dev_lockfile),
3932 "%s/%s", etc_dev_dir, DEV_LOCK_FILE);
3933
3934 vprint(LOCK_MID, "enter_dev_lock: lock file %s\n", dev_lockfile);
3935
3936 dev_lock_fd = open(dev_lockfile, O_CREAT|O_RDWR, 0644);
3937 if (dev_lock_fd < 0) {
3938 err_print(OPEN_FAILED, dev_lockfile, strerror(errno));
3939 devfsadm_exit(1);
3940 /*NOTREACHED*/
3941 }
3942
3943 lock.l_type = F_WRLCK;
3944 lock.l_whence = SEEK_SET;
3945 lock.l_start = 0;
3946 lock.l_len = 0;
3947
3948 /* try for the lock, but don't wait */
3949 if (fcntl(dev_lock_fd, F_SETLK, &lock) == -1) {
3950 if ((errno == EACCES) || (errno == EAGAIN)) {
3951 pid = 0;
3952 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3953 vprint(LOCK_MID, "waiting for PID %d to complete\n",
3954 (int)pid);
3955 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3956 err_print(LSEEK_FAILED, dev_lockfile,
3957 strerror(errno));
3958 devfsadm_exit(1);
3959 /*NOTREACHED*/
3960 }
3961 /*
3962 * wait for the dev lock. If we have the database open,
3963 * close it first - the order of lock acquisition should
3964 * always be: 1) dev_lock 2) database
3965 * This is to prevent deadlocks with any locks the
3966 * database code may hold.
3967 */
3968 (void) di_devlink_close(&devlink_cache, 0);
3969
3970 /* send any sysevents that were queued up. */
3971 process_syseventq();
3972
3973 if (fcntl(dev_lock_fd, F_SETLKW, &lock) == -1) {
3974 err_print(LOCK_FAILED, dev_lockfile,
3975 strerror(errno));
3976 devfsadm_exit(1);
3977 /*NOTREACHED*/
3978 }
3979 }
3980 }
3981
3982 hold_dev_lock = TRUE;
3983 pid = 0;
3984 n = read(dev_lock_fd, &pid, sizeof (pid_t));
3985 if (n == sizeof (pid_t) && pid == getpid()) {
3986 return (pid);
3987 }
3988
3989 last_owner_pid = pid;
3990
3991 if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
3992 err_print(LSEEK_FAILED, dev_lockfile, strerror(errno));
3993 devfsadm_exit(1);
3994 /*NOTREACHED*/
3995 }
3996 pid = getpid();
3997 n = write(dev_lock_fd, &pid, sizeof (pid_t));
3998 if (n != sizeof (pid_t)) {
3999 err_print(WRITE_FAILED, dev_lockfile, strerror(errno));
4000 devfsadm_exit(1);
4001 /*NOTREACHED*/
4002 }
4003
4004 return (last_owner_pid);
4005 }
4006
4007 /*
4008 * Drop the advisory /dev lock, close lock file. Close and re-open the
4009 * file every time so to ensure a resync if for some reason the lock file
4010 * gets removed.
4011 */
4012 void
exit_dev_lock(int exiting)4013 exit_dev_lock(int exiting)
4014 {
4015 struct flock unlock;
4016
4017 if (hold_dev_lock == FALSE) {
4018 return;
4019 }
4020
4021 vprint(LOCK_MID, "exit_dev_lock: lock file %s, exiting = %d\n",
4022 dev_lockfile, exiting);
4023
4024 unlock.l_type = F_UNLCK;
4025 unlock.l_whence = SEEK_SET;
4026 unlock.l_start = 0;
4027 unlock.l_len = 0;
4028
4029 if (fcntl(dev_lock_fd, F_SETLK, &unlock) == -1) {
4030 err_print(UNLOCK_FAILED, dev_lockfile, strerror(errno));
4031 }
4032
4033 hold_dev_lock = FALSE;
4034
4035 if (close(dev_lock_fd) == -1) {
4036 err_print(CLOSE_FAILED, dev_lockfile, strerror(errno));
4037 if (!exiting)
4038 devfsadm_exit(1);
4039 /*NOTREACHED*/
4040 }
4041 }
4042
4043 /*
4044 *
4045 * Use an advisory lock to ensure that only one daemon process is active
4046 * in the system at any point in time. If the lock is held by another
4047 * process, do not block but return the pid owner of the lock to the
4048 * caller immediately. The lock is cleared if the holding daemon process
4049 * exits for any reason even if the lock file remains, so the daemon can
4050 * be restarted if necessary. The lock file is DAEMON_LOCK_FILE.
4051 */
4052 pid_t
enter_daemon_lock(void)4053 enter_daemon_lock(void)
4054 {
4055 struct flock lock;
4056
4057 (void) snprintf(daemon_lockfile, sizeof (daemon_lockfile),
4058 "%s/%s", etc_dev_dir, DAEMON_LOCK_FILE);
4059
4060 vprint(LOCK_MID, "enter_daemon_lock: lock file %s\n", daemon_lockfile);
4061
4062 daemon_lock_fd = open(daemon_lockfile, O_CREAT|O_RDWR, 0644);
4063 if (daemon_lock_fd < 0) {
4064 err_print(OPEN_FAILED, daemon_lockfile, strerror(errno));
4065 devfsadm_exit(1);
4066 /*NOTREACHED*/
4067 }
4068
4069 lock.l_type = F_WRLCK;
4070 lock.l_whence = SEEK_SET;
4071 lock.l_start = 0;
4072 lock.l_len = 0;
4073
4074 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
4075
4076 if (errno == EAGAIN || errno == EDEADLK) {
4077 if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) {
4078 err_print(LOCK_FAILED, daemon_lockfile,
4079 strerror(errno));
4080 devfsadm_exit(1);
4081 /*NOTREACHED*/
4082 }
4083 return (lock.l_pid);
4084 }
4085 }
4086 hold_daemon_lock = TRUE;
4087 return (getpid());
4088 }
4089
4090 /*
4091 * Drop the advisory daemon lock, close lock file
4092 */
4093 void
exit_daemon_lock(int exiting)4094 exit_daemon_lock(int exiting)
4095 {
4096 struct flock lock;
4097
4098 if (hold_daemon_lock == FALSE) {
4099 return;
4100 }
4101
4102 vprint(LOCK_MID, "exit_daemon_lock: lock file %s, exiting = %d\n",
4103 daemon_lockfile, exiting);
4104
4105 lock.l_type = F_UNLCK;
4106 lock.l_whence = SEEK_SET;
4107 lock.l_start = 0;
4108 lock.l_len = 0;
4109
4110 if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
4111 err_print(UNLOCK_FAILED, daemon_lockfile, strerror(errno));
4112 }
4113
4114 if (close(daemon_lock_fd) == -1) {
4115 err_print(CLOSE_FAILED, daemon_lockfile, strerror(errno));
4116 if (!exiting)
4117 devfsadm_exit(1);
4118 /*NOTREACHED*/
4119 }
4120 }
4121
4122 /*
4123 * Called to removed danging nodes in two different modes: RM_PRE, RM_POST.
4124 * RM_PRE mode is called before processing the entire devinfo tree, and RM_POST
4125 * is called after processing the entire devinfo tree.
4126 */
4127 static void
pre_and_post_cleanup(int flags)4128 pre_and_post_cleanup(int flags)
4129 {
4130 remove_list_t *rm;
4131 recurse_dev_t rd;
4132 cleanup_data_t cleanup_data;
4133 char *fcn = "pre_and_post_cleanup: ";
4134
4135 if (build_dev == FALSE)
4136 return;
4137
4138 vprint(CHATTY_MID, "attempting %s-cleanup\n",
4139 flags == RM_PRE ? "pre" : "post");
4140 vprint(REMOVE_MID, "%sflags = %d\n", fcn, flags);
4141
4142 /*
4143 * the generic function recurse_dev_re is shared among different
4144 * functions, so set the method and data that it should use for
4145 * matches.
4146 */
4147 rd.fcn = matching_dev;
4148 rd.data = (void *)&cleanup_data;
4149 cleanup_data.flags = flags;
4150
4151 (void) mutex_lock(&nfp_mutex);
4152 nfphash_create();
4153
4154 for (rm = remove_head; rm != NULL; rm = rm->next) {
4155 if ((flags & rm->remove->flags) == flags) {
4156 cleanup_data.rm = rm;
4157 /*
4158 * If reached this point, RM_PRE or RM_POST cleanup is
4159 * desired. clean_ok() decides whether to clean
4160 * under the given circumstances.
4161 */
4162 vprint(REMOVE_MID, "%scleanup: PRE or POST\n", fcn);
4163 if (clean_ok(rm->remove) == DEVFSADM_SUCCESS) {
4164 vprint(REMOVE_MID, "cleanup: cleanup OK\n");
4165 recurse_dev_re(dev_dir,
4166 rm->remove->dev_dirs_re, &rd);
4167 }
4168 }
4169 }
4170 nfphash_destroy();
4171 (void) mutex_unlock(&nfp_mutex);
4172 }
4173
4174 /*
4175 * clean_ok() determines whether cleanup should be done according
4176 * to the following matrix:
4177 *
4178 * command line arguments RM_PRE RM_POST RM_PRE && RM_POST &&
4179 * RM_ALWAYS RM_ALWAYS
4180 * ---------------------- ------ ----- --------- ----------
4181 *
4182 * <neither -c nor -C> - - pre-clean post-clean
4183 *
4184 * -C pre-clean post-clean pre-clean post-clean
4185 *
4186 * -C -c class pre-clean post-clean pre-clean post-clean
4187 * if class if class if class if class
4188 * matches matches matches matches
4189 *
4190 * -c class - - pre-clean post-clean
4191 * if class if class
4192 * matches matches
4193 *
4194 */
4195 static int
clean_ok(devfsadm_remove_V1_t * remove)4196 clean_ok(devfsadm_remove_V1_t *remove)
4197 {
4198 int i;
4199
4200 if (single_drv == TRUE) {
4201 /* no cleanup at all when using -i option */
4202 return (DEVFSADM_FAILURE);
4203 }
4204
4205 /*
4206 * no cleanup if drivers are not loaded. We make an exception
4207 * for the "disks" program however, since disks has a public
4208 * cleanup flag (-C) and disk drivers are usually never
4209 * unloaded.
4210 */
4211 if (load_attach_drv == FALSE && strcmp(prog, DISKS) != 0) {
4212 return (DEVFSADM_FAILURE);
4213 }
4214
4215 /* if the cleanup flag was not specified, return false */
4216 if ((cleanup == FALSE) && ((remove->flags & RM_ALWAYS) == 0)) {
4217 return (DEVFSADM_FAILURE);
4218 }
4219
4220 if (num_classes == 0) {
4221 return (DEVFSADM_SUCCESS);
4222 }
4223
4224 /*
4225 * if reached this point, check to see if the class in the given
4226 * remove structure matches a class given on the command line
4227 */
4228
4229 for (i = 0; i < num_classes; i++) {
4230 if (strcmp(remove->device_class, classes[i]) == 0) {
4231 return (DEVFSADM_SUCCESS);
4232 }
4233 }
4234
4235 return (DEVFSADM_FAILURE);
4236 }
4237
4238 /*
4239 * Called to remove dangling nodes after receiving a hotplug event
4240 * containing the physical node pathname to be removed.
4241 */
4242 void
hot_cleanup(char * node_path,char * minor_name,char * ev_subclass,char * driver_name,int instance)4243 hot_cleanup(char *node_path, char *minor_name, char *ev_subclass,
4244 char *driver_name, int instance)
4245 {
4246 link_t *link;
4247 linkhead_t *head;
4248 remove_list_t *rm;
4249 char *fcn = "hot_cleanup: ";
4250 char path[PATH_MAX + 1];
4251 int path_len;
4252 char rmlink[PATH_MAX + 1];
4253 nvlist_t *nvl = NULL;
4254 int skip;
4255 int ret;
4256
4257 /*
4258 * dev links can go away as part of hot cleanup.
4259 * So first build event attributes in order capture dev links.
4260 */
4261 if (ev_subclass != NULL)
4262 nvl = build_event_attributes(EC_DEV_REMOVE, ev_subclass,
4263 node_path, DI_NODE_NIL, driver_name, instance, minor_name);
4264
4265 (void) strcpy(path, node_path);
4266 (void) strcat(path, ":");
4267 (void) strcat(path, minor_name == NULL ? "" : minor_name);
4268
4269 path_len = strlen(path);
4270
4271 vprint(REMOVE_MID, "%spath=%s\n", fcn, path);
4272
4273 (void) mutex_lock(&nfp_mutex);
4274 nfphash_create();
4275
4276 for (rm = remove_head; rm != NULL; rm = rm->next) {
4277 if ((RM_HOT & rm->remove->flags) == RM_HOT) {
4278 head = get_cached_links(rm->remove->dev_dirs_re);
4279 assert(head->nextlink == NULL);
4280 for (link = head->link;
4281 link != NULL; link = head->nextlink) {
4282 /*
4283 * The remove callback below may remove
4284 * the current and/or any or all of the
4285 * subsequent links in the list.
4286 * Save the next link in the head. If
4287 * the callback removes the next link
4288 * the saved pointer in the head will be
4289 * updated by the callback to point at
4290 * the next valid link.
4291 */
4292 head->nextlink = link->next;
4293
4294 /*
4295 * if devlink is in no-further-process hash,
4296 * skip its remove
4297 */
4298 if (nfphash_lookup(link->devlink) != NULL)
4299 continue;
4300
4301 if (minor_name)
4302 skip = strcmp(link->contents, path);
4303 else
4304 skip = strncmp(link->contents, path,
4305 path_len);
4306 if (skip ||
4307 (call_minor_init(rm->modptr) ==
4308 DEVFSADM_FAILURE))
4309 continue;
4310
4311 vprint(REMOVE_MID,
4312 "%sremoving %s -> %s\n", fcn,
4313 link->devlink, link->contents);
4314 /*
4315 * Use a copy of the cached link name
4316 * as the cache entry will go away
4317 * during link removal
4318 */
4319 (void) snprintf(rmlink, sizeof (rmlink),
4320 "%s", link->devlink);
4321 if (rm->remove->flags & RM_NOINTERPOSE) {
4322 (void)
4323 (rm->remove->callback_fcn)(rmlink);
4324 } else {
4325 ret =
4326 (rm->remove->callback_fcn)(rmlink);
4327 if (ret == DEVFSADM_TERMINATE)
4328 nfphash_insert(rmlink);
4329 }
4330 }
4331 }
4332 }
4333
4334 nfphash_destroy();
4335 (void) mutex_unlock(&nfp_mutex);
4336
4337 /* update device allocation database */
4338 if (system_labeled) {
4339 int devtype = 0;
4340
4341 if (strstr(path, DA_SOUND_NAME))
4342 devtype = DA_AUDIO;
4343 else if (strstr(path, "storage"))
4344 devtype = DA_RMDISK;
4345 else if (strstr(path, "disk"))
4346 devtype = DA_RMDISK;
4347 else if (strstr(path, "floppy"))
4348 /* TODO: detect usb cds and floppies at insert time */
4349 devtype = DA_RMDISK;
4350 else
4351 goto out;
4352
4353 (void) _update_devalloc_db(&devlist, devtype, DA_REMOVE,
4354 node_path, root_dir);
4355 }
4356
4357 out:
4358 /* now log an event */
4359 if (nvl) {
4360 log_event(EC_DEV_REMOVE, ev_subclass, nvl);
4361 free(nvl);
4362 }
4363 }
4364
4365 /*
4366 * Open the dir current_dir. For every file which matches the first dir
4367 * component of path_re, recurse. If there are no more *dir* path
4368 * components left in path_re (ie no more /), then call function rd->fcn.
4369 */
4370 static void
recurse_dev_re(char * current_dir,char * path_re,recurse_dev_t * rd)4371 recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
4372 {
4373 regex_t re1;
4374 char *slash;
4375 char new_path[PATH_MAX + 1];
4376 char *anchored_path_re;
4377 size_t len;
4378 finddevhdl_t fhandle;
4379 const char *fp;
4380
4381 vprint(RECURSEDEV_MID, "recurse_dev_re: curr = %s path=%s\n",
4382 current_dir, path_re);
4383
4384 if (finddev_readdir(current_dir, &fhandle) != 0)
4385 return;
4386
4387 len = strlen(path_re);
4388 if ((slash = strchr(path_re, '/')) != NULL) {
4389 len = (slash - path_re);
4390 }
4391
4392 anchored_path_re = s_malloc(len + 3);
4393 (void) sprintf(anchored_path_re, "^%.*s$", len, path_re);
4394
4395 if (regcomp(&re1, anchored_path_re, REG_EXTENDED) != 0) {
4396 free(anchored_path_re);
4397 goto out;
4398 }
4399
4400 free(anchored_path_re);
4401
4402 while ((fp = finddev_next(fhandle)) != NULL) {
4403
4404 if (regexec(&re1, fp, 0, NULL, 0) == 0) {
4405 /* match */
4406 (void) strcpy(new_path, current_dir);
4407 (void) strcat(new_path, "/");
4408 (void) strcat(new_path, fp);
4409
4410 vprint(RECURSEDEV_MID, "recurse_dev_re: match, new "
4411 "path = %s\n", new_path);
4412
4413 if (slash != NULL) {
4414 recurse_dev_re(new_path, slash + 1, rd);
4415 } else {
4416 /* reached the leaf component of path_re */
4417 vprint(RECURSEDEV_MID,
4418 "recurse_dev_re: calling fcn\n");
4419 (*(rd->fcn))(new_path, rd->data);
4420 }
4421 }
4422 }
4423
4424 regfree(&re1);
4425
4426 out:
4427 finddev_close(fhandle);
4428 }
4429
4430 /*
4431 * Found a devpath which matches a RE in the remove structure.
4432 * Now check to see if it is dangling.
4433 */
4434 static void
matching_dev(char * devpath,void * data)4435 matching_dev(char *devpath, void *data)
4436 {
4437 cleanup_data_t *cleanup_data = data;
4438 int norm_len = strlen(dev_dir) + strlen("/");
4439 int ret;
4440 char *fcn = "matching_dev: ";
4441
4442 vprint(RECURSEDEV_MID, "%sexamining devpath = '%s'\n", fcn,
4443 devpath);
4444
4445 /*
4446 * If the link is in the no-further-process hash
4447 * don't do any remove operation on it.
4448 */
4449 if (nfphash_lookup(devpath + norm_len) != NULL)
4450 return;
4451
4452 /*
4453 * Dangling check will work whether "alias" or "current"
4454 * so no need to redirect.
4455 */
4456 if (resolve_link(devpath, NULL, NULL, NULL, 1) == TRUE) {
4457 if (call_minor_init(cleanup_data->rm->modptr) ==
4458 DEVFSADM_FAILURE) {
4459 return;
4460 }
4461
4462 devpath += norm_len;
4463
4464 vprint(RECURSEDEV_MID, "%scalling callback %s\n", fcn, devpath);
4465 if (cleanup_data->rm->remove->flags & RM_NOINTERPOSE)
4466 (void)
4467 (cleanup_data->rm->remove->callback_fcn)(devpath);
4468 else {
4469 ret =
4470 (cleanup_data->rm->remove->callback_fcn)(devpath);
4471 if (ret == DEVFSADM_TERMINATE) {
4472 /*
4473 * We want no further remove processing for
4474 * this link. Add it to the nfp_hash;
4475 */
4476 nfphash_insert(devpath);
4477 }
4478 }
4479 }
4480 }
4481
4482 int
devfsadm_read_link(di_node_t anynode,char * link,char ** devfs_path)4483 devfsadm_read_link(di_node_t anynode, char *link, char **devfs_path)
4484 {
4485 char devlink[PATH_MAX];
4486 char *path;
4487
4488 *devfs_path = NULL;
4489
4490 /* prepend link with dev_dir contents */
4491 (void) strcpy(devlink, dev_dir);
4492 (void) strcat(devlink, "/");
4493 (void) strcat(devlink, link);
4494
4495 /* We *don't* want a stat of the /devices node */
4496 path = NULL;
4497 (void) resolve_link(devlink, NULL, NULL, &path, 0);
4498 if (path != NULL) {
4499 /* redirect if alias to current */
4500 *devfs_path = di_alias2curr(anynode, path);
4501 free(path);
4502 }
4503 return (*devfs_path ? DEVFSADM_SUCCESS : DEVFSADM_FAILURE);
4504 }
4505
4506 int
devfsadm_link_valid(di_node_t anynode,char * link)4507 devfsadm_link_valid(di_node_t anynode, char *link)
4508 {
4509 struct stat sb;
4510 char devlink[PATH_MAX + 1], *contents, *raw_contents;
4511 int rv, type;
4512 int instance = 0;
4513
4514 /* prepend link with dev_dir contents */
4515 (void) strcpy(devlink, dev_dir);
4516 (void) strcat(devlink, "/");
4517 (void) strcat(devlink, link);
4518
4519 if (!device_exists(devlink) || lstat(devlink, &sb) != 0) {
4520 return (DEVFSADM_FALSE);
4521 }
4522
4523 raw_contents = NULL;
4524 type = 0;
4525 if (resolve_link(devlink, &raw_contents, &type, NULL, 1) == TRUE) {
4526 rv = DEVFSADM_FALSE;
4527 } else {
4528 rv = DEVFSADM_TRUE;
4529 }
4530
4531 /*
4532 * resolve alias paths for primary links
4533 */
4534 contents = raw_contents;
4535 if (type == DI_PRIMARY_LINK) {
4536 contents = di_alias2curr(anynode, raw_contents);
4537 free(raw_contents);
4538 }
4539
4540 /*
4541 * The link exists. Add it to the database
4542 */
4543 (void) di_devlink_add_link(devlink_cache, link, contents, type);
4544 if (system_labeled && (rv == DEVFSADM_TRUE) &&
4545 strstr(devlink, DA_AUDIO_NAME) && contents) {
4546 (void) sscanf(contents, "%*[a-z]%d", &instance);
4547 (void) da_add_list(&devlist, devlink, instance,
4548 DA_ADD|DA_AUDIO);
4549 _update_devalloc_db(&devlist, 0, DA_ADD, NULL, root_dir);
4550 }
4551 free(contents);
4552
4553 return (rv);
4554 }
4555
4556 /*
4557 * devpath: Absolute path to /dev link
4558 * content_p: Returns malloced string (link content)
4559 * type_p: Returns link type: primary or secondary
4560 * devfs_path: Returns malloced string: /devices path w/out "/devices"
4561 * dangle: if set, check if link is dangling
4562 * Returns:
4563 * TRUE if dangling
4564 * FALSE if not or if caller doesn't care
4565 * Caller is assumed to have initialized pointer contents to NULL
4566 *
4567 */
4568 static int
resolve_link(char * devpath,char ** content_p,int * type_p,char ** devfs_path,int dangle)4569 resolve_link(char *devpath, char **content_p, int *type_p, char **devfs_path,
4570 int dangle)
4571 {
4572 char contents[PATH_MAX + 1];
4573 char stage_link[PATH_MAX + 1];
4574 char *fcn = "resolve_link: ";
4575 char *ptr;
4576 int linksize;
4577 int rv = TRUE;
4578 struct stat sb;
4579
4580 /*
4581 * This routine will return the "raw" contents. It is upto the
4582 * the caller to redirect "alias" to "current" (or vice versa)
4583 */
4584 linksize = readlink(devpath, contents, PATH_MAX);
4585
4586 if (linksize <= 0) {
4587 return (FALSE);
4588 } else {
4589 contents[linksize] = '\0';
4590 }
4591 vprint(REMOVE_MID, "%s %s -> %s\n", fcn, devpath, contents);
4592
4593 if (content_p) {
4594 *content_p = s_strdup(contents);
4595 }
4596
4597 /*
4598 * Check to see if this is a link pointing to another link in /dev. The
4599 * cheap way to do this is to look for a lack of ../devices/.
4600 */
4601
4602 if (is_minor_node(contents, &ptr) == DEVFSADM_FALSE) {
4603
4604 if (type_p) {
4605 *type_p = DI_SECONDARY_LINK;
4606 }
4607
4608 /*
4609 * assume that linkcontents is really a pointer to another
4610 * link, and if so recurse and read its link contents.
4611 */
4612 if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
4613 (void) strcpy(stage_link, dev_dir);
4614 (void) strcat(stage_link, "/");
4615 (void) strcpy(stage_link,
4616 &contents[strlen(DEV) + strlen("/")]);
4617 } else {
4618 if ((ptr = strrchr(devpath, '/')) == NULL) {
4619 vprint(REMOVE_MID, "%s%s -> %s invalid link. "
4620 "missing '/'\n", fcn, devpath, contents);
4621 return (TRUE);
4622 }
4623 *ptr = '\0';
4624 (void) strcpy(stage_link, devpath);
4625 *ptr = '/';
4626 (void) strcat(stage_link, "/");
4627 (void) strcat(stage_link, contents);
4628 }
4629 return (resolve_link(stage_link, NULL, NULL, devfs_path,
4630 dangle));
4631 }
4632
4633 /* Current link points at a /devices minor node */
4634 if (type_p) {
4635 *type_p = DI_PRIMARY_LINK;
4636 }
4637
4638 if (devfs_path)
4639 *devfs_path = s_strdup(ptr);
4640
4641 rv = FALSE;
4642 if (dangle)
4643 rv = (stat(ptr - strlen(DEVICES), &sb) == -1);
4644
4645 vprint(REMOVE_MID, "%slink=%s, returning %s\n", fcn,
4646 devpath, ((rv == TRUE) ? "TRUE" : "FALSE"));
4647
4648 return (rv);
4649 }
4650
4651 /*
4652 * Returns the substring of interest, given a path.
4653 */
4654 static char *
alloc_cmp_str(const char * path,devfsadm_enumerate_t * dep)4655 alloc_cmp_str(const char *path, devfsadm_enumerate_t *dep)
4656 {
4657 uint_t match;
4658 char *np, *ap, *mp;
4659 char *cmp_str = NULL;
4660 char at[] = "@";
4661 char *fcn = "alloc_cmp_str";
4662
4663 np = ap = mp = NULL;
4664
4665 /*
4666 * extract match flags from the flags argument.
4667 */
4668 match = (dep->flags & MATCH_MASK);
4669
4670 vprint(ENUM_MID, "%s: enumeration match type: 0x%x"
4671 " path: %s\n", fcn, match, path);
4672
4673 /*
4674 * MATCH_CALLBACK and MATCH_ALL are the only flags
4675 * which may be used if "path" is a /dev path
4676 */
4677 if (match == MATCH_CALLBACK) {
4678 if (dep->sel_fcn == NULL) {
4679 vprint(ENUM_MID, "%s: invalid enumerate"
4680 " callback: path: %s\n", fcn, path);
4681 return (NULL);
4682 }
4683 cmp_str = dep->sel_fcn(path, dep->cb_arg);
4684 return (cmp_str);
4685 }
4686
4687 cmp_str = s_strdup(path);
4688
4689 if (match == MATCH_ALL) {
4690 return (cmp_str);
4691 }
4692
4693 /*
4694 * The remaining flags make sense only for /devices
4695 * paths
4696 */
4697 if ((mp = strrchr(cmp_str, ':')) == NULL) {
4698 vprint(ENUM_MID, "%s: invalid path: %s\n",
4699 fcn, path);
4700 goto err;
4701 }
4702
4703 if (match == MATCH_MINOR) {
4704 /* A NULL "match_arg" values implies entire minor */
4705 if (get_component(mp + 1, dep->match_arg) == NULL) {
4706 vprint(ENUM_MID, "%s: invalid minor component:"
4707 " path: %s\n", fcn, path);
4708 goto err;
4709 }
4710 return (cmp_str);
4711 }
4712
4713 if ((np = strrchr(cmp_str, '/')) == NULL) {
4714 vprint(ENUM_MID, "%s: invalid path: %s\n", fcn, path);
4715 goto err;
4716 }
4717
4718 if (match == MATCH_PARENT) {
4719 if (strcmp(cmp_str, "/") == 0) {
4720 vprint(ENUM_MID, "%s: invalid path: %s\n",
4721 fcn, path);
4722 goto err;
4723 }
4724
4725 if (np == cmp_str) {
4726 *(np + 1) = '\0';
4727 } else {
4728 *np = '\0';
4729 }
4730 return (cmp_str);
4731 }
4732
4733 /* ap can be NULL - Leaf address may not exist or be empty string */
4734 ap = strchr(np+1, '@');
4735
4736 /* minor is no longer of interest */
4737 *mp = '\0';
4738
4739 if (match == MATCH_NODE) {
4740 if (ap)
4741 *ap = '\0';
4742 return (cmp_str);
4743 } else if (match == MATCH_ADDR) {
4744 /*
4745 * The empty string is a valid address. The only MATCH_ADDR
4746 * allowed in this case is against the whole address or
4747 * the first component of the address (match_arg=NULL/"0"/"1")
4748 * Note that in this case, the path won't have an "@"
4749 * As a result ap will be NULL. We fake up an ap = @'\0'
4750 * so that get_component() will work correctly.
4751 */
4752 if (ap == NULL) {
4753 ap = at;
4754 }
4755
4756 if (get_component(ap + 1, dep->match_arg) == NULL) {
4757 vprint(ENUM_MID, "%s: invalid leaf addr. component:"
4758 " path: %s\n", fcn, path);
4759 goto err;
4760 }
4761 return (cmp_str);
4762 }
4763
4764 vprint(ENUM_MID, "%s: invalid enumeration flags: 0x%x"
4765 " path: %s\n", fcn, dep->flags, path);
4766
4767 /*FALLTHRU*/
4768 err:
4769 free(cmp_str);
4770 return (NULL);
4771 }
4772
4773
4774 /*
4775 * "str" is expected to be a string with components separated by ','
4776 * The terminating null char is considered a separator.
4777 * get_component() will remove the portion of the string beyond
4778 * the component indicated.
4779 * If comp_str is NULL, the entire "str" is returned.
4780 */
4781 static char *
get_component(char * str,const char * comp_str)4782 get_component(char *str, const char *comp_str)
4783 {
4784 long comp;
4785 char *cp;
4786
4787 if (str == NULL) {
4788 return (NULL);
4789 }
4790
4791 if (comp_str == NULL) {
4792 return (str);
4793 }
4794
4795 errno = 0;
4796 comp = strtol(comp_str, &cp, 10);
4797 if (errno != 0 || *cp != '\0' || comp < 0) {
4798 return (NULL);
4799 }
4800
4801 if (comp == 0)
4802 return (str);
4803
4804 for (cp = str; ; cp++) {
4805 if (*cp == ',' || *cp == '\0')
4806 comp--;
4807 if (*cp == '\0' || comp <= 0) {
4808 break;
4809 }
4810 }
4811
4812 if (comp == 0) {
4813 *cp = '\0';
4814 } else {
4815 str = NULL;
4816 }
4817
4818 return (str);
4819 }
4820
4821
4822 /*
4823 * Enumerate serves as a generic counter as well as a means to determine
4824 * logical unit/controller numbers for such items as disk and tape
4825 * drives.
4826 *
4827 * rules[] is an array of devfsadm_enumerate_t structures which defines
4828 * the enumeration rules to be used for a specified set of links in /dev.
4829 * The set of links is specified through regular expressions (of the flavor
4830 * described in regex(7)). These regular expressions are used to determine
4831 * the set of links in /dev to examine. The last path component in these
4832 * regular expressions MUST contain a parenthesized subexpression surrounding
4833 * the RE which is to be considered the enumerating component. The subexp
4834 * member in a rule is the subexpression number of the enumerating
4835 * component. Subexpressions in the last path component are numbered starting
4836 * from 1.
4837 *
4838 * A cache of current id assignments is built up from existing symlinks and
4839 * new assignments use the lowest unused id. Assignments are based on a
4840 * match of a specified substring of a symlink's contents. If the specified
4841 * component for the devfs_path argument matches the corresponding substring
4842 * for a existing symlink's contents, the cached id is returned. Else, a new
4843 * id is created and returned in *buf. *buf must be freed by the caller.
4844 *
4845 * An id assignment may be governed by a combination of rules, each rule
4846 * applicable to a different subset of links in /dev. For example, controller
4847 * numbers may be determined by a combination of disk symlinks in /dev/[r]dsk
4848 * and controller symlinks in /dev/cfg, with the two sets requiring different
4849 * rules to derive the "substring of interest". In such cases, the rules
4850 * array will have more than one element.
4851 */
4852 int
devfsadm_enumerate_int(char * devfs_path,int index,char ** buf,devfsadm_enumerate_t rules[],int nrules)4853 devfsadm_enumerate_int(char *devfs_path, int index, char **buf,
4854 devfsadm_enumerate_t rules[], int nrules)
4855 {
4856 return (find_enum_id(rules, nrules,
4857 devfs_path, index, "0", INTEGER, buf, 0));
4858 }
4859
4860 int
ctrl_enumerate_int(char * devfs_path,int index,char ** buf,devfsadm_enumerate_t rules[],int nrules,int multiple,boolean_t scsi_vhci)4861 ctrl_enumerate_int(char *devfs_path, int index, char **buf,
4862 devfsadm_enumerate_t rules[], int nrules, int multiple,
4863 boolean_t scsi_vhci)
4864 {
4865 return (find_enum_id(rules, nrules,
4866 devfs_path, index, scsi_vhci ? "0" : "1", INTEGER, buf, multiple));
4867 }
4868
4869 /*
4870 * Same as above, but allows a starting value to be specified.
4871 * Private to devfsadm.... used by devlinks.
4872 */
4873 static int
devfsadm_enumerate_int_start(char * devfs_path,int index,char ** buf,devfsadm_enumerate_t rules[],int nrules,char * start)4874 devfsadm_enumerate_int_start(char *devfs_path, int index, char **buf,
4875 devfsadm_enumerate_t rules[], int nrules, char *start)
4876 {
4877 return (find_enum_id(rules, nrules,
4878 devfs_path, index, start, INTEGER, buf, 0));
4879 }
4880
4881 /*
4882 * devfsadm_enumerate_char serves as a generic counter returning
4883 * a single letter.
4884 */
4885 int
devfsadm_enumerate_char(char * devfs_path,int index,char ** buf,devfsadm_enumerate_t rules[],int nrules)4886 devfsadm_enumerate_char(char *devfs_path, int index, char **buf,
4887 devfsadm_enumerate_t rules[], int nrules)
4888 {
4889 return (find_enum_id(rules, nrules,
4890 devfs_path, index, "a", LETTER, buf, 0));
4891 }
4892
4893 /*
4894 * Same as above, but allows a starting char to be specified.
4895 * Private to devfsadm - used by ports module (port_link.c)
4896 */
4897 int
devfsadm_enumerate_char_start(char * devfs_path,int index,char ** buf,devfsadm_enumerate_t rules[],int nrules,char * start)4898 devfsadm_enumerate_char_start(char *devfs_path, int index, char **buf,
4899 devfsadm_enumerate_t rules[], int nrules, char *start)
4900 {
4901 return (find_enum_id(rules, nrules,
4902 devfs_path, index, start, LETTER, buf, 0));
4903 }
4904
4905
4906 /*
4907 * For a given numeral_set (see get_cached_set for desc of numeral_set),
4908 * search all cached entries looking for matches on a specified substring
4909 * of devfs_path. The substring is derived from devfs_path based on the
4910 * rule specified by "index". If a match is found on a cached entry,
4911 * return the enumerated id in buf. Otherwise, create a new id by calling
4912 * new_id, then cache and return that entry.
4913 */
4914 static int
find_enum_id(devfsadm_enumerate_t rules[],int nrules,char * devfs_path,int index,char * min,int type,char ** buf,int multiple)4915 find_enum_id(devfsadm_enumerate_t rules[], int nrules,
4916 char *devfs_path, int index, char *min, int type, char **buf,
4917 int multiple)
4918 {
4919 numeral_t *matchnp;
4920 numeral_t *numeral;
4921 int matchcount = 0;
4922 char *cmp_str;
4923 char *fcn = "find_enum_id";
4924 numeral_set_t *set;
4925
4926 if (rules == NULL) {
4927 vprint(ENUM_MID, "%s: no rules. path: %s\n",
4928 fcn, devfs_path ? devfs_path : "<NULL path>");
4929 return (DEVFSADM_FAILURE);
4930 }
4931
4932 if (devfs_path == NULL) {
4933 vprint(ENUM_MID, "%s: NULL path\n", fcn);
4934 return (DEVFSADM_FAILURE);
4935 }
4936
4937 if (nrules <= 0 || index < 0 || index >= nrules || buf == NULL) {
4938 vprint(ENUM_MID, "%s: invalid arguments. path: %s\n",
4939 fcn, devfs_path);
4940 return (DEVFSADM_FAILURE);
4941 }
4942
4943 *buf = NULL;
4944
4945
4946 cmp_str = alloc_cmp_str(devfs_path, &rules[index]);
4947 if (cmp_str == NULL) {
4948 return (DEVFSADM_FAILURE);
4949 }
4950
4951 if ((set = get_enum_cache(rules, nrules)) == NULL) {
4952 free(cmp_str);
4953 return (DEVFSADM_FAILURE);
4954 }
4955
4956 assert(nrules == set->re_count);
4957
4958 /*
4959 * Check and see if a matching entry is already cached.
4960 */
4961 matchcount = lookup_enum_cache(set, cmp_str, rules, index,
4962 &matchnp);
4963
4964 if (matchcount < 0 || matchcount > 1) {
4965 free(cmp_str);
4966 if (multiple && matchcount > 1)
4967 return (DEVFSADM_MULTIPLE);
4968 else
4969 return (DEVFSADM_FAILURE);
4970 }
4971
4972 /* if matching entry already cached, return it */
4973 if (matchcount == 1) {
4974 /* should never create a link with a reserved ID */
4975 vprint(ENUM_MID, "%s: 1 match w/ ID: %s\n", fcn, matchnp->id);
4976 assert(matchnp->flags == 0);
4977 *buf = s_strdup(matchnp->id);
4978 free(cmp_str);
4979 return (DEVFSADM_SUCCESS);
4980 }
4981
4982 /*
4983 * no cached entry, initialize a numeral struct
4984 * by calling new_id() and cache onto the numeral_set
4985 */
4986 numeral = s_malloc(sizeof (numeral_t));
4987 numeral->id = new_id(set->headnumeral, type, min);
4988 numeral->full_path = s_strdup(devfs_path);
4989 numeral->rule_index = index;
4990 numeral->cmp_str = cmp_str;
4991 cmp_str = NULL;
4992 numeral->flags = 0;
4993 vprint(RSRV_MID, "%s: alloc new_id: %s numeral flags = %d\n",
4994 fcn, numeral->id, numeral->flags);
4995
4996
4997 /* insert to head of list for fast lookups */
4998 numeral->next = set->headnumeral;
4999 set->headnumeral = numeral;
5000
5001 *buf = s_strdup(numeral->id);
5002 return (DEVFSADM_SUCCESS);
5003 }
5004
5005
5006 /*
5007 * Looks up the specified cache for a match with a specified string
5008 * Returns:
5009 * -1 : on error.
5010 * 0/1/2 : Number of matches.
5011 * Returns the matching element only if there is a single match.
5012 * If the "uncached" flag is set, derives the "cmp_str" afresh
5013 * for the match instead of using cached values.
5014 */
5015 static int
lookup_enum_cache(numeral_set_t * set,char * cmp_str,devfsadm_enumerate_t rules[],int index,numeral_t ** matchnpp)5016 lookup_enum_cache(numeral_set_t *set, char *cmp_str,
5017 devfsadm_enumerate_t rules[], int index, numeral_t **matchnpp)
5018 {
5019 int matchcount = 0, rv = -1;
5020 int uncached;
5021 numeral_t *np;
5022 char *fcn = "lookup_enum_cache";
5023 char *cp;
5024
5025 *matchnpp = NULL;
5026
5027 assert(index < set->re_count);
5028
5029 if (cmp_str == NULL) {
5030 return (-1);
5031 }
5032
5033 uncached = 0;
5034 if ((rules[index].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
5035 uncached = 1;
5036 }
5037
5038 /*
5039 * Check and see if a matching entry is already cached.
5040 */
5041 for (np = set->headnumeral; np != NULL; np = np->next) {
5042
5043 /*
5044 * Skip reserved IDs
5045 */
5046 if (np->flags & NUMERAL_RESERVED) {
5047 vprint(RSRV_MID, "lookup_enum_cache: "
5048 "Cannot Match with reserved ID (%s), "
5049 "skipping\n", np->id);
5050 assert(np->flags == NUMERAL_RESERVED);
5051 continue;
5052 } else {
5053 vprint(RSRV_MID, "lookup_enum_cache: "
5054 "Attempting match with numeral ID: %s"
5055 " numeral flags = %d\n", np->id, np->flags);
5056 assert(np->flags == 0);
5057 }
5058
5059 if (np->cmp_str == NULL) {
5060 vprint(ENUM_MID, "%s: invalid entry in enumerate"
5061 " cache. path: %s\n", fcn, np->full_path);
5062 return (-1);
5063 }
5064
5065 if (uncached) {
5066 vprint(CHATTY_MID, "%s: bypassing enumerate cache."
5067 " path: %s\n", fcn, cmp_str);
5068 cp = alloc_cmp_str(np->full_path,
5069 &rules[np->rule_index]);
5070 if (cp == NULL)
5071 return (-1);
5072 rv = strcmp(cmp_str, cp);
5073 free(cp);
5074 } else {
5075 rv = strcmp(cmp_str, np->cmp_str);
5076 }
5077
5078 if (rv == 0) {
5079 if (matchcount++ != 0) {
5080 break; /* more than 1 match. */
5081 }
5082 *matchnpp = np;
5083 }
5084 }
5085
5086 return (matchcount);
5087 }
5088
5089 #ifdef DEBUG
5090 static void
dump_enum_cache(numeral_set_t * setp)5091 dump_enum_cache(numeral_set_t *setp)
5092 {
5093 int i;
5094 numeral_t *np;
5095 char *fcn = "dump_enum_cache";
5096
5097 vprint(ENUM_MID, "%s: re_count = %d\n", fcn, setp->re_count);
5098 for (i = 0; i < setp->re_count; i++) {
5099 vprint(ENUM_MID, "%s: re[%d] = %s\n", fcn, i, setp->re[i]);
5100 }
5101
5102 for (np = setp->headnumeral; np != NULL; np = np->next) {
5103 vprint(ENUM_MID, "%s: id: %s\n", fcn, np->id);
5104 vprint(ENUM_MID, "%s: full_path: %s\n", fcn, np->full_path);
5105 vprint(ENUM_MID, "%s: rule_index: %d\n", fcn, np->rule_index);
5106 vprint(ENUM_MID, "%s: cmp_str: %s\n", fcn, np->cmp_str);
5107 vprint(ENUM_MID, "%s: flags: %d\n", fcn, np->flags);
5108 }
5109 }
5110 #endif
5111
5112 /*
5113 * For a given set of regular expressions in rules[], this function returns
5114 * either a previously cached struct numeral_set or it will create and
5115 * cache a new struct numeral_set. There is only one struct numeral_set
5116 * for the combination of REs present in rules[]. Each numeral_set contains
5117 * the regular expressions in rules[] used for cache selection AND a linked
5118 * list of struct numerals, ONE FOR EACH *UNIQUE* numeral or character ID
5119 * selected by the grouping parenthesized subexpression found in the last
5120 * path component of each rules[].re. For example, the RE: "rmt/([0-9]+)"
5121 * selects all the logical nodes of the correct form in dev/rmt/.
5122 * Each rmt/X will store a *single* struct numeral... ie 0, 1, 2 each get a
5123 * single struct numeral. There is no need to store more than a single logical
5124 * node matching X since the information desired in the devfspath would be
5125 * identical for the portion of the devfspath of interest. (the part up to,
5126 * but not including the minor name in this example.)
5127 *
5128 * If the given numeral_set is not yet cached, call enumerate_recurse to
5129 * create it.
5130 */
5131 static numeral_set_t *
get_enum_cache(devfsadm_enumerate_t rules[],int nrules)5132 get_enum_cache(devfsadm_enumerate_t rules[], int nrules)
5133 {
5134 /* linked list of numeral sets */
5135 numeral_set_t *setp;
5136 int i;
5137 int ret;
5138 char *path_left;
5139 enumerate_file_t *entry;
5140 char *fcn = "get_enum_cache";
5141
5142 /*
5143 * See if we've already cached this numeral set.
5144 */
5145 for (setp = head_numeral_set; setp != NULL; setp = setp->next) {
5146 /*
5147 * check all regexp's passed in function against
5148 * those in cached set.
5149 */
5150 if (nrules != setp->re_count) {
5151 continue;
5152 }
5153
5154 for (i = 0; i < nrules; i++) {
5155 if (strcmp(setp->re[i], rules[i].re) != 0) {
5156 break;
5157 }
5158 }
5159
5160 if (i == nrules) {
5161 return (setp);
5162 }
5163 }
5164
5165 /*
5166 * If the MATCH_UNCACHED flag is set, we should not be here.
5167 */
5168 for (i = 0; i < nrules; i++) {
5169 if ((rules[i].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
5170 vprint(ENUM_MID, "%s: invalid enumeration flags: "
5171 "0x%x\n", fcn, rules[i].flags);
5172 return (NULL);
5173 }
5174 }
5175
5176 /*
5177 * Since we made it here, we have not yet cached the given set of
5178 * logical nodes matching the passed re. Create a cached entry
5179 * struct numeral_set and populate it with a minimal set of
5180 * logical nodes from /dev.
5181 */
5182
5183 setp = s_malloc(sizeof (numeral_set_t));
5184 setp->re = s_malloc(sizeof (char *) * nrules);
5185 for (i = 0; i < nrules; i++) {
5186 setp->re[i] = s_strdup(rules[i].re);
5187 }
5188 setp->re_count = nrules;
5189 setp->headnumeral = NULL;
5190
5191 /* put this new cached set on the cached set list */
5192 setp->next = head_numeral_set;
5193 head_numeral_set = setp;
5194
5195 /*
5196 * For each RE, search the "reserved" list to create numeral IDs that
5197 * are reserved.
5198 */
5199 for (entry = enumerate_reserved; entry; entry = entry->er_next) {
5200
5201 vprint(RSRV_MID, "parsing rstring: %s\n", entry->er_file);
5202
5203 for (i = 0; i < nrules; i++) {
5204 path_left = s_strdup(setp->re[i]);
5205 vprint(RSRV_MID, "parsing rule RE: %s\n", path_left);
5206 ret = enumerate_parse(entry->er_file, path_left,
5207 setp, rules, i);
5208 if (ret == 1) {
5209 /*
5210 * We found the reserved ID for this entry.
5211 * We still keep the entry since it is needed
5212 * by the new link bypass code in disks
5213 */
5214 vprint(RSRV_MID, "found rsv ID: rstring: %s "
5215 "rule RE: %s\n", entry->er_file, path_left);
5216 free(path_left);
5217 break;
5218 }
5219 free(path_left);
5220 }
5221 }
5222
5223 /*
5224 * For each RE, search disk and cache any matches on the
5225 * numeral list.
5226 */
5227 for (i = 0; i < nrules; i++) {
5228 path_left = s_strdup(setp->re[i]);
5229 enumerate_recurse(dev_dir, path_left, setp, rules, i);
5230 free(path_left);
5231 }
5232
5233 #ifdef DEBUG
5234 dump_enum_cache(setp);
5235 #endif
5236
5237 return (setp);
5238 }
5239
5240
5241 /*
5242 * This function stats the pathname namebuf. If this is a directory
5243 * entry, we recurse down dname/fname until we find the first symbolic
5244 * link, and then stat and return it. This is valid for the same reason
5245 * that we only need to read a single pathname for multiple matching
5246 * logical ID's... ie, all the logical nodes should contain identical
5247 * physical paths for the parts we are interested.
5248 */
5249 int
get_stat_info(char * namebuf,struct stat * sb)5250 get_stat_info(char *namebuf, struct stat *sb)
5251 {
5252 char *cp;
5253 finddevhdl_t fhandle;
5254 const char *fp;
5255
5256 if (lstat(namebuf, sb) < 0) {
5257 (void) err_print(LSTAT_FAILED, namebuf, strerror(errno));
5258 return (DEVFSADM_FAILURE);
5259 }
5260
5261 if ((sb->st_mode & S_IFMT) == S_IFLNK) {
5262 return (DEVFSADM_SUCCESS);
5263 }
5264
5265 /*
5266 * If it is a dir, recurse down until we find a link and
5267 * then use the link.
5268 */
5269 if ((sb->st_mode & S_IFMT) == S_IFDIR) {
5270
5271 if (finddev_readdir(namebuf, &fhandle) != 0) {
5272 return (DEVFSADM_FAILURE);
5273 }
5274
5275 /*
5276 * Search each dir entry looking for a symlink. Return
5277 * the first symlink found in namebuf. Recurse dirs.
5278 */
5279 while ((fp = finddev_next(fhandle)) != NULL) {
5280 cp = namebuf + strlen(namebuf);
5281 if ((strlcat(namebuf, "/", PATH_MAX) >= PATH_MAX) ||
5282 (strlcat(namebuf, fp, PATH_MAX) >= PATH_MAX)) {
5283 *cp = '\0';
5284 finddev_close(fhandle);
5285 return (DEVFSADM_FAILURE);
5286 }
5287 if (get_stat_info(namebuf, sb) == DEVFSADM_SUCCESS) {
5288 finddev_close(fhandle);
5289 return (DEVFSADM_SUCCESS);
5290 }
5291 *cp = '\0';
5292 }
5293 finddev_close(fhandle);
5294 }
5295
5296 /* no symlink found, so return error */
5297 return (DEVFSADM_FAILURE);
5298 }
5299
5300 /*
5301 * An existing matching ID was not found, so this function is called to
5302 * create the next lowest ID. In the INTEGER case, return the next
5303 * lowest unused integer. In the case of LETTER, return the next lowest
5304 * unused letter. Return empty string if all 26 are used.
5305 * Only IDs >= min will be returned.
5306 */
5307 char *
new_id(numeral_t * numeral,int type,char * min)5308 new_id(numeral_t *numeral, int type, char *min)
5309 {
5310 int imin;
5311 temp_t *temp;
5312 temp_t *ptr;
5313 temp_t **previous;
5314 temp_t *head = NULL;
5315 char *retval;
5316 static char tempbuff[8];
5317 numeral_t *np;
5318
5319 if (type == LETTER) {
5320
5321 char letter[26];
5322 int i;
5323
5324 if (numeral == NULL) {
5325 return (s_strdup(min));
5326 }
5327
5328 for (i = 0; i < 26; i++) {
5329 letter[i] = 0;
5330 }
5331
5332 for (np = numeral; np != NULL; np = np->next) {
5333 assert(np->flags == 0 ||
5334 np->flags == NUMERAL_RESERVED);
5335 letter[*np->id - 'a']++;
5336 }
5337
5338 imin = *min - 'a';
5339
5340 for (i = imin; i < 26; i++) {
5341 if (letter[i] == 0) {
5342 retval = s_malloc(2);
5343 retval[0] = 'a' + i;
5344 retval[1] = '\0';
5345 return (retval);
5346 }
5347 }
5348
5349 return (s_strdup(""));
5350 }
5351
5352 if (type == INTEGER) {
5353
5354 if (numeral == NULL) {
5355 return (s_strdup(min));
5356 }
5357
5358 imin = atoi(min);
5359
5360 /* sort list */
5361 for (np = numeral; np != NULL; np = np->next) {
5362 assert(np->flags == 0 ||
5363 np->flags == NUMERAL_RESERVED);
5364 temp = s_malloc(sizeof (temp_t));
5365 temp->integer = atoi(np->id);
5366 temp->next = NULL;
5367
5368 previous = &head;
5369 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5370 if (temp->integer < ptr->integer) {
5371 temp->next = ptr;
5372 *previous = temp;
5373 break;
5374 }
5375 previous = &(ptr->next);
5376 }
5377 if (ptr == NULL) {
5378 *previous = temp;
5379 }
5380 }
5381
5382 /* now search sorted list for first hole >= imin */
5383 for (ptr = head; ptr != NULL; ptr = ptr->next) {
5384 if (imin == ptr->integer) {
5385 imin++;
5386 } else {
5387 if (imin < ptr->integer) {
5388 break;
5389 }
5390 }
5391
5392 }
5393
5394 /* free temp list */
5395 for (ptr = head; ptr != NULL; ) {
5396 temp = ptr;
5397 ptr = ptr->next;
5398 free(temp);
5399 }
5400
5401 (void) sprintf(tempbuff, "%d", imin);
5402 return (s_strdup(tempbuff));
5403 }
5404
5405 return (s_strdup(""));
5406 }
5407
5408 static int
enumerate_parse(char * rsvstr,char * path_left,numeral_set_t * setp,devfsadm_enumerate_t rules[],int index)5409 enumerate_parse(char *rsvstr, char *path_left, numeral_set_t *setp,
5410 devfsadm_enumerate_t rules[], int index)
5411 {
5412 char *slash1 = NULL;
5413 char *slash2 = NULL;
5414 char *numeral_id;
5415 char *path_left_save;
5416 char *rsvstr_save;
5417 int ret = 0;
5418 static int warned = 0;
5419
5420 rsvstr_save = rsvstr;
5421 path_left_save = path_left;
5422
5423 if (rsvstr == NULL || rsvstr[0] == '\0' || rsvstr[0] == '/') {
5424 if (!warned) {
5425 err_print("invalid reserved filepath: %s\n",
5426 rsvstr ? rsvstr : "<NULL>");
5427 warned = 1;
5428 }
5429 return (0);
5430 }
5431
5432 vprint(RSRV_MID, "processing rule: %s, rstring: %s\n",
5433 path_left, rsvstr);
5434
5435
5436 for (;;) {
5437 /* get rid of any extra '/' in the reserve string */
5438 while (*rsvstr == '/') {
5439 rsvstr++;
5440 }
5441
5442 /* get rid of any extra '/' in the RE */
5443 while (*path_left == '/') {
5444 path_left++;
5445 }
5446
5447 if ((slash1 = strchr(path_left, '/')) != NULL) {
5448 *slash1 = '\0';
5449 }
5450 if ((slash2 = strchr(rsvstr, '/')) != NULL) {
5451 *slash2 = '\0';
5452 }
5453
5454 if ((slash1 != NULL) ^ (slash2 != NULL)) {
5455 ret = 0;
5456 vprint(RSRV_MID, "mismatch in # of path components\n");
5457 goto out;
5458 }
5459
5460 /*
5461 * Returns true if path_left matches the list entry.
5462 * If it is the last path component, pass subexp
5463 * so that it will return the corresponding ID in
5464 * numeral_id.
5465 */
5466 numeral_id = NULL;
5467 if (match_path_component(path_left, rsvstr, &numeral_id,
5468 slash1 ? 0 : rules[index].subexp)) {
5469
5470 /* We have a match. */
5471 if (slash1 == NULL) {
5472 /* Is last path component */
5473 vprint(RSRV_MID, "match and last component\n");
5474 create_reserved_numeral(setp, numeral_id);
5475 if (numeral_id != NULL) {
5476 free(numeral_id);
5477 }
5478 ret = 1;
5479 goto out;
5480 } else {
5481 /* Not last path component. Continue parsing */
5482 *slash1 = '/';
5483 *slash2 = '/';
5484 path_left = slash1 + 1;
5485 rsvstr = slash2 + 1;
5486 vprint(RSRV_MID,
5487 "match and NOT last component\n");
5488 continue;
5489 }
5490 } else {
5491 /* No match */
5492 ret = 0;
5493 vprint(RSRV_MID, "No match: rule RE = %s, "
5494 "rstring = %s\n", path_left, rsvstr);
5495 goto out;
5496 }
5497 }
5498
5499 out:
5500 if (slash1)
5501 *slash1 = '/';
5502 if (slash2)
5503 *slash2 = '/';
5504
5505 if (ret == 1) {
5506 vprint(RSRV_MID, "match: rule RE: %s, rstring: %s\n",
5507 path_left_save, rsvstr_save);
5508 } else {
5509 vprint(RSRV_MID, "NO match: rule RE: %s, rstring: %s\n",
5510 path_left_save, rsvstr_save);
5511 }
5512
5513 return (ret);
5514 }
5515
5516 /*
5517 * Search current_dir for all files which match the first path component
5518 * of path_left, which is an RE. If a match is found, but there are more
5519 * components of path_left, then recurse, otherwise, if we have reached
5520 * the last component of path_left, call create_cached_numerals for each
5521 * file. At some point, recurse_dev_re() should be rewritten so that this
5522 * function can be eliminated.
5523 */
5524 static void
enumerate_recurse(char * current_dir,char * path_left,numeral_set_t * setp,devfsadm_enumerate_t rules[],int index)5525 enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
5526 devfsadm_enumerate_t rules[], int index)
5527 {
5528 char *slash;
5529 char *new_path;
5530 char *numeral_id;
5531 finddevhdl_t fhandle;
5532 const char *fp;
5533
5534 if (finddev_readdir(current_dir, &fhandle) != 0) {
5535 return;
5536 }
5537
5538 /* get rid of any extra '/' */
5539 while (*path_left == '/') {
5540 path_left++;
5541 }
5542
5543 if ((slash = strchr(path_left, '/')) != NULL) {
5544 *slash = '\0';
5545 }
5546
5547 while ((fp = finddev_next(fhandle)) != NULL) {
5548
5549 /*
5550 * Returns true if path_left matches the list entry.
5551 * If it is the last path component, pass subexp
5552 * so that it will return the corresponding ID in
5553 * numeral_id.
5554 */
5555 numeral_id = NULL;
5556 if (match_path_component(path_left, (char *)fp, &numeral_id,
5557 slash ? 0 : rules[index].subexp)) {
5558
5559 new_path = s_malloc(strlen(current_dir) +
5560 strlen(fp) + 2);
5561
5562 (void) strcpy(new_path, current_dir);
5563 (void) strcat(new_path, "/");
5564 (void) strcat(new_path, fp);
5565
5566 if (slash != NULL) {
5567 enumerate_recurse(new_path, slash + 1,
5568 setp, rules, index);
5569 } else {
5570 create_cached_numeral(new_path, setp,
5571 numeral_id, rules, index);
5572 if (numeral_id != NULL) {
5573 free(numeral_id);
5574 }
5575 }
5576 free(new_path);
5577 }
5578 }
5579
5580 if (slash != NULL) {
5581 *slash = '/';
5582 }
5583 finddev_close(fhandle);
5584 }
5585
5586
5587 /*
5588 * Returns true if file matches file_re. If subexp is non-zero, it means
5589 * we are searching the last path component and need to return the
5590 * parenthesized subexpression subexp in id.
5591 *
5592 */
5593 static int
match_path_component(char * file_re,char * file,char ** id,int subexp)5594 match_path_component(char *file_re, char *file, char **id, int subexp)
5595 {
5596 regex_t re1;
5597 int match = 0;
5598 int nelements;
5599 regmatch_t *pmatch;
5600
5601 if (subexp != 0) {
5602 nelements = subexp + 1;
5603 pmatch =
5604 (regmatch_t *)s_malloc(sizeof (regmatch_t) * nelements);
5605 } else {
5606 pmatch = NULL;
5607 nelements = 0;
5608 }
5609
5610 if (regcomp(&re1, file_re, REG_EXTENDED) != 0) {
5611 if (pmatch != NULL) {
5612 free(pmatch);
5613 }
5614 return (0);
5615 }
5616
5617 if (regexec(&re1, file, nelements, pmatch, 0) == 0) {
5618 match = 1;
5619 }
5620
5621 if ((match != 0) && (subexp != 0)) {
5622 int size = pmatch[subexp].rm_eo - pmatch[subexp].rm_so;
5623 *id = s_malloc(size + 1);
5624 (void) strncpy(*id, &file[pmatch[subexp].rm_so], size);
5625 (*id)[size] = '\0';
5626 }
5627
5628 if (pmatch != NULL) {
5629 free(pmatch);
5630 }
5631 regfree(&re1);
5632 return (match);
5633 }
5634
5635 static void
create_reserved_numeral(numeral_set_t * setp,char * numeral_id)5636 create_reserved_numeral(numeral_set_t *setp, char *numeral_id)
5637 {
5638 numeral_t *np;
5639
5640 vprint(RSRV_MID, "Attempting to create reserved numeral: %s\n",
5641 numeral_id);
5642
5643 /*
5644 * We found a numeral_id from an entry in the enumerate_reserved file
5645 * which matched the re passed in from devfsadm_enumerate. We only
5646 * need to make sure ONE copy of numeral_id exists on the numeral list.
5647 * We only need to store /dev/dsk/cNtod0s0 and no other entries
5648 * hanging off of controller N.
5649 */
5650 for (np = setp->headnumeral; np != NULL; np = np->next) {
5651 if (strcmp(numeral_id, np->id) == 0) {
5652 vprint(RSRV_MID, "ID: %s, already reserved\n", np->id);
5653 assert(np->flags == NUMERAL_RESERVED);
5654 return;
5655 } else {
5656 assert(np->flags == 0 ||
5657 np->flags == NUMERAL_RESERVED);
5658 }
5659 }
5660
5661 /* NOT on list, so add it */
5662 np = s_malloc(sizeof (numeral_t));
5663 np->id = s_strdup(numeral_id);
5664 np->full_path = NULL;
5665 np->rule_index = 0;
5666 np->cmp_str = NULL;
5667 np->flags = NUMERAL_RESERVED;
5668 np->next = setp->headnumeral;
5669 setp->headnumeral = np;
5670
5671 vprint(RSRV_MID, "Reserved numeral ID: %s\n", np->id);
5672 }
5673
5674 /*
5675 * This function is called for every file which matched the leaf
5676 * component of the RE. If the "numeral_id" is not already on the
5677 * numeral set's numeral list, add it and its physical path.
5678 */
5679 static void
create_cached_numeral(char * path,numeral_set_t * setp,char * numeral_id,devfsadm_enumerate_t rules[],int index)5680 create_cached_numeral(char *path, numeral_set_t *setp, char *numeral_id,
5681 devfsadm_enumerate_t rules[], int index)
5682 {
5683 char linkbuf[PATH_MAX + 1];
5684 char lpath[PATH_MAX + 1];
5685 char *linkptr, *cmp_str;
5686 numeral_t *np;
5687 int linksize;
5688 struct stat sb;
5689 char *contents;
5690 const char *fcn = "create_cached_numeral";
5691
5692 assert(index >= 0 && index < setp->re_count);
5693 assert(strcmp(rules[index].re, setp->re[index]) == 0);
5694
5695 /*
5696 * We found a numeral_id from an entry in /dev which matched
5697 * the re passed in from devfsadm_enumerate. We only need to make sure
5698 * ONE copy of numeral_id exists on the numeral list. We only need
5699 * to store /dev/dsk/cNtod0s0 and no other entries hanging off
5700 * of controller N.
5701 */
5702 for (np = setp->headnumeral; np != NULL; np = np->next) {
5703 assert(np->flags == 0 || np->flags == NUMERAL_RESERVED);
5704 if (strcmp(numeral_id, np->id) == 0) {
5705 /*
5706 * Note that we can't assert that the flags field
5707 * of the numeral is 0, since both reserved and
5708 * unreserved links in /dev come here
5709 */
5710 if (np->flags == NUMERAL_RESERVED) {
5711 vprint(RSRV_MID, "ID derived from /dev link is"
5712 " reserved: %s\n", np->id);
5713 } else {
5714 vprint(RSRV_MID, "ID derived from /dev link is"
5715 " NOT reserved: %s\n", np->id);
5716 }
5717 return;
5718 }
5719 }
5720
5721 /* NOT on list, so add it */
5722
5723 (void) strcpy(lpath, path);
5724 /*
5725 * If path is a dir, it is changed to the first symbolic link it find
5726 * if it finds one.
5727 */
5728 if (get_stat_info(lpath, &sb) == DEVFSADM_FAILURE) {
5729 return;
5730 }
5731
5732 /* If we get here, we found a symlink */
5733 linksize = readlink(lpath, linkbuf, PATH_MAX);
5734
5735 if (linksize <= 0) {
5736 err_print(READLINK_FAILED, fcn, lpath, strerror(errno));
5737 return;
5738 }
5739
5740 linkbuf[linksize] = '\0';
5741
5742 /*
5743 * redirect alias path to current path
5744 * devi_root_node is protected by lock_dev()
5745 */
5746 contents = di_alias2curr(devi_root_node, linkbuf);
5747
5748 /*
5749 * the following just points linkptr to the root of the /devices
5750 * node if it is a minor node, otherwise, to the first char of
5751 * linkbuf if it is a link.
5752 */
5753 (void) is_minor_node(contents, &linkptr);
5754
5755 cmp_str = alloc_cmp_str(linkptr, &rules[index]);
5756 if (cmp_str == NULL) {
5757 free(contents);
5758 return;
5759 }
5760
5761 np = s_malloc(sizeof (numeral_t));
5762
5763 np->id = s_strdup(numeral_id);
5764 np->full_path = s_strdup(linkptr);
5765 np->rule_index = index;
5766 np->cmp_str = cmp_str;
5767 np->flags = 0;
5768
5769 np->next = setp->headnumeral;
5770 setp->headnumeral = np;
5771
5772 free(contents);
5773 }
5774
5775
5776 /*
5777 * This should be called either before or after granting access to a
5778 * command line version of devfsadm running, since it may have changed
5779 * the state of /dev. It forces future enumerate calls to re-build
5780 * cached information from /dev.
5781 */
5782 void
invalidate_enumerate_cache(void)5783 invalidate_enumerate_cache(void)
5784 {
5785 numeral_set_t *setp;
5786 numeral_set_t *savedsetp;
5787 numeral_t *savednumset;
5788 numeral_t *numset;
5789 int i;
5790
5791 for (setp = head_numeral_set; setp != NULL; ) {
5792 /*
5793 * check all regexp's passed in function against
5794 * those in cached set.
5795 */
5796
5797 savedsetp = setp;
5798 setp = setp->next;
5799
5800 for (i = 0; i < savedsetp->re_count; i++) {
5801 free(savedsetp->re[i]);
5802 }
5803 free(savedsetp->re);
5804
5805 for (numset = savedsetp->headnumeral; numset != NULL; ) {
5806 savednumset = numset;
5807 numset = numset->next;
5808 assert(savednumset->rule_index < savedsetp->re_count);
5809 free(savednumset->id);
5810 free(savednumset->full_path);
5811 free(savednumset->cmp_str);
5812 free(savednumset);
5813 }
5814 free(savedsetp);
5815 }
5816 head_numeral_set = NULL;
5817 }
5818
5819 /*
5820 * Copies over links from /dev to <root>/dev and device special files in
5821 * /devices to <root>/devices, preserving the existing file modes. If
5822 * the link or special file already exists on <root>, skip the copy. (it
5823 * would exist only if a package hard coded it there, so assume package
5824 * knows best?). Use /etc/name_to_major and <root>/etc/name_to_major to
5825 * make translations for major numbers on device special files. No need to
5826 * make a translation on minor_perm since if the file was created in the
5827 * miniroot then it would presumably have the same minor_perm entry in
5828 * <root>/etc/minor_perm. To be used only by install.
5829 */
5830 int
devfsadm_copy(void)5831 devfsadm_copy(void)
5832 {
5833 char filename[PATH_MAX + 1];
5834
5835 /* load the installed root's name_to_major for translations */
5836 (void) snprintf(filename, sizeof (filename), "%s%s", root_dir,
5837 NAME_TO_MAJOR);
5838 if (load_n2m_table(filename) == DEVFSADM_FAILURE) {
5839 return (DEVFSADM_FAILURE);
5840 }
5841
5842 /* Copy /dev to target disk. No need to copy /devices with devfs */
5843 (void) nftw(DEV, devfsadm_copy_file, 20, FTW_PHYS);
5844
5845 /* Let install handle copying over path_to_inst */
5846
5847 return (DEVFSADM_SUCCESS);
5848 }
5849
5850 /*
5851 * This function copies links, dirs, and device special files.
5852 * Note that it always returns DEVFSADM_SUCCESS, so that nftw doesn't
5853 * abort.
5854 */
5855 /*ARGSUSED*/
5856 static int
devfsadm_copy_file(const char * file,const struct stat * stat,int flags,struct FTW * ftw)5857 devfsadm_copy_file(const char *file, const struct stat *stat,
5858 int flags, struct FTW *ftw)
5859 {
5860 struct stat sp;
5861 dev_t newdev;
5862 char newfile[PATH_MAX + 1];
5863 char linkcontents[PATH_MAX + 1];
5864 int bytes;
5865 const char *fcn = "devfsadm_copy_file";
5866
5867 (void) strcpy(newfile, root_dir);
5868 (void) strcat(newfile, "/");
5869 (void) strcat(newfile, file);
5870
5871 if (lstat(newfile, &sp) == 0) {
5872 /* newfile already exists, so no need to continue */
5873 return (DEVFSADM_SUCCESS);
5874 }
5875
5876 if (((stat->st_mode & S_IFMT) == S_IFBLK) ||
5877 ((stat->st_mode & S_IFMT) == S_IFCHR)) {
5878 if (translate_major(stat->st_rdev, &newdev) ==
5879 DEVFSADM_FAILURE) {
5880 return (DEVFSADM_SUCCESS);
5881 }
5882 if (mknod(newfile, stat->st_mode, newdev) == -1) {
5883 err_print(MKNOD_FAILED, newfile, strerror(errno));
5884 return (DEVFSADM_SUCCESS);
5885 }
5886 } else if ((stat->st_mode & S_IFMT) == S_IFDIR) {
5887 if (mknod(newfile, stat->st_mode, 0) == -1) {
5888 err_print(MKNOD_FAILED, newfile, strerror(errno));
5889 return (DEVFSADM_SUCCESS);
5890 }
5891 } else if ((stat->st_mode & S_IFMT) == S_IFLNK) {
5892 /*
5893 * No need to redirect alias paths. We want a
5894 * true copy. The system on first boot after install
5895 * will redirect paths
5896 */
5897 if ((bytes = readlink(file, linkcontents, PATH_MAX)) == -1) {
5898 err_print(READLINK_FAILED, fcn, file, strerror(errno));
5899 return (DEVFSADM_SUCCESS);
5900 }
5901 linkcontents[bytes] = '\0';
5902 if (symlink(linkcontents, newfile) == -1) {
5903 err_print(SYMLINK_FAILED, newfile, newfile,
5904 strerror(errno));
5905 return (DEVFSADM_SUCCESS);
5906 }
5907 }
5908
5909 (void) lchown(newfile, stat->st_uid, stat->st_gid);
5910 return (DEVFSADM_SUCCESS);
5911 }
5912
5913 /*
5914 * Given a dev_t from the running kernel, return the new_dev_t
5915 * by translating to the major number found on the installed
5916 * target's root name_to_major file.
5917 */
5918 static int
translate_major(dev_t old_dev,dev_t * new_dev)5919 translate_major(dev_t old_dev, dev_t *new_dev)
5920 {
5921 major_t oldmajor;
5922 major_t newmajor;
5923 minor_t oldminor;
5924 minor_t newminor;
5925 char cdriver[FILENAME_MAX + 1];
5926 char driver[FILENAME_MAX + 1];
5927 char *fcn = "translate_major: ";
5928
5929 oldmajor = major(old_dev);
5930 if (modctl(MODGETNAME, driver, sizeof (driver), &oldmajor) != 0) {
5931 return (DEVFSADM_FAILURE);
5932 }
5933
5934 if (strcmp(driver, "clone") != 0) {
5935 /* non-clone case */
5936
5937 /* look up major number is target's name2major */
5938 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5939 return (DEVFSADM_FAILURE);
5940 }
5941
5942 *new_dev = makedev(newmajor, minor(old_dev));
5943 if (old_dev != *new_dev) {
5944 vprint(CHATTY_MID, "%sdriver: %s old: %lu,%lu "
5945 "new: %lu,%lu\n", fcn, driver, major(old_dev),
5946 minor(old_dev), major(*new_dev), minor(*new_dev));
5947 }
5948 return (DEVFSADM_SUCCESS);
5949 } else {
5950 /*
5951 * The clone is a special case. Look at its minor
5952 * number since it is the major number of the real driver.
5953 */
5954 if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5955 return (DEVFSADM_FAILURE);
5956 }
5957
5958 oldminor = minor(old_dev);
5959 if (modctl(MODGETNAME, cdriver, sizeof (cdriver),
5960 &oldminor) != 0) {
5961 err_print(MODGETNAME_FAILED, oldminor);
5962 return (DEVFSADM_FAILURE);
5963 }
5964
5965 if (get_major_no(cdriver, &newminor) == DEVFSADM_FAILURE) {
5966 return (DEVFSADM_FAILURE);
5967 }
5968
5969 *new_dev = makedev(newmajor, newminor);
5970 if (old_dev != *new_dev) {
5971 vprint(CHATTY_MID, "%sdriver: %s old: "
5972 "%lu,%lu new: %lu,%lu\n", fcn, driver,
5973 major(old_dev), minor(old_dev),
5974 major(*new_dev), minor(*new_dev));
5975 }
5976 return (DEVFSADM_SUCCESS);
5977 }
5978 }
5979
5980 /*
5981 *
5982 * Find the major number for driver, searching the n2m_list that was
5983 * built in load_n2m_table().
5984 */
5985 static int
get_major_no(char * driver,major_t * major)5986 get_major_no(char *driver, major_t *major)
5987 {
5988 n2m_t *ptr;
5989
5990 for (ptr = n2m_list; ptr != NULL; ptr = ptr->next) {
5991 if (strcmp(ptr->driver, driver) == 0) {
5992 *major = ptr->major;
5993 return (DEVFSADM_SUCCESS);
5994 }
5995 }
5996 err_print(FIND_MAJOR_FAILED, driver);
5997 return (DEVFSADM_FAILURE);
5998 }
5999
6000 /*
6001 * Loads a name_to_major table into memory. Used only for suninstall's
6002 * private -R option to devfsadm, to translate major numbers from the
6003 * running to the installed target disk.
6004 */
6005 static int
load_n2m_table(char * file)6006 load_n2m_table(char *file)
6007 {
6008 FILE *fp;
6009 char line[1024], *cp;
6010 char driver[PATH_MAX + 1];
6011 major_t major;
6012 n2m_t *ptr;
6013 int ln = 0;
6014
6015 if ((fp = fopen(file, "r")) == NULL) {
6016 err_print(FOPEN_FAILED, file, strerror(errno));
6017 return (DEVFSADM_FAILURE);
6018 }
6019
6020 while (fgets(line, sizeof (line), fp) != NULL) {
6021 ln++;
6022 /* cut off comments starting with '#' */
6023 if ((cp = strchr(line, '#')) != NULL)
6024 *cp = '\0';
6025 /* ignore comment or blank lines */
6026 if (is_blank(line))
6027 continue;
6028 /* sanity-check */
6029 if (sscanf(line, "%1024s%lu", driver, &major) != 2) {
6030 err_print(IGNORING_LINE_IN, ln, file);
6031 continue;
6032 }
6033 ptr = (n2m_t *)s_malloc(sizeof (n2m_t));
6034 ptr->major = major;
6035 ptr->driver = s_strdup(driver);
6036 ptr->next = n2m_list;
6037 n2m_list = ptr;
6038 }
6039 if (fclose(fp) == EOF) {
6040 err_print(FCLOSE_FAILED, file, strerror(errno));
6041 }
6042 return (DEVFSADM_SUCCESS);
6043 }
6044
6045 /*
6046 * Called at devfsadm startup to read the file /etc/dev/enumerate_reserved
6047 * Creates a linked list of devlinks from which reserved IDs can be derived
6048 */
6049 static void
read_enumerate_file(void)6050 read_enumerate_file(void)
6051 {
6052 FILE *fp;
6053 int linenum;
6054 char line[PATH_MAX+1];
6055 enumerate_file_t *entry;
6056 struct stat current_sb;
6057 static struct stat cached_sb;
6058 static int cached = FALSE;
6059
6060 assert(enumerate_file);
6061
6062 if (stat(enumerate_file, ¤t_sb) == -1) {
6063 vprint(RSRV_MID, "No reserved file: %s\n", enumerate_file);
6064 cached = FALSE;
6065 if (enumerate_reserved != NULL) {
6066 vprint(RSRV_MID, "invalidating %s cache\n",
6067 enumerate_file);
6068 }
6069 while (enumerate_reserved != NULL) {
6070 entry = enumerate_reserved;
6071 enumerate_reserved = entry->er_next;
6072 free(entry->er_file);
6073 free(entry->er_id);
6074 free(entry);
6075 }
6076 return;
6077 }
6078
6079 /* if already cached, check to see if it is still valid */
6080 if (cached == TRUE) {
6081
6082 if (current_sb.st_mtime == cached_sb.st_mtime) {
6083 vprint(RSRV_MID, "%s cache valid\n", enumerate_file);
6084 vprint(FILES_MID, "%s cache valid\n", enumerate_file);
6085 return;
6086 }
6087
6088 vprint(RSRV_MID, "invalidating %s cache\n", enumerate_file);
6089 vprint(FILES_MID, "invalidating %s cache\n", enumerate_file);
6090
6091 while (enumerate_reserved != NULL) {
6092 entry = enumerate_reserved;
6093 enumerate_reserved = entry->er_next;
6094 free(entry->er_file);
6095 free(entry->er_id);
6096 free(entry);
6097 }
6098 vprint(RSRV_MID, "Recaching file: %s\n", enumerate_file);
6099 } else {
6100 vprint(RSRV_MID, "Caching file (first time): %s\n",
6101 enumerate_file);
6102 cached = TRUE;
6103 }
6104
6105 (void) stat(enumerate_file, &cached_sb);
6106
6107 if ((fp = fopen(enumerate_file, "r")) == NULL) {
6108 err_print(FOPEN_FAILED, enumerate_file, strerror(errno));
6109 return;
6110 }
6111
6112 vprint(RSRV_MID, "Reading reserve file: %s\n", enumerate_file);
6113 linenum = 0;
6114 while (fgets(line, sizeof (line), fp) != NULL) {
6115 char *cp, *ncp;
6116
6117 linenum++;
6118
6119 /* remove newline */
6120 cp = strchr(line, '\n');
6121 if (cp)
6122 *cp = '\0';
6123
6124 vprint(RSRV_MID, "Reserve file: line %d: %s\n", linenum, line);
6125
6126 /* skip over space and tab */
6127 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
6128 ;
6129
6130 if (*cp == '\0' || *cp == '#') {
6131 vprint(RSRV_MID, "Skipping line: '%s'\n", line);
6132 continue; /* blank line or comment line */
6133 }
6134
6135 ncp = cp;
6136
6137 /* delete trailing blanks */
6138 for (; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++)
6139 ;
6140 *cp = '\0';
6141
6142 entry = s_zalloc(sizeof (enumerate_file_t));
6143 entry->er_file = s_strdup(ncp);
6144 entry->er_id = NULL;
6145 entry->er_next = enumerate_reserved;
6146 enumerate_reserved = entry;
6147 }
6148
6149 if (fclose(fp) == EOF) {
6150 err_print(FCLOSE_FAILED, enumerate_file, strerror(errno));
6151 }
6152 }
6153
6154 /*
6155 * Called at devfsadm startup to read in the devlink.tab file. Creates
6156 * a linked list of devlinktab_list structures which will be
6157 * searched for every minor node.
6158 */
6159 static void
read_devlinktab_file(void)6160 read_devlinktab_file(void)
6161 {
6162 devlinktab_list_t *headp = NULL;
6163 devlinktab_list_t *entryp;
6164 devlinktab_list_t **previous;
6165 devlinktab_list_t *save;
6166 char line[MAX_DEVLINK_LINE], *cp;
6167 char *selector;
6168 char *p_link;
6169 char *s_link;
6170 FILE *fp;
6171 int i;
6172 static struct stat cached_sb;
6173 struct stat current_sb;
6174 static int cached = FALSE;
6175
6176 if (devlinktab_file == NULL) {
6177 return;
6178 }
6179
6180 (void) stat(devlinktab_file, ¤t_sb);
6181
6182 /* if already cached, check to see if it is still valid */
6183 if (cached == TRUE) {
6184
6185 if (current_sb.st_mtime == cached_sb.st_mtime) {
6186 vprint(FILES_MID, "%s cache valid\n", devlinktab_file);
6187 return;
6188 }
6189
6190 vprint(FILES_MID, "invalidating %s cache\n", devlinktab_file);
6191
6192 while (devlinktab_list != NULL) {
6193 free_link_list(devlinktab_list->p_link);
6194 free_link_list(devlinktab_list->s_link);
6195 free_selector_list(devlinktab_list->selector);
6196 free(devlinktab_list->selector_pattern);
6197 free(devlinktab_list->p_link_pattern);
6198 if (devlinktab_list->s_link_pattern != NULL) {
6199 free(devlinktab_list->s_link_pattern);
6200 }
6201 save = devlinktab_list;
6202 devlinktab_list = devlinktab_list->next;
6203 free(save);
6204 }
6205 } else {
6206 cached = TRUE;
6207 }
6208
6209 (void) stat(devlinktab_file, &cached_sb);
6210
6211 if ((fp = fopen(devlinktab_file, "r")) == NULL) {
6212 err_print(FOPEN_FAILED, devlinktab_file, strerror(errno));
6213 return;
6214 }
6215
6216 previous = &headp;
6217
6218 while (fgets(line, sizeof (line), fp) != NULL) {
6219 devlinktab_line++;
6220 i = strlen(line);
6221 if (line[i-1] == NEWLINE) {
6222 line[i-1] = '\0';
6223 } else if (i == sizeof (line) - 1) {
6224 err_print(LINE_TOO_LONG, devlinktab_line,
6225 devlinktab_file, sizeof (line) - 1);
6226 while (((i = getc(fp)) != '\n') && (i != EOF))
6227 ;
6228 continue;
6229 }
6230
6231 /* cut off comments starting with '#' */
6232 if ((cp = strchr(line, '#')) != NULL)
6233 *cp = '\0';
6234 /* ignore comment or blank lines */
6235 if (is_blank(line))
6236 continue;
6237
6238 vprint(DEVLINK_MID, "table: %s line %d: '%s'\n",
6239 devlinktab_file, devlinktab_line, line);
6240
6241 /* break each entry into fields. s_link may be NULL */
6242 if (split_devlinktab_entry(line, &selector, &p_link,
6243 &s_link) == DEVFSADM_FAILURE) {
6244 vprint(DEVLINK_MID, "split_entry returns failure\n");
6245 continue;
6246 } else {
6247 vprint(DEVLINK_MID, "split_entry selector='%s' "
6248 "p_link='%s' s_link='%s'\n\n", selector,
6249 p_link, (s_link == NULL) ? "" : s_link);
6250 }
6251
6252 entryp =
6253 (devlinktab_list_t *)s_malloc(sizeof (devlinktab_list_t));
6254
6255 entryp->line_number = devlinktab_line;
6256
6257 if ((entryp->selector = create_selector_list(selector))
6258 == NULL) {
6259 free(entryp);
6260 continue;
6261 }
6262 entryp->selector_pattern = s_strdup(selector);
6263
6264 if ((entryp->p_link = create_link_list(p_link)) == NULL) {
6265 free_selector_list(entryp->selector);
6266 free(entryp->selector_pattern);
6267 free(entryp);
6268 continue;
6269 }
6270
6271 entryp->p_link_pattern = s_strdup(p_link);
6272
6273 if (s_link != NULL) {
6274 if ((entryp->s_link =
6275 create_link_list(s_link)) == NULL) {
6276 free_selector_list(entryp->selector);
6277 free_link_list(entryp->p_link);
6278 free(entryp->selector_pattern);
6279 free(entryp->p_link_pattern);
6280 free(entryp);
6281 continue;
6282 }
6283 entryp->s_link_pattern = s_strdup(s_link);
6284 } else {
6285 entryp->s_link = NULL;
6286 entryp->s_link_pattern = NULL;
6287
6288 }
6289
6290 /* append to end of list */
6291
6292 entryp->next = NULL;
6293 *previous = entryp;
6294 previous = &(entryp->next);
6295 }
6296 if (fclose(fp) == EOF) {
6297 err_print(FCLOSE_FAILED, devlinktab_file, strerror(errno));
6298 }
6299 devlinktab_list = headp;
6300 }
6301
6302 /*
6303 *
6304 * For a single line entry in devlink.tab, split the line into fields
6305 * selector, p_link, and an optionally s_link. If s_link field is not
6306 * present, then return NULL in s_link (not NULL string).
6307 */
6308 static int
split_devlinktab_entry(char * entry,char ** selector,char ** p_link,char ** s_link)6309 split_devlinktab_entry(char *entry, char **selector, char **p_link,
6310 char **s_link)
6311 {
6312 char *tab;
6313
6314 *selector = entry;
6315
6316 if ((tab = strchr(entry, TAB)) != NULL) {
6317 *tab = '\0';
6318 *p_link = ++tab;
6319 } else {
6320 err_print(MISSING_TAB, devlinktab_line, devlinktab_file);
6321 return (DEVFSADM_FAILURE);
6322 }
6323
6324 if (**p_link == '\0') {
6325 err_print(MISSING_DEVNAME, devlinktab_line, devlinktab_file);
6326 return (DEVFSADM_FAILURE);
6327 }
6328
6329 if ((tab = strchr(*p_link, TAB)) != NULL) {
6330 *tab = '\0';
6331 *s_link = ++tab;
6332 if (strchr(*s_link, TAB) != NULL) {
6333 err_print(TOO_MANY_FIELDS, devlinktab_line,
6334 devlinktab_file);
6335 return (DEVFSADM_FAILURE);
6336 }
6337 } else {
6338 *s_link = NULL;
6339 }
6340
6341 return (DEVFSADM_SUCCESS);
6342 }
6343
6344 /*
6345 * For a given devfs_spec field, for each element in the field, add it to
6346 * a linked list of devfs_spec structures. Return the linked list in
6347 * devfs_spec_list.
6348 */
6349 static selector_list_t *
create_selector_list(char * selector)6350 create_selector_list(char *selector)
6351 {
6352 char *key;
6353 char *val;
6354 int error = FALSE;
6355 selector_list_t *head_selector_list = NULL;
6356 selector_list_t *selector_list;
6357
6358 /* parse_devfs_spec splits the next field into keyword & value */
6359 while ((*selector != '\0') && (error == FALSE)) {
6360 if (parse_selector(&selector, &key, &val) == DEVFSADM_FAILURE) {
6361 error = TRUE;
6362 break;
6363 } else {
6364 selector_list = (selector_list_t *)
6365 s_malloc(sizeof (selector_list_t));
6366 if (strcmp(NAME_S, key) == 0) {
6367 selector_list->key = NAME;
6368 } else if (strcmp(TYPE_S, key) == 0) {
6369 selector_list->key = TYPE;
6370 } else if (strncmp(ADDR_S, key, ADDR_S_LEN) == 0) {
6371 selector_list->key = ADDR;
6372 if (key[ADDR_S_LEN] == '\0') {
6373 selector_list->arg = 0;
6374 } else if (isdigit(key[ADDR_S_LEN]) != FALSE) {
6375 selector_list->arg =
6376 atoi(&key[ADDR_S_LEN]);
6377 } else {
6378 error = TRUE;
6379 free(selector_list);
6380 err_print(BADKEYWORD, key,
6381 devlinktab_line, devlinktab_file);
6382 break;
6383 }
6384 } else if (strncmp(MINOR_S, key, MINOR_S_LEN) == 0) {
6385 selector_list->key = MINOR;
6386 if (key[MINOR_S_LEN] == '\0') {
6387 selector_list->arg = 0;
6388 } else if (isdigit(key[MINOR_S_LEN]) != FALSE) {
6389 selector_list->arg =
6390 atoi(&key[MINOR_S_LEN]);
6391 } else {
6392 error = TRUE;
6393 free(selector_list);
6394 err_print(BADKEYWORD, key,
6395 devlinktab_line, devlinktab_file);
6396 break;
6397 }
6398 vprint(DEVLINK_MID, "MINOR = %s\n", val);
6399 } else {
6400 err_print(UNRECOGNIZED_KEY, key,
6401 devlinktab_line, devlinktab_file);
6402 error = TRUE;
6403 free(selector_list);
6404 break;
6405 }
6406 selector_list->val = s_strdup(val);
6407 selector_list->next = head_selector_list;
6408 head_selector_list = selector_list;
6409 vprint(DEVLINK_MID, "key='%s' val='%s' arg=%d\n",
6410 key, val, selector_list->arg);
6411 }
6412 }
6413
6414 if ((error == FALSE) && (head_selector_list != NULL)) {
6415 return (head_selector_list);
6416 } else {
6417 /* parse failed. Free any allocated structs */
6418 free_selector_list(head_selector_list);
6419 return (NULL);
6420 }
6421 }
6422
6423 /*
6424 * Takes a semicolon separated list of selector elements and breaks up
6425 * into a keyword-value pair. semicolon and equal characters are
6426 * replaced with NULL's. On success, selector is updated to point to the
6427 * terminating NULL character terminating the keyword-value pair, and the
6428 * function returns DEVFSADM_SUCCESS. If there is a syntax error,
6429 * devfs_spec is not modified and function returns DEVFSADM_FAILURE.
6430 */
6431 static int
parse_selector(char ** selector,char ** key,char ** val)6432 parse_selector(char **selector, char **key, char **val)
6433 {
6434 char *equal;
6435 char *semi_colon;
6436
6437 *key = *selector;
6438
6439 if ((equal = strchr(*key, '=')) != NULL) {
6440 *equal = '\0';
6441 } else {
6442 err_print(MISSING_EQUAL, devlinktab_line, devlinktab_file);
6443 return (DEVFSADM_FAILURE);
6444 }
6445
6446 *val = ++equal;
6447 if ((semi_colon = strchr(equal, ';')) != NULL) {
6448 *semi_colon = '\0';
6449 *selector = semi_colon + 1;
6450 } else {
6451 *selector = equal + strlen(equal);
6452 }
6453 return (DEVFSADM_SUCCESS);
6454 }
6455
6456 /*
6457 * link is either the second or third field of devlink.tab. Parse link
6458 * into a linked list of devlink structures and return ptr to list. Each
6459 * list element is either a constant string, or one of the following
6460 * escape sequences: \M, \A, \N, or \D. The first three escape sequences
6461 * take a numerical argument.
6462 */
6463 static link_list_t *
create_link_list(char * link)6464 create_link_list(char *link)
6465 {
6466 int x = 0;
6467 int error = FALSE;
6468 int counter_found = FALSE;
6469 link_list_t *head = NULL;
6470 link_list_t **ptr;
6471 link_list_t *link_list;
6472 char constant[MAX_DEVLINK_LINE];
6473 char *error_str;
6474
6475 if (link == NULL) {
6476 return (NULL);
6477 }
6478
6479 while ((*link != '\0') && (error == FALSE)) {
6480 link_list = (link_list_t *)s_malloc(sizeof (link_list_t));
6481 link_list->next = NULL;
6482
6483 while ((*link != '\0') && (*link != '\\')) {
6484 /* a non-escaped string */
6485 constant[x++] = *(link++);
6486 }
6487 if (x != 0) {
6488 constant[x] = '\0';
6489 link_list->type = CONSTANT;
6490 link_list->constant = s_strdup(constant);
6491 x = 0;
6492 vprint(DEVLINK_MID, "CONSTANT FOUND %s\n", constant);
6493 } else {
6494 switch (*(++link)) {
6495 case 'M':
6496 link_list->type = MINOR;
6497 break;
6498 case 'A':
6499 link_list->type = ADDR;
6500 break;
6501 case 'N':
6502 if (counter_found == TRUE) {
6503 error = TRUE;
6504 error_str =
6505 "multiple counters not permitted";
6506 free(link_list);
6507 link_list = NULL;
6508 } else {
6509 counter_found = TRUE;
6510 link_list->type = COUNTER;
6511 }
6512 break;
6513 case 'D':
6514 link_list->type = NAME;
6515 break;
6516 default:
6517 error = TRUE;
6518 free(link_list);
6519 link_list = NULL;
6520 error_str = "unrecognized escape sequence";
6521 break;
6522 }
6523 if (*(link++) != 'D') {
6524 if (isdigit(*link) == FALSE) {
6525 error_str = "escape sequence must be "
6526 "followed by a digit\n";
6527 error = TRUE;
6528 free(link_list);
6529 link_list = NULL;
6530 } else {
6531 link_list->arg =
6532 (int)strtoul(link, &link, 10);
6533 vprint(DEVLINK_MID, "link_list->arg = "
6534 "%d\n", link_list->arg);
6535 }
6536 }
6537 }
6538 /* append link_list struct to end of list */
6539 if (error == FALSE) {
6540 for (ptr = &head; *ptr != NULL; ptr = &((*ptr)->next))
6541 ;
6542 *ptr = link_list;
6543 }
6544 }
6545
6546 if (error == FALSE) {
6547 return (head);
6548 } else {
6549 err_print(CONFIG_INCORRECT, devlinktab_line, devlinktab_file,
6550 error_str);
6551 free_link_list(head);
6552 return (NULL);
6553 }
6554 }
6555
6556 /*
6557 * Called for each minor node devfsadm processes; for each minor node,
6558 * look for matches in the devlinktab_list list which was created on
6559 * startup read_devlinktab_file(). If there is a match, call build_links()
6560 * to build a logical devlink and a possible extra devlink.
6561 */
6562 static int
process_devlink_compat(di_minor_t minor,di_node_t node)6563 process_devlink_compat(di_minor_t minor, di_node_t node)
6564 {
6565 int link_built = FALSE;
6566 devlinktab_list_t *entry;
6567 char *nodetype;
6568 char *dev_path;
6569
6570 if (devlinks_debug == TRUE) {
6571 nodetype = di_minor_nodetype(minor);
6572 assert(nodetype != NULL);
6573 if ((dev_path = di_devfs_path(node)) != NULL) {
6574 vprint(INFO_MID, "'%s' entry: %s:%s\n",
6575 nodetype, dev_path,
6576 di_minor_name(minor) ? di_minor_name(minor) : "");
6577 di_devfs_path_free(dev_path);
6578 }
6579
6580 }
6581
6582
6583 /* don't process devlink.tab if devfsadm invoked with -c <class> */
6584 if (num_classes > 0) {
6585 return (FALSE);
6586 }
6587
6588 for (entry = devlinktab_list; entry != NULL; entry = entry->next) {
6589 if (devlink_matches(entry, minor, node) == DEVFSADM_SUCCESS) {
6590 link_built = TRUE;
6591 (void) build_links(entry, minor, node);
6592 }
6593 }
6594 return (link_built);
6595 }
6596
6597 /*
6598 * For a given devlink.tab devlinktab_list entry, see if the selector
6599 * field matches this minor node. If it does, return DEVFSADM_SUCCESS,
6600 * otherwise DEVFSADM_FAILURE.
6601 */
6602 static int
devlink_matches(devlinktab_list_t * entry,di_minor_t minor,di_node_t node)6603 devlink_matches(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6604 {
6605 selector_list_t *selector = entry->selector;
6606 char *addr;
6607 char *minor_name;
6608 char *node_type;
6609
6610 for (; selector != NULL; selector = selector->next) {
6611 switch (selector->key) {
6612 case NAME:
6613 if (strcmp(di_node_name(node), selector->val) != 0) {
6614 return (DEVFSADM_FAILURE);
6615 }
6616 break;
6617 case TYPE:
6618 node_type = di_minor_nodetype(minor);
6619 assert(node_type != NULL);
6620 if (strcmp(node_type, selector->val) != 0) {
6621 return (DEVFSADM_FAILURE);
6622 }
6623 break;
6624 case ADDR:
6625 if ((addr = di_bus_addr(node)) == NULL) {
6626 return (DEVFSADM_FAILURE);
6627 }
6628 if (selector->arg == 0) {
6629 if (strcmp(addr, selector->val) != 0) {
6630 return (DEVFSADM_FAILURE);
6631 }
6632 } else {
6633 if (compare_field(addr, selector->val,
6634 selector->arg) == DEVFSADM_FAILURE) {
6635 return (DEVFSADM_FAILURE);
6636 }
6637 }
6638 break;
6639 case MINOR:
6640 if ((minor_name = di_minor_name(minor)) == NULL) {
6641 return (DEVFSADM_FAILURE);
6642 }
6643 if (selector->arg == 0) {
6644 if (strcmp(minor_name, selector->val) != 0) {
6645 return (DEVFSADM_FAILURE);
6646 }
6647 } else {
6648 if (compare_field(minor_name, selector->val,
6649 selector->arg) == DEVFSADM_FAILURE) {
6650 return (DEVFSADM_FAILURE);
6651 }
6652 }
6653 break;
6654 default:
6655 return (DEVFSADM_FAILURE);
6656 }
6657 }
6658
6659 return (DEVFSADM_SUCCESS);
6660 }
6661
6662 /*
6663 * For the given minor node and devlinktab_list entry from devlink.tab,
6664 * build a logical dev link and a possible extra devlink.
6665 * Return DEVFSADM_SUCCESS if link is created, otherwise DEVFSADM_FAILURE.
6666 */
6667 static int
build_links(devlinktab_list_t * entry,di_minor_t minor,di_node_t node)6668 build_links(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6669 {
6670 char secondary_link[PATH_MAX + 1];
6671 char primary_link[PATH_MAX + 1];
6672 char contents[PATH_MAX + 1];
6673 char *dev_path;
6674
6675 if ((dev_path = di_devfs_path(node)) == NULL) {
6676 err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
6677 devfsadm_exit(1);
6678 /*NOTREACHED*/
6679 }
6680 (void) strcpy(contents, dev_path);
6681 di_devfs_path_free(dev_path);
6682
6683 (void) strcat(contents, ":");
6684 (void) strcat(contents, di_minor_name(minor));
6685
6686 if (construct_devlink(primary_link, entry->p_link, contents,
6687 minor, node, entry->p_link_pattern) == DEVFSADM_FAILURE) {
6688 return (DEVFSADM_FAILURE);
6689 }
6690 (void) devfsadm_mklink(primary_link, node, minor, 0);
6691
6692 if (entry->s_link == NULL) {
6693 return (DEVFSADM_SUCCESS);
6694 }
6695
6696 if (construct_devlink(secondary_link, entry->s_link, primary_link,
6697 minor, node, entry->s_link_pattern) == DEVFSADM_FAILURE) {
6698 return (DEVFSADM_FAILURE);
6699 }
6700
6701 (void) devfsadm_secondary_link(secondary_link, primary_link, 0);
6702
6703 return (DEVFSADM_SUCCESS);
6704 }
6705
6706 /*
6707 * The counter rule for devlink.tab entries is implemented via
6708 * devfsadm_enumerate_int_start(). One of the arguments to this function
6709 * is a path, where each path component is treated as a regular expression.
6710 * For devlink.tab entries, this path regular expression is derived from
6711 * the devlink spec. get_anchored_re() accepts path regular expressions derived
6712 * from devlink.tab entries and inserts the anchors '^' and '$' at the beginning
6713 * and end respectively of each path component. This is done to prevent
6714 * false matches. For example, without anchors, "a/([0-9]+)" will match "ab/c9"
6715 * and incorrect links will be generated.
6716 */
6717 static int
get_anchored_re(char * link,char * anchored_re,char * pattern)6718 get_anchored_re(char *link, char *anchored_re, char *pattern)
6719 {
6720 if (*link == '/' || *link == '\0') {
6721 err_print(INVALID_DEVLINK_SPEC, pattern);
6722 return (DEVFSADM_FAILURE);
6723 }
6724
6725 *anchored_re++ = '^';
6726 for (; *link != '\0'; ) {
6727 if (*link == '/') {
6728 while (*link == '/')
6729 link++;
6730 *anchored_re++ = '$';
6731 *anchored_re++ = '/';
6732 if (*link != '\0') {
6733 *anchored_re++ = '^';
6734 }
6735 } else {
6736 *anchored_re++ = *link++;
6737 if (*link == '\0') {
6738 *anchored_re++ = '$';
6739 }
6740 }
6741 }
6742 *anchored_re = '\0';
6743
6744 return (DEVFSADM_SUCCESS);
6745 }
6746
6747 static int
construct_devlink(char * link,link_list_t * link_build,char * contents,di_minor_t minor,di_node_t node,char * pattern)6748 construct_devlink(char *link, link_list_t *link_build, char *contents,
6749 di_minor_t minor, di_node_t node, char *pattern)
6750 {
6751 int counter_offset = -1;
6752 devfsadm_enumerate_t rules[1] = {NULL};
6753 char templink[PATH_MAX + 1];
6754 char *buff;
6755 char start[10];
6756 char *node_path;
6757 char anchored_re[PATH_MAX + 1];
6758
6759 link[0] = '\0';
6760
6761 for (; link_build != NULL; link_build = link_build->next) {
6762 switch (link_build->type) {
6763 case NAME:
6764 (void) strcat(link, di_node_name(node));
6765 break;
6766 case CONSTANT:
6767 (void) strcat(link, link_build->constant);
6768 break;
6769 case ADDR:
6770 if (component_cat(link, di_bus_addr(node),
6771 link_build->arg) == DEVFSADM_FAILURE) {
6772 node_path = di_devfs_path(node);
6773 err_print(CANNOT_BE_USED, pattern, node_path,
6774 di_minor_name(minor));
6775 di_devfs_path_free(node_path);
6776 return (DEVFSADM_FAILURE);
6777 }
6778 break;
6779 case MINOR:
6780 if (component_cat(link, di_minor_name(minor),
6781 link_build->arg) == DEVFSADM_FAILURE) {
6782 node_path = di_devfs_path(node);
6783 err_print(CANNOT_BE_USED, pattern, node_path,
6784 di_minor_name(minor));
6785 di_devfs_path_free(node_path);
6786 return (DEVFSADM_FAILURE);
6787 }
6788 break;
6789 case COUNTER:
6790 counter_offset = strlen(link);
6791 (void) strcat(link, "([0-9]+)");
6792 (void) sprintf(start, "%d", link_build->arg);
6793 break;
6794 default:
6795 return (DEVFSADM_FAILURE);
6796 }
6797 }
6798
6799 if (counter_offset != -1) {
6800 /*
6801 * copy anything appended after "([0-9]+)" into
6802 * templink
6803 */
6804
6805 (void) strcpy(templink,
6806 &link[counter_offset + strlen("([0-9]+)")]);
6807 if (get_anchored_re(link, anchored_re, pattern)
6808 != DEVFSADM_SUCCESS) {
6809 return (DEVFSADM_FAILURE);
6810 }
6811 rules[0].re = anchored_re;
6812 rules[0].subexp = 1;
6813 rules[0].flags = MATCH_ALL;
6814 if (devfsadm_enumerate_int_start(contents, 0, &buff,
6815 rules, 1, start) == DEVFSADM_FAILURE) {
6816 return (DEVFSADM_FAILURE);
6817 }
6818 (void) strcpy(&link[counter_offset], buff);
6819 free(buff);
6820 (void) strcat(link, templink);
6821 vprint(DEVLINK_MID, "COUNTER is %s\n", link);
6822 }
6823 return (DEVFSADM_SUCCESS);
6824 }
6825
6826 /*
6827 * Compares "field" number of the comma separated list "full_name" with
6828 * field_item. Returns DEVFSADM_SUCCESS for match,
6829 * DEVFSADM_FAILURE for no match.
6830 */
6831 static int
compare_field(char * full_name,char * field_item,int field)6832 compare_field(char *full_name, char *field_item, int field)
6833 {
6834 --field;
6835 while ((*full_name != '\0') && (field != 0)) {
6836 if (*(full_name++) == ',') {
6837 field--;
6838 }
6839 }
6840
6841 if (field != 0) {
6842 return (DEVFSADM_FAILURE);
6843 }
6844
6845 while ((*full_name != '\0') && (*field_item != '\0') &&
6846 (*full_name != ',')) {
6847 if (*(full_name++) != *(field_item++)) {
6848 return (DEVFSADM_FAILURE);
6849 }
6850 }
6851
6852 if (*field_item != '\0') {
6853 return (DEVFSADM_FAILURE);
6854 }
6855
6856 if ((*full_name == '\0') || (*full_name == ','))
6857 return (DEVFSADM_SUCCESS);
6858
6859 return (DEVFSADM_FAILURE);
6860 }
6861
6862 /*
6863 * strcat() field # "field" of comma separated list "name" to "link".
6864 * Field 0 is the entire name.
6865 * Return DEVFSADM_SUCCESS or DEVFSADM_FAILURE.
6866 */
6867 static int
component_cat(char * link,char * name,int field)6868 component_cat(char *link, char *name, int field)
6869 {
6870
6871 if (name == NULL) {
6872 return (DEVFSADM_FAILURE);
6873 }
6874
6875 if (field == 0) {
6876 (void) strcat(link, name);
6877 return (DEVFSADM_SUCCESS);
6878 }
6879
6880 while (*link != '\0') {
6881 link++;
6882 }
6883
6884 --field;
6885 while ((*name != '\0') && (field != 0)) {
6886 if (*(name++) == ',') {
6887 --field;
6888 }
6889 }
6890
6891 if (field != 0) {
6892 return (DEVFSADM_FAILURE);
6893 }
6894
6895 while ((*name != '\0') && (*name != ',')) {
6896 *(link++) = *(name++);
6897 }
6898
6899 *link = '\0';
6900 return (DEVFSADM_SUCCESS);
6901 }
6902
6903 static void
free_selector_list(selector_list_t * head)6904 free_selector_list(selector_list_t *head)
6905 {
6906 selector_list_t *temp;
6907
6908 while (head != NULL) {
6909 temp = head;
6910 head = head->next;
6911 free(temp->val);
6912 free(temp);
6913 }
6914 }
6915
6916 static void
free_link_list(link_list_t * head)6917 free_link_list(link_list_t *head)
6918 {
6919 link_list_t *temp;
6920
6921 while (head != NULL) {
6922 temp = head;
6923 head = head->next;
6924 if (temp->type == CONSTANT) {
6925 free(temp->constant);
6926 }
6927 free(temp);
6928 }
6929 }
6930
6931 /*
6932 * Prints only if level matches one of the debug levels
6933 * given on command line. INFO_MID is always printed.
6934 *
6935 * See devfsadm.h for a listing of globally defined levels and
6936 * meanings. Modules should prefix the level with their
6937 * module name to prevent collisions.
6938 */
6939 /*PRINTFLIKE2*/
6940 void
devfsadm_print(char * msgid,char * message,...)6941 devfsadm_print(char *msgid, char *message, ...)
6942 {
6943 va_list ap;
6944 static int newline = TRUE;
6945 int x;
6946
6947 if (msgid != NULL) {
6948 for (x = 0; x < num_verbose; x++) {
6949 if (strcmp(verbose[x], msgid) == 0) {
6950 break;
6951 }
6952 if (strcmp(verbose[x], ALL_MID) == 0) {
6953 break;
6954 }
6955 }
6956 if (x == num_verbose) {
6957 return;
6958 }
6959 }
6960
6961 va_start(ap, message);
6962
6963 if (msgid == NULL) {
6964 if (logflag == TRUE) {
6965 (void) vsyslog(LOG_NOTICE, message, ap);
6966 } else {
6967 (void) vfprintf(stdout, message, ap);
6968 }
6969
6970 } else {
6971 if (logflag == TRUE) {
6972 (void) syslog(LOG_DEBUG, "%s[%ld]: %s: ",
6973 prog, getpid(), msgid);
6974 (void) vsyslog(LOG_DEBUG, message, ap);
6975 } else {
6976 if (newline == TRUE) {
6977 (void) fprintf(stdout, "%s[%ld]: %s: ",
6978 prog, getpid(), msgid);
6979 }
6980 (void) vfprintf(stdout, message, ap);
6981 }
6982 }
6983
6984 if (message[strlen(message) - 1] == '\n') {
6985 newline = TRUE;
6986 } else {
6987 newline = FALSE;
6988 }
6989 va_end(ap);
6990 }
6991
6992 /*
6993 * print error messages to the terminal or to syslog
6994 */
6995 /*PRINTFLIKE1*/
6996 void
devfsadm_errprint(char * message,...)6997 devfsadm_errprint(char *message, ...)
6998 {
6999 va_list ap;
7000
7001 va_start(ap, message);
7002
7003 if (logflag == TRUE) {
7004 (void) vsyslog(LOG_ERR, message, ap);
7005 } else {
7006 (void) fprintf(stderr, "%s: ", prog);
7007 (void) vfprintf(stderr, message, ap);
7008 }
7009 va_end(ap);
7010 }
7011
7012 /*
7013 * return noupdate state (-s)
7014 */
7015 int
devfsadm_noupdate(void)7016 devfsadm_noupdate(void)
7017 {
7018 return (file_mods == TRUE ? DEVFSADM_TRUE : DEVFSADM_FALSE);
7019 }
7020
7021 /*
7022 * return current root update path (-r)
7023 */
7024 const char *
devfsadm_root_path(void)7025 devfsadm_root_path(void)
7026 {
7027 if (root_dir[0] == '\0') {
7028 return ("/");
7029 } else {
7030 return ((const char *)root_dir);
7031 }
7032 }
7033
7034 void
devfsadm_free_dev_names(char ** dev_names,int len)7035 devfsadm_free_dev_names(char **dev_names, int len)
7036 {
7037 int i;
7038
7039 for (i = 0; i < len; i++)
7040 free(dev_names[i]);
7041 free(dev_names);
7042 }
7043
7044 /*
7045 * Return all devlinks corresponding to phys_path as an array of strings.
7046 * The number of entries in the array is returned through lenp.
7047 * devfsadm_free_dev_names() is used to free the returned array.
7048 * NULL is returned on failure or when there are no matching devlinks.
7049 *
7050 * re is an extended regular expression in regex(7) format used to further
7051 * match devlinks pointing to phys_path; it may be NULL to match all
7052 */
7053 char **
devfsadm_lookup_dev_names(char * phys_path,char * re,int * lenp)7054 devfsadm_lookup_dev_names(char *phys_path, char *re, int *lenp)
7055 {
7056 struct devlink_cb_arg cb_arg;
7057 char **dev_names = NULL;
7058 int i;
7059
7060 *lenp = 0;
7061 cb_arg.count = 0;
7062 cb_arg.rv = 0;
7063 (void) di_devlink_cache_walk(devlink_cache, re, phys_path,
7064 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
7065
7066 if (cb_arg.rv == -1 || cb_arg.count <= 0)
7067 return (NULL);
7068
7069 dev_names = s_malloc(cb_arg.count * sizeof (char *));
7070 if (dev_names == NULL)
7071 goto out;
7072
7073 for (i = 0; i < cb_arg.count; i++) {
7074 dev_names[i] = s_strdup(cb_arg.dev_names[i]);
7075 if (dev_names[i] == NULL) {
7076 devfsadm_free_dev_names(dev_names, i);
7077 dev_names = NULL;
7078 goto out;
7079 }
7080 }
7081 *lenp = cb_arg.count;
7082
7083 out:
7084 free_dev_names(&cb_arg);
7085 return (dev_names);
7086 }
7087
7088 /* common exit function which ensures releasing locks */
7089 static void
devfsadm_exit(int status)7090 devfsadm_exit(int status)
7091 {
7092 if (DEVFSADM_DEBUG_ON) {
7093 vprint(INFO_MID, "exit status = %d\n", status);
7094 }
7095
7096 exit_dev_lock(1);
7097 exit_daemon_lock(1);
7098
7099 if (logflag == TRUE) {
7100 closelog();
7101 }
7102
7103 exit(status);
7104 /*NOTREACHED*/
7105 }
7106
7107 /*
7108 * set root_dir, devices_dir, dev_dir using optarg.
7109 */
7110 static void
set_root_devices_dev_dir(char * dir)7111 set_root_devices_dev_dir(char *dir)
7112 {
7113 size_t len;
7114
7115 root_dir = s_strdup(dir);
7116 len = strlen(dir) + strlen(DEVICES) + 1;
7117 devices_dir = s_malloc(len);
7118 (void) snprintf(devices_dir, len, "%s%s", root_dir, DEVICES);
7119 len = strlen(root_dir) + strlen(DEV) + 1;
7120 dev_dir = s_malloc(len);
7121 (void) snprintf(dev_dir, len, "%s%s", root_dir, DEV);
7122 }
7123
7124 /*
7125 * Removes quotes.
7126 */
7127 static char *
dequote(char * src)7128 dequote(char *src)
7129 {
7130 char *dst;
7131 int len;
7132
7133 len = strlen(src);
7134 dst = s_malloc(len + 1);
7135 if (src[0] == '\"' && src[len - 1] == '\"') {
7136 len -= 2;
7137 (void) strncpy(dst, &src[1], len);
7138 dst[len] = '\0';
7139 } else {
7140 (void) strcpy(dst, src);
7141 }
7142 return (dst);
7143 }
7144
7145 /*
7146 * For a given physical device pathname and spectype, return the
7147 * ownership and permissions attributes by looking in data from
7148 * /etc/minor_perm. If currently in installation mode, check for
7149 * possible major number translations from the miniroot to the installed
7150 * root's name_to_major table. Note that there can be multiple matches,
7151 * but the last match takes effect. pts seems to rely on this
7152 * implementation behavior.
7153 */
7154 static void
getattr(char * phy_path,char * aminor,int spectype,dev_t dev,mode_t * mode,uid_t * uid,gid_t * gid)7155 getattr(char *phy_path, char *aminor, int spectype, dev_t dev, mode_t *mode,
7156 uid_t *uid, gid_t *gid)
7157 {
7158 char devname[PATH_MAX + 1];
7159 char *node_name;
7160 char *minor_name;
7161 int match = FALSE;
7162 int is_clone;
7163 int mp_drvname_matches_node_name;
7164 int mp_drvname_matches_minor_name;
7165 int mp_drvname_is_clone;
7166 int mp_drvname_matches_drvname;
7167 struct mperm *mp;
7168 major_t major_no;
7169 char driver[PATH_MAX + 1];
7170
7171 /*
7172 * Get the driver name based on the major number since the name
7173 * in /devices may be generic. Could be running with more major
7174 * numbers than are in /etc/name_to_major, so get it from the kernel
7175 */
7176 major_no = major(dev);
7177
7178 if (modctl(MODGETNAME, driver, sizeof (driver), &major_no) != 0) {
7179 /* return default values */
7180 goto use_defaults;
7181 }
7182
7183 (void) strcpy(devname, phy_path);
7184
7185 node_name = strrchr(devname, '/'); /* node name is the last */
7186 /* component */
7187 if (node_name == NULL) {
7188 err_print(NO_NODE, devname);
7189 goto use_defaults;
7190 }
7191
7192 minor_name = strchr(++node_name, '@'); /* see if it has address part */
7193
7194 if (minor_name != NULL) {
7195 *minor_name++ = '\0';
7196 } else {
7197 minor_name = node_name;
7198 }
7199
7200 minor_name = strchr(minor_name, ':'); /* look for minor name */
7201
7202 if (minor_name == NULL) {
7203 err_print(NO_MINOR, devname);
7204 goto use_defaults;
7205 }
7206 *minor_name++ = '\0';
7207
7208 /*
7209 * mp->mp_drvname = device name from minor_perm
7210 * mp->mp_minorname = minor part of device name from
7211 * minor_perm
7212 * drvname = name of driver for this device
7213 */
7214
7215 is_clone = (strcmp(node_name, "clone") == 0 ? TRUE : FALSE);
7216 for (mp = minor_perms; mp != NULL; mp = mp->mp_next) {
7217 mp_drvname_matches_node_name =
7218 (strcmp(mp->mp_drvname, node_name) == 0 ? TRUE : FALSE);
7219 mp_drvname_matches_minor_name =
7220 (strcmp(mp->mp_drvname, minor_name) == 0 ? TRUE:FALSE);
7221 mp_drvname_is_clone =
7222 (strcmp(mp->mp_drvname, "clone") == 0 ? TRUE : FALSE);
7223 mp_drvname_matches_drvname =
7224 (strcmp(mp->mp_drvname, driver) == 0 ? TRUE : FALSE);
7225
7226 /*
7227 * If one of the following cases is true, then we try to change
7228 * the permissions if a "shell global pattern match" of
7229 * mp_>mp_minorname matches minor_name.
7230 *
7231 * 1. mp->mp_drvname matches driver.
7232 *
7233 * OR
7234 *
7235 * 2. mp->mp_drvname matches node_name and this
7236 * name is an alias of the driver name
7237 *
7238 * OR
7239 *
7240 * 3. /devices entry is the clone device and either
7241 * minor_perm entry is the clone device or matches
7242 * the minor part of the clone device.
7243 */
7244
7245 if ((mp_drvname_matches_drvname == TRUE)||
7246 ((mp_drvname_matches_node_name == TRUE) &&
7247 (alias(driver, node_name) == TRUE)) ||
7248 ((is_clone == TRUE) &&
7249 ((mp_drvname_is_clone == TRUE) ||
7250 (mp_drvname_matches_minor_name == TRUE)))) {
7251 /*
7252 * Check that the minor part of the
7253 * device name from the minor_perm
7254 * entry matches and if so, set the
7255 * permissions.
7256 *
7257 * Under real devfs, clone minor name is changed
7258 * to match the driver name, but minor_perm may
7259 * not match. We reconcile it here.
7260 */
7261 if (aminor != NULL)
7262 minor_name = aminor;
7263
7264 if (gmatch(minor_name, mp->mp_minorname) != 0) {
7265 *uid = mp->mp_uid;
7266 *gid = mp->mp_gid;
7267 *mode = spectype | mp->mp_mode;
7268 match = TRUE;
7269 }
7270 }
7271 }
7272
7273 if (match == TRUE) {
7274 return;
7275 }
7276
7277 use_defaults:
7278 /* not found in minor_perm, so just use default values */
7279 *uid = root_uid;
7280 *gid = sys_gid;
7281 *mode = (spectype | 0600);
7282 }
7283
7284 /*
7285 * Called by devfs_read_minor_perm() to report errors
7286 * key is:
7287 * line number: ignoring line number error
7288 * errno: open/close errors
7289 * size: alloc errors
7290 */
7291 static void
minorperm_err_cb(minorperm_err_t mp_err,int key)7292 minorperm_err_cb(minorperm_err_t mp_err, int key)
7293 {
7294 switch (mp_err) {
7295 case MP_FOPEN_ERR:
7296 err_print(FOPEN_FAILED, MINOR_PERM_FILE, strerror(key));
7297 break;
7298 case MP_FCLOSE_ERR:
7299 err_print(FCLOSE_FAILED, MINOR_PERM_FILE, strerror(key));
7300 break;
7301 case MP_IGNORING_LINE_ERR:
7302 err_print(IGNORING_LINE_IN, key, MINOR_PERM_FILE);
7303 break;
7304 case MP_ALLOC_ERR:
7305 err_print(MALLOC_FAILED, key);
7306 break;
7307 case MP_NVLIST_ERR:
7308 err_print(NVLIST_ERROR, MINOR_PERM_FILE, strerror(key));
7309 break;
7310 case MP_CANT_FIND_USER_ERR:
7311 err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
7312 break;
7313 case MP_CANT_FIND_GROUP_ERR:
7314 err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
7315 break;
7316 }
7317 }
7318
7319 static void
read_minor_perm_file(void)7320 read_minor_perm_file(void)
7321 {
7322 static int cached = FALSE;
7323 static struct stat cached_sb;
7324 struct stat current_sb;
7325
7326 (void) stat(MINOR_PERM_FILE, ¤t_sb);
7327
7328 /* If already cached, check to see if it is still valid */
7329 if (cached == TRUE) {
7330
7331 if (current_sb.st_mtime == cached_sb.st_mtime) {
7332 vprint(FILES_MID, "%s cache valid\n", MINOR_PERM_FILE);
7333 return;
7334 }
7335 devfs_free_minor_perm(minor_perms);
7336 minor_perms = NULL;
7337 } else {
7338 cached = TRUE;
7339 }
7340
7341 (void) stat(MINOR_PERM_FILE, &cached_sb);
7342
7343 vprint(FILES_MID, "loading binding file: %s\n", MINOR_PERM_FILE);
7344
7345 minor_perms = devfs_read_minor_perm(minorperm_err_cb);
7346 }
7347
7348 static void
load_minor_perm_file(void)7349 load_minor_perm_file(void)
7350 {
7351 read_minor_perm_file();
7352 if (devfs_load_minor_perm(minor_perms, minorperm_err_cb) != 0)
7353 err_print(gettext("minor_perm load failed\n"));
7354 }
7355
7356 static char *
convert_to_re(char * dev)7357 convert_to_re(char *dev)
7358 {
7359 char *p, *l, *out;
7360 int i;
7361
7362 out = s_malloc(PATH_MAX);
7363
7364 for (l = p = dev, i = 0; (*p != '\0') && (i < (PATH_MAX - 1));
7365 ++p, i++) {
7366 if ((*p == '*') && ((l != p) && (*l == '/'))) {
7367 out[i++] = '.';
7368 out[i] = '+';
7369 } else {
7370 out[i] = *p;
7371 }
7372 l = p;
7373 }
7374 out[i] = '\0';
7375 p = (char *)s_malloc(strlen(out) + 1);
7376 (void) strlcpy(p, out, strlen(out) + 1);
7377 free(out);
7378
7379 vprint(FILES_MID, "converted %s -> %s\n", dev, p);
7380
7381 return (p);
7382 }
7383
7384 static void
read_logindevperm_file(void)7385 read_logindevperm_file(void)
7386 {
7387 static int cached = FALSE;
7388 static struct stat cached_sb;
7389 struct stat current_sb;
7390 struct login_dev *ldev = NULL;
7391 FILE *fp;
7392 char line[MAX_LDEV_LINE];
7393 int ln, perm, rv;
7394 char *cp, *console, *dlist, *dev;
7395 char *lasts, *devlasts, *permstr, *drv;
7396 struct driver_list *list, *next;
7397
7398 /* Read logindevperm only when enabled */
7399 if (login_dev_enable != TRUE)
7400 return;
7401
7402 if (cached == TRUE) {
7403 if (stat(LDEV_FILE, ¤t_sb) == 0 &&
7404 current_sb.st_mtime == cached_sb.st_mtime) {
7405 vprint(FILES_MID, "%s cache valid\n", LDEV_FILE);
7406 return;
7407 }
7408 vprint(FILES_MID, "invalidating %s cache\n", LDEV_FILE);
7409 while (login_dev_cache != NULL) {
7410
7411 ldev = login_dev_cache;
7412 login_dev_cache = ldev->ldev_next;
7413 free(ldev->ldev_console);
7414 free(ldev->ldev_device);
7415 regfree(&ldev->ldev_device_regex);
7416 list = ldev->ldev_driver_list;
7417 while (list) {
7418 next = list->next;
7419 free(list);
7420 list = next;
7421 }
7422 free(ldev);
7423 ldev = NULL;
7424 }
7425 } else {
7426 cached = TRUE;
7427 }
7428
7429 assert(login_dev_cache == NULL);
7430
7431 if (stat(LDEV_FILE, &cached_sb) != 0) {
7432 cached = FALSE;
7433 return;
7434 }
7435
7436 vprint(FILES_MID, "loading file: %s\n", LDEV_FILE);
7437
7438 if ((fp = fopen(LDEV_FILE, "r")) == NULL) {
7439 /* Not fatal to devfsadm */
7440 cached = FALSE;
7441 err_print(FOPEN_FAILED, LDEV_FILE, strerror(errno));
7442 return;
7443 }
7444
7445 ln = 0;
7446 while (fgets(line, MAX_LDEV_LINE, fp) != NULL) {
7447 ln++;
7448
7449 /* Remove comments */
7450 if ((cp = strchr(line, '#')) != NULL)
7451 *cp = '\0';
7452
7453 if ((console = strtok_r(line, LDEV_DELIMS, &lasts)) == NULL)
7454 continue; /* Blank line */
7455
7456 if ((permstr = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7457 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7458 continue; /* Malformed line */
7459 }
7460
7461 /*
7462 * permstr is string in octal format. Convert to int
7463 */
7464 cp = NULL;
7465 errno = 0;
7466 perm = strtol(permstr, &cp, 8);
7467 if (errno || perm < 0 || perm > 0777 || *cp != '\0') {
7468 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7469 continue;
7470 }
7471
7472 if ((dlist = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7473 err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7474 continue;
7475 }
7476
7477 dev = strtok_r(dlist, LDEV_DEV_DELIM, &devlasts);
7478 while (dev) {
7479
7480 ldev = (struct login_dev *)s_zalloc(
7481 sizeof (struct login_dev));
7482 ldev->ldev_console = s_strdup(console);
7483 ldev->ldev_perms = perm;
7484
7485 /*
7486 * the logical device name may contain '*' which
7487 * we convert to a regular expression
7488 */
7489 ldev->ldev_device = convert_to_re(dev);
7490 if (ldev->ldev_device &&
7491 (rv = regcomp(&ldev->ldev_device_regex,
7492 ldev->ldev_device, REG_EXTENDED))) {
7493 bzero(&ldev->ldev_device_regex,
7494 sizeof (ldev->ldev_device_regex));
7495 err_print(REGCOMP_FAILED,
7496 ldev->ldev_device, rv);
7497 }
7498 ldev->ldev_next = login_dev_cache;
7499 login_dev_cache = ldev;
7500 dev = strtok_r(NULL, LDEV_DEV_DELIM, &devlasts);
7501 }
7502
7503 drv = strtok_r(NULL, LDEV_DRVLIST_DELIMS, &lasts);
7504 if (drv != NULL && ldev != NULL) {
7505 if (strcmp(drv, LDEV_DRVLIST_NAME) == 0) {
7506
7507 drv = strtok_r(NULL, LDEV_DRV_DELIMS, &lasts);
7508
7509 while (drv != NULL) {
7510 vprint(FILES_MID,
7511 "logindevperm driver=%s\n", drv);
7512
7513 /*
7514 * create a linked list of driver
7515 * names
7516 */
7517 list = (struct driver_list *)
7518 s_zalloc(
7519 sizeof (struct driver_list));
7520 (void) strlcpy(list->driver_name, drv,
7521 sizeof (list->driver_name));
7522 list->next = ldev->ldev_driver_list;
7523 ldev->ldev_driver_list = list;
7524 drv = strtok_r(NULL, LDEV_DRV_DELIMS,
7525 &lasts);
7526 }
7527 }
7528 }
7529 }
7530 (void) fclose(fp);
7531 }
7532
7533 /*
7534 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
7535 *
7536 * Returns DEVFSADM_SUCCESS if token found, DEVFSADM_FAILURE otherwise.
7537 */
7538 static int
getnexttoken(char * next,char ** nextp,char ** tokenpp,char * tchar)7539 getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar)
7540 {
7541 char *cp;
7542 char *cp1;
7543 char *tokenp;
7544
7545 cp = next;
7546 while (*cp == ' ' || *cp == '\t') {
7547 cp++; /* skip leading spaces */
7548 }
7549 tokenp = cp; /* start of token */
7550 while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' &&
7551 *cp != ':' && *cp != '=' && *cp != '&' &&
7552 *cp != '|' && *cp != ';') {
7553 cp++; /* point to next character */
7554 }
7555 /*
7556 * If terminating character is a space or tab, look ahead to see if
7557 * there's another terminator that's not a space or a tab.
7558 * (This code handles trailing spaces.)
7559 */
7560 if (*cp == ' ' || *cp == '\t') {
7561 cp1 = cp;
7562 while (*++cp1 == ' ' || *cp1 == '\t')
7563 ;
7564 if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' ||
7565 *cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') {
7566 *cp = '\0'; /* terminate token */
7567 cp = cp1;
7568 }
7569 }
7570 if (tchar != NULL) {
7571 *tchar = *cp; /* save terminating character */
7572 if (*tchar == '\0') {
7573 *tchar = '\n';
7574 }
7575 }
7576 *cp++ = '\0'; /* terminate token, point to next */
7577 *nextp = cp; /* set pointer to next character */
7578 if (cp - tokenp - 1 == 0) {
7579 return (DEVFSADM_FAILURE);
7580 }
7581 *tokenpp = tokenp;
7582 return (DEVFSADM_SUCCESS);
7583 }
7584
7585 /*
7586 * read or reread the driver aliases file
7587 */
7588 static void
read_driver_aliases_file(void)7589 read_driver_aliases_file(void)
7590 {
7591
7592 driver_alias_t *save;
7593 driver_alias_t *lst_tail = NULL;
7594 driver_alias_t *ap;
7595 static int cached = FALSE;
7596 FILE *afd;
7597 char line[256];
7598 char *cp;
7599 char *p;
7600 char t;
7601 int ln = 0;
7602 static struct stat cached_sb;
7603 struct stat current_sb;
7604
7605 (void) stat(ALIASFILE, ¤t_sb);
7606
7607 /* If already cached, check to see if it is still valid */
7608 if (cached == TRUE) {
7609
7610 if (current_sb.st_mtime == cached_sb.st_mtime) {
7611 vprint(FILES_MID, "%s cache valid\n", ALIASFILE);
7612 return;
7613 }
7614
7615 vprint(FILES_MID, "invalidating %s cache\n", ALIASFILE);
7616 while (driver_aliases != NULL) {
7617 free(driver_aliases->alias_name);
7618 free(driver_aliases->driver_name);
7619 save = driver_aliases;
7620 driver_aliases = driver_aliases->next;
7621 free(save);
7622 }
7623 } else {
7624 cached = TRUE;
7625 }
7626
7627 (void) stat(ALIASFILE, &cached_sb);
7628
7629 vprint(FILES_MID, "loading binding file: %s\n", ALIASFILE);
7630
7631 if ((afd = fopen(ALIASFILE, "r")) == NULL) {
7632 err_print(FOPEN_FAILED, ALIASFILE, strerror(errno));
7633 devfsadm_exit(1);
7634 /*NOTREACHED*/
7635 }
7636
7637 while (fgets(line, sizeof (line), afd) != NULL) {
7638 ln++;
7639 /* cut off comments starting with '#' */
7640 if ((cp = strchr(line, '#')) != NULL)
7641 *cp = '\0';
7642 /* ignore comment or blank lines */
7643 if (is_blank(line))
7644 continue;
7645 cp = line;
7646 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7647 err_print(IGNORING_LINE_IN, ln, ALIASFILE);
7648 continue;
7649 }
7650 if (t == '\n' || t == '\0') {
7651 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7652 continue;
7653 }
7654 ap = (struct driver_alias *)
7655 s_zalloc(sizeof (struct driver_alias));
7656 ap->driver_name = s_strdup(p);
7657 if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7658 err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7659 free(ap->driver_name);
7660 free(ap);
7661 continue;
7662 }
7663 if (*p == '"') {
7664 if (p[strlen(p) - 1] == '"') {
7665 p[strlen(p) - 1] = '\0';
7666 p++;
7667 }
7668 }
7669 ap->alias_name = s_strdup(p);
7670 if (driver_aliases == NULL) {
7671 driver_aliases = ap;
7672 lst_tail = ap;
7673 } else {
7674 lst_tail->next = ap;
7675 lst_tail = ap;
7676 }
7677 }
7678 if (fclose(afd) == EOF) {
7679 err_print(FCLOSE_FAILED, ALIASFILE, strerror(errno));
7680 }
7681 }
7682
7683 /*
7684 * return TRUE if alias_name is an alias for driver_name, otherwise
7685 * return FALSE.
7686 */
7687 static int
alias(char * driver_name,char * alias_name)7688 alias(char *driver_name, char *alias_name)
7689 {
7690 driver_alias_t *alias;
7691
7692 /*
7693 * check for a match
7694 */
7695 for (alias = driver_aliases; alias != NULL; alias = alias->next) {
7696 if ((strcmp(alias->driver_name, driver_name) == 0) &&
7697 (strcmp(alias->alias_name, alias_name) == 0)) {
7698 return (TRUE);
7699 }
7700 }
7701 return (FALSE);
7702 }
7703
7704 /*
7705 * convenience functions
7706 */
7707 static int
s_stat(const char * path,struct stat * sbufp)7708 s_stat(const char *path, struct stat *sbufp)
7709 {
7710 int rv;
7711 retry:
7712 if ((rv = stat(path, sbufp)) == -1) {
7713 if (errno == EINTR)
7714 goto retry;
7715 }
7716 return (rv);
7717 }
7718
7719 static void *
s_malloc(const size_t size)7720 s_malloc(const size_t size)
7721 {
7722 void *rp;
7723
7724 rp = malloc(size);
7725 if (rp == NULL) {
7726 err_print(MALLOC_FAILED, size);
7727 devfsadm_exit(1);
7728 /*NOTREACHED*/
7729 }
7730 return (rp);
7731 }
7732
7733 /*
7734 * convenience functions
7735 */
7736 static void *
s_realloc(void * ptr,const size_t size)7737 s_realloc(void *ptr, const size_t size)
7738 {
7739 ptr = realloc(ptr, size);
7740 if (ptr == NULL) {
7741 err_print(REALLOC_FAILED, size);
7742 devfsadm_exit(1);
7743 /*NOTREACHED*/
7744 }
7745 return (ptr);
7746 }
7747
7748 static void *
s_zalloc(const size_t size)7749 s_zalloc(const size_t size)
7750 {
7751 void *rp;
7752
7753 rp = calloc(1, size);
7754 if (rp == NULL) {
7755 err_print(CALLOC_FAILED, size);
7756 devfsadm_exit(1);
7757 /*NOTREACHED*/
7758 }
7759 return (rp);
7760 }
7761
7762 char *
s_strdup(const char * ptr)7763 s_strdup(const char *ptr)
7764 {
7765 void *rp;
7766
7767 rp = strdup(ptr);
7768 if (rp == NULL) {
7769 err_print(STRDUP_FAILED, ptr);
7770 devfsadm_exit(1);
7771 /*NOTREACHED*/
7772 }
7773 return (rp);
7774 }
7775
7776 static void
s_closedir(DIR * dirp)7777 s_closedir(DIR *dirp)
7778 {
7779 retry:
7780 if (closedir(dirp) != 0) {
7781 if (errno == EINTR)
7782 goto retry;
7783 err_print(CLOSEDIR_FAILED, strerror(errno));
7784 }
7785 }
7786
7787 static void
s_mkdirp(const char * path,const mode_t mode)7788 s_mkdirp(const char *path, const mode_t mode)
7789 {
7790 vprint(CHATTY_MID, "mkdirp(%s, 0x%lx)\n", path, mode);
7791 if (mkdirp(path, mode) == -1) {
7792 if (errno != EEXIST) {
7793 err_print(MKDIR_FAILED, path, mode, strerror(errno));
7794 }
7795 }
7796 }
7797
7798 static void
s_unlink(const char * file)7799 s_unlink(const char *file)
7800 {
7801 retry:
7802 if (unlink(file) == -1) {
7803 if (errno == EINTR || errno == EAGAIN)
7804 goto retry;
7805 if (errno != ENOENT) {
7806 err_print(UNLINK_FAILED, file, strerror(errno));
7807 }
7808 }
7809 }
7810
7811 static void
add_verbose_id(char * mid)7812 add_verbose_id(char *mid)
7813 {
7814 num_verbose++;
7815 verbose = s_realloc(verbose, num_verbose * sizeof (char *));
7816 verbose[num_verbose - 1] = mid;
7817 }
7818
7819 /*
7820 * returns DEVFSADM_TRUE if contents is a minor node in /devices.
7821 * If mn_root is not NULL, mn_root is set to:
7822 * if contents is a /dev node, mn_root = contents
7823 * OR
7824 * if contents is a /devices node, mn_root set to the '/'
7825 * following /devices.
7826 */
7827 static int
is_minor_node(char * contents,char ** mn_root)7828 is_minor_node(char *contents, char **mn_root)
7829 {
7830 char *ptr;
7831 char device_prefix[100];
7832
7833 (void) snprintf(device_prefix, sizeof (device_prefix), "../devices/");
7834
7835 if ((ptr = strstr(contents, device_prefix)) != NULL) {
7836 if (mn_root != NULL) {
7837 /* mn_root should point to the / following /devices */
7838 *mn_root = ptr += strlen(device_prefix) - 1;
7839 }
7840 return (DEVFSADM_TRUE);
7841 }
7842
7843 (void) snprintf(device_prefix, sizeof (device_prefix), "/devices/");
7844
7845 if (strncmp(contents, device_prefix, strlen(device_prefix)) == 0) {
7846 if (mn_root != NULL) {
7847 /* mn_root should point to the / following /devices */
7848 *mn_root = contents + strlen(device_prefix) - 1;
7849 }
7850 return (DEVFSADM_TRUE);
7851 }
7852
7853 if (mn_root != NULL) {
7854 *mn_root = contents;
7855 }
7856 return (DEVFSADM_FALSE);
7857 }
7858
7859 /*
7860 * Add the specified property to nvl.
7861 * Returns:
7862 * 0 successfully added
7863 * -1 an error occurred
7864 * 1 could not add the property for reasons not due to errors.
7865 */
7866 static int
add_property(nvlist_t * nvl,di_prop_t prop)7867 add_property(nvlist_t *nvl, di_prop_t prop)
7868 {
7869 char *name;
7870 char *attr_name;
7871 int n, len;
7872 int32_t *int32p;
7873 int64_t *int64p;
7874 char *str;
7875 char **strarray;
7876 uchar_t *bytep;
7877 int rv = 0;
7878 int i;
7879
7880 if ((name = di_prop_name(prop)) == NULL)
7881 return (-1);
7882
7883 len = sizeof (DEV_PROP_PREFIX) + strlen(name);
7884 if ((attr_name = malloc(len)) == NULL)
7885 return (-1);
7886
7887 (void) strlcpy(attr_name, DEV_PROP_PREFIX, len);
7888 (void) strlcat(attr_name, name, len);
7889
7890 switch (di_prop_type(prop)) {
7891 case DI_PROP_TYPE_BOOLEAN:
7892 if (nvlist_add_boolean(nvl, attr_name) != 0)
7893 goto out;
7894 break;
7895
7896 case DI_PROP_TYPE_INT:
7897 if ((n = di_prop_ints(prop, &int32p)) < 1)
7898 goto out;
7899
7900 if (n <= (PROP_LEN_LIMIT / sizeof (int32_t))) {
7901 if (nvlist_add_int32_array(nvl, attr_name, int32p,
7902 n) != 0)
7903 goto out;
7904 } else
7905 rv = 1;
7906 break;
7907
7908 case DI_PROP_TYPE_INT64:
7909 if ((n = di_prop_int64(prop, &int64p)) < 1)
7910 goto out;
7911
7912 if (n <= (PROP_LEN_LIMIT / sizeof (int64_t))) {
7913 if (nvlist_add_int64_array(nvl, attr_name, int64p,
7914 n) != 0)
7915 goto out;
7916 } else
7917 rv = 1;
7918 break;
7919
7920 case DI_PROP_TYPE_BYTE:
7921 case DI_PROP_TYPE_UNKNOWN:
7922 if ((n = di_prop_bytes(prop, &bytep)) < 1)
7923 goto out;
7924
7925 if (n <= PROP_LEN_LIMIT) {
7926 if (nvlist_add_byte_array(nvl, attr_name, bytep, n)
7927 != 0)
7928 goto out;
7929 } else
7930 rv = 1;
7931 break;
7932
7933 case DI_PROP_TYPE_STRING:
7934 if ((n = di_prop_strings(prop, &str)) < 1)
7935 goto out;
7936
7937 if ((strarray = malloc(n * sizeof (char *))) == NULL)
7938 goto out;
7939
7940 len = 0;
7941 for (i = 0; i < n; i++) {
7942 strarray[i] = str + len;
7943 len += strlen(strarray[i]) + 1;
7944 }
7945
7946 if (len <= PROP_LEN_LIMIT) {
7947 if (nvlist_add_string_array(nvl, attr_name, strarray,
7948 n) != 0) {
7949 free(strarray);
7950 goto out;
7951 }
7952 } else
7953 rv = 1;
7954 free(strarray);
7955 break;
7956
7957 default:
7958 rv = 1;
7959 break;
7960 }
7961
7962 free(attr_name);
7963 return (rv);
7964
7965 out:
7966 free(attr_name);
7967 return (-1);
7968 }
7969
7970 static void
free_dev_names(struct devlink_cb_arg * x)7971 free_dev_names(struct devlink_cb_arg *x)
7972 {
7973 int i;
7974
7975 for (i = 0; i < x->count; i++) {
7976 free(x->dev_names[i]);
7977 free(x->link_contents[i]);
7978 }
7979 }
7980
7981 /* callback function for di_devlink_cache_walk */
7982 static int
devlink_cb(di_devlink_t dl,void * arg)7983 devlink_cb(di_devlink_t dl, void *arg)
7984 {
7985 struct devlink_cb_arg *x = (struct devlink_cb_arg *)arg;
7986 const char *path;
7987 const char *content;
7988
7989 if ((path = di_devlink_path(dl)) == NULL ||
7990 (content = di_devlink_content(dl)) == NULL ||
7991 (x->dev_names[x->count] = s_strdup(path)) == NULL)
7992 goto out;
7993
7994 if ((x->link_contents[x->count] = s_strdup(content)) == NULL) {
7995 free(x->dev_names[x->count]);
7996 goto out;
7997 }
7998
7999 x->count++;
8000 if (x->count >= MAX_DEV_NAME_COUNT)
8001 return (DI_WALK_TERMINATE);
8002
8003 return (DI_WALK_CONTINUE);
8004
8005 out:
8006 x->rv = -1;
8007 free_dev_names(x);
8008 return (DI_WALK_TERMINATE);
8009 }
8010
8011 /*
8012 * Lookup dev name corresponding to the phys_path.
8013 * phys_path is path to a node or minor node.
8014 * Returns:
8015 * 0 with *dev_name set to the dev name
8016 * Lookup succeeded and dev_name found
8017 * 0 with *dev_name set to NULL
8018 * Lookup encountered no errors but dev name not found
8019 * -1
8020 * Lookup failed
8021 */
8022 static int
lookup_dev_name(char * phys_path,char ** dev_name)8023 lookup_dev_name(char *phys_path, char **dev_name)
8024 {
8025 struct devlink_cb_arg cb_arg;
8026
8027 *dev_name = NULL;
8028
8029 cb_arg.count = 0;
8030 cb_arg.rv = 0;
8031 (void) di_devlink_cache_walk(devlink_cache, NULL, phys_path,
8032 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8033
8034 if (cb_arg.rv == -1)
8035 return (-1);
8036
8037 if (cb_arg.count > 0) {
8038 *dev_name = s_strdup(cb_arg.dev_names[0]);
8039 free_dev_names(&cb_arg);
8040 if (*dev_name == NULL)
8041 return (-1);
8042 }
8043
8044 return (0);
8045 }
8046
8047 static char *
lookup_disk_dev_name(char * node_path)8048 lookup_disk_dev_name(char *node_path)
8049 {
8050 struct devlink_cb_arg cb_arg;
8051 char *dev_name = NULL;
8052 int i;
8053 char *p;
8054 int len1, len2;
8055
8056 #define DEV_RDSK "/dev/rdsk/"
8057 #define DISK_RAW_MINOR ",raw"
8058
8059 cb_arg.count = 0;
8060 cb_arg.rv = 0;
8061 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8062 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8063
8064 if (cb_arg.rv == -1 || cb_arg.count == 0)
8065 return (NULL);
8066
8067 /* first try lookup based on /dev/rdsk name */
8068 for (i = 0; i < cb_arg.count; i++) {
8069 if (strncmp(cb_arg.dev_names[i], DEV_RDSK,
8070 sizeof (DEV_RDSK) - 1) == 0) {
8071 dev_name = s_strdup(cb_arg.dev_names[i]);
8072 break;
8073 }
8074 }
8075
8076 if (dev_name == NULL) {
8077 /* now try lookup based on a minor name ending with ",raw" */
8078 len1 = sizeof (DISK_RAW_MINOR) - 1;
8079 for (i = 0; i < cb_arg.count; i++) {
8080 len2 = strlen(cb_arg.link_contents[i]);
8081 if (len2 >= len1 &&
8082 strcmp(cb_arg.link_contents[i] + len2 - len1,
8083 DISK_RAW_MINOR) == 0) {
8084 dev_name = s_strdup(cb_arg.dev_names[i]);
8085 break;
8086 }
8087 }
8088 }
8089
8090 free_dev_names(&cb_arg);
8091
8092 if (dev_name == NULL)
8093 return (NULL);
8094 if (strlen(dev_name) == 0) {
8095 free(dev_name);
8096 return (NULL);
8097 }
8098
8099 /* if the name contains slice or partition number strip it */
8100 p = dev_name + strlen(dev_name) - 1;
8101 if (isdigit(*p)) {
8102 while (p != dev_name && isdigit(*p))
8103 p--;
8104 if (*p == 's' || *p == 'p')
8105 *p = '\0';
8106 }
8107
8108 return (dev_name);
8109 }
8110
8111 static char *
lookup_lofi_dev_name(char * node_path,char * minor)8112 lookup_lofi_dev_name(char *node_path, char *minor)
8113 {
8114 struct devlink_cb_arg cb_arg;
8115 char *dev_name = NULL;
8116 int i;
8117 int len1, len2;
8118
8119 cb_arg.count = 0;
8120 cb_arg.rv = 0;
8121 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8122 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8123
8124 if (cb_arg.rv == -1 || cb_arg.count == 0)
8125 return (NULL);
8126
8127 /* lookup based on a minor name ending with ",raw" */
8128 len1 = strlen(minor);
8129 for (i = 0; i < cb_arg.count; i++) {
8130 len2 = strlen(cb_arg.link_contents[i]);
8131 if (len2 >= len1 &&
8132 strcmp(cb_arg.link_contents[i] + len2 - len1,
8133 minor) == 0) {
8134 dev_name = s_strdup(cb_arg.dev_names[i]);
8135 break;
8136 }
8137 }
8138
8139 free_dev_names(&cb_arg);
8140
8141 if (dev_name == NULL)
8142 return (NULL);
8143 if (strlen(dev_name) == 0) {
8144 free(dev_name);
8145 return (NULL);
8146 }
8147
8148 return (dev_name);
8149 }
8150
8151 static char *
lookup_network_dev_name(char * node_path,char * driver_name)8152 lookup_network_dev_name(char *node_path, char *driver_name)
8153 {
8154 char *dev_name = NULL;
8155 char phys_path[MAXPATHLEN];
8156
8157 if (lookup_dev_name(node_path, &dev_name) == -1)
8158 return (NULL);
8159
8160 if (dev_name == NULL) {
8161 /* dlpi style-2 only interface */
8162 (void) snprintf(phys_path, sizeof (phys_path),
8163 "/pseudo/clone@0:%s", driver_name);
8164 if (lookup_dev_name(phys_path, &dev_name) == -1 ||
8165 dev_name == NULL)
8166 return (NULL);
8167 }
8168
8169 return (dev_name);
8170 }
8171
8172 static char *
lookup_printer_dev_name(char * node_path)8173 lookup_printer_dev_name(char *node_path)
8174 {
8175 struct devlink_cb_arg cb_arg;
8176 char *dev_name = NULL;
8177 int i;
8178
8179 #define DEV_PRINTERS "/dev/printers/"
8180
8181 cb_arg.count = 0;
8182 cb_arg.rv = 0;
8183 (void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8184 DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8185
8186 if (cb_arg.rv == -1 || cb_arg.count == 0)
8187 return (NULL);
8188
8189 /* first try lookup based on /dev/printers name */
8190 for (i = 0; i < cb_arg.count; i++) {
8191 if (strncmp(cb_arg.dev_names[i], DEV_PRINTERS,
8192 sizeof (DEV_PRINTERS) - 1) == 0) {
8193 dev_name = s_strdup(cb_arg.dev_names[i]);
8194 break;
8195 }
8196 }
8197
8198 /* fallback to the first name */
8199 if ((dev_name == NULL) && (cb_arg.count > 0))
8200 dev_name = s_strdup(cb_arg.dev_names[0]);
8201
8202 free_dev_names(&cb_arg);
8203
8204 return (dev_name);
8205 }
8206
8207 /*
8208 * Build an nvlist containing all attributes for devfs events.
8209 * Returns nvlist pointer on success, NULL on failure.
8210 */
8211 static nvlist_t *
build_event_attributes(char * class,char * subclass,char * node_path,di_node_t node,char * driver_name,int instance,char * minor)8212 build_event_attributes(char *class, char *subclass, char *node_path,
8213 di_node_t node, char *driver_name, int instance, char *minor)
8214 {
8215 nvlist_t *nvl;
8216 int err = 0;
8217 di_prop_t prop;
8218 int count;
8219 char *prop_name;
8220 int x;
8221 char *dev_name = NULL;
8222 int dev_name_lookup_err = 0;
8223
8224 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) {
8225 nvl = NULL;
8226 goto out;
8227 }
8228
8229 if ((err = nvlist_add_int32(nvl, EV_VERSION, EV_V1)) != 0)
8230 goto out;
8231
8232 if ((err = nvlist_add_string(nvl, DEV_PHYS_PATH, node_path)) != 0)
8233 goto out;
8234
8235 if (strcmp(class, EC_DEV_ADD) != 0 &&
8236 strcmp(class, EC_DEV_REMOVE) != 0)
8237 return (nvl);
8238
8239 if (driver_name == NULL || instance == -1)
8240 goto out;
8241
8242 if (strcmp(subclass, ESC_DISK) == 0) {
8243 /*
8244 * While we're removing labeled lofi device, we will receive
8245 * event for every registered minor device and lastly,
8246 * an event with minor set to NULL, as in following example:
8247 * class: EC_dev_remove subclass: disk
8248 * node_path: /pseudo/lofi@1 driver: lofi minor: u,raw
8249 * class: EC_dev_remove subclass: disk
8250 * node_path: /pseudo/lofi@1 driver: lofi minor: NULL
8251 *
8252 * When we receive this last event with minor set to NULL,
8253 * all lofi minor devices are already removed and the call to
8254 * lookup_disk_dev_name() would result in error.
8255 * To prevent name lookup error messages for this case, we
8256 * need to filter out that last event.
8257 */
8258 if (strcmp(class, EC_DEV_REMOVE) == 0 &&
8259 strcmp(driver_name, "lofi") == 0 && minor == NULL) {
8260 nvlist_free(nvl);
8261 return (NULL);
8262 }
8263 if ((dev_name = lookup_disk_dev_name(node_path)) == NULL) {
8264 dev_name_lookup_err = 1;
8265 goto out;
8266 }
8267 } else if (strcmp(subclass, ESC_NETWORK) == 0) {
8268 if ((dev_name = lookup_network_dev_name(node_path, driver_name))
8269 == NULL) {
8270 dev_name_lookup_err = 1;
8271 goto out;
8272 }
8273 } else if (strcmp(subclass, ESC_PRINTER) == 0) {
8274 if ((dev_name = lookup_printer_dev_name(node_path)) == NULL) {
8275 dev_name_lookup_err = 1;
8276 goto out;
8277 }
8278 } else if (strcmp(subclass, ESC_LOFI) == 0) {
8279 /*
8280 * The raw minor node is created or removed after the block
8281 * node. Lofi devfs events are dependent on this behavior.
8282 * Generate the sysevent only for the raw minor node.
8283 *
8284 * If the lofi mapping is created, we will receive the following
8285 * event: class: EC_dev_add subclass: lofi minor: NULL
8286 *
8287 * As in case of EC_dev_add, the minor is NULL pointer,
8288 * to get device links created, we will need to provide the
8289 * type of minor node for lookup_lofi_dev_name()
8290 *
8291 * If the lofi device is unmapped, we will receive following
8292 * events:
8293 * class: EC_dev_remove subclass: lofi minor: disk
8294 * class: EC_dev_remove subclass: lofi minor: disk,raw
8295 * class: EC_dev_remove subclass: lofi minor: NULL
8296 */
8297
8298 if (strcmp(class, EC_DEV_ADD) == 0 && minor == NULL)
8299 minor = "disk,raw";
8300
8301 if (minor == NULL || strstr(minor, "raw") == NULL) {
8302 nvlist_free(nvl);
8303 return (NULL);
8304 }
8305 if ((dev_name = lookup_lofi_dev_name(node_path, minor)) ==
8306 NULL) {
8307 dev_name_lookup_err = 1;
8308 goto out;
8309 }
8310 }
8311
8312 if (dev_name) {
8313 if ((err = nvlist_add_string(nvl, DEV_NAME, dev_name)) != 0)
8314 goto out;
8315 free(dev_name);
8316 dev_name = NULL;
8317 }
8318
8319 if ((err = nvlist_add_string(nvl, DEV_DRIVER_NAME, driver_name)) != 0)
8320 goto out;
8321
8322 if ((err = nvlist_add_int32(nvl, DEV_INSTANCE, instance)) != 0)
8323 goto out;
8324
8325 if (strcmp(class, EC_DEV_ADD) == 0) {
8326 /* add properties */
8327 count = 0;
8328 for (prop = di_prop_next(node, DI_PROP_NIL);
8329 prop != DI_PROP_NIL && count < MAX_PROP_COUNT;
8330 prop = di_prop_next(node, prop)) {
8331
8332 if (di_prop_devt(prop) != DDI_DEV_T_NONE)
8333 continue;
8334
8335 if ((x = add_property(nvl, prop)) == 0)
8336 count++;
8337 else if (x == -1) {
8338 if ((prop_name = di_prop_name(prop)) == NULL)
8339 prop_name = "";
8340 err_print(PROP_ADD_FAILED, prop_name);
8341 goto out;
8342 }
8343 }
8344 }
8345
8346 return (nvl);
8347
8348 out:
8349 nvlist_free(nvl);
8350
8351 if (dev_name)
8352 free(dev_name);
8353
8354 if (dev_name_lookup_err) {
8355 /*
8356 * If a lofi mount fails, the /devices node may well have
8357 * disappeared by the time we run, so let's not complain.
8358 */
8359 if (strcmp(subclass, ESC_LOFI) != 0)
8360 err_print(DEV_NAME_LOOKUP_FAILED, node_path);
8361 } else {
8362 err_print(BUILD_EVENT_ATTR_FAILED, (err) ? strerror(err) : "");
8363 }
8364 return (NULL);
8365 }
8366
8367 static void
log_event(char * class,char * subclass,nvlist_t * nvl)8368 log_event(char *class, char *subclass, nvlist_t *nvl)
8369 {
8370 sysevent_id_t eid;
8371
8372 if (sysevent_post_event(class, subclass, "SUNW", DEVFSADMD,
8373 nvl, &eid) != 0) {
8374 err_print(LOG_EVENT_FAILED, strerror(errno));
8375 }
8376 }
8377
8378 /*
8379 * When devfsadmd needs to generate sysevents, they are queued for later
8380 * delivery this allows them to be delivered after the devlinks db cache has
8381 * been flushed guaranteeing that applications consuming these events have
8382 * access to an accurate devlinks db. The queue is a FIFO, sysevents to be
8383 * inserted in the front of the queue and consumed off the back.
8384 */
8385 static void
enqueue_sysevent(char * class,char * subclass,nvlist_t * nvl)8386 enqueue_sysevent(char *class, char *subclass, nvlist_t *nvl)
8387 {
8388 syseventq_t *tmp;
8389
8390 if ((tmp = s_zalloc(sizeof (*tmp))) == NULL)
8391 return;
8392
8393 tmp->class = s_strdup(class);
8394 tmp->subclass = s_strdup(subclass);
8395 tmp->nvl = nvl;
8396
8397 (void) mutex_lock(&syseventq_mutex);
8398 if (syseventq_front != NULL)
8399 syseventq_front->next = tmp;
8400 else
8401 syseventq_back = tmp;
8402 syseventq_front = tmp;
8403 (void) mutex_unlock(&syseventq_mutex);
8404 }
8405
8406 static void
process_syseventq()8407 process_syseventq()
8408 {
8409 (void) mutex_lock(&syseventq_mutex);
8410 while (syseventq_back != NULL) {
8411 syseventq_t *tmp = syseventq_back;
8412
8413 vprint(CHATTY_MID, "sending queued event: %s, %s\n",
8414 tmp->class, tmp->subclass);
8415
8416 log_event(tmp->class, tmp->subclass, tmp->nvl);
8417
8418 if (tmp->class != NULL)
8419 free(tmp->class);
8420 if (tmp->subclass != NULL)
8421 free(tmp->subclass);
8422 nvlist_free(tmp->nvl);
8423 syseventq_back = syseventq_back->next;
8424 if (syseventq_back == NULL)
8425 syseventq_front = NULL;
8426 free(tmp);
8427 }
8428 (void) mutex_unlock(&syseventq_mutex);
8429 }
8430
8431 static void
build_and_enq_event(char * class,char * subclass,char * node_path,di_node_t node,char * minor)8432 build_and_enq_event(char *class, char *subclass, char *node_path,
8433 di_node_t node, char *minor)
8434 {
8435 nvlist_t *nvl;
8436
8437 vprint(CHATTY_MID, "build_and_enq_event(%s, %s, %s, 0x%8.8x)\n",
8438 class, subclass, node_path, (int)node);
8439
8440 if (node != DI_NODE_NIL)
8441 nvl = build_event_attributes(class, subclass, node_path, node,
8442 di_driver_name(node), di_instance(node), minor);
8443 else
8444 nvl = build_event_attributes(class, subclass, node_path, node,
8445 NULL, -1, minor);
8446
8447 if (nvl) {
8448 enqueue_sysevent(class, subclass, nvl);
8449 }
8450 }
8451
8452 /*
8453 * is_blank() returns 1 (true) if a line specified is composed of
8454 * whitespace characters only. otherwise, it returns 0 (false).
8455 *
8456 * Note. the argument (line) must be null-terminated.
8457 */
8458 static int
is_blank(char * line)8459 is_blank(char *line)
8460 {
8461 for (/* nothing */; *line != '\0'; line++)
8462 if (!isspace(*line))
8463 return (0);
8464 return (1);
8465 }
8466
8467 /*
8468 * Functions to deal with the no-further-processing hash
8469 */
8470
8471 static void
nfphash_create(void)8472 nfphash_create(void)
8473 {
8474 assert(nfp_hash == NULL);
8475 nfp_hash = s_zalloc(NFP_HASH_SZ * sizeof (item_t *));
8476 }
8477
8478 static int
nfphash_fcn(char * key)8479 nfphash_fcn(char *key)
8480 {
8481 int i;
8482 uint64_t sum = 0;
8483
8484 for (i = 0; key[i] != '\0'; i++) {
8485 sum += (uchar_t)key[i];
8486 }
8487
8488 return (sum % NFP_HASH_SZ);
8489 }
8490
8491 static item_t *
nfphash_lookup(char * key)8492 nfphash_lookup(char *key)
8493 {
8494 int index;
8495 item_t *ip;
8496
8497 index = nfphash_fcn(key);
8498
8499 assert(index >= 0);
8500
8501 for (ip = nfp_hash[index]; ip; ip = ip->i_next) {
8502 if (strcmp(ip->i_key, key) == 0)
8503 return (ip);
8504 }
8505
8506 return (NULL);
8507 }
8508
8509 static void
nfphash_insert(char * key)8510 nfphash_insert(char *key)
8511 {
8512 item_t *ip;
8513 int index;
8514
8515 index = nfphash_fcn(key);
8516
8517 assert(index >= 0);
8518
8519 ip = s_zalloc(sizeof (item_t));
8520 ip->i_key = s_strdup(key);
8521
8522 ip->i_next = nfp_hash[index];
8523 nfp_hash[index] = ip;
8524 }
8525
8526 static void
nfphash_destroy(void)8527 nfphash_destroy(void)
8528 {
8529 int i;
8530 item_t *ip;
8531
8532 for (i = 0; i < NFP_HASH_SZ; i++) {
8533 while ((ip = nfp_hash[i]) != NULL) {
8534 nfp_hash[i] = ip->i_next;
8535 free(ip->i_key);
8536 free(ip);
8537 }
8538 }
8539
8540 free(nfp_hash);
8541 nfp_hash = NULL;
8542 }
8543
8544 static int
devname_kcall(int subcmd,void * args)8545 devname_kcall(int subcmd, void *args)
8546 {
8547 int error = 0;
8548
8549 switch (subcmd) {
8550 case MODDEVNAME_LOOKUPDOOR:
8551 error = modctl(MODDEVNAME, subcmd, (uintptr_t)args);
8552 if (error) {
8553 vprint(INFO_MID, "modctl(MODDEVNAME, "
8554 "MODDEVNAME_LOOKUPDOOR) failed - %s\n",
8555 strerror(errno));
8556 }
8557 break;
8558 default:
8559 error = EINVAL;
8560 break;
8561 }
8562 return (error);
8563 }
8564
8565 /* ARGSUSED */
8566 static void
devname_lookup_handler(void * cookie,char * argp,size_t arg_size,door_desc_t * dp,uint_t n_desc)8567 devname_lookup_handler(void *cookie, char *argp, size_t arg_size,
8568 door_desc_t *dp, uint_t n_desc)
8569 {
8570 int32_t error = 0;
8571 door_cred_t dcred;
8572 struct dca_impl dci;
8573 uint8_t cmd;
8574 sdev_door_res_t res;
8575 sdev_door_arg_t *args;
8576
8577 if (argp == NULL || arg_size == 0) {
8578 vprint(DEVNAME_MID, "devname_lookup_handler: argp wrong\n");
8579 error = DEVFSADM_RUN_INVALID;
8580 goto done;
8581 }
8582 vprint(DEVNAME_MID, "devname_lookup_handler\n");
8583
8584 if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
8585 vprint(DEVNAME_MID, "devname_lookup_handler: cred wrong\n");
8586 error = DEVFSADM_RUN_EPERM;
8587 goto done;
8588 }
8589
8590 args = (sdev_door_arg_t *)argp;
8591 cmd = args->devfsadm_cmd;
8592
8593 vprint(DEVNAME_MID, "devname_lookup_handler: cmd %d\n", cmd);
8594 switch (cmd) {
8595 case DEVFSADMD_RUN_ALL:
8596 /*
8597 * run "devfsadm"
8598 */
8599 dci.dci_root = "/";
8600 dci.dci_minor = NULL;
8601 dci.dci_driver = NULL;
8602 dci.dci_error = 0;
8603 dci.dci_flags = 0;
8604 dci.dci_arg = NULL;
8605
8606 lock_dev();
8607 update_drvconf((major_t)-1, 0);
8608 dci.dci_flags |= DCA_FLUSH_PATHINST;
8609
8610 pre_and_post_cleanup(RM_PRE);
8611 devi_tree_walk(&dci, DI_CACHE_SNAPSHOT_FLAGS, NULL);
8612 error = (int32_t)dci.dci_error;
8613 if (!error) {
8614 pre_and_post_cleanup(RM_POST);
8615 update_database = TRUE;
8616 unlock_dev(SYNC_STATE);
8617 update_database = FALSE;
8618 } else {
8619 if (DEVFSADM_DEBUG_ON) {
8620 vprint(INFO_MID, "devname_lookup_handler: "
8621 "DEVFSADMD_RUN_ALL failed\n");
8622 }
8623
8624 unlock_dev(SYNC_STATE);
8625 }
8626 break;
8627 default:
8628 /* log an error here? */
8629 error = DEVFSADM_RUN_NOTSUP;
8630 break;
8631 }
8632
8633 done:
8634 vprint(DEVNAME_MID, "devname_lookup_handler: error %d\n", error);
8635 res.devfsadm_error = error;
8636 (void) door_return((char *)&res, sizeof (struct sdev_door_res),
8637 NULL, 0);
8638 }
8639
8640
8641 di_devlink_handle_t
devfsadm_devlink_cache(void)8642 devfsadm_devlink_cache(void)
8643 {
8644 return (devlink_cache);
8645 }
8646
8647 int
devfsadm_reserve_id_cache(devlink_re_t re_array[],enumerate_file_t * head)8648 devfsadm_reserve_id_cache(devlink_re_t re_array[], enumerate_file_t *head)
8649 {
8650 enumerate_file_t *entry;
8651 int nelem;
8652 int i;
8653 int subex;
8654 char *re;
8655 size_t size;
8656 regmatch_t *pmch;
8657
8658 /*
8659 * Check the <RE, subexp> array passed in and compile it.
8660 */
8661 for (i = 0; re_array[i].d_re; i++) {
8662 if (re_array[i].d_subexp == 0) {
8663 err_print("bad subexp value in RE: %s\n",
8664 re_array[i].d_re);
8665 goto bad_re;
8666 }
8667
8668 re = re_array[i].d_re;
8669 if (regcomp(&re_array[i].d_rcomp, re, REG_EXTENDED) != 0) {
8670 err_print("reg. exp. failed to compile: %s\n", re);
8671 goto bad_re;
8672 }
8673 subex = re_array[i].d_subexp;
8674 nelem = subex + 1;
8675 re_array[i].d_pmatch = s_malloc(sizeof (regmatch_t) * nelem);
8676 }
8677
8678 entry = head ? head : enumerate_reserved;
8679 for (; entry; entry = entry->er_next) {
8680 if (entry->er_id) {
8681 vprint(RSBY_MID, "entry %s already has ID %s\n",
8682 entry->er_file, entry->er_id);
8683 continue;
8684 }
8685 for (i = 0; re_array[i].d_re; i++) {
8686 subex = re_array[i].d_subexp;
8687 pmch = re_array[i].d_pmatch;
8688 if (regexec(&re_array[i].d_rcomp, entry->er_file,
8689 subex + 1, pmch, 0) != 0) {
8690 /* No match */
8691 continue;
8692 }
8693 size = pmch[subex].rm_eo - pmch[subex].rm_so;
8694 entry->er_id = s_malloc(size + 1);
8695 (void) strncpy(entry->er_id,
8696 &entry->er_file[pmch[subex].rm_so], size);
8697 entry->er_id[size] = '\0';
8698 if (head) {
8699 vprint(RSBY_MID, "devlink(%s) matches RE(%s). "
8700 "ID is %s\n", entry->er_file,
8701 re_array[i].d_re, entry->er_id);
8702 } else {
8703 vprint(RSBY_MID, "rsrv entry(%s) matches "
8704 "RE(%s) ID is %s\n", entry->er_file,
8705 re_array[i].d_re, entry->er_id);
8706 }
8707 break;
8708 }
8709 }
8710
8711 for (i = 0; re_array[i].d_re; i++) {
8712 regfree(&re_array[i].d_rcomp);
8713 assert(re_array[i].d_pmatch);
8714 free(re_array[i].d_pmatch);
8715 }
8716
8717 entry = head ? head : enumerate_reserved;
8718 for (; entry; entry = entry->er_next) {
8719 if (entry->er_id == NULL)
8720 continue;
8721 if (head) {
8722 vprint(RSBY_MID, "devlink: %s\n", entry->er_file);
8723 vprint(RSBY_MID, "ID: %s\n", entry->er_id);
8724 } else {
8725 vprint(RSBY_MID, "reserve file entry: %s\n",
8726 entry->er_file);
8727 vprint(RSBY_MID, "reserve file id: %s\n",
8728 entry->er_id);
8729 }
8730 }
8731
8732 return (DEVFSADM_SUCCESS);
8733
8734 bad_re:
8735 for (i = i-1; i >= 0; i--) {
8736 regfree(&re_array[i].d_rcomp);
8737 assert(re_array[i].d_pmatch);
8738 free(re_array[i].d_pmatch);
8739 }
8740 return (DEVFSADM_FAILURE);
8741 }
8742
8743 /*
8744 * Return 1 if we have reserved links.
8745 */
8746 int
devfsadm_have_reserved()8747 devfsadm_have_reserved()
8748 {
8749 return (enumerate_reserved ? 1 : 0);
8750 }
8751
8752 /*
8753 * This functions errs on the side of caution. If there is any error
8754 * we assume that the devlink is *not* reserved
8755 */
8756 int
devfsadm_is_reserved(devlink_re_t re_array[],char * devlink)8757 devfsadm_is_reserved(devlink_re_t re_array[], char *devlink)
8758 {
8759 int match;
8760 enumerate_file_t estruct = {NULL};
8761 enumerate_file_t *entry;
8762
8763 match = 0;
8764 estruct.er_file = devlink;
8765 estruct.er_id = NULL;
8766 estruct.er_next = NULL;
8767
8768 if (devfsadm_reserve_id_cache(re_array, &estruct) != DEVFSADM_SUCCESS) {
8769 err_print("devfsadm_is_reserved: devlink (%s) does not "
8770 "match RE\n", devlink);
8771 return (0);
8772 }
8773 if (estruct.er_id == NULL) {
8774 err_print("devfsadm_is_reserved: ID derived from devlink %s "
8775 "is NULL\n", devlink);
8776 return (0);
8777 }
8778
8779 entry = enumerate_reserved;
8780 for (; entry; entry = entry->er_next) {
8781 if (entry->er_id == NULL)
8782 continue;
8783 if (strcmp(entry->er_id, estruct.er_id) != 0)
8784 continue;
8785 match = 1;
8786 vprint(RSBY_MID, "reserve file entry (%s) and devlink (%s) "
8787 "match\n", entry->er_file, devlink);
8788 break;
8789 }
8790
8791 free(estruct.er_id);
8792 return (match);
8793 }
8794