mdev: add support for $DEVNAME and /dev/mdev.log debug aid
No binary (build failed)
—
(log yok)
| @@ -108,6 +108,3 @@ usbdev[0-9].[0-9]_.* root:root 660 | |
| # zaptel devices | |
| zap(.*) root:dialout 660 =zap/%1 | |
| dahdi!(.*) root:dialout 660 =dahdi/%1 | |
| - | |
| - | # If $DEVNAME exists, create device with this name |
| - | $DEVNAME=bus/usb/.* 0:0 660 ! @mkdir -p "`dirname "$DEVNAME"`"; mknod -m 0660 "$DEVNAME" c "$MAJOR" "$MINOR" |
| @@ -93,6 +93,8 @@ | |
| //usage: "If /dev/mdev.seq file exists, mdev will wait for its value\n" | |
| //usage: "to match $SEQNUM variable. This prevents plug/unplug races.\n" | |
| //usage: "To activate this feature, create empty /dev/mdev.seq at boot." | |
| + | //usage: "\n" |
| + | //usage: "If /dev/mdev.log file exists, debug log will be appended to it.\n" |
| #include "libbb.h" | |
| #include "xregex.h" | |
| @@ -139,6 +141,98 @@ | |
| * This happens regardless of /sys/class/.../dev existence. | |
| */ | |
| + | /* Kernel's hotplug environment constantly changes. |
| + | * Here are new cases I observed on 3.1.0: |
| + | * |
| + | * Case with $DEVNAME and $DEVICE, not just $DEVPATH: |
| + | * ACTION=add |
| + | * BUSNUM=001 |
| + | * DEVICE=/proc/bus/usb/001/003 |
| + | * DEVNAME=bus/usb/001/003 |
| + | * DEVNUM=003 |
| + | * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5 |
| + | * DEVTYPE=usb_device |
| + | * MAJOR=189 |
| + | * MINOR=2 |
| + | * PRODUCT=18d1/4e12/227 |
| + | * SUBSYSTEM=usb |
| + | * TYPE=0/0/0 |
| + | * |
| + | * Case with $DEVICE, but no $DEVNAME - apparenty, usb iface notification? |
| + | * "Please load me a module" thing? |
| + | * ACTION=add |
| + | * DEVICE=/proc/bus/usb/001/003 |
| + | * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0 |
| + | * DEVTYPE=usb_interface |
| + | * INTERFACE=8/6/80 |
| + | * MODALIAS=usb:v18D1p4E12d0227dc00dsc00dp00ic08isc06ip50 |
| + | * PRODUCT=18d1/4e12/227 |
| + | * SUBSYSTEM=usb |
| + | * TYPE=0/0/0 |
| + | * |
| + | * ACTION=add |
| + | * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5 |
| + | * DEVTYPE=scsi_host |
| + | * SUBSYSTEM=scsi |
| + | * |
| + | * ACTION=add |
| + | * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/scsi_host/host5 |
| + | * SUBSYSTEM=scsi_host |
| + | * |
| + | * ACTION=add |
| + | * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0 |
| + | * DEVTYPE=scsi_target |
| + | * SUBSYSTEM=scsi |
| + | * |
| + | * Case with strange $MODALIAS: |
| + | * ACTION=add |
| + | * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0 |
| + | * DEVTYPE=scsi_device |
| + | * MODALIAS=scsi:t-0x00 |
| + | * SUBSYSTEM=scsi |
| + | * |
| + | * ACTION=add |
| + | * 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 |
| + | * SUBSYSTEM=scsi_disk |
| + | * |
| + | * ACTION=add |
| + | * 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 |
| + | * SUBSYSTEM=scsi_device |
| + | * |
| + | * Case with explicit $MAJOR/$MINOR (no need to read /sys/$DEVPATH/dev?): |
| + | * ACTION=add |
| + | * DEVNAME=bsg/5:0:0:0 |
| + | * 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 |
| + | * MAJOR=253 |
| + | * MINOR=1 |
| + | * SUBSYSTEM=bsg |
| + | * |
| + | * ACTION=add |
| + | * DEVPATH=/devices/virtual/bdi/8:16 |
| + | * SUBSYSTEM=bdi |
| + | * |
| + | * ACTION=add |
| + | * DEVNAME=sdb |
| + | * DEVPATH=/block/sdb |
| + | * DEVTYPE=disk |
| + | * MAJOR=8 |
| + | * MINOR=16 |
| + | * SUBSYSTEM=block |
| + | * |
| + | * Case with ACTION=change: |
| + | * ACTION=change |
| + | * DEVNAME=sdb |
| + | * DEVPATH=/block/sdb |
| + | * DEVTYPE=disk |
| + | * DISK_MEDIA_CHANGE=1 |
| + | * MAJOR=8 |
| + | * MINOR=16 |
| + | * SUBSYSTEM=block |
| + | */ |
| + | |
| + | static const char keywords[] ALIGN1 = "add\0remove\0change\0"; |
| + | enum { OP_add, OP_remove }; |
| + | |
| struct rule { | |
| bool keep_matching; | |
| bool regex_compiled; | |
| @@ -154,6 +248,7 @@ struct rule { | |
| struct globals { | |
| int root_major, root_minor; | |
| + | smallint verbose; |
| char *subsystem; | |
| #if ENABLE_FEATURE_MDEV_CONF | |
| const char *filename; | |
| @@ -366,13 +461,17 @@ static char *build_alias(char *alias, const char *device_name) | |
| * after NUL, but we promise to not mangle (IOW: to restore if needed) | |
| * path string. | |
| * NB2: "mdev -s" may call us many times, do not leak memory/fds! | |
| + | * |
| + | * device_name = $DEVNAME (may be NULL) |
| + | * path = /sys/$DEVPATH |
| */ | |
| - | static void make_device(char *path, int delete) |
| + | static void make_device(char *device_name, char *path, int operation) |
| { | |
| - | char *device_name, *subsystem_slash_devname; |
| + | char *subsystem_slash_devname; |
| int major, minor, type, len; | |
| - | dbg("%s('%s', delete:%d)", __func__, path, delete); |
| + | if (G.verbose) |
| + | bb_error_msg("make_device: %s, %s, op:%d", device_name, path, operation); |
| /* Try to read major/minor string. Note that the kernel puts \n after | |
| * 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) | |
| * We also depend on path having writeable space after it. | |
| */ | |
| major = -1; | |
| - | if (!delete) { |
| + | if (operation != OP_remove) { |
| char *dev_maj_min = path + strlen(path); | |
| strcpy(dev_maj_min, "/dev"); | |
| @@ -398,7 +497,8 @@ static void make_device(char *path, int delete) | |
| /* else: for delete, -1 still deletes the node, but < -1 suppresses that */ | |
| /* Determine device name, type, major and minor */ | |
| - | device_name = (char*) bb_basename(path); |
| + | if (!device_name) |
| + | device_name = (char*) bb_basename(path); |
| /* http://kernel.org/doc/pending/hotplug.txt says that only | |
| * "/sys/block/..." is for block devices. "/sys/bus" etc is not. | |
| * 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) | |
| char *command; | |
| char *alias; | |
| char aliaslink = aliaslink; /* for compiler */ | |
| - | const char *node_name; |
| + | char *node_name; |
| const struct rule *rule; | |
| str_to_match = ""; | |
| @@ -456,6 +556,8 @@ static void make_device(char *path, int delete) | |
| if (!str_to_match) | |
| continue; | |
| } else { | |
| + | //TODO: $DEVNAME can have slashes too, |
| + | // we should stop abusing '/' as a special syntax in our regex'es |
| /* regex to match [subsystem/]device_name */ | |
| str_to_match = (rule->regex_has_slash ? path : device_name); | |
| } | |
| @@ -537,7 +639,7 @@ static void make_device(char *path, int delete) | |
| /* Are we running this command now? | |
| * Run $cmd on delete, @cmd on create, *cmd on both | |
| */ | |
| - | if (s2 - s != delete) { |
| + | if (s2 - s != (operation == OP_remove) || *s2 == '*') { |
| /* We are here if: '*', | |
| * or: '@' and delete = 0, | |
| * or: '$' and delete = 1 | |
| @@ -556,8 +658,15 @@ static void make_device(char *path, int delete) | |
| dbg("alias2:'%s'", alias); | |
| } | |
| - | if (!delete && major >= 0) { |
| - | dbg("mknod('%s',%o,(%d,%d))", node_name, rule->mode | type, major, minor); |
| + | if (operation == OP_add && major >= 0) { |
| + | char *slash = strrchr(node_name, '/'); |
| + | if (slash) { |
| + | *slash = '\0'; |
| + | bb_make_directory(node_name, 0755, FILEUTILS_RECUR); |
| + | *slash = '/'; |
| + | } |
| + | if (G.verbose) |
| + | bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type); |
| if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) | |
| bb_perror_msg("can't create '%s'", node_name); | |
| if (major == G.root_major && minor == G.root_minor) | |
| @@ -571,6 +680,8 @@ static void make_device(char *path, int delete) | |
| //TODO: on devtmpfs, device_name already exists and symlink() fails. | |
| //End result is that instead of symlink, we have two nodes. | |
| //What should be done? | |
| + | if (G.verbose) |
| + | bb_error_msg("symlink: %s", device_name); |
| symlink(node_name, device_name); | |
| } | |
| } | |
| @@ -582,17 +693,24 @@ static void make_device(char *path, int delete) | |
| char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); | |
| putenv(s); | |
| putenv(s1); | |
| + | if (G.verbose) |
| + | bb_error_msg("running: %s", command); |
| if (system(command) == -1) | |
| bb_perror_msg("can't run '%s'", command); | |
| bb_unsetenv_and_free(s1); | |
| bb_unsetenv_and_free(s); | |
| } | |
| - | if (delete && major >= -1) { |
| + | if (operation == OP_remove && major >= -1) { |
| if (ENABLE_FEATURE_MDEV_RENAME && alias) { | |
| - | if (aliaslink == '>') |
| + | if (aliaslink == '>') { |
| + | if (G.verbose) |
| + | bb_error_msg("unlink: %s", device_name); |
| unlink(device_name); | |
| + | } |
| } | |
| + | if (G.verbose) |
| + | bb_error_msg("unlink: %s", node_name); |
| unlink(node_name); | |
| } | |
| @@ -624,7 +742,7 @@ static int FAST_FUNC fileAction(const char *fileName, | |
| strcpy(scratch, fileName); | |
| scratch[len] = '\0'; | |
| - | make_device(scratch, /*delete:*/ 0); |
| + | make_device(/*DEVNAME:*/ NULL, scratch, OP_add); |
| return TRUE; | |
| } | |
| @@ -762,9 +880,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |
| char *fw; | |
| char *seq; | |
| char *action; | |
| - | char *env_path; |
| - | static const char keywords[] ALIGN1 = "remove\0add\0"; |
| - | enum { OP_remove = 0, OP_add }; |
| + | char *env_devname; |
| + | char *env_devpath; |
| smalluint op; | |
| /* Hotplug: | |
| @@ -773,9 +890,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |
| * DEVPATH is like "/block/sda" or "/class/input/mice" | |
| */ | |
| action = getenv("ACTION"); | |
| - | env_path = getenv("DEVPATH"); |
| + | env_devname = getenv("DEVNAME"); /* can be NULL */ |
| + | env_devpath = getenv("DEVPATH"); |
| G.subsystem = getenv("SUBSYSTEM"); | |
| - | if (!action || !env_path /*|| !G.subsystem*/) |
| + | if (!action || !env_devpath /*|| !G.subsystem*/) |
| bb_show_usage(); | |
| fw = getenv("FIRMWARE"); | |
| op = index_in_strings(keywords, action); | |
| @@ -804,16 +922,25 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |
| } while (--timeout); | |
| } | |
| - | snprintf(temp, PATH_MAX, "/sys%s", env_path); |
| + | { |
| + | int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND); |
| + | if (logfd >= 0) { |
| + | xmove_fd(logfd, STDERR_FILENO); |
| + | G.verbose = 1; |
| + | bb_error_msg("pid: %u seq: %s action: %s", getpid(), seq, action); |
| + | } |
| + | } |
| + | |
| + | snprintf(temp, PATH_MAX, "/sys%s", env_devpath); |
| if (op == OP_remove) { | |
| /* Ignoring "remove firmware". It was reported | |
| * to happen and to cause erroneous deletion | |
| * of device nodes. */ | |
| if (!fw) | |
| - | make_device(temp, /*delete:*/ 1); |
| + | make_device(env_devname, temp, op); |
| } | |
| else if (op == OP_add) { | |
| - | make_device(temp, /*delete:*/ 0); |
| + | make_device(env_devname, temp, op); |
| if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { | |
| if (fw) | |
| load_firmware(fw, temp); | |