diff --git a/examples/mdev_fat.conf b/examples/mdev_fat.conf index da28bc36b..df329b4b5 100644 --- a/examples/mdev_fat.conf +++ b/examples/mdev_fat.conf @@ -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" 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 @@ //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);