diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2012-05-18 04:45:35 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2012-05-18 04:45:35 +0200 |
| commit | 516530c932bd17d87c9eb4347a490be051e495f4 (patch) | |
| tree | 28c18a2785280ff61f819174c5a72d4348298f9e /util-linux | |
| parent | 7c8aa2b47cf76f710959a1f4a914010aca425016 (diff) | |
| download | busybox-w32-516530c932bd17d87c9eb4347a490be051e495f4.tar.gz busybox-w32-516530c932bd17d87c9eb4347a490be051e495f4.tar.bz2 busybox-w32-516530c932bd17d87c9eb4347a490be051e495f4.zip | |
mdev: add support for $DEVNAME and /dev/mdev.log debug aid
function old new delta
make_device 1843 2083 +240
mdev_main 712 804 +92
packed_usage 29236 29251 +15
keywords 288 19 -269
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/1 up/down: 347/-269) Total: 78 bytes
text data bss dec hex filename
887506 497 7584 895587 daa63 busybox_old
887982 497 7584 896063 dac3f busybox_unstripped
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'util-linux')
| -rw-r--r-- | util-linux/mdev.c | 167 |
1 files changed, 147 insertions, 20 deletions
diff --git a/util-linux/mdev.c b/util-linux/mdev.c index 0a34122b4..70d19033d 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c | |||
| @@ -93,6 +93,8 @@ | |||
| 93 | //usage: "If /dev/mdev.seq file exists, mdev will wait for its value\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" | 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." | 95 | //usage: "To activate this feature, create empty /dev/mdev.seq at boot." |
| 96 | //usage: "\n" | ||
| 97 | //usage: "If /dev/mdev.log file exists, debug log will be appended to it.\n" | ||
| 96 | 98 | ||
| 97 | #include "libbb.h" | 99 | #include "libbb.h" |
| 98 | #include "xregex.h" | 100 | #include "xregex.h" |
| @@ -139,6 +141,98 @@ | |||
| 139 | * This happens regardless of /sys/class/.../dev existence. | 141 | * This happens regardless of /sys/class/.../dev existence. |
| 140 | */ | 142 | */ |
| 141 | 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 | static const char keywords[] ALIGN1 = "add\0remove\0change\0"; | ||
| 234 | enum { OP_add, OP_remove }; | ||
| 235 | |||
| 142 | struct rule { | 236 | struct rule { |
| 143 | bool keep_matching; | 237 | bool keep_matching; |
| 144 | bool regex_compiled; | 238 | bool regex_compiled; |
| @@ -154,6 +248,7 @@ struct rule { | |||
| 154 | 248 | ||
| 155 | struct globals { | 249 | struct globals { |
| 156 | int root_major, root_minor; | 250 | int root_major, root_minor; |
| 251 | smallint verbose; | ||
| 157 | char *subsystem; | 252 | char *subsystem; |
| 158 | #if ENABLE_FEATURE_MDEV_CONF | 253 | #if ENABLE_FEATURE_MDEV_CONF |
| 159 | const char *filename; | 254 | const char *filename; |
| @@ -366,13 +461,17 @@ static char *build_alias(char *alias, const char *device_name) | |||
| 366 | * after NUL, but we promise to not mangle (IOW: to restore if needed) | 461 | * after NUL, but we promise to not mangle (IOW: to restore if needed) |
| 367 | * path string. | 462 | * path string. |
| 368 | * NB2: "mdev -s" may call us many times, do not leak memory/fds! | 463 | * NB2: "mdev -s" may call us many times, do not leak memory/fds! |
| 464 | * | ||
| 465 | * device_name = $DEVNAME (may be NULL) | ||
| 466 | * path = /sys/$DEVPATH | ||
| 369 | */ | 467 | */ |
| 370 | static void make_device(char *path, int delete) | 468 | static void make_device(char *device_name, char *path, int operation) |
| 371 | { | 469 | { |
| 372 | char *device_name, *subsystem_slash_devname; | 470 | char *subsystem_slash_devname; |
| 373 | int major, minor, type, len; | 471 | int major, minor, type, len; |
| 374 | 472 | ||
| 375 | dbg("%s('%s', delete:%d)", __func__, path, delete); | 473 | if (G.verbose) |
| 474 | bb_error_msg("make_device: %s, %s, op:%d", device_name, path, operation); | ||
| 376 | 475 | ||
| 377 | /* Try to read major/minor string. Note that the kernel puts \n after | 476 | /* Try to read major/minor string. Note that the kernel puts \n after |
| 378 | * the data, so we don't need to worry about null terminating the string | 477 | * the data, so we don't need to worry about null terminating the string |
| @@ -380,7 +479,7 @@ static void make_device(char *path, int delete) | |||
| 380 | * We also depend on path having writeable space after it. | 479 | * We also depend on path having writeable space after it. |
| 381 | */ | 480 | */ |
| 382 | major = -1; | 481 | major = -1; |
| 383 | if (!delete) { | 482 | if (operation != OP_remove) { |
| 384 | char *dev_maj_min = path + strlen(path); | 483 | char *dev_maj_min = path + strlen(path); |
| 385 | 484 | ||
| 386 | strcpy(dev_maj_min, "/dev"); | 485 | strcpy(dev_maj_min, "/dev"); |
| @@ -398,7 +497,8 @@ static void make_device(char *path, int delete) | |||
| 398 | /* else: for delete, -1 still deletes the node, but < -1 suppresses that */ | 497 | /* else: for delete, -1 still deletes the node, but < -1 suppresses that */ |
| 399 | 498 | ||
| 400 | /* Determine device name, type, major and minor */ | 499 | /* Determine device name, type, major and minor */ |
| 401 | device_name = (char*) bb_basename(path); | 500 | if (!device_name) |
| 501 | device_name = (char*) bb_basename(path); | ||
| 402 | /* http://kernel.org/doc/pending/hotplug.txt says that only | 502 | /* http://kernel.org/doc/pending/hotplug.txt says that only |
| 403 | * "/sys/block/..." is for block devices. "/sys/bus" etc is not. | 503 | * "/sys/block/..." is for block devices. "/sys/bus" etc is not. |
| 404 | * But since 2.6.25 block devices are also in /sys/class/block. | 504 | * But since 2.6.25 block devices are also in /sys/class/block. |
| @@ -434,7 +534,7 @@ static void make_device(char *path, int delete) | |||
| 434 | char *command; | 534 | char *command; |
| 435 | char *alias; | 535 | char *alias; |
| 436 | char aliaslink = aliaslink; /* for compiler */ | 536 | char aliaslink = aliaslink; /* for compiler */ |
| 437 | const char *node_name; | 537 | char *node_name; |
| 438 | const struct rule *rule; | 538 | const struct rule *rule; |
| 439 | 539 | ||
| 440 | str_to_match = ""; | 540 | str_to_match = ""; |
| @@ -456,6 +556,8 @@ static void make_device(char *path, int delete) | |||
| 456 | if (!str_to_match) | 556 | if (!str_to_match) |
| 457 | continue; | 557 | continue; |
| 458 | } else { | 558 | } else { |
| 559 | //TODO: $DEVNAME can have slashes too, | ||
| 560 | // we should stop abusing '/' as a special syntax in our regex'es | ||
| 459 | /* regex to match [subsystem/]device_name */ | 561 | /* regex to match [subsystem/]device_name */ |
| 460 | str_to_match = (rule->regex_has_slash ? path : device_name); | 562 | str_to_match = (rule->regex_has_slash ? path : device_name); |
| 461 | } | 563 | } |
| @@ -537,7 +639,7 @@ static void make_device(char *path, int delete) | |||
| 537 | /* Are we running this command now? | 639 | /* Are we running this command now? |
| 538 | * Run $cmd on delete, @cmd on create, *cmd on both | 640 | * Run $cmd on delete, @cmd on create, *cmd on both |
| 539 | */ | 641 | */ |
| 540 | if (s2 - s != delete) { | 642 | if (s2 - s != (operation == OP_remove) || *s2 == '*') { |
| 541 | /* We are here if: '*', | 643 | /* We are here if: '*', |
| 542 | * or: '@' and delete = 0, | 644 | * or: '@' and delete = 0, |
| 543 | * or: '$' and delete = 1 | 645 | * or: '$' and delete = 1 |
| @@ -556,8 +658,15 @@ static void make_device(char *path, int delete) | |||
| 556 | dbg("alias2:'%s'", alias); | 658 | dbg("alias2:'%s'", alias); |
| 557 | } | 659 | } |
| 558 | 660 | ||
| 559 | if (!delete && major >= 0) { | 661 | if (operation == OP_add && major >= 0) { |
| 560 | dbg("mknod('%s',%o,(%d,%d))", node_name, rule->mode | type, major, minor); | 662 | char *slash = strrchr(node_name, '/'); |
| 663 | if (slash) { | ||
| 664 | *slash = '\0'; | ||
| 665 | bb_make_directory(node_name, 0755, FILEUTILS_RECUR); | ||
| 666 | *slash = '/'; | ||
| 667 | } | ||
| 668 | if (G.verbose) | ||
| 669 | bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type); | ||
| 561 | if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) | 670 | if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) |
| 562 | bb_perror_msg("can't create '%s'", node_name); | 671 | bb_perror_msg("can't create '%s'", node_name); |
| 563 | if (major == G.root_major && minor == G.root_minor) | 672 | if (major == G.root_major && minor == G.root_minor) |
| @@ -571,6 +680,8 @@ static void make_device(char *path, int delete) | |||
| 571 | //TODO: on devtmpfs, device_name already exists and symlink() fails. | 680 | //TODO: on devtmpfs, device_name already exists and symlink() fails. |
| 572 | //End result is that instead of symlink, we have two nodes. | 681 | //End result is that instead of symlink, we have two nodes. |
| 573 | //What should be done? | 682 | //What should be done? |
| 683 | if (G.verbose) | ||
| 684 | bb_error_msg("symlink: %s", device_name); | ||
| 574 | symlink(node_name, device_name); | 685 | symlink(node_name, device_name); |
| 575 | } | 686 | } |
| 576 | } | 687 | } |
| @@ -582,17 +693,24 @@ static void make_device(char *path, int delete) | |||
| 582 | char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); | 693 | char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); |
| 583 | putenv(s); | 694 | putenv(s); |
| 584 | putenv(s1); | 695 | putenv(s1); |
| 696 | if (G.verbose) | ||
| 697 | bb_error_msg("running: %s", command); | ||
| 585 | if (system(command) == -1) | 698 | if (system(command) == -1) |
| 586 | bb_perror_msg("can't run '%s'", command); | 699 | bb_perror_msg("can't run '%s'", command); |
| 587 | bb_unsetenv_and_free(s1); | 700 | bb_unsetenv_and_free(s1); |
| 588 | bb_unsetenv_and_free(s); | 701 | bb_unsetenv_and_free(s); |
| 589 | } | 702 | } |
| 590 | 703 | ||
| 591 | if (delete && major >= -1) { | 704 | if (operation == OP_remove && major >= -1) { |
| 592 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { | 705 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
| 593 | if (aliaslink == '>') | 706 | if (aliaslink == '>') { |
| 707 | if (G.verbose) | ||
| 708 | bb_error_msg("unlink: %s", device_name); | ||
| 594 | unlink(device_name); | 709 | unlink(device_name); |
| 710 | } | ||
| 595 | } | 711 | } |
| 712 | if (G.verbose) | ||
| 713 | bb_error_msg("unlink: %s", node_name); | ||
| 596 | unlink(node_name); | 714 | unlink(node_name); |
| 597 | } | 715 | } |
| 598 | 716 | ||
| @@ -624,7 +742,7 @@ static int FAST_FUNC fileAction(const char *fileName, | |||
| 624 | 742 | ||
| 625 | strcpy(scratch, fileName); | 743 | strcpy(scratch, fileName); |
| 626 | scratch[len] = '\0'; | 744 | scratch[len] = '\0'; |
| 627 | make_device(scratch, /*delete:*/ 0); | 745 | make_device(/*DEVNAME:*/ NULL, scratch, OP_add); |
| 628 | 746 | ||
| 629 | return TRUE; | 747 | return TRUE; |
| 630 | } | 748 | } |
| @@ -762,9 +880,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
| 762 | char *fw; | 880 | char *fw; |
| 763 | char *seq; | 881 | char *seq; |
| 764 | char *action; | 882 | char *action; |
| 765 | char *env_path; | 883 | char *env_devname; |
| 766 | static const char keywords[] ALIGN1 = "remove\0add\0"; | 884 | char *env_devpath; |
| 767 | enum { OP_remove = 0, OP_add }; | ||
| 768 | smalluint op; | 885 | smalluint op; |
| 769 | 886 | ||
| 770 | /* Hotplug: | 887 | /* Hotplug: |
| @@ -773,9 +890,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
| 773 | * DEVPATH is like "/block/sda" or "/class/input/mice" | 890 | * DEVPATH is like "/block/sda" or "/class/input/mice" |
| 774 | */ | 891 | */ |
| 775 | action = getenv("ACTION"); | 892 | action = getenv("ACTION"); |
| 776 | env_path = getenv("DEVPATH"); | 893 | env_devname = getenv("DEVNAME"); /* can be NULL */ |
| 894 | env_devpath = getenv("DEVPATH"); | ||
| 777 | G.subsystem = getenv("SUBSYSTEM"); | 895 | G.subsystem = getenv("SUBSYSTEM"); |
| 778 | if (!action || !env_path /*|| !G.subsystem*/) | 896 | if (!action || !env_devpath /*|| !G.subsystem*/) |
| 779 | bb_show_usage(); | 897 | bb_show_usage(); |
| 780 | fw = getenv("FIRMWARE"); | 898 | fw = getenv("FIRMWARE"); |
| 781 | op = index_in_strings(keywords, action); | 899 | op = index_in_strings(keywords, action); |
| @@ -804,16 +922,25 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
| 804 | } while (--timeout); | 922 | } while (--timeout); |
| 805 | } | 923 | } |
| 806 | 924 | ||
| 807 | snprintf(temp, PATH_MAX, "/sys%s", env_path); | 925 | { |
| 926 | int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND); | ||
| 927 | if (logfd >= 0) { | ||
| 928 | xmove_fd(logfd, STDERR_FILENO); | ||
| 929 | G.verbose = 1; | ||
| 930 | bb_error_msg("pid: %u seq: %s action: %s", getpid(), seq, action); | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | snprintf(temp, PATH_MAX, "/sys%s", env_devpath); | ||
| 808 | if (op == OP_remove) { | 935 | if (op == OP_remove) { |
| 809 | /* Ignoring "remove firmware". It was reported | 936 | /* Ignoring "remove firmware". It was reported |
| 810 | * to happen and to cause erroneous deletion | 937 | * to happen and to cause erroneous deletion |
| 811 | * of device nodes. */ | 938 | * of device nodes. */ |
| 812 | if (!fw) | 939 | if (!fw) |
| 813 | make_device(temp, /*delete:*/ 1); | 940 | make_device(env_devname, temp, op); |
| 814 | } | 941 | } |
| 815 | else if (op == OP_add) { | 942 | else if (op == OP_add) { |
| 816 | make_device(temp, /*delete:*/ 0); | 943 | make_device(env_devname, temp, op); |
| 817 | if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { | 944 | if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { |
| 818 | if (fw) | 945 | if (fw) |
| 819 | load_firmware(fw, temp); | 946 | load_firmware(fw, temp); |
