Changeset 3232 in MondoRescue for branches/3.2/mindi-busybox/util-linux/mdev.c
- Timestamp:
- Jan 1, 2014, 12:47:38 AM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/3.2/mindi-busybox/util-linux/mdev.c
r2725 r3232 8 8 * Licensed under GPLv2, see file LICENSE in this source tree. 9 9 */ 10 11 //config:config MDEV 12 //config: bool "mdev" 13 //config: default y 14 //config: select PLATFORM_LINUX 15 //config: help 16 //config: mdev is a mini-udev implementation for dynamically creating device 17 //config: nodes in the /dev directory. 18 //config: 19 //config: For more information, please see docs/mdev.txt 20 //config: 21 //config:config FEATURE_MDEV_CONF 22 //config: bool "Support /etc/mdev.conf" 23 //config: default y 24 //config: depends on MDEV 25 //config: help 26 //config: Add support for the mdev config file to control ownership and 27 //config: permissions of the device nodes. 28 //config: 29 //config: For more information, please see docs/mdev.txt 30 //config: 31 //config:config FEATURE_MDEV_RENAME 32 //config: bool "Support subdirs/symlinks" 33 //config: default y 34 //config: depends on FEATURE_MDEV_CONF 35 //config: help 36 //config: Add support for renaming devices and creating symlinks. 37 //config: 38 //config: For more information, please see docs/mdev.txt 39 //config: 40 //config:config FEATURE_MDEV_RENAME_REGEXP 41 //config: bool "Support regular expressions substitutions when renaming device" 42 //config: default y 43 //config: depends on FEATURE_MDEV_RENAME 44 //config: help 45 //config: Add support for regular expressions substitutions when renaming 46 //config: device. 47 //config: 48 //config:config FEATURE_MDEV_EXEC 49 //config: bool "Support command execution at device addition/removal" 50 //config: default y 51 //config: depends on FEATURE_MDEV_CONF 52 //config: help 53 //config: This adds support for an optional field to /etc/mdev.conf for 54 //config: executing commands when devices are created/removed. 55 //config: 56 //config: For more information, please see docs/mdev.txt 57 //config: 58 //config:config FEATURE_MDEV_LOAD_FIRMWARE 59 //config: bool "Support loading of firmwares" 60 //config: default y 61 //config: depends on MDEV 62 //config: help 63 //config: Some devices need to load firmware before they can be usable. 64 //config: 65 //config: These devices will request userspace look up the files in 66 //config: /lib/firmware/ and if it exists, send it to the kernel for 67 //config: loading into the hardware. 68 69 //applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP)) 70 71 //kbuild:lib-$(CONFIG_MDEV) += mdev.o 72 73 //usage:#define mdev_trivial_usage 74 //usage: "[-s]" 75 //usage:#define mdev_full_usage "\n\n" 76 //usage: "mdev -s is to be run during boot to scan /sys and populate /dev.\n" 77 //usage: "\n" 78 //usage: "Bare mdev is a kernel hotplug helper. To activate it:\n" 79 //usage: " echo /sbin/mdev >/proc/sys/kernel/hotplug\n" 80 //usage: IF_FEATURE_MDEV_CONF( 81 //usage: "\n" 82 //usage: "It uses /etc/mdev.conf with lines\n" 83 //usage: " [-][ENV=regex;]...DEVNAME UID:GID PERM" 84 //usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]") 85 //usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]") 86 //usage: "\n" 87 //usage: "where DEVNAME is device name regex, @major,minor[-minor2], or\n" 88 //usage: "environment variable regex. A common use of the latter is\n" 89 //usage: "to load modules for hotplugged devices:\n" 90 //usage: " $MODALIAS=.* 0:0 660 @modprobe \"$MODALIAS\"\n" 91 //usage: ) 92 //usage: "\n" 93 //usage: "If /dev/mdev.seq file exists, mdev will wait for its value\n" 94 //usage: "to match $SEQNUM variable. This prevents plug/unplug races.\n" 95 //usage: "To activate this feature, create empty /dev/mdev.seq at boot.\n" 96 //usage: "\n" 97 //usage: "If /dev/mdev.log file exists, debug log will be appended to it." 98 10 99 #include "libbb.h" 11 100 #include "xregex.h" … … 28 117 * 29 118 * If /etc/mdev.conf exists, it may modify /dev/device_name's properties. 30 * /etc/mdev.conf file format:31 *32 * [-][subsystem/]device user:grp mode [>|=path] [@|$|*command args...]33 * [-]@maj,min[-min2] user:grp mode [>|=path] [@|$|*command args...]34 * [-]$envvar=val user:grp mode [>|=path] [@|$|*command args...]35 119 * 36 120 * Leading minus in 1st field means "don't stop on this line", otherwise 37 121 * search is stopped after the matching line is encountered. 38 122 * 39 * The device name or "subsystem/device" combo is matched against 1st field 40 * (which is a regex), or maj,min is matched against 1st field, 41 * or specified environment variable (as regex) is matched against 1st field. 42 * 43 * $envvar=val format is useful for loading modules for hot-plugged devices 123 * $envvar=regex format is useful for loading modules for hot-plugged devices 44 124 * which do not have driver loaded yet. In this case /sys/class/.../dev 45 125 * does not exist, but $MODALIAS is set to needed module's name … … 62 142 */ 63 143 144 /* Kernel's hotplug environment constantly changes. 145 * Here are new cases I observed on 3.1.0: 146 * 147 * Case with $DEVNAME and $DEVICE, not just $DEVPATH: 148 * ACTION=add 149 * BUSNUM=001 150 * DEVICE=/proc/bus/usb/001/003 151 * DEVNAME=bus/usb/001/003 152 * DEVNUM=003 153 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5 154 * DEVTYPE=usb_device 155 * MAJOR=189 156 * MINOR=2 157 * PRODUCT=18d1/4e12/227 158 * SUBSYSTEM=usb 159 * TYPE=0/0/0 160 * 161 * Case with $DEVICE, but no $DEVNAME - apparenty, usb iface notification? 162 * "Please load me a module" thing? 163 * ACTION=add 164 * DEVICE=/proc/bus/usb/001/003 165 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0 166 * DEVTYPE=usb_interface 167 * INTERFACE=8/6/80 168 * MODALIAS=usb:v18D1p4E12d0227dc00dsc00dp00ic08isc06ip50 169 * PRODUCT=18d1/4e12/227 170 * SUBSYSTEM=usb 171 * TYPE=0/0/0 172 * 173 * ACTION=add 174 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5 175 * DEVTYPE=scsi_host 176 * SUBSYSTEM=scsi 177 * 178 * ACTION=add 179 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/scsi_host/host5 180 * SUBSYSTEM=scsi_host 181 * 182 * ACTION=add 183 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0 184 * DEVTYPE=scsi_target 185 * SUBSYSTEM=scsi 186 * 187 * Case with strange $MODALIAS: 188 * ACTION=add 189 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0 190 * DEVTYPE=scsi_device 191 * MODALIAS=scsi:t-0x00 192 * SUBSYSTEM=scsi 193 * 194 * ACTION=add 195 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_disk/5:0:0:0 196 * SUBSYSTEM=scsi_disk 197 * 198 * ACTION=add 199 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/scsi_device/5:0:0:0 200 * SUBSYSTEM=scsi_device 201 * 202 * Case with explicit $MAJOR/$MINOR (no need to read /sys/$DEVPATH/dev?): 203 * ACTION=add 204 * DEVNAME=bsg/5:0:0:0 205 * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/bsg/5:0:0:0 206 * MAJOR=253 207 * MINOR=1 208 * SUBSYSTEM=bsg 209 * 210 * ACTION=add 211 * DEVPATH=/devices/virtual/bdi/8:16 212 * SUBSYSTEM=bdi 213 * 214 * ACTION=add 215 * DEVNAME=sdb 216 * DEVPATH=/block/sdb 217 * DEVTYPE=disk 218 * MAJOR=8 219 * MINOR=16 220 * SUBSYSTEM=block 221 * 222 * Case with ACTION=change: 223 * ACTION=change 224 * DEVNAME=sdb 225 * DEVPATH=/block/sdb 226 * DEVTYPE=disk 227 * DISK_MEDIA_CHANGE=1 228 * MAJOR=8 229 * MINOR=16 230 * SUBSYSTEM=block 231 */ 232 233 #define DEBUG_LVL 2 234 235 #if DEBUG_LVL >= 1 236 # define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0) 237 #else 238 # define dbg1(...) ((void)0) 239 #endif 240 #if DEBUG_LVL >= 2 241 # define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0) 242 #else 243 # define dbg2(...) ((void)0) 244 #endif 245 #if DEBUG_LVL >= 3 246 # define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0) 247 #else 248 # define dbg3(...) ((void)0) 249 #endif 250 251 252 static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0" 253 enum { OP_add, OP_remove }; 254 255 struct envmatch { 256 struct envmatch *next; 257 char *envname; 258 regex_t match; 259 }; 260 261 struct rule { 262 bool keep_matching; 263 bool regex_compiled; 264 mode_t mode; 265 int maj, min0, min1; 266 struct bb_uidgid_t ugid; 267 char *envvar; 268 char *ren_mov; 269 IF_FEATURE_MDEV_EXEC(char *r_cmd;) 270 regex_t match; 271 struct envmatch *envmatch; 272 }; 273 64 274 struct globals { 65 275 int root_major, root_minor; 276 smallint verbose; 66 277 char *subsystem; 278 char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */ 279 #if ENABLE_FEATURE_MDEV_CONF 280 const char *filename; 281 parser_t *parser; 282 struct rule **rule_vec; 283 unsigned rule_idx; 284 #endif 285 struct rule cur_rule; 286 char timestr[sizeof("60.123456")]; 67 287 } FIX_ALIASING; 68 288 #define G (*(struct globals*)&bb_common_bufsiz1) 289 #define INIT_G() do { \ 290 IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \ 291 IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \ 292 } while (0) 293 69 294 70 295 /* Prevent infinite loops in /sys symlinks */ 71 296 #define MAX_SYSFS_DEPTH 3 72 297 73 /* We use additional 64+ bytes in make_device() */ 74 #define SCRATCH_SIZE 80 298 /* We use additional bytes in make_device() */ 299 #define SCRATCH_SIZE 128 300 301 #if ENABLE_FEATURE_MDEV_CONF 302 303 static void make_default_cur_rule(void) 304 { 305 memset(&G.cur_rule, 0, sizeof(G.cur_rule)); 306 G.cur_rule.maj = -1; /* "not a @major,minor rule" */ 307 G.cur_rule.mode = 0660; 308 } 309 310 static void clean_up_cur_rule(void) 311 { 312 struct envmatch *e; 313 314 free(G.cur_rule.envvar); 315 free(G.cur_rule.ren_mov); 316 if (G.cur_rule.regex_compiled) 317 regfree(&G.cur_rule.match); 318 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);) 319 e = G.cur_rule.envmatch; 320 while (e) { 321 free(e->envname); 322 regfree(&e->match); 323 e = e->next; 324 } 325 make_default_cur_rule(); 326 } 327 328 /* In later versions, endofname is in libbb */ 329 #define endofname mdev_endofname 330 static 331 const char* FAST_FUNC 332 endofname(const char *name) 333 { 334 #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) 335 #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) 336 if (!is_name(*name)) 337 return name; 338 while (*++name) { 339 if (!is_in_name(*name)) 340 break; 341 } 342 return name; 343 } 344 345 static char *parse_envmatch_pfx(char *val) 346 { 347 struct envmatch **nextp = &G.cur_rule.envmatch; 348 349 for (;;) { 350 struct envmatch *e; 351 char *semicolon; 352 char *eq = strchr(val, '='); 353 if (!eq /* || eq == val? */) 354 return val; 355 if (endofname(val) != eq) 356 return val; 357 semicolon = strchr(eq, ';'); 358 if (!semicolon) 359 return val; 360 /* ENVVAR=regex;... */ 361 *nextp = e = xzalloc(sizeof(*e)); 362 nextp = &e->next; 363 e->envname = xstrndup(val, eq - val); 364 *semicolon = '\0'; 365 xregcomp(&e->match, eq + 1, REG_EXTENDED); 366 *semicolon = ';'; 367 val = semicolon + 1; 368 } 369 } 370 371 static void parse_next_rule(void) 372 { 373 /* Note: on entry, G.cur_rule is set to default */ 374 while (1) { 375 char *tokens[4]; 376 char *val; 377 378 /* No PARSE_EOL_COMMENTS, because command may contain '#' chars */ 379 if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS)) 380 break; 381 382 /* Fields: [-]regex uid:gid mode [alias] [cmd] */ 383 dbg3("token1:'%s'", tokens[1]); 384 385 /* 1st field */ 386 val = tokens[0]; 387 G.cur_rule.keep_matching = ('-' == val[0]); 388 val += G.cur_rule.keep_matching; /* swallow leading dash */ 389 val = parse_envmatch_pfx(val); 390 if (val[0] == '@') { 391 /* @major,minor[-minor2] */ 392 /* (useful when name is ambiguous: 393 * "/sys/class/usb/lp0" and 394 * "/sys/class/printer/lp0") 395 */ 396 int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1); 397 if (sc < 2 || G.cur_rule.maj < 0) { 398 bb_error_msg("bad @maj,min on line %d", G.parser->lineno); 399 goto next_rule; 400 } 401 if (sc == 2) 402 G.cur_rule.min1 = G.cur_rule.min0; 403 } else { 404 char *eq = strchr(val, '='); 405 if (val[0] == '$') { 406 /* $ENVVAR=regex ... */ 407 val++; 408 if (!eq) { 409 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno); 410 goto next_rule; 411 } 412 G.cur_rule.envvar = xstrndup(val, eq - val); 413 val = eq + 1; 414 } 415 xregcomp(&G.cur_rule.match, val, REG_EXTENDED); 416 G.cur_rule.regex_compiled = 1; 417 } 418 419 /* 2nd field: uid:gid - device ownership */ 420 if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) { 421 bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno); 422 goto next_rule; 423 } 424 425 /* 3rd field: mode - device permissions */ 426 bb_parse_mode(tokens[2], &G.cur_rule.mode); 427 428 /* 4th field (opt): ">|=alias" or "!" to not create the node */ 429 val = tokens[3]; 430 if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) { 431 char *s = skip_non_whitespace(val); 432 G.cur_rule.ren_mov = xstrndup(val, s - val); 433 val = skip_whitespace(s); 434 } 435 436 if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) { 437 const char *s = "$@*"; 438 const char *s2 = strchr(s, val[0]); 439 if (!s2) { 440 bb_error_msg("bad line %u", G.parser->lineno); 441 goto next_rule; 442 } 443 IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);) 444 } 445 446 return; 447 next_rule: 448 clean_up_cur_rule(); 449 } /* while (config_read) */ 450 451 dbg3("config_close(G.parser)"); 452 config_close(G.parser); 453 G.parser = NULL; 454 455 return; 456 } 457 458 /* If mdev -s, we remember rules in G.rule_vec[]. 459 * Otherwise, there is no point in doing it, and we just 460 * save only one parsed rule in G.cur_rule. 461 */ 462 static const struct rule *next_rule(void) 463 { 464 struct rule *rule; 465 466 /* Open conf file if we didn't do it yet */ 467 if (!G.parser && G.filename) { 468 dbg3("config_open('%s')", G.filename); 469 G.parser = config_open2(G.filename, fopen_for_read); 470 G.filename = NULL; 471 } 472 473 if (G.rule_vec) { 474 /* mdev -s */ 475 /* Do we have rule parsed already? */ 476 if (G.rule_vec[G.rule_idx]) { 477 dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); 478 return G.rule_vec[G.rule_idx++]; 479 } 480 make_default_cur_rule(); 481 } else { 482 /* not mdev -s */ 483 clean_up_cur_rule(); 484 } 485 486 /* Parse one more rule if file isn't fully read */ 487 rule = &G.cur_rule; 488 if (G.parser) { 489 parse_next_rule(); 490 if (G.rule_vec) { /* mdev -s */ 491 rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule)); 492 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx); 493 G.rule_vec[G.rule_idx++] = rule; 494 dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); 495 } 496 } 497 498 return rule; 499 } 500 501 static int env_matches(struct envmatch *e) 502 { 503 while (e) { 504 int r; 505 char *val = getenv(e->envname); 506 if (!val) 507 return 0; 508 r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0); 509 if (r != 0) /* no match */ 510 return 0; 511 e = e->next; 512 } 513 return 1; 514 } 515 516 #else 517 518 # define next_rule() (&G.cur_rule) 519 520 #endif 521 522 static void mkdir_recursive(char *name) 523 { 524 /* if name has many levels ("dir1/dir2"), 525 * bb_make_directory() will create dir1 according to umask, 526 * not according to its "mode" parameter. 527 * Since we run with umask=0, need to temporarily switch it. 528 */ 529 umask(022); /* "dir1" (if any) will be 0755 too */ 530 bb_make_directory(name, 0755, FILEUTILS_RECUR); 531 umask(0); 532 } 75 533 76 534 /* Builds an alias path. … … 87 545 if (dest) { /* ">bar/[baz]" ? */ 88 546 *dest = '\0'; /* mkdir bar */ 89 bb_make_directory(alias, 0755, FILEUTILS_RECUR);547 mkdir_recursive(alias); 90 548 *dest = '/'; 91 549 if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */ … … 101 559 /* mknod in /dev based on a path like "/sys/block/hda/hda1" 102 560 * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes 103 * after NUL, but we promise to not mangle (IOW: to restore if needed)561 * after NUL, but we promise to not mangle (IOW: to restore NUL if needed) 104 562 * path string. 105 563 * NB2: "mdev -s" may call us many times, do not leak memory/fds! 564 * 565 * device_name = $DEVNAME (may be NULL) 566 * path = /sys/$DEVPATH 106 567 */ 107 static void make_device(char *path, int delete) 108 { 109 char *device_name, *subsystem_slash_devname; 568 static void make_device(char *device_name, char *path, int operation) 569 { 110 570 int major, minor, type, len; 111 mode_t mode; 112 parser_t *parser; 571 char *path_end = path + strlen(path); 113 572 114 573 /* Try to read major/minor string. Note that the kernel puts \n after … … 118 577 */ 119 578 major = -1; 120 if (!delete) { 121 char *dev_maj_min = path + strlen(path); 122 123 strcpy(dev_maj_min, "/dev"); 124 len = open_read_close(path, dev_maj_min + 1, 64); 125 *dev_maj_min = '\0'; 579 if (operation == OP_add) { 580 strcpy(path_end, "/dev"); 581 len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1); 582 *path_end = '\0'; 126 583 if (len < 1) { 127 584 if (!ENABLE_FEATURE_MDEV_EXEC) … … 129 586 /* no "dev" file, but we can still run scripts 130 587 * based on device name */ 131 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) { 588 } else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) { 589 dbg1("dev %u,%u", major, minor); 590 } else { 132 591 major = -1; 133 592 } … … 135 594 /* else: for delete, -1 still deletes the node, but < -1 suppresses that */ 136 595 137 /* Determine device name, type, major and minor */ 138 device_name = (char*) bb_basename(path); 139 /* http://kernel.org/doc/pending/hotplug.txt says that only 596 /* Determine device name */ 597 if (!device_name) { 598 /* 599 * There was no $DEVNAME envvar (for example, mdev -s never has). 600 * But it is very useful: it contains the *path*, not only basename, 601 * Thankfully, uevent file has it. 602 * Example of .../sound/card0/controlC0/uevent file on Linux-3.7.7: 603 * MAJOR=116 604 * MINOR=7 605 * DEVNAME=snd/controlC0 606 */ 607 strcpy(path_end, "/uevent"); 608 len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1); 609 if (len < 0) 610 len = 0; 611 *path_end = '\0'; 612 path_end[1 + len] = '\0'; 613 device_name = strstr(path_end + 1, "\nDEVNAME="); 614 if (device_name) { 615 device_name += sizeof("\nDEVNAME=")-1; 616 strchrnul(device_name, '\n')[0] = '\0'; 617 } else { 618 /* Fall back to just basename */ 619 device_name = (char*) bb_basename(path); 620 } 621 } 622 /* Determine device type */ 623 /* 624 * http://kernel.org/doc/pending/hotplug.txt says that only 140 625 * "/sys/block/..." is for block devices. "/sys/bus" etc is not. 141 626 * But since 2.6.25 block devices are also in /sys/class/block. 142 * We use strstr("/block/") to forestall future surprises. */ 627 * We use strstr("/block/") to forestall future surprises. 628 */ 143 629 type = S_IFCHR; 144 630 if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0)) 145 631 type = S_IFBLK; 146 632 147 /* Make path point to "subsystem/device_name" */ 148 subsystem_slash_devname = NULL; 149 /* Check for coldplug invocations first */ 150 if (strncmp(path, "/sys/block/", 11) == 0) /* legacy case */ 151 path += sizeof("/sys/") - 1; 152 else if (strncmp(path, "/sys/class/", 11) == 0) 153 path += sizeof("/sys/class/") - 1; 154 else { 155 /* Example of a hotplug invocation: 156 * SUBSYSTEM="block" 157 * DEVPATH="/sys" + "/devices/virtual/mtd/mtd3/mtdblock3" 158 * ("/sys" is added by mdev_main) 159 * - path does not contain subsystem 633 #if ENABLE_FEATURE_MDEV_CONF 634 G.rule_idx = 0; /* restart from the beginning (think mdev -s) */ 635 #endif 636 for (;;) { 637 const char *str_to_match; 638 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP]; 639 char *command; 640 char *alias; 641 char aliaslink = aliaslink; /* for compiler */ 642 char *node_name; 643 const struct rule *rule; 644 645 str_to_match = device_name; 646 647 rule = next_rule(); 648 649 #if ENABLE_FEATURE_MDEV_CONF 650 if (!env_matches(rule->envmatch)) 651 continue; 652 if (rule->maj >= 0) { /* @maj,min rule */ 653 if (major != rule->maj) 654 continue; 655 if (minor < rule->min0 || minor > rule->min1) 656 continue; 657 memset(off, 0, sizeof(off)); 658 goto rule_matches; 659 } 660 if (rule->envvar) { /* $envvar=regex rule */ 661 str_to_match = getenv(rule->envvar); 662 dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match); 663 if (!str_to_match) 664 continue; 665 } 666 /* else: str_to_match = device_name */ 667 668 if (rule->regex_compiled) { 669 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0); 670 dbg3("regex_match for '%s':%d", str_to_match, regex_match); 671 //bb_error_msg("matches:"); 672 //for (int i = 0; i < ARRAY_SIZE(off); i++) { 673 // if (off[i].rm_so < 0) continue; 674 // bb_error_msg("match %d: '%.*s'\n", i, 675 // (int)(off[i].rm_eo - off[i].rm_so), 676 // device_name + off[i].rm_so); 677 //} 678 679 if (regex_match != 0 680 /* regexec returns whole pattern as "range" 0 */ 681 || off[0].rm_so != 0 682 || (int)off[0].rm_eo != (int)strlen(str_to_match) 683 ) { 684 continue; /* this rule doesn't match */ 685 } 686 } 687 /* else: it's final implicit "match-all" rule */ 688 rule_matches: 689 dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1); 690 #endif 691 /* Build alias name */ 692 alias = NULL; 693 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) { 694 aliaslink = rule->ren_mov[0]; 695 if (aliaslink == '!') { 696 /* "!": suppress node creation/deletion */ 697 major = -2; 698 } 699 else if (aliaslink == '>' || aliaslink == '=') { 700 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) { 701 char *s; 702 char *p; 703 unsigned n; 704 705 /* substitute %1..9 with off[1..9], if any */ 706 n = 0; 707 s = rule->ren_mov; 708 while (*s) 709 if (*s++ == '%') 710 n++; 711 712 p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match)); 713 s = rule->ren_mov + 1; 714 while (*s) { 715 *p = *s; 716 if ('%' == *s) { 717 unsigned i = (s[1] - '0'); 718 if (i <= 9 && off[i].rm_so >= 0) { 719 n = off[i].rm_eo - off[i].rm_so; 720 strncpy(p, str_to_match + off[i].rm_so, n); 721 p += n - 1; 722 s++; 723 } 724 } 725 p++; 726 s++; 727 } 728 } else { 729 alias = xstrdup(rule->ren_mov + 1); 730 } 731 } 732 } 733 dbg3("alias:'%s'", alias); 734 735 command = NULL; 736 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;) 737 if (command) { 738 /* Are we running this command now? 739 * Run @cmd on create, $cmd on delete, *cmd on any 740 */ 741 if ((command[0] == '@' && operation == OP_add) 742 || (command[0] == '$' && operation == OP_remove) 743 || (command[0] == '*') 744 ) { 745 command++; 746 } else { 747 command = NULL; 748 } 749 } 750 dbg3("command:'%s'", command); 751 752 /* "Execute" the line we found */ 753 node_name = device_name; 754 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 755 node_name = alias = build_alias(alias, device_name); 756 dbg3("alias2:'%s'", alias); 757 } 758 759 if (operation == OP_add && major >= 0) { 760 char *slash = strrchr(node_name, '/'); 761 if (slash) { 762 *slash = '\0'; 763 mkdir_recursive(node_name); 764 *slash = '/'; 765 } 766 if (ENABLE_FEATURE_MDEV_CONF) { 767 dbg1("mknod %s (%d,%d) %o" 768 " %u:%u", 769 node_name, major, minor, rule->mode | type, 770 rule->ugid.uid, rule->ugid.gid 771 ); 772 } else { 773 dbg1("mknod %s (%d,%d) %o", 774 node_name, major, minor, rule->mode | type 775 ); 776 } 777 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) 778 bb_perror_msg("can't create '%s'", node_name); 779 if (ENABLE_FEATURE_MDEV_CONF) { 780 chmod(node_name, rule->mode); 781 chown(node_name, rule->ugid.uid, rule->ugid.gid); 782 } 783 if (major == G.root_major && minor == G.root_minor) 784 symlink(node_name, "root"); 785 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 786 if (aliaslink == '>') { 787 //TODO: on devtmpfs, device_name already exists and symlink() fails. 788 //End result is that instead of symlink, we have two nodes. 789 //What should be done? 790 dbg1("symlink: %s", device_name); 791 symlink(node_name, device_name); 792 } 793 } 794 } 795 796 if (ENABLE_FEATURE_MDEV_EXEC && command) { 797 /* setenv will leak memory, use putenv/unsetenv/free */ 798 char *s = xasprintf("%s=%s", "MDEV", node_name); 799 putenv(s); 800 dbg1("running: %s", command); 801 if (system(command) == -1) 802 bb_perror_msg("can't run '%s'", command); 803 bb_unsetenv_and_free(s); 804 } 805 806 if (operation == OP_remove && major >= -1) { 807 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 808 if (aliaslink == '>') { 809 dbg1("unlink: %s", device_name); 810 unlink(device_name); 811 } 812 } 813 dbg1("unlink: %s", node_name); 814 unlink(node_name); 815 } 816 817 if (ENABLE_FEATURE_MDEV_RENAME) 818 free(alias); 819 820 /* We found matching line. 821 * Stop unless it was prefixed with '-' 160 822 */ 161 subsystem_slash_devname = concat_path_file(G.subsystem, device_name); 162 path = subsystem_slash_devname; 163 } 164 165 /* If we have config file, look up user settings */ 166 if (ENABLE_FEATURE_MDEV_CONF) 167 parser = config_open2("/etc/mdev.conf", fopen_for_read); 168 169 do { 170 int keep_matching; 171 struct bb_uidgid_t ugid; 172 char *tokens[4]; 173 char *command = NULL; 174 char *alias = NULL; 175 char aliaslink = aliaslink; /* for compiler */ 176 177 /* Defaults in case we won't match any line */ 178 ugid.uid = ugid.gid = 0; 179 keep_matching = 0; 180 mode = 0660; 181 182 if (ENABLE_FEATURE_MDEV_CONF 183 && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL) 184 ) { 185 char *val; 186 char *str_to_match; 187 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP]; 188 189 val = tokens[0]; 190 keep_matching = ('-' == val[0]); 191 val += keep_matching; /* swallow leading dash */ 192 193 /* Match against either "subsystem/device_name" 194 * or "device_name" alone */ 195 str_to_match = strchr(val, '/') ? path : device_name; 196 197 /* Fields: regex uid:gid mode [alias] [cmd] */ 198 199 if (val[0] == '@') { 200 /* @major,minor[-minor2] */ 201 /* (useful when name is ambiguous: 202 * "/sys/class/usb/lp0" and 203 * "/sys/class/printer/lp0") */ 204 int cmaj, cmin0, cmin1, sc; 205 if (major < 0) 206 continue; /* no dev, no match */ 207 sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1); 208 if (sc < 1 209 || major != cmaj 210 || (sc == 2 && minor != cmin0) 211 || (sc == 3 && (minor < cmin0 || minor > cmin1)) 212 ) { 213 continue; /* this line doesn't match */ 214 } 215 goto line_matches; 216 } 217 if (val[0] == '$') { 218 /* regex to match an environment variable */ 219 char *eq = strchr(++val, '='); 220 if (!eq) 221 continue; 222 *eq = '\0'; 223 str_to_match = getenv(val); 224 if (!str_to_match) 225 continue; 226 str_to_match -= strlen(val) + 1; 227 *eq = '='; 228 } 229 /* else: regex to match [subsystem/]device_name */ 230 231 { 232 regex_t match; 233 int result; 234 235 xregcomp(&match, val, REG_EXTENDED); 236 result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0); 237 regfree(&match); 238 //bb_error_msg("matches:"); 239 //for (int i = 0; i < ARRAY_SIZE(off); i++) { 240 // if (off[i].rm_so < 0) continue; 241 // bb_error_msg("match %d: '%.*s'\n", i, 242 // (int)(off[i].rm_eo - off[i].rm_so), 243 // device_name + off[i].rm_so); 244 //} 245 246 /* If no match, skip rest of line */ 247 /* (regexec returns whole pattern as "range" 0) */ 248 if (result 249 || off[0].rm_so 250 || ((int)off[0].rm_eo != (int)strlen(str_to_match)) 251 ) { 252 continue; /* this line doesn't match */ 253 } 254 } 255 line_matches: 256 /* This line matches. Stop parsing after parsing 257 * the rest the line unless keep_matching == 1 */ 258 259 /* 2nd field: uid:gid - device ownership */ 260 if (get_uidgid(&ugid, tokens[1], 1) == 0) 261 bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno); 262 263 /* 3rd field: mode - device permissions */ 264 bb_parse_mode(tokens[2], &mode); 265 266 val = tokens[3]; 267 /* 4th field (opt): ">|=alias" or "!" to not create the node */ 268 269 if (ENABLE_FEATURE_MDEV_RENAME && val) { 270 char *a, *s, *st; 271 272 a = val; 273 s = strchrnul(val, ' '); 274 st = strchrnul(val, '\t'); 275 if (st < s) 276 s = st; 277 st = (s[0] && s[1]) ? s+1 : NULL; 278 279 aliaslink = a[0]; 280 if (aliaslink == '!' && s == a+1) { 281 val = st; 282 /* "!": suppress node creation/deletion */ 283 major = -2; 284 } 285 else if (aliaslink == '>' || aliaslink == '=') { 286 val = st; 287 s[0] = '\0'; 288 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) { 289 char *p; 290 unsigned i, n; 291 292 /* substitute %1..9 with off[1..9], if any */ 293 n = 0; 294 s = a; 295 while (*s) 296 if (*s++ == '%') 297 n++; 298 299 p = alias = xzalloc(strlen(a) + n * strlen(str_to_match)); 300 s = a + 1; 301 while (*s) { 302 *p = *s; 303 if ('%' == *s) { 304 i = (s[1] - '0'); 305 if (i <= 9 && off[i].rm_so >= 0) { 306 n = off[i].rm_eo - off[i].rm_so; 307 strncpy(p, str_to_match + off[i].rm_so, n); 308 p += n - 1; 309 s++; 310 } 311 } 312 p++; 313 s++; 314 } 315 } else { 316 alias = xstrdup(a + 1); 317 } 318 } 319 } 320 321 if (ENABLE_FEATURE_MDEV_EXEC && val) { 322 const char *s = "$@*"; 323 const char *s2 = strchr(s, val[0]); 324 325 if (!s2) { 326 bb_error_msg("bad line %u", parser->lineno); 327 if (ENABLE_FEATURE_MDEV_RENAME) 328 free(alias); 329 continue; 330 } 331 332 /* Are we running this command now? 333 * Run $cmd on delete, @cmd on create, *cmd on both 334 */ 335 if (s2 - s != delete) { 336 /* We are here if: '*', 337 * or: '@' and delete = 0, 338 * or: '$' and delete = 1 339 */ 340 command = xstrdup(val + 1); 341 } 342 } 343 } 344 345 /* End of field parsing */ 346 347 /* "Execute" the line we found */ 348 { 349 const char *node_name; 350 351 node_name = device_name; 352 if (ENABLE_FEATURE_MDEV_RENAME && alias) 353 node_name = alias = build_alias(alias, device_name); 354 355 if (!delete && major >= 0) { 356 if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST) 357 bb_perror_msg("can't create '%s'", node_name); 358 if (major == G.root_major && minor == G.root_minor) 359 symlink(node_name, "root"); 360 if (ENABLE_FEATURE_MDEV_CONF) { 361 chmod(node_name, mode); 362 chown(node_name, ugid.uid, ugid.gid); 363 } 364 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 365 if (aliaslink == '>') 366 symlink(node_name, device_name); 367 } 368 } 369 370 if (ENABLE_FEATURE_MDEV_EXEC && command) { 371 /* setenv will leak memory, use putenv/unsetenv/free */ 372 char *s = xasprintf("%s=%s", "MDEV", node_name); 373 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); 374 putenv(s); 375 putenv(s1); 376 if (system(command) == -1) 377 bb_perror_msg("can't run '%s'", command); 378 bb_unsetenv_and_free(s1); 379 bb_unsetenv_and_free(s); 380 free(command); 381 } 382 383 if (delete && major >= -1) { 384 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 385 if (aliaslink == '>') 386 unlink(device_name); 387 } 388 unlink(node_name); 389 } 390 391 if (ENABLE_FEATURE_MDEV_RENAME) 392 free(alias); 393 } 394 395 /* We found matching line. 396 * Stop unless it was prefixed with '-' */ 397 if (ENABLE_FEATURE_MDEV_CONF && !keep_matching) 823 if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching) 398 824 break; 399 400 /* end of "while line is read from /etc/mdev.conf" */ 401 } while (ENABLE_FEATURE_MDEV_CONF); 402 403 if (ENABLE_FEATURE_MDEV_CONF) 404 config_close(parser); 405 free(subsystem_slash_devname); 825 } /* for (;;) */ 406 826 } 407 827 … … 421 841 strcpy(scratch, fileName); 422 842 scratch[len] = '\0'; 423 make_device( scratch, /*delete:*/ 0);843 make_device(/*DEVNAME:*/ NULL, scratch, OP_add); 424 844 425 845 return TRUE; … … 436 856 if (1 == depth) { 437 857 free(G.subsystem); 858 if (G.subsys_env) { 859 bb_unsetenv_and_free(G.subsys_env); 860 G.subsys_env = NULL; 861 } 438 862 G.subsystem = strrchr(fileName, '/'); 439 if (G.subsystem) 863 if (G.subsystem) { 440 864 G.subsystem = xstrdup(G.subsystem + 1); 865 G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); 866 putenv(G.subsys_env); 867 } 441 868 } 442 869 … … 458 885 { 459 886 int cnt; 460 int firmware_fd, loading_fd , data_fd;887 int firmware_fd, loading_fd; 461 888 462 889 /* check for /lib/firmware/$FIRMWARE */ 463 890 xchdir("/lib/firmware"); 464 firmware_fd = xopen(firmware, O_RDONLY); 465 466 /* in case we goto out ... */ 467 data_fd = -1; 891 firmware_fd = open(firmware, O_RDONLY); /* can fail */ 468 892 469 893 /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */ … … 471 895 for (cnt = 0; cnt < 30; ++cnt) { 472 896 loading_fd = open("loading", O_WRONLY); 473 if (loading_fd != -1)897 if (loading_fd >= 0) 474 898 goto loading; 475 899 sleep(1); … … 478 902 479 903 loading: 480 /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */ 481 if (full_write(loading_fd, "1", 1) != 1) 482 goto out; 483 484 /* load firmware into /sys/$DEVPATH/data */ 485 data_fd = open("data", O_WRONLY); 486 if (data_fd == -1) 487 goto out; 488 cnt = bb_copyfd_eof(firmware_fd, data_fd); 489 490 /* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */ 904 cnt = 0; 905 if (firmware_fd >= 0) { 906 int data_fd; 907 908 /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */ 909 if (full_write(loading_fd, "1", 1) != 1) 910 goto out; 911 912 /* load firmware into /sys/$DEVPATH/data */ 913 data_fd = open("data", O_WRONLY); 914 if (data_fd < 0) 915 goto out; 916 cnt = bb_copyfd_eof(firmware_fd, data_fd); 917 if (ENABLE_FEATURE_CLEAN_UP) 918 close(data_fd); 919 } 920 921 /* Tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" 922 * Note: we emit -1 also if firmware file wasn't found. 923 * There are cases when otherwise kernel would wait for minutes 924 * before timing out. 925 */ 491 926 if (cnt > 0) 492 927 full_write(loading_fd, "0", 1); … … 495 930 496 931 out: 932 xchdir("/dev"); 497 933 if (ENABLE_FEATURE_CLEAN_UP) { 498 934 close(firmware_fd); 499 935 close(loading_fd); 500 close(data_fd); 936 } 937 } 938 939 static char *curtime(void) 940 { 941 struct timeval tv; 942 gettimeofday(&tv, NULL); 943 sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec); 944 return G.timestr; 945 } 946 947 static void open_mdev_log(const char *seq, unsigned my_pid) 948 { 949 int logfd = open("mdev.log", O_WRONLY | O_APPEND); 950 if (logfd >= 0) { 951 xmove_fd(logfd, STDERR_FILENO); 952 G.verbose = 2; 953 applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid)); 954 } 955 } 956 957 /* If it exists, does /dev/mdev.seq match $SEQNUM? 958 * If it does not match, earlier mdev is running 959 * in parallel, and we need to wait. 960 * Active mdev pokes us with SIGCHLD to check the new file. 961 */ 962 static int 963 wait_for_seqfile(const char *seq) 964 { 965 /* We time out after 2 sec */ 966 static const struct timespec ts = { 0, 32*1000*1000 }; 967 int timeout = 2000 / 32; 968 int seq_fd = -1; 969 int do_once = 1; 970 sigset_t set_CHLD; 971 972 sigemptyset(&set_CHLD); 973 sigaddset(&set_CHLD, SIGCHLD); 974 sigprocmask(SIG_BLOCK, &set_CHLD, NULL); 975 976 for (;;) { 977 int seqlen; 978 char seqbuf[sizeof(int)*3 + 2]; 979 980 if (seq_fd < 0) { 981 seq_fd = open("mdev.seq", O_RDWR); 982 if (seq_fd < 0) 983 break; 984 } 985 seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0); 986 if (seqlen < 0) { 987 close(seq_fd); 988 seq_fd = -1; 989 break; 990 } 991 seqbuf[seqlen] = '\0'; 992 if (seqbuf[0] == '\n') { 993 /* seed file: write out seq ASAP */ 994 xwrite_str(seq_fd, seq); 995 xlseek(seq_fd, 0, SEEK_SET); 996 dbg2("first seq written"); 997 break; 998 } 999 if (strcmp(seq, seqbuf) == 0) { 1000 /* correct idx */ 1001 break; 1002 } 1003 if (do_once) { 1004 dbg2("%s waiting for '%s'", curtime(), seqbuf); 1005 do_once = 0; 1006 } 1007 if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) { 1008 dbg3("woken up"); 1009 continue; /* don't decrement timeout! */ 1010 } 1011 if (--timeout == 0) { 1012 dbg1("%s waiting for '%s'", "timed out", seqbuf); 1013 break; 1014 } 1015 } 1016 sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL); 1017 return seq_fd; 1018 } 1019 1020 static void signal_mdevs(unsigned my_pid) 1021 { 1022 procps_status_t* p = NULL; 1023 while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) { 1024 if (p->pid != my_pid 1025 && p->argv0 1026 && strcmp(bb_basename(p->argv0), "mdev") == 0 1027 ) { 1028 kill(p->pid, SIGCHLD); 1029 } 501 1030 } 502 1031 } … … 506 1035 { 507 1036 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE); 1037 1038 INIT_G(); 1039 1040 #if ENABLE_FEATURE_MDEV_CONF 1041 G.filename = "/etc/mdev.conf"; 1042 #endif 508 1043 509 1044 /* We can be called as hotplug helper */ … … 517 1052 518 1053 if (argv[1] && strcmp(argv[1], "-s") == 0) { 519 /* Scan:520 * mdev -s1054 /* 1055 * Scan: mdev -s 521 1056 */ 522 1057 struct stat st; 523 1058 1059 #if ENABLE_FEATURE_MDEV_CONF 1060 /* Same as xrealloc_vector(NULL, 4, 0): */ 1061 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec)); 1062 #endif 524 1063 xstat("/", &st); 525 1064 G.root_major = major(st.st_dev); 526 1065 G.root_minor = minor(st.st_dev); 1066 1067 putenv((char*)"ACTION=add"); 527 1068 528 1069 /* ACTION_FOLLOWLINKS is needed since in newer kernels … … 549 1090 char *seq; 550 1091 char *action; 551 char *env_path; 552 static const char keywords[] ALIGN1 = "remove\0add\0"; 553 enum { OP_remove = 0, OP_add }; 1092 char *env_devname; 1093 char *env_devpath; 1094 unsigned my_pid; 1095 int seq_fd; 554 1096 smalluint op; 555 1097 556 1098 /* Hotplug: 557 1099 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev 558 * ACTION can be "add" or "remove"1100 * ACTION can be "add", "remove", "change" 559 1101 * DEVPATH is like "/block/sda" or "/class/input/mice" 560 1102 */ 561 1103 action = getenv("ACTION"); 562 env_path = getenv("DEVPATH"); 1104 op = index_in_strings(keywords, action); 1105 env_devname = getenv("DEVNAME"); /* can be NULL */ 1106 env_devpath = getenv("DEVPATH"); 563 1107 G.subsystem = getenv("SUBSYSTEM"); 564 if (!action || !env_ path /*|| !G.subsystem*/)1108 if (!action || !env_devpath /*|| !G.subsystem*/) 565 1109 bb_show_usage(); 566 1110 fw = getenv("FIRMWARE"); 567 op = index_in_strings(keywords, action);568 /* If it exists, does /dev/mdev.seq match $SEQNUM?569 * If it does not match, earlier mdev is running570 * in parallel, and we need to wait */571 1111 seq = getenv("SEQNUM"); 572 if (seq) { 573 int timeout = 2000 / 32; /* 2000 msec */ 574 do { 575 int seqlen; 576 char seqbuf[sizeof(int)*3 + 2]; 577 578 seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1)); 579 if (seqlen < 0) { 580 seq = NULL; 581 break; 582 } 583 seqbuf[seqlen] = '\0'; 584 if (seqbuf[0] == '\n' /* seed file? */ 585 || strcmp(seq, seqbuf) == 0 /* correct idx? */ 586 ) { 587 break; 588 } 589 usleep(32*1000); 590 } while (--timeout); 591 } 592 593 snprintf(temp, PATH_MAX, "/sys%s", env_path); 1112 1113 my_pid = getpid(); 1114 open_mdev_log(seq, my_pid); 1115 1116 seq_fd = seq ? wait_for_seqfile(seq) : -1; 1117 1118 dbg1("%s " 1119 "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s" 1120 "%s%s", 1121 curtime(), 1122 action, G.subsystem, env_devname, env_devpath, 1123 fw ? " FW:" : "", fw ? fw : "" 1124 ); 1125 1126 snprintf(temp, PATH_MAX, "/sys%s", env_devpath); 594 1127 if (op == OP_remove) { 595 1128 /* Ignoring "remove firmware". It was reported … … 597 1130 * of device nodes. */ 598 1131 if (!fw) 599 make_device( temp, /*delete:*/ 1);600 } 601 else if (op == OP_add){602 make_device( temp, /*delete:*/ 0);1132 make_device(env_devname, temp, op); 1133 } 1134 else { 1135 make_device(env_devname, temp, op); 603 1136 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { 604 if ( fw)1137 if (op == OP_add && fw) 605 1138 load_firmware(fw, temp); 606 1139 } 607 1140 } 608 1141 609 if (seq) { 610 xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1)); 1142 dbg1("%s exiting", curtime()); 1143 if (seq_fd >= 0) { 1144 xwrite_str(seq_fd, utoa(xatou(seq) + 1)); 1145 signal_mdevs(my_pid); 611 1146 } 612 1147 }
Note:
See TracChangeset
for help on using the changeset viewer.