mdev: add support for $DEVNAME and /dev/mdev.log debug aid
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 18 May 2012 02:45:35 +0000 (04:45 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 18 May 2012 02:45:35 +0000 (04:45 +0200)
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>
examples/mdev_fat.conf
util-linux/mdev.c

index da28bc3..df329b4 100644 (file)
@@ -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"
index 0a34122..70d1903 100644 (file)
@@ -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"
  * 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);