setpriv: placete "declaration of 'index' shadows a global declaration" warning
[oweals/busybox.git] / util-linux / mdev.c
index ccc00d3656ac088b4a8412fb45b3a201f034a3ab..8acc4d21da99d0af701b623b73e4c1b2c5e34110 100644 (file)
@@ -9,62 +9,62 @@
  */
 
 //config:config MDEV
-//config:      bool "mdev"
+//config:      bool "mdev (16 kb)"
 //config:      default y
 //config:      select PLATFORM_LINUX
 //config:      help
-//config:        mdev is a mini-udev implementation for dynamically creating device
-//config:        nodes in the /dev directory.
+//config:      mdev is a mini-udev implementation for dynamically creating device
+//config:      nodes in the /dev directory.
 //config:
-//config:        For more information, please see docs/mdev.txt
+//config:      For more information, please see docs/mdev.txt
 //config:
 //config:config FEATURE_MDEV_CONF
 //config:      bool "Support /etc/mdev.conf"
 //config:      default y
 //config:      depends on MDEV
 //config:      help
-//config:        Add support for the mdev config file to control ownership and
-//config:        permissions of the device nodes.
+//config:      Add support for the mdev config file to control ownership and
+//config:      permissions of the device nodes.
 //config:
-//config:        For more information, please see docs/mdev.txt
+//config:      For more information, please see docs/mdev.txt
 //config:
 //config:config FEATURE_MDEV_RENAME
 //config:      bool "Support subdirs/symlinks"
 //config:      default y
 //config:      depends on FEATURE_MDEV_CONF
 //config:      help
-//config:        Add support for renaming devices and creating symlinks.
+//config:      Add support for renaming devices and creating symlinks.
 //config:
-//config:        For more information, please see docs/mdev.txt
+//config:      For more information, please see docs/mdev.txt
 //config:
 //config:config FEATURE_MDEV_RENAME_REGEXP
 //config:      bool "Support regular expressions substitutions when renaming device"
 //config:      default y
 //config:      depends on FEATURE_MDEV_RENAME
 //config:      help
-//config:        Add support for regular expressions substitutions when renaming
-//config:        device.
+//config:      Add support for regular expressions substitutions when renaming
+//config:      device.
 //config:
 //config:config FEATURE_MDEV_EXEC
 //config:      bool "Support command execution at device addition/removal"
 //config:      default y
 //config:      depends on FEATURE_MDEV_CONF
 //config:      help
-//config:        This adds support for an optional field to /etc/mdev.conf for
-//config:        executing commands when devices are created/removed.
+//config:      This adds support for an optional field to /etc/mdev.conf for
+//config:      executing commands when devices are created/removed.
 //config:
-//config:        For more information, please see docs/mdev.txt
+//config:      For more information, please see docs/mdev.txt
 //config:
 //config:config FEATURE_MDEV_LOAD_FIRMWARE
-//config:      bool "Support loading of firmwares"
+//config:      bool "Support loading of firmware"
 //config:      default y
 //config:      depends on MDEV
 //config:      help
-//config:        Some devices need to load firmware before they can be usable.
+//config:      Some devices need to load firmware before they can be usable.
 //config:
-//config:        These devices will request userspace look up the files in
-//config:        /lib/firmware/ and if it exists, send it to the kernel for
-//config:        loading into the hardware.
+//config:      These devices will request userspace look up the files in
+//config:      /lib/firmware/ and if it exists, send it to the kernel for
+//config:      loading into the hardware.
 
 //applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
 
@@ -97,6 +97,7 @@
 //usage:       "If /dev/mdev.log file exists, debug log will be appended to it."
 
 #include "libbb.h"
+#include "common_bufsiz.h"
 #include "xregex.h"
 
 /* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
@@ -283,10 +284,11 @@ struct globals {
        unsigned rule_idx;
 #endif
        struct rule cur_rule;
-       char timestr[sizeof("60.123456")];
+       char timestr[sizeof("HH:MM:SS.123456")];
 } FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
 #define INIT_G() do { \
+       setup_common_bufsiz(); \
        IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \
        IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \
 } while (0)
@@ -400,13 +402,13 @@ static void parse_next_rule(void)
                }
 
                /* 2nd field: uid:gid - device ownership */
-               if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) {
+               if (get_uidgid(&G.cur_rule.ugid, tokens[1]) == 0) {
                        bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
                        goto next_rule;
                }
 
                /* 3rd field: mode - device permissions */
-               bb_parse_mode(tokens[2], &G.cur_rule.mode);
+               G.cur_rule.mode = bb_parse_mode(tokens[2], G.cur_rule.mode);
 
                /* 4th field (opt): ">|=alias" or "!" to not create the node */
                val = tokens[3];
@@ -471,7 +473,7 @@ static const struct rule *next_rule(void)
        if (G.parser) {
                parse_next_rule();
                if (G.rule_vec) { /* mdev -s */
-                       rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
+                       rule = xmemdup(&G.cur_rule, sizeof(G.cur_rule));
                        G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
                        G.rule_vec[G.rule_idx++] = rule;
                        dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
@@ -541,8 +543,7 @@ static char *build_alias(char *alias, const char *device_name)
 
 /* mknod in /dev based on a path like "/sys/block/hda/hda1"
  * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
- * after NUL, but we promise to not mangle (IOW: to restore NUL if needed)
- * path string.
+ * after NUL, but we promise to not mangle it (IOW: to restore NUL if needed).
  * NB2: "mdev -s" may call us many times, do not leak memory/fds!
  *
  * device_name = $DEVNAME (may be NULL)
@@ -808,41 +809,39 @@ static void make_device(char *device_name, char *path, int operation)
        } /* for (;;) */
 }
 
-/* File callback for /sys/ traversal */
+/* File callback for /sys/ traversal.
+ * We act only on "/sys/.../dev" (pseudo)file
+ */
 static int FAST_FUNC fileAction(const char *fileName,
                struct stat *statbuf UNUSED_PARAM,
                void *userData,
                int depth UNUSED_PARAM)
 {
        size_t len = strlen(fileName) - 4; /* can't underflow */
-       char *scratch = userData;
-
-       /* len check is for paranoid reasons */
-       if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
-               return FALSE;
-
-       strcpy(scratch, fileName);
-       scratch[len] = '\0';
-       make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
-
-       return TRUE;
-}
-
-/* Directory callback for /sys/ traversal */
-static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
-               struct stat *statbuf UNUSED_PARAM,
-               void *userData UNUSED_PARAM,
-               int depth)
-{
-       /* Extract device subsystem -- the name of the directory
-        * under /sys/class/ */
-       if (1 == depth) {
+       char *path = userData;  /* char array[PATH_MAX + SCRATCH_SIZE] */
+       char subsys[PATH_MAX];
+       int res;
+
+       /* Is it a ".../dev" file? (len check is for paranoid reasons) */
+       if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
+               return FALSE; /* not .../dev */
+
+       strcpy(path, fileName);
+       path[len] = '\0';
+
+       /* Read ".../subsystem" symlink in the same directory where ".../dev" is */
+       strcpy(subsys, path);
+       strcpy(subsys + len, "/subsystem");
+       res = readlink(subsys, subsys, sizeof(subsys)-1);
+       if (res > 0) {
+               subsys[res] = '\0';
                free(G.subsystem);
                if (G.subsys_env) {
                        bb_unsetenv_and_free(G.subsys_env);
                        G.subsys_env = NULL;
                }
-               G.subsystem = strrchr(fileName, '/');
+               /* Set G.subsystem and $SUBSYSTEM from symlink's last component */
+               G.subsystem = strrchr(subsys, '/');
                if (G.subsystem) {
                        G.subsystem = xstrdup(G.subsystem + 1);
                        G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
@@ -850,6 +849,17 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
                }
        }
 
+       make_device(/*DEVNAME:*/ NULL, path, OP_add);
+
+       return TRUE;
+}
+
+/* Directory callback for /sys/ traversal */
+static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
+               struct stat *statbuf UNUSED_PARAM,
+               void *userData UNUSED_PARAM,
+               int depth)
+{
        return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
 }
 
@@ -870,8 +880,9 @@ static void load_firmware(const char *firmware, const char *sysfs_path)
        int firmware_fd, loading_fd;
 
        /* check for /lib/firmware/$FIRMWARE */
-       xchdir("/lib/firmware");
-       firmware_fd = open(firmware, O_RDONLY); /* can fail */
+       firmware_fd = -1;
+       if (chdir("/lib/firmware") == 0)
+               firmware_fd = open(firmware, O_RDONLY); /* can fail */
 
        /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
        xchdir(sysfs_path);
@@ -923,7 +934,11 @@ static char *curtime(void)
 {
        struct timeval tv;
        gettimeofday(&tv, NULL);
-       sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
+       sprintf(
+               strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec),
+               ".%06u",
+               (unsigned)tv.tv_usec
+       );
        return G.timestr;
 }
 
@@ -943,7 +958,7 @@ static void open_mdev_log(const char *seq, unsigned my_pid)
  * Active mdev pokes us with SIGCHLD to check the new file.
  */
 static int
-wait_for_seqfile(const char *seq)
+wait_for_seqfile(unsigned expected_seq)
 {
        /* We time out after 2 sec */
        static const struct timespec ts = { 0, 32*1000*1000 };
@@ -958,12 +973,14 @@ wait_for_seqfile(const char *seq)
 
        for (;;) {
                int seqlen;
-               char seqbuf[sizeof(int)*3 + 2];
+               char seqbuf[sizeof(long)*3 + 2];
+               unsigned seqbufnum;
 
                if (seq_fd < 0) {
                        seq_fd = open("mdev.seq", O_RDWR);
                        if (seq_fd < 0)
                                break;
+                       close_on_exec_on(seq_fd);
                }
                seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
                if (seqlen < 0) {
@@ -974,17 +991,25 @@ wait_for_seqfile(const char *seq)
                seqbuf[seqlen] = '\0';
                if (seqbuf[0] == '\n' || seqbuf[0] == '\0') {
                        /* seed file: write out seq ASAP */
-                       xwrite_str(seq_fd, seq);
+                       xwrite_str(seq_fd, utoa(expected_seq));
                        xlseek(seq_fd, 0, SEEK_SET);
                        dbg2("first seq written");
                        break;
                }
-               if (strcmp(seq, seqbuf) == 0) {
+               seqbufnum = atoll(seqbuf);
+               if (seqbufnum == expected_seq) {
                        /* correct idx */
                        break;
                }
+               if (seqbufnum > expected_seq) {
+                       /* a later mdev runs already (this was seen by users to happen) */
+                       /* do not overwrite seqfile on exit */
+                       close(seq_fd);
+                       seq_fd = -1;
+                       break;
+               }
                if (do_once) {
-                       dbg2("%s waiting for '%s'", curtime(), seqbuf);
+                       dbg2("%s mdev.seq='%s', need '%u'", curtime(), seqbuf, expected_seq);
                        do_once = 0;
                }
                if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
@@ -992,7 +1017,7 @@ wait_for_seqfile(const char *seq)
                        continue; /* don't decrement timeout! */
                }
                if (--timeout == 0) {
-                       dbg1("%s waiting for '%s'", "timed out", seqbuf);
+                       dbg1("%s mdev.seq='%s'", "timed out", seqbuf);
                        break;
                }
        }
@@ -1049,25 +1074,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
 
                putenv((char*)"ACTION=add");
 
-               /* ACTION_FOLLOWLINKS is needed since in newer kernels
-                * /sys/block/loop* (for example) are symlinks to dirs,
-                * not real directories.
-                * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
-                * but we can't enforce that on users)
-                */
-               if (access("/sys/class/block", F_OK) != 0) {
-                       /* Scan obsolete /sys/block only if /sys/class/block
-                        * doesn't exist. Otherwise we'll have dupes.
-                        * Also, do not complain if it doesn't exist.
-                        * Some people configure kernel to have no blockdevs.
-                        */
-                       recursive_action("/sys/block",
-                               ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
-                               fileAction, dirAction, temp, 0);
-               }
-               recursive_action("/sys/class",
-                       ACTION_RECURSE | ACTION_FOLLOWLINKS,
-                       fileAction, dirAction, temp, 0);
+               /* Create all devices from /sys/dev hierarchy */
+               recursive_action("/sys/dev",
+                                ACTION_RECURSE | ACTION_FOLLOWLINKS,
+                                fileAction, dirAction, temp, 0);
        } else {
                char *fw;
                char *seq;
@@ -1075,6 +1085,7 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
                char *env_devname;
                char *env_devpath;
                unsigned my_pid;
+               unsigned seqnum = seqnum; /* for compiler */
                int seq_fd;
                smalluint op;
 
@@ -1096,7 +1107,11 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
                my_pid = getpid();
                open_mdev_log(seq, my_pid);
 
-               seq_fd = seq ? wait_for_seqfile(seq) : -1;
+               seq_fd = -1;
+               if (seq) {
+                       seqnum = atoll(seq);
+                       seq_fd = wait_for_seqfile(seqnum);
+               }
 
                dbg1("%s "
                        "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
@@ -1124,7 +1139,7 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
 
                dbg1("%s exiting", curtime());
                if (seq_fd >= 0) {
-                       xwrite_str(seq_fd, utoa(xatou(seq) + 1));
+                       xwrite_str(seq_fd, utoa(seqnum + 1));
                        signal_mdevs(my_pid);
                }
        }