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