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