Changeset 3232 in MondoRescue for branches/3.2/mindi-busybox/util-linux/mdev.c


Ignore:
Timestamp:
Jan 1, 2014, 12:47:38 AM (10 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.21.1
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/3.2/mindi-busybox/util-linux/mdev.c

    r2725 r3232  
    88 * Licensed under GPLv2, see file LICENSE in this source tree.
    99 */
     10
     11//config:config MDEV
     12//config:   bool "mdev"
     13//config:   default y
     14//config:   select PLATFORM_LINUX
     15//config:   help
     16//config:     mdev is a mini-udev implementation for dynamically creating device
     17//config:     nodes in the /dev directory.
     18//config:
     19//config:     For more information, please see docs/mdev.txt
     20//config:
     21//config:config FEATURE_MDEV_CONF
     22//config:   bool "Support /etc/mdev.conf"
     23//config:   default y
     24//config:   depends on MDEV
     25//config:   help
     26//config:     Add support for the mdev config file to control ownership and
     27//config:     permissions of the device nodes.
     28//config:
     29//config:     For more information, please see docs/mdev.txt
     30//config:
     31//config:config FEATURE_MDEV_RENAME
     32//config:   bool "Support subdirs/symlinks"
     33//config:   default y
     34//config:   depends on FEATURE_MDEV_CONF
     35//config:   help
     36//config:     Add support for renaming devices and creating symlinks.
     37//config:
     38//config:     For more information, please see docs/mdev.txt
     39//config:
     40//config:config FEATURE_MDEV_RENAME_REGEXP
     41//config:   bool "Support regular expressions substitutions when renaming device"
     42//config:   default y
     43//config:   depends on FEATURE_MDEV_RENAME
     44//config:   help
     45//config:     Add support for regular expressions substitutions when renaming
     46//config:     device.
     47//config:
     48//config:config FEATURE_MDEV_EXEC
     49//config:   bool "Support command execution at device addition/removal"
     50//config:   default y
     51//config:   depends on FEATURE_MDEV_CONF
     52//config:   help
     53//config:     This adds support for an optional field to /etc/mdev.conf for
     54//config:     executing commands when devices are created/removed.
     55//config:
     56//config:     For more information, please see docs/mdev.txt
     57//config:
     58//config:config FEATURE_MDEV_LOAD_FIRMWARE
     59//config:   bool "Support loading of firmwares"
     60//config:   default y
     61//config:   depends on MDEV
     62//config:   help
     63//config:     Some devices need to load firmware before they can be usable.
     64//config:
     65//config:     These devices will request userspace look up the files in
     66//config:     /lib/firmware/ and if it exists, send it to the kernel for
     67//config:     loading into the hardware.
     68
     69//applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
     70
     71//kbuild:lib-$(CONFIG_MDEV) += mdev.o
     72
     73//usage:#define mdev_trivial_usage
     74//usage:       "[-s]"
     75//usage:#define mdev_full_usage "\n\n"
     76//usage:       "mdev -s is to be run during boot to scan /sys and populate /dev.\n"
     77//usage:       "\n"
     78//usage:       "Bare mdev is a kernel hotplug helper. To activate it:\n"
     79//usage:       "    echo /sbin/mdev >/proc/sys/kernel/hotplug\n"
     80//usage:    IF_FEATURE_MDEV_CONF(
     81//usage:       "\n"
     82//usage:       "It uses /etc/mdev.conf with lines\n"
     83//usage:       "    [-][ENV=regex;]...DEVNAME UID:GID PERM"
     84//usage:            IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
     85//usage:            IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
     86//usage:       "\n"
     87//usage:       "where DEVNAME is device name regex, @major,minor[-minor2], or\n"
     88//usage:       "environment variable regex. A common use of the latter is\n"
     89//usage:       "to load modules for hotplugged devices:\n"
     90//usage:       "    $MODALIAS=.* 0:0 660 @modprobe \"$MODALIAS\"\n"
     91//usage:    )
     92//usage:       "\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"
     95//usage:       "To activate this feature, create empty /dev/mdev.seq at boot.\n"
     96//usage:       "\n"
     97//usage:       "If /dev/mdev.log file exists, debug log will be appended to it."
     98
    1099#include "libbb.h"
    11100#include "xregex.h"
     
    28117 *
    29118 * If /etc/mdev.conf exists, it may modify /dev/device_name's properties.
    30  * /etc/mdev.conf file format:
    31  *
    32  * [-][subsystem/]device  user:grp  mode  [>|=path] [@|$|*command args...]
    33  * [-]@maj,min[-min2]     user:grp  mode  [>|=path] [@|$|*command args...]
    34  * [-]$envvar=val         user:grp  mode  [>|=path] [@|$|*command args...]
    35119 *
    36120 * Leading minus in 1st field means "don't stop on this line", otherwise
    37121 * search is stopped after the matching line is encountered.
    38122 *
    39  * The device name or "subsystem/device" combo is matched against 1st field
    40  * (which is a regex), or maj,min is matched against 1st field,
    41  * or specified environment variable (as regex) is matched against 1st field.
    42  *
    43  * $envvar=val format is useful for loading modules for hot-plugged devices
     123 * $envvar=regex format is useful for loading modules for hot-plugged devices
    44124 * which do not have driver loaded yet. In this case /sys/class/.../dev
    45125 * does not exist, but $MODALIAS is set to needed module's name
     
    62142 */
    63143
     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#define DEBUG_LVL 2
     234
     235#if DEBUG_LVL >= 1
     236# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
     237#else
     238# define dbg1(...) ((void)0)
     239#endif
     240#if DEBUG_LVL >= 2
     241# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
     242#else
     243# define dbg2(...) ((void)0)
     244#endif
     245#if DEBUG_LVL >= 3
     246# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
     247#else
     248# define dbg3(...) ((void)0)
     249#endif
     250
     251
     252static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
     253enum { OP_add, OP_remove };
     254
     255struct envmatch {
     256    struct envmatch *next;
     257    char *envname;
     258    regex_t match;
     259};
     260
     261struct rule {
     262    bool keep_matching;
     263    bool regex_compiled;
     264    mode_t mode;
     265    int maj, min0, min1;
     266    struct bb_uidgid_t ugid;
     267    char *envvar;
     268    char *ren_mov;
     269    IF_FEATURE_MDEV_EXEC(char *r_cmd;)
     270    regex_t match;
     271    struct envmatch *envmatch;
     272};
     273
    64274struct globals {
    65275    int root_major, root_minor;
     276    smallint verbose;
    66277    char *subsystem;
     278    char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
     279#if ENABLE_FEATURE_MDEV_CONF
     280    const char *filename;
     281    parser_t *parser;
     282    struct rule **rule_vec;
     283    unsigned rule_idx;
     284#endif
     285    struct rule cur_rule;
     286    char timestr[sizeof("60.123456")];
    67287} FIX_ALIASING;
    68288#define G (*(struct globals*)&bb_common_bufsiz1)
     289#define INIT_G() do { \
     290    IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \
     291    IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \
     292} while (0)
     293
    69294
    70295/* Prevent infinite loops in /sys symlinks */
    71296#define MAX_SYSFS_DEPTH 3
    72297
    73 /* We use additional 64+ bytes in make_device() */
    74 #define SCRATCH_SIZE 80
     298/* We use additional bytes in make_device() */
     299#define SCRATCH_SIZE 128
     300
     301#if ENABLE_FEATURE_MDEV_CONF
     302
     303static void make_default_cur_rule(void)
     304{
     305    memset(&G.cur_rule, 0, sizeof(G.cur_rule));
     306    G.cur_rule.maj = -1; /* "not a @major,minor rule" */
     307    G.cur_rule.mode = 0660;
     308}
     309
     310static void clean_up_cur_rule(void)
     311{
     312    struct envmatch *e;
     313
     314    free(G.cur_rule.envvar);
     315    free(G.cur_rule.ren_mov);
     316    if (G.cur_rule.regex_compiled)
     317        regfree(&G.cur_rule.match);
     318    IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
     319    e = G.cur_rule.envmatch;
     320    while (e) {
     321        free(e->envname);
     322        regfree(&e->match);
     323        e = e->next;
     324    }
     325    make_default_cur_rule();
     326}
     327
     328/* In later versions, endofname is in libbb */
     329#define endofname mdev_endofname
     330static
     331const char* FAST_FUNC
     332endofname(const char *name)
     333{
     334#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
     335#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
     336    if (!is_name(*name))
     337        return name;
     338    while (*++name) {
     339        if (!is_in_name(*name))
     340            break;
     341    }
     342    return name;
     343}
     344
     345static char *parse_envmatch_pfx(char *val)
     346{
     347    struct envmatch **nextp = &G.cur_rule.envmatch;
     348
     349    for (;;) {
     350        struct envmatch *e;
     351        char *semicolon;
     352        char *eq = strchr(val, '=');
     353        if (!eq /* || eq == val? */)
     354            return val;
     355        if (endofname(val) != eq)
     356            return val;
     357        semicolon = strchr(eq, ';');
     358        if (!semicolon)
     359            return val;
     360        /* ENVVAR=regex;... */
     361        *nextp = e = xzalloc(sizeof(*e));
     362        nextp = &e->next;
     363        e->envname = xstrndup(val, eq - val);
     364        *semicolon = '\0';
     365        xregcomp(&e->match, eq + 1, REG_EXTENDED);
     366        *semicolon = ';';
     367        val = semicolon + 1;
     368    }
     369}
     370
     371static void parse_next_rule(void)
     372{
     373    /* Note: on entry, G.cur_rule is set to default */
     374    while (1) {
     375        char *tokens[4];
     376        char *val;
     377
     378        /* No PARSE_EOL_COMMENTS, because command may contain '#' chars */
     379        if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS))
     380            break;
     381
     382        /* Fields: [-]regex uid:gid mode [alias] [cmd] */
     383        dbg3("token1:'%s'", tokens[1]);
     384
     385        /* 1st field */
     386        val = tokens[0];
     387        G.cur_rule.keep_matching = ('-' == val[0]);
     388        val += G.cur_rule.keep_matching; /* swallow leading dash */
     389        val = parse_envmatch_pfx(val);
     390        if (val[0] == '@') {
     391            /* @major,minor[-minor2] */
     392            /* (useful when name is ambiguous:
     393             * "/sys/class/usb/lp0" and
     394             * "/sys/class/printer/lp0")
     395             */
     396            int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1);
     397            if (sc < 2 || G.cur_rule.maj < 0) {
     398                bb_error_msg("bad @maj,min on line %d", G.parser->lineno);
     399                goto next_rule;
     400            }
     401            if (sc == 2)
     402                G.cur_rule.min1 = G.cur_rule.min0;
     403        } else {
     404            char *eq = strchr(val, '=');
     405            if (val[0] == '$') {
     406                /* $ENVVAR=regex ... */
     407                val++;
     408                if (!eq) {
     409                    bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
     410                    goto next_rule;
     411                }
     412                G.cur_rule.envvar = xstrndup(val, eq - val);
     413                val = eq + 1;
     414            }
     415            xregcomp(&G.cur_rule.match, val, REG_EXTENDED);
     416            G.cur_rule.regex_compiled = 1;
     417        }
     418
     419        /* 2nd field: uid:gid - device ownership */
     420        if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) {
     421            bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
     422            goto next_rule;
     423        }
     424
     425        /* 3rd field: mode - device permissions */
     426        bb_parse_mode(tokens[2], &G.cur_rule.mode);
     427
     428        /* 4th field (opt): ">|=alias" or "!" to not create the node */
     429        val = tokens[3];
     430        if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) {
     431            char *s = skip_non_whitespace(val);
     432            G.cur_rule.ren_mov = xstrndup(val, s - val);
     433            val = skip_whitespace(s);
     434        }
     435
     436        if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) {
     437            const char *s = "$@*";
     438            const char *s2 = strchr(s, val[0]);
     439            if (!s2) {
     440                bb_error_msg("bad line %u", G.parser->lineno);
     441                goto next_rule;
     442            }
     443            IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);)
     444        }
     445
     446        return;
     447 next_rule:
     448        clean_up_cur_rule();
     449    } /* while (config_read) */
     450
     451    dbg3("config_close(G.parser)");
     452    config_close(G.parser);
     453    G.parser = NULL;
     454
     455    return;
     456}
     457
     458/* If mdev -s, we remember rules in G.rule_vec[].
     459 * Otherwise, there is no point in doing it, and we just
     460 * save only one parsed rule in G.cur_rule.
     461 */
     462static const struct rule *next_rule(void)
     463{
     464    struct rule *rule;
     465
     466    /* Open conf file if we didn't do it yet */
     467    if (!G.parser && G.filename) {
     468        dbg3("config_open('%s')", G.filename);
     469        G.parser = config_open2(G.filename, fopen_for_read);
     470        G.filename = NULL;
     471    }
     472
     473    if (G.rule_vec) {
     474        /* mdev -s */
     475        /* Do we have rule parsed already? */
     476        if (G.rule_vec[G.rule_idx]) {
     477            dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
     478            return G.rule_vec[G.rule_idx++];
     479        }
     480        make_default_cur_rule();
     481    } else {
     482        /* not mdev -s */
     483        clean_up_cur_rule();
     484    }
     485
     486    /* Parse one more rule if file isn't fully read */
     487    rule = &G.cur_rule;
     488    if (G.parser) {
     489        parse_next_rule();
     490        if (G.rule_vec) { /* mdev -s */
     491            rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
     492            G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
     493            G.rule_vec[G.rule_idx++] = rule;
     494            dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
     495        }
     496    }
     497
     498    return rule;
     499}
     500
     501static int env_matches(struct envmatch *e)
     502{
     503    while (e) {
     504        int r;
     505        char *val = getenv(e->envname);
     506        if (!val)
     507            return 0;
     508        r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
     509        if (r != 0) /* no match */
     510            return 0;
     511        e = e->next;
     512    }
     513    return 1;
     514}
     515
     516#else
     517
     518# define next_rule() (&G.cur_rule)
     519
     520#endif
     521
     522static void mkdir_recursive(char *name)
     523{
     524    /* if name has many levels ("dir1/dir2"),
     525     * bb_make_directory() will create dir1 according to umask,
     526     * not according to its "mode" parameter.
     527     * Since we run with umask=0, need to temporarily switch it.
     528     */
     529    umask(022); /* "dir1" (if any) will be 0755 too */
     530    bb_make_directory(name, 0755, FILEUTILS_RECUR);
     531    umask(0);
     532}
    75533
    76534/* Builds an alias path.
     
    87545    if (dest) { /* ">bar/[baz]" ? */
    88546        *dest = '\0'; /* mkdir bar */
    89         bb_make_directory(alias, 0755, FILEUTILS_RECUR);
     547        mkdir_recursive(alias);
    90548        *dest = '/';
    91549        if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
     
    101559/* mknod in /dev based on a path like "/sys/block/hda/hda1"
    102560 * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
    103  * after NUL, but we promise to not mangle (IOW: to restore if needed)
     561 * after NUL, but we promise to not mangle (IOW: to restore NUL if needed)
    104562 * path string.
    105563 * NB2: "mdev -s" may call us many times, do not leak memory/fds!
     564 *
     565 * device_name = $DEVNAME (may be NULL)
     566 * path        = /sys/$DEVPATH
    106567 */
    107 static void make_device(char *path, int delete)
    108 {
    109     char *device_name, *subsystem_slash_devname;
     568static void make_device(char *device_name, char *path, int operation)
     569{
    110570    int major, minor, type, len;
    111     mode_t mode;
    112     parser_t *parser;
     571    char *path_end = path + strlen(path);
    113572
    114573    /* Try to read major/minor string.  Note that the kernel puts \n after
     
    118577     */
    119578    major = -1;
    120     if (!delete) {
    121         char *dev_maj_min = path + strlen(path);
    122 
    123         strcpy(dev_maj_min, "/dev");
    124         len = open_read_close(path, dev_maj_min + 1, 64);
    125         *dev_maj_min = '\0';
     579    if (operation == OP_add) {
     580        strcpy(path_end, "/dev");
     581        len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
     582        *path_end = '\0';
    126583        if (len < 1) {
    127584            if (!ENABLE_FEATURE_MDEV_EXEC)
     
    129586            /* no "dev" file, but we can still run scripts
    130587             * based on device name */
    131         } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) {
     588        } else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) {
     589            dbg1("dev %u,%u", major, minor);
     590        } else {
    132591            major = -1;
    133592        }
     
    135594    /* else: for delete, -1 still deletes the node, but < -1 suppresses that */
    136595
    137     /* Determine device name, type, major and minor */
    138     device_name = (char*) bb_basename(path);
    139     /* http://kernel.org/doc/pending/hotplug.txt says that only
     596    /* Determine device name */
     597    if (!device_name) {
     598        /*
     599         * There was no $DEVNAME envvar (for example, mdev -s never has).
     600         * But it is very useful: it contains the *path*, not only basename,
     601         * Thankfully, uevent file has it.
     602         * Example of .../sound/card0/controlC0/uevent file on Linux-3.7.7:
     603         * MAJOR=116
     604         * MINOR=7
     605         * DEVNAME=snd/controlC0
     606         */
     607        strcpy(path_end, "/uevent");
     608        len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
     609        if (len < 0)
     610            len = 0;
     611        *path_end = '\0';
     612        path_end[1 + len] = '\0';
     613        device_name = strstr(path_end + 1, "\nDEVNAME=");
     614        if (device_name) {
     615            device_name += sizeof("\nDEVNAME=")-1;
     616            strchrnul(device_name, '\n')[0] = '\0';
     617        } else {
     618            /* Fall back to just basename */
     619            device_name = (char*) bb_basename(path);
     620        }
     621    }
     622    /* Determine device type */
     623    /*
     624     * http://kernel.org/doc/pending/hotplug.txt says that only
    140625     * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
    141626     * But since 2.6.25 block devices are also in /sys/class/block.
    142      * We use strstr("/block/") to forestall future surprises. */
     627     * We use strstr("/block/") to forestall future surprises.
     628     */
    143629    type = S_IFCHR;
    144630    if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0))
    145631        type = S_IFBLK;
    146632
    147     /* Make path point to "subsystem/device_name" */
    148     subsystem_slash_devname = NULL;
    149     /* Check for coldplug invocations first */
    150     if (strncmp(path, "/sys/block/", 11) == 0) /* legacy case */
    151         path += sizeof("/sys/") - 1;
    152     else if (strncmp(path, "/sys/class/", 11) == 0)
    153         path += sizeof("/sys/class/") - 1;
    154     else {
    155         /* Example of a hotplug invocation:
    156          * SUBSYSTEM="block"
    157          * DEVPATH="/sys" + "/devices/virtual/mtd/mtd3/mtdblock3"
    158          * ("/sys" is added by mdev_main)
    159          * - path does not contain subsystem
     633#if ENABLE_FEATURE_MDEV_CONF
     634    G.rule_idx = 0; /* restart from the beginning (think mdev -s) */
     635#endif
     636    for (;;) {
     637        const char *str_to_match;
     638        regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
     639        char *command;
     640        char *alias;
     641        char aliaslink = aliaslink; /* for compiler */
     642        char *node_name;
     643        const struct rule *rule;
     644
     645        str_to_match = device_name;
     646
     647        rule = next_rule();
     648
     649#if ENABLE_FEATURE_MDEV_CONF
     650        if (!env_matches(rule->envmatch))
     651            continue;
     652        if (rule->maj >= 0) {  /* @maj,min rule */
     653            if (major != rule->maj)
     654                continue;
     655            if (minor < rule->min0 || minor > rule->min1)
     656                continue;
     657            memset(off, 0, sizeof(off));
     658            goto rule_matches;
     659        }
     660        if (rule->envvar) { /* $envvar=regex rule */
     661            str_to_match = getenv(rule->envvar);
     662            dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
     663            if (!str_to_match)
     664                continue;
     665        }
     666        /* else: str_to_match = device_name */
     667
     668        if (rule->regex_compiled) {
     669            int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
     670            dbg3("regex_match for '%s':%d", str_to_match, regex_match);
     671            //bb_error_msg("matches:");
     672            //for (int i = 0; i < ARRAY_SIZE(off); i++) {
     673            //  if (off[i].rm_so < 0) continue;
     674            //  bb_error_msg("match %d: '%.*s'\n", i,
     675            //      (int)(off[i].rm_eo - off[i].rm_so),
     676            //      device_name + off[i].rm_so);
     677            //}
     678
     679            if (regex_match != 0
     680            /* regexec returns whole pattern as "range" 0 */
     681             || off[0].rm_so != 0
     682             || (int)off[0].rm_eo != (int)strlen(str_to_match)
     683            ) {
     684                continue; /* this rule doesn't match */
     685            }
     686        }
     687        /* else: it's final implicit "match-all" rule */
     688 rule_matches:
     689        dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
     690#endif
     691        /* Build alias name */
     692        alias = NULL;
     693        if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
     694            aliaslink = rule->ren_mov[0];
     695            if (aliaslink == '!') {
     696                /* "!": suppress node creation/deletion */
     697                major = -2;
     698            }
     699            else if (aliaslink == '>' || aliaslink == '=') {
     700                if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
     701                    char *s;
     702                    char *p;
     703                    unsigned n;
     704
     705                    /* substitute %1..9 with off[1..9], if any */
     706                    n = 0;
     707                    s = rule->ren_mov;
     708                    while (*s)
     709                        if (*s++ == '%')
     710                            n++;
     711
     712                    p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match));
     713                    s = rule->ren_mov + 1;
     714                    while (*s) {
     715                        *p = *s;
     716                        if ('%' == *s) {
     717                            unsigned i = (s[1] - '0');
     718                            if (i <= 9 && off[i].rm_so >= 0) {
     719                                n = off[i].rm_eo - off[i].rm_so;
     720                                strncpy(p, str_to_match + off[i].rm_so, n);
     721                                p += n - 1;
     722                                s++;
     723                            }
     724                        }
     725                        p++;
     726                        s++;
     727                    }
     728                } else {
     729                    alias = xstrdup(rule->ren_mov + 1);
     730                }
     731            }
     732        }
     733        dbg3("alias:'%s'", alias);
     734
     735        command = NULL;
     736        IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
     737        if (command) {
     738            /* Are we running this command now?
     739             * Run @cmd on create, $cmd on delete, *cmd on any
     740             */
     741            if ((command[0] == '@' && operation == OP_add)
     742             || (command[0] == '$' && operation == OP_remove)
     743             || (command[0] == '*')
     744            ) {
     745                command++;
     746            } else {
     747                command = NULL;
     748            }
     749        }
     750        dbg3("command:'%s'", command);
     751
     752        /* "Execute" the line we found */
     753        node_name = device_name;
     754        if (ENABLE_FEATURE_MDEV_RENAME && alias) {
     755            node_name = alias = build_alias(alias, device_name);
     756            dbg3("alias2:'%s'", alias);
     757        }
     758
     759        if (operation == OP_add && major >= 0) {
     760            char *slash = strrchr(node_name, '/');
     761            if (slash) {
     762                *slash = '\0';
     763                mkdir_recursive(node_name);
     764                *slash = '/';
     765            }
     766            if (ENABLE_FEATURE_MDEV_CONF) {
     767                dbg1("mknod %s (%d,%d) %o"
     768                    " %u:%u",
     769                    node_name, major, minor, rule->mode | type,
     770                    rule->ugid.uid, rule->ugid.gid
     771                );
     772            } else {
     773                dbg1("mknod %s (%d,%d) %o",
     774                    node_name, major, minor, rule->mode | type
     775                );
     776            }
     777            if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
     778                bb_perror_msg("can't create '%s'", node_name);
     779            if (ENABLE_FEATURE_MDEV_CONF) {
     780                chmod(node_name, rule->mode);
     781                chown(node_name, rule->ugid.uid, rule->ugid.gid);
     782            }
     783            if (major == G.root_major && minor == G.root_minor)
     784                symlink(node_name, "root");
     785            if (ENABLE_FEATURE_MDEV_RENAME && alias) {
     786                if (aliaslink == '>') {
     787//TODO: on devtmpfs, device_name already exists and symlink() fails.
     788//End result is that instead of symlink, we have two nodes.
     789//What should be done?
     790                    dbg1("symlink: %s", device_name);
     791                    symlink(node_name, device_name);
     792                }
     793            }
     794        }
     795
     796        if (ENABLE_FEATURE_MDEV_EXEC && command) {
     797            /* setenv will leak memory, use putenv/unsetenv/free */
     798            char *s = xasprintf("%s=%s", "MDEV", node_name);
     799            putenv(s);
     800            dbg1("running: %s", command);
     801            if (system(command) == -1)
     802                bb_perror_msg("can't run '%s'", command);
     803            bb_unsetenv_and_free(s);
     804        }
     805
     806        if (operation == OP_remove && major >= -1) {
     807            if (ENABLE_FEATURE_MDEV_RENAME && alias) {
     808                if (aliaslink == '>') {
     809                    dbg1("unlink: %s", device_name);
     810                    unlink(device_name);
     811                }
     812            }
     813            dbg1("unlink: %s", node_name);
     814            unlink(node_name);
     815        }
     816
     817        if (ENABLE_FEATURE_MDEV_RENAME)
     818            free(alias);
     819
     820        /* We found matching line.
     821         * Stop unless it was prefixed with '-'
    160822         */
    161         subsystem_slash_devname = concat_path_file(G.subsystem, device_name);
    162         path = subsystem_slash_devname;
    163     }
    164 
    165     /* If we have config file, look up user settings */
    166     if (ENABLE_FEATURE_MDEV_CONF)
    167         parser = config_open2("/etc/mdev.conf", fopen_for_read);
    168 
    169     do {
    170         int keep_matching;
    171         struct bb_uidgid_t ugid;
    172         char *tokens[4];
    173         char *command = NULL;
    174         char *alias = NULL;
    175         char aliaslink = aliaslink; /* for compiler */
    176 
    177         /* Defaults in case we won't match any line */
    178         ugid.uid = ugid.gid = 0;
    179         keep_matching = 0;
    180         mode = 0660;
    181 
    182         if (ENABLE_FEATURE_MDEV_CONF
    183          && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)
    184         ) {
    185             char *val;
    186             char *str_to_match;
    187             regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
    188 
    189             val = tokens[0];
    190             keep_matching = ('-' == val[0]);
    191             val += keep_matching; /* swallow leading dash */
    192 
    193             /* Match against either "subsystem/device_name"
    194              * or "device_name" alone */
    195             str_to_match = strchr(val, '/') ? path : device_name;
    196 
    197             /* Fields: regex uid:gid mode [alias] [cmd] */
    198 
    199             if (val[0] == '@') {
    200                 /* @major,minor[-minor2] */
    201                 /* (useful when name is ambiguous:
    202                  * "/sys/class/usb/lp0" and
    203                  * "/sys/class/printer/lp0") */
    204                 int cmaj, cmin0, cmin1, sc;
    205                 if (major < 0)
    206                     continue; /* no dev, no match */
    207                 sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1);
    208                 if (sc < 1
    209                  || major != cmaj
    210                  || (sc == 2 && minor != cmin0)
    211                  || (sc == 3 && (minor < cmin0 || minor > cmin1))
    212                 ) {
    213                     continue; /* this line doesn't match */
    214                 }
    215                 goto line_matches;
    216             }
    217             if (val[0] == '$') {
    218                 /* regex to match an environment variable */
    219                 char *eq = strchr(++val, '=');
    220                 if (!eq)
    221                     continue;
    222                 *eq = '\0';
    223                 str_to_match = getenv(val);
    224                 if (!str_to_match)
    225                     continue;
    226                 str_to_match -= strlen(val) + 1;
    227                 *eq = '=';
    228             }
    229             /* else: regex to match [subsystem/]device_name */
    230 
    231             {
    232                 regex_t match;
    233                 int result;
    234 
    235                 xregcomp(&match, val, REG_EXTENDED);
    236                 result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0);
    237                 regfree(&match);
    238                 //bb_error_msg("matches:");
    239                 //for (int i = 0; i < ARRAY_SIZE(off); i++) {
    240                 //  if (off[i].rm_so < 0) continue;
    241                 //  bb_error_msg("match %d: '%.*s'\n", i,
    242                 //      (int)(off[i].rm_eo - off[i].rm_so),
    243                 //      device_name + off[i].rm_so);
    244                 //}
    245 
    246                 /* If no match, skip rest of line */
    247                 /* (regexec returns whole pattern as "range" 0) */
    248                 if (result
    249                  || off[0].rm_so
    250                  || ((int)off[0].rm_eo != (int)strlen(str_to_match))
    251                 ) {
    252                     continue; /* this line doesn't match */
    253                 }
    254             }
    255  line_matches:
    256             /* This line matches. Stop parsing after parsing
    257              * the rest the line unless keep_matching == 1 */
    258 
    259             /* 2nd field: uid:gid - device ownership */
    260             if (get_uidgid(&ugid, tokens[1], 1) == 0)
    261                 bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno);
    262 
    263             /* 3rd field: mode - device permissions */
    264             bb_parse_mode(tokens[2], &mode);
    265 
    266             val = tokens[3];
    267             /* 4th field (opt): ">|=alias" or "!" to not create the node */
    268 
    269             if (ENABLE_FEATURE_MDEV_RENAME && val) {
    270                 char *a, *s, *st;
    271 
    272                 a = val;
    273                 s = strchrnul(val, ' ');
    274                 st = strchrnul(val, '\t');
    275                 if (st < s)
    276                     s = st;
    277                 st = (s[0] && s[1]) ? s+1 : NULL;
    278 
    279                 aliaslink = a[0];
    280                 if (aliaslink == '!' && s == a+1) {
    281                     val = st;
    282                     /* "!": suppress node creation/deletion */
    283                     major = -2;
    284                 }
    285                 else if (aliaslink == '>' || aliaslink == '=') {
    286                     val = st;
    287                     s[0] = '\0';
    288                     if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
    289                         char *p;
    290                         unsigned i, n;
    291 
    292                         /* substitute %1..9 with off[1..9], if any */
    293                         n = 0;
    294                         s = a;
    295                         while (*s)
    296                             if (*s++ == '%')
    297                                 n++;
    298 
    299                         p = alias = xzalloc(strlen(a) + n * strlen(str_to_match));
    300                         s = a + 1;
    301                         while (*s) {
    302                             *p = *s;
    303                             if ('%' == *s) {
    304                                 i = (s[1] - '0');
    305                                 if (i <= 9 && off[i].rm_so >= 0) {
    306                                     n = off[i].rm_eo - off[i].rm_so;
    307                                     strncpy(p, str_to_match + off[i].rm_so, n);
    308                                     p += n - 1;
    309                                     s++;
    310                                 }
    311                             }
    312                             p++;
    313                             s++;
    314                         }
    315                     } else {
    316                         alias = xstrdup(a + 1);
    317                     }
    318                 }
    319             }
    320 
    321             if (ENABLE_FEATURE_MDEV_EXEC && val) {
    322                 const char *s = "$@*";
    323                 const char *s2 = strchr(s, val[0]);
    324 
    325                 if (!s2) {
    326                     bb_error_msg("bad line %u", parser->lineno);
    327                     if (ENABLE_FEATURE_MDEV_RENAME)
    328                         free(alias);
    329                     continue;
    330                 }
    331 
    332                 /* Are we running this command now?
    333                  * Run $cmd on delete, @cmd on create, *cmd on both
    334                  */
    335                 if (s2 - s != delete) {
    336                     /* We are here if: '*',
    337                      * or: '@' and delete = 0,
    338                      * or: '$' and delete = 1
    339                      */
    340                     command = xstrdup(val + 1);
    341                 }
    342             }
    343         }
    344 
    345         /* End of field parsing */
    346 
    347         /* "Execute" the line we found */
    348         {
    349             const char *node_name;
    350 
    351             node_name = device_name;
    352             if (ENABLE_FEATURE_MDEV_RENAME && alias)
    353                 node_name = alias = build_alias(alias, device_name);
    354 
    355             if (!delete && major >= 0) {
    356                 if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST)
    357                     bb_perror_msg("can't create '%s'", node_name);
    358                 if (major == G.root_major && minor == G.root_minor)
    359                     symlink(node_name, "root");
    360                 if (ENABLE_FEATURE_MDEV_CONF) {
    361                     chmod(node_name, mode);
    362                     chown(node_name, ugid.uid, ugid.gid);
    363                 }
    364                 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
    365                     if (aliaslink == '>')
    366                         symlink(node_name, device_name);
    367                 }
    368             }
    369 
    370             if (ENABLE_FEATURE_MDEV_EXEC && command) {
    371                 /* setenv will leak memory, use putenv/unsetenv/free */
    372                 char *s = xasprintf("%s=%s", "MDEV", node_name);
    373                 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
    374                 putenv(s);
    375                 putenv(s1);
    376                 if (system(command) == -1)
    377                     bb_perror_msg("can't run '%s'", command);
    378                 bb_unsetenv_and_free(s1);
    379                 bb_unsetenv_and_free(s);
    380                 free(command);
    381             }
    382 
    383             if (delete && major >= -1) {
    384                 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
    385                     if (aliaslink == '>')
    386                         unlink(device_name);
    387                 }
    388                 unlink(node_name);
    389             }
    390 
    391             if (ENABLE_FEATURE_MDEV_RENAME)
    392                 free(alias);
    393         }
    394 
    395         /* We found matching line.
    396          * Stop unless it was prefixed with '-' */
    397         if (ENABLE_FEATURE_MDEV_CONF && !keep_matching)
     823        if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
    398824            break;
    399 
    400     /* end of "while line is read from /etc/mdev.conf" */
    401     } while (ENABLE_FEATURE_MDEV_CONF);
    402 
    403     if (ENABLE_FEATURE_MDEV_CONF)
    404         config_close(parser);
    405     free(subsystem_slash_devname);
     825    } /* for (;;) */
    406826}
    407827
     
    421841    strcpy(scratch, fileName);
    422842    scratch[len] = '\0';
    423     make_device(scratch, /*delete:*/ 0);
     843    make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
    424844
    425845    return TRUE;
     
    436856    if (1 == depth) {
    437857        free(G.subsystem);
     858        if (G.subsys_env) {
     859            bb_unsetenv_and_free(G.subsys_env);
     860            G.subsys_env = NULL;
     861        }
    438862        G.subsystem = strrchr(fileName, '/');
    439         if (G.subsystem)
     863        if (G.subsystem) {
    440864            G.subsystem = xstrdup(G.subsystem + 1);
     865            G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
     866            putenv(G.subsys_env);
     867        }
    441868    }
    442869
     
    458885{
    459886    int cnt;
    460     int firmware_fd, loading_fd, data_fd;
     887    int firmware_fd, loading_fd;
    461888
    462889    /* check for /lib/firmware/$FIRMWARE */
    463890    xchdir("/lib/firmware");
    464     firmware_fd = xopen(firmware, O_RDONLY);
    465 
    466     /* in case we goto out ... */
    467     data_fd = -1;
     891    firmware_fd = open(firmware, O_RDONLY); /* can fail */
    468892
    469893    /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
     
    471895    for (cnt = 0; cnt < 30; ++cnt) {
    472896        loading_fd = open("loading", O_WRONLY);
    473         if (loading_fd != -1)
     897        if (loading_fd >= 0)
    474898            goto loading;
    475899        sleep(1);
     
    478902
    479903 loading:
    480     /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
    481     if (full_write(loading_fd, "1", 1) != 1)
    482         goto out;
    483 
    484     /* load firmware into /sys/$DEVPATH/data */
    485     data_fd = open("data", O_WRONLY);
    486     if (data_fd == -1)
    487         goto out;
    488     cnt = bb_copyfd_eof(firmware_fd, data_fd);
    489 
    490     /* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */
     904    cnt = 0;
     905    if (firmware_fd >= 0) {
     906        int data_fd;
     907
     908        /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
     909        if (full_write(loading_fd, "1", 1) != 1)
     910            goto out;
     911
     912        /* load firmware into /sys/$DEVPATH/data */
     913        data_fd = open("data", O_WRONLY);
     914        if (data_fd < 0)
     915            goto out;
     916        cnt = bb_copyfd_eof(firmware_fd, data_fd);
     917        if (ENABLE_FEATURE_CLEAN_UP)
     918            close(data_fd);
     919    }
     920
     921    /* Tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading"
     922     * Note: we emit -1 also if firmware file wasn't found.
     923     * There are cases when otherwise kernel would wait for minutes
     924     * before timing out.
     925     */
    491926    if (cnt > 0)
    492927        full_write(loading_fd, "0", 1);
     
    495930
    496931 out:
     932    xchdir("/dev");
    497933    if (ENABLE_FEATURE_CLEAN_UP) {
    498934        close(firmware_fd);
    499935        close(loading_fd);
    500         close(data_fd);
     936    }
     937}
     938
     939static char *curtime(void)
     940{
     941    struct timeval tv;
     942    gettimeofday(&tv, NULL);
     943    sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
     944    return G.timestr;
     945}
     946
     947static void open_mdev_log(const char *seq, unsigned my_pid)
     948{
     949    int logfd = open("mdev.log", O_WRONLY | O_APPEND);
     950    if (logfd >= 0) {
     951        xmove_fd(logfd, STDERR_FILENO);
     952        G.verbose = 2;
     953        applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
     954    }
     955}
     956
     957/* If it exists, does /dev/mdev.seq match $SEQNUM?
     958 * If it does not match, earlier mdev is running
     959 * in parallel, and we need to wait.
     960 * Active mdev pokes us with SIGCHLD to check the new file.
     961 */
     962static int
     963wait_for_seqfile(const char *seq)
     964{
     965    /* We time out after 2 sec */
     966    static const struct timespec ts = { 0, 32*1000*1000 };
     967    int timeout = 2000 / 32;
     968    int seq_fd = -1;
     969    int do_once = 1;
     970    sigset_t set_CHLD;
     971
     972    sigemptyset(&set_CHLD);
     973    sigaddset(&set_CHLD, SIGCHLD);
     974    sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
     975
     976    for (;;) {
     977        int seqlen;
     978        char seqbuf[sizeof(int)*3 + 2];
     979
     980        if (seq_fd < 0) {
     981            seq_fd = open("mdev.seq", O_RDWR);
     982            if (seq_fd < 0)
     983                break;
     984        }
     985        seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
     986        if (seqlen < 0) {
     987            close(seq_fd);
     988            seq_fd = -1;
     989            break;
     990        }
     991        seqbuf[seqlen] = '\0';
     992        if (seqbuf[0] == '\n') {
     993            /* seed file: write out seq ASAP */
     994            xwrite_str(seq_fd, seq);
     995            xlseek(seq_fd, 0, SEEK_SET);
     996            dbg2("first seq written");
     997            break;
     998        }
     999        if (strcmp(seq, seqbuf) == 0) {
     1000            /* correct idx */
     1001            break;
     1002        }
     1003        if (do_once) {
     1004            dbg2("%s waiting for '%s'", curtime(), seqbuf);
     1005            do_once = 0;
     1006        }
     1007        if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
     1008            dbg3("woken up");
     1009            continue; /* don't decrement timeout! */
     1010        }
     1011        if (--timeout == 0) {
     1012            dbg1("%s waiting for '%s'", "timed out", seqbuf);
     1013            break;
     1014        }
     1015    }
     1016    sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
     1017    return seq_fd;
     1018}
     1019
     1020static void signal_mdevs(unsigned my_pid)
     1021{
     1022    procps_status_t* p = NULL;
     1023    while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
     1024        if (p->pid != my_pid
     1025         && p->argv0
     1026         && strcmp(bb_basename(p->argv0), "mdev") == 0
     1027        ) {
     1028            kill(p->pid, SIGCHLD);
     1029        }
    5011030    }
    5021031}
     
    5061035{
    5071036    RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
     1037
     1038    INIT_G();
     1039
     1040#if ENABLE_FEATURE_MDEV_CONF
     1041    G.filename = "/etc/mdev.conf";
     1042#endif
    5081043
    5091044    /* We can be called as hotplug helper */
     
    5171052
    5181053    if (argv[1] && strcmp(argv[1], "-s") == 0) {
    519         /* Scan:
    520          * mdev -s
     1054        /*
     1055         * Scan: mdev -s
    5211056         */
    5221057        struct stat st;
    5231058
     1059#if ENABLE_FEATURE_MDEV_CONF
     1060        /* Same as xrealloc_vector(NULL, 4, 0): */
     1061        G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
     1062#endif
    5241063        xstat("/", &st);
    5251064        G.root_major = major(st.st_dev);
    5261065        G.root_minor = minor(st.st_dev);
     1066
     1067        putenv((char*)"ACTION=add");
    5271068
    5281069        /* ACTION_FOLLOWLINKS is needed since in newer kernels
     
    5491090        char *seq;
    5501091        char *action;
    551         char *env_path;
    552         static const char keywords[] ALIGN1 = "remove\0add\0";
    553         enum { OP_remove = 0, OP_add };
     1092        char *env_devname;
     1093        char *env_devpath;
     1094        unsigned my_pid;
     1095        int seq_fd;
    5541096        smalluint op;
    5551097
    5561098        /* Hotplug:
    5571099         * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
    558          * ACTION can be "add" or "remove"
     1100         * ACTION can be "add", "remove", "change"
    5591101         * DEVPATH is like "/block/sda" or "/class/input/mice"
    5601102         */
    5611103        action = getenv("ACTION");
    562         env_path = getenv("DEVPATH");
     1104        op = index_in_strings(keywords, action);
     1105        env_devname = getenv("DEVNAME"); /* can be NULL */
     1106        env_devpath = getenv("DEVPATH");
    5631107        G.subsystem = getenv("SUBSYSTEM");
    564         if (!action || !env_path /*|| !G.subsystem*/)
     1108        if (!action || !env_devpath /*|| !G.subsystem*/)
    5651109            bb_show_usage();
    5661110        fw = getenv("FIRMWARE");
    567         op = index_in_strings(keywords, action);
    568         /* If it exists, does /dev/mdev.seq match $SEQNUM?
    569          * If it does not match, earlier mdev is running
    570          * in parallel, and we need to wait */
    5711111        seq = getenv("SEQNUM");
    572         if (seq) {
    573             int timeout = 2000 / 32; /* 2000 msec */
    574             do {
    575                 int seqlen;
    576                 char seqbuf[sizeof(int)*3 + 2];
    577 
    578                 seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));
    579                 if (seqlen < 0) {
    580                     seq = NULL;
    581                     break;
    582                 }
    583                 seqbuf[seqlen] = '\0';
    584                 if (seqbuf[0] == '\n' /* seed file? */
    585                  || strcmp(seq, seqbuf) == 0 /* correct idx? */
    586                 ) {
    587                     break;
    588                 }
    589                 usleep(32*1000);
    590             } while (--timeout);
    591         }
    592 
    593         snprintf(temp, PATH_MAX, "/sys%s", env_path);
     1112
     1113        my_pid = getpid();
     1114        open_mdev_log(seq, my_pid);
     1115
     1116        seq_fd = seq ? wait_for_seqfile(seq) : -1;
     1117
     1118        dbg1("%s "
     1119            "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
     1120            "%s%s",
     1121            curtime(),
     1122            action, G.subsystem, env_devname, env_devpath,
     1123            fw ? " FW:" : "", fw ? fw : ""
     1124        );
     1125
     1126        snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
    5941127        if (op == OP_remove) {
    5951128            /* Ignoring "remove firmware". It was reported
     
    5971130             * of device nodes. */
    5981131            if (!fw)
    599                 make_device(temp, /*delete:*/ 1);
    600         }
    601         else if (op == OP_add) {
    602             make_device(temp, /*delete:*/ 0);
     1132                make_device(env_devname, temp, op);
     1133        }
     1134        else {
     1135            make_device(env_devname, temp, op);
    6031136            if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
    604                 if (fw)
     1137                if (op == OP_add && fw)
    6051138                    load_firmware(fw, temp);
    6061139            }
    6071140        }
    6081141
    609         if (seq) {
    610             xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
     1142        dbg1("%s exiting", curtime());
     1143        if (seq_fd >= 0) {
     1144            xwrite_str(seq_fd, utoa(xatou(seq) + 1));
     1145            signal_mdevs(my_pid);
    6111146        }
    6121147    }
Note: See TracChangeset for help on using the changeset viewer.