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