Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

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

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  *
    43 * mdev - Mini udev for busybox
    54 *
     
    76 * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
    87 *
    9  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
     8 * Licensed under GPLv2, see file LICENSE in this source tree.
    109 */
    11 
    1210#include "libbb.h"
    1311#include "xregex.h"
    1412
     13/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
     14 * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev
     15 * contains "4:0\n". Directory name is taken as device name, path component
     16 * directly after /sys/class/ as subsystem. In this example, "tty0" and "tty".
     17 * Then mdev creates the /dev/device_name node.
     18 * If /sys/class/.../dev file does not exist, mdev still may act
     19 * on this device: see "@|$|*command args..." parameter in config file.
     20 *
     21 * mdev w/o parameters is called as hotplug helper. It takes device
     22 * and subsystem names from $DEVPATH and $SUBSYSTEM, extracts
     23 * maj,min from "/sys/$DEVPATH/dev" and also examines
     24 * $ACTION ("add"/"delete") and $FIRMWARE.
     25 *
     26 * If action is "add", mdev creates /dev/device_name similarly to mdev -s.
     27 * (todo: explain "delete" and $FIRMWARE)
     28 *
     29 * 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...]
     35 *
     36 * Leading minus in 1st field means "don't stop on this line", otherwise
     37 * search is stopped after the matching line is encountered.
     38 *
     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
     44 * which do not have driver loaded yet. In this case /sys/class/.../dev
     45 * does not exist, but $MODALIAS is set to needed module's name
     46 * (actually, an alias to it) by kernel. This rule instructs mdev
     47 * to load the module and exit:
     48 *    $MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"
     49 * The kernel will generate another hotplug event when /sys/class/.../dev
     50 * file appears.
     51 *
     52 * When line matches, the device node is created, chmod'ed and chown'ed,
     53 * moved to path, and if >path, a symlink to moved node is created,
     54 * all this if /sys/class/.../dev exists.
     55 *    Examples:
     56 *    =loop/      - moves to /dev/loop
     57 *    >disk/sda%1 - moves to /dev/disk/sdaN, makes /dev/sdaN a symlink
     58 *
     59 * Then "command args..." is executed (via sh -c 'command args...').
     60 * @:execute on creation, $:on deletion, *:on both.
     61 * This happens regardless of /sys/class/.../dev existence.
     62 */
     63
    1564struct globals {
    1665    int root_major, root_minor;
    17 };
     66    char *subsystem;
     67} FIX_ALIASING;
    1868#define G (*(struct globals*)&bb_common_bufsiz1)
    19 #define root_major (G.root_major)
    20 #define root_minor (G.root_minor)
    21 
    22 #define MAX_SYSFS_DEPTH 3 /* prevent infinite loops in /sys symlinks */
    23 
    24 /* mknod in /dev based on a path like "/sys/block/hda/hda1" */
     69
     70/* Prevent infinite loops in /sys symlinks */
     71#define MAX_SYSFS_DEPTH 3
     72
     73/* We use additional 64+ bytes in make_device() */
     74#define SCRATCH_SIZE 80
     75
     76/* Builds an alias path.
     77 * This function potentionally reallocates the alias parameter.
     78 * Only used for ENABLE_FEATURE_MDEV_RENAME
     79 */
     80static char *build_alias(char *alias, const char *device_name)
     81{
     82    char *dest;
     83
     84    /* ">bar/": rename to bar/device_name */
     85    /* ">bar[/]baz": rename to bar[/]baz */
     86    dest = strrchr(alias, '/');
     87    if (dest) { /* ">bar/[baz]" ? */
     88        *dest = '\0'; /* mkdir bar */
     89        bb_make_directory(alias, 0755, FILEUTILS_RECUR);
     90        *dest = '/';
     91        if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
     92            dest = alias;
     93            alias = concat_path_file(alias, device_name);
     94            free(dest);
     95        }
     96    }
     97
     98    return alias;
     99}
     100
     101/* mknod in /dev based on a path like "/sys/block/hda/hda1"
     102 * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
     103 * after NUL, but we promise to not mangle (IOW: to restore if needed)
     104 * path string.
     105 * NB2: "mdev -s" may call us many times, do not leak memory/fds!
     106 */
    25107static void make_device(char *path, int delete)
    26108{
    27     const char *device_name;
     109    char *device_name, *subsystem_slash_devname;
    28110    int major, minor, type, len;
    29     int mode = 0660;
    30     uid_t uid = 0;
    31     gid_t gid = 0;
    32     char *temp = path + strlen(path);
    33     char *command = NULL;
     111    mode_t mode;
     112    parser_t *parser;
    34113
    35114    /* Try to read major/minor string.  Note that the kernel puts \n after
    36115     * the data, so we don't need to worry about null terminating the string
    37      * because sscanf() will stop at the first nondigit, which \n is.  We
    38      * also depend on path having writeable space after it. */
    39 
     116     * because sscanf() will stop at the first nondigit, which \n is.
     117     * We also depend on path having writeable space after it.
     118     */
     119    major = -1;
    40120    if (!delete) {
    41         strcat(path, "/dev");
    42         len = open_read_close(path, temp + 1, 64);
    43         *temp++ = 0;
    44         if (len < 1) return;
     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';
     126        if (len < 1) {
     127            if (!ENABLE_FEATURE_MDEV_EXEC)
     128                return;
     129            /* no "dev" file, but we can still run scripts
     130             * based on device name */
     131        } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) {
     132            major = -1;
     133        }
    45134    }
     135    /* else: for delete, -1 still deletes the node, but < -1 suppresses that */
    46136
    47137    /* Determine device name, type, major and minor */
    48 
    49     device_name = bb_basename(path);
    50     type = path[5]=='c' ? S_IFCHR : S_IFBLK;
    51 
    52     /* If we have a config file, look up permissions for this device */
    53 
    54     if (ENABLE_FEATURE_MDEV_CONF) {
    55         char *conf, *pos, *end;
    56         int line, fd;
    57 
    58         /* mmap the config file */
    59         fd = open("/etc/mdev.conf", O_RDONLY);
    60         if (fd < 0)
    61             goto end_parse;
    62         len = xlseek(fd, 0, SEEK_END);
    63         conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
    64         close(fd);
    65         if (!conf)
    66             goto end_parse;
    67 
    68         line = 0;
    69         /* Loop through lines in mmaped file*/
    70         for (pos=conf; pos-conf<len;) {
    71             int field;
    72             char *end2;
    73 
    74             line++;
    75             /* find end of this line */
    76             for (end=pos; end-conf<len && *end!='\n'; end++)
    77                 ;
    78 
    79             /* Three fields: regex, uid:gid, mode */
    80             for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC);
    81                     field++)
     138    device_name = (char*) bb_basename(path);
     139    /* http://kernel.org/doc/pending/hotplug.txt says that only
     140     * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
     141     * But since 2.6.25 block devices are also in /sys/class/block.
     142     * We use strstr("/block/") to forestall future surprises. */
     143    type = S_IFCHR;
     144    if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0))
     145        type = S_IFBLK;
     146
     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
     160         */
     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
    82231            {
    83                 /* Skip whitespace */
    84                 while (pos<end && isspace(*pos)) pos++;
    85                 if (pos==end || *pos=='#') break;
    86                 for (end2=pos;
    87                     end2<end && !isspace(*end2) && *end2!='#'; end2++)
    88                     ;
    89 
    90                 if (field == 0) {
    91                     /* Regex to match this device */
    92 
    93                     char *regex = xstrndup(pos, end2-pos);
    94                     regex_t match;
    95                     regmatch_t off;
    96                     int result;
    97 
    98                     /* Is this it? */
    99                     xregcomp(&match,regex, REG_EXTENDED);
    100                     result = regexec(&match, device_name, 1, &off, 0);
    101                     regfree(&match);
    102                     free(regex);
    103 
    104                     /* If not this device, skip rest of line */
    105                     if (result || off.rm_so
    106                             || off.rm_eo != strlen(device_name))
    107                         break;
    108                 }
    109                 if (field == 1) {
    110                     /* uid:gid */
    111 
    112                     char *s, *s2;
    113 
    114                     /* Find : */
    115                     for (s=pos; s<end2 && *s!=':'; s++)
    116                         ;
    117                     if (s == end2) break;
    118 
    119                     /* Parse UID */
    120                     uid = strtoul(pos, &s2, 10);
    121                     if (s != s2) {
    122                         struct passwd *pass;
    123                         char *_unam = xstrndup(pos, s-pos);
    124                         pass = getpwnam(_unam);
    125                         free(_unam);
    126                         if (!pass) break;
    127                         uid = pass->pw_uid;
     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);
    128317                    }
    129                     s++;
    130                     /* parse GID */
    131                     gid = strtoul(s, &s2, 10);
    132                     if (end2 != s2) {
    133                         struct group *grp;
    134                         char *_grnam = xstrndup(s, end2-s);
    135                         grp = getgrnam(_grnam);
    136                         free(_grnam);
    137                         if (!grp) break;
    138                         gid = grp->gr_gid;
    139                     }
    140                 }
    141                 if (field == 2) {
    142                     /* mode */
    143 
    144                     mode = strtoul(pos, &pos, 8);
    145                     if (pos != end2) break;
    146                 }
    147                 if (ENABLE_FEATURE_MDEV_EXEC && field == 3) {
    148                     // Command to run
    149                     const char *s = "@$*";
    150                     const char *s2;
    151                     s2 = strchr(s, *pos++);
    152                     if (!s2) {
    153                         // Force error
    154                         field = 1;
    155                         break;
    156                     }
    157                     if ((s2-s+1) & (1<<delete))
    158                         command = xstrndup(pos, end-pos);
    159                 }
    160 
    161                 pos = end2;
    162             }
    163 
    164             /* Did everything parse happily? */
    165 
    166             if (field > 2) break;
    167             if (field) bb_error_msg_and_die("bad line %d",line);
    168 
    169             /* Next line */
    170             pos = ++end;
    171         }
    172         munmap(conf, len);
    173  end_parse: /* nothing */ ;
    174     }
    175 
    176     umask(0);
    177     if (!delete) {
    178         if (sscanf(temp, "%d:%d", &major, &minor) != 2) return;
    179         if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
    180             bb_perror_msg_and_die("mknod %s", device_name);
    181 
    182         if (major == root_major && minor == root_minor)
    183             symlink(device_name, "root");
    184 
    185         if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid);
    186     }
    187     if (command) {
    188         /* setenv will leak memory, so use putenv */
    189         char *s = xasprintf("MDEV=%s", device_name);
    190         putenv(s);
    191         if (system(command) == -1)
    192             bb_perror_msg_and_die("cannot run %s", command);
    193         s[4] = '\0';
    194         unsetenv(s);
    195         free(s);
    196         free(command);
    197     }
    198     if (delete) unlink(device_name);
     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)
     398            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);
    199406}
    200407
    201408/* File callback for /sys/ traversal */
    202 static int fileAction(const char *fileName, struct stat *statbuf,
    203                       void *userData, int depth)
     409static int FAST_FUNC fileAction(const char *fileName,
     410        struct stat *statbuf UNUSED_PARAM,
     411        void *userData,
     412        int depth UNUSED_PARAM)
    204413{
    205     size_t len = strlen(fileName) - 4;
     414    size_t len = strlen(fileName) - 4; /* can't underflow */
    206415    char *scratch = userData;
    207416
    208     if (strcmp(fileName + len, "/dev"))
     417    /* len check is for paranoid reasons */
     418    if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
    209419        return FALSE;
    210420
    211421    strcpy(scratch, fileName);
    212     scratch[len] = 0;
    213     make_device(scratch, 0);
     422    scratch[len] = '\0';
     423    make_device(scratch, /*delete:*/ 0);
    214424
    215425    return TRUE;
     
    217427
    218428/* Directory callback for /sys/ traversal */
    219 static int dirAction(const char *fileName, struct stat *statbuf,
    220                       void *userData, int depth)
     429static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
     430        struct stat *statbuf UNUSED_PARAM,
     431        void *userData UNUSED_PARAM,
     432        int depth)
    221433{
     434    /* Extract device subsystem -- the name of the directory
     435     * under /sys/class/ */
     436    if (1 == depth) {
     437        free(G.subsystem);
     438        G.subsystem = strrchr(fileName, '/');
     439        if (G.subsystem)
     440            G.subsystem = xstrdup(G.subsystem + 1);
     441    }
     442
    222443    return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
    223444}
     
    234455 * - kernel loads firmware into device
    235456 */
    236 static void load_firmware(const char *const firmware, const char *const sysfs_path)
     457static void load_firmware(const char *firmware, const char *sysfs_path)
    237458{
    238459    int cnt;
    239460    int firmware_fd, loading_fd, data_fd;
    240 
    241     /* check for $FIRMWARE from kernel */
    242     /* XXX: dont bother: open(NULL) works same as open("no-such-file")
    243      * if (!firmware)
    244      *  return;
    245      */
    246461
    247462    /* check for /lib/firmware/$FIRMWARE */
     
    256471    for (cnt = 0; cnt < 30; ++cnt) {
    257472        loading_fd = open("loading", O_WRONLY);
    258         if (loading_fd == -1)
    259             sleep(1);
    260         else
    261             break;
     473        if (loading_fd != -1)
     474            goto loading;
     475        sleep(1);
    262476    }
    263     if (loading_fd == -1)
     477    goto out;
     478
     479 loading:
     480    /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
     481    if (full_write(loading_fd, "1", 1) != 1)
    264482        goto out;
    265483
    266     /* tell kernel we're loading by `echo 1 > /sys/$DEVPATH/loading` */
    267     if (write(loading_fd, "1", 1) != 1)
    268         goto out;
    269 
    270     /* load firmware by `cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data */
     484    /* load firmware into /sys/$DEVPATH/data */
    271485    data_fd = open("data", O_WRONLY);
    272486    if (data_fd == -1)
     
    274488    cnt = bb_copyfd_eof(firmware_fd, data_fd);
    275489
    276     /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading` */
     490    /* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */
    277491    if (cnt > 0)
    278         write(loading_fd, "0", 1);
     492        full_write(loading_fd, "0", 1);
    279493    else
    280         write(loading_fd, "-1", 2);
     494        full_write(loading_fd, "-1", 2);
    281495
    282496 out:
     
    288502}
    289503
    290 int mdev_main(int argc, char **argv);
    291 int mdev_main(int argc, char **argv)
     504int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     505int mdev_main(int argc UNUSED_PARAM, char **argv)
    292506{
    293     char *action;
    294     char *env_path;
    295     RESERVE_CONFIG_BUFFER(temp,PATH_MAX);
     507    RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
     508
     509    /* We can be called as hotplug helper */
     510    /* Kernel cannot provide suitable stdio fds for us, do it ourself */
     511    bb_sanitize_stdio();
     512
     513    /* Force the configuration file settings exactly */
     514    umask(0);
    296515
    297516    xchdir("/dev");
    298517
    299     /* Scan */
    300 
    301     if (argc == 2 && !strcmp(argv[1],"-s")) {
     518    if (argv[1] && strcmp(argv[1], "-s") == 0) {
     519        /* Scan:
     520         * mdev -s
     521         */
    302522        struct stat st;
    303523
    304524        xstat("/", &st);
    305         root_major = major(st.st_dev);
    306         root_minor = minor(st.st_dev);
    307 
    308         recursive_action("/sys/block",
    309             ACTION_RECURSE | ACTION_FOLLOWLINKS,
    310             fileAction, dirAction, temp, 0);
    311 
     525        G.root_major = major(st.st_dev);
     526        G.root_minor = minor(st.st_dev);
     527
     528        /* ACTION_FOLLOWLINKS is needed since in newer kernels
     529         * /sys/block/loop* (for example) are symlinks to dirs,
     530         * not real directories.
     531         * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
     532         * but we can't enforce that on users)
     533         */
     534        if (access("/sys/class/block", F_OK) != 0) {
     535            /* Scan obsolete /sys/block only if /sys/class/block
     536             * doesn't exist. Otherwise we'll have dupes.
     537             * Also, do not complain if it doesn't exist.
     538             * Some people configure kernel to have no blockdevs.
     539             */
     540            recursive_action("/sys/block",
     541                ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
     542                fileAction, dirAction, temp, 0);
     543        }
    312544        recursive_action("/sys/class",
    313545            ACTION_RECURSE | ACTION_FOLLOWLINKS,
    314546            fileAction, dirAction, temp, 0);
    315 
    316     /* Hotplug */
    317 
    318547    } else {
     548        char *fw;
     549        char *seq;
     550        char *action;
     551        char *env_path;
     552        static const char keywords[] ALIGN1 = "remove\0add\0";
     553        enum { OP_remove = 0, OP_add };
     554        smalluint op;
     555
     556        /* Hotplug:
     557         * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
     558         * ACTION can be "add" or "remove"
     559         * DEVPATH is like "/block/sda" or "/class/input/mice"
     560         */
    319561        action = getenv("ACTION");
    320562        env_path = getenv("DEVPATH");
    321         if (!action || !env_path)
     563        G.subsystem = getenv("SUBSYSTEM");
     564        if (!action || !env_path /*|| !G.subsystem*/)
    322565            bb_show_usage();
    323 
    324         sprintf(temp, "/sys%s", env_path);
    325         if (!strcmp(action, "remove"))
    326             make_device(temp, 1);
    327         else if (!strcmp(action, "add")) {
    328             make_device(temp, 0);
    329 
    330             if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE)
    331                 load_firmware(getenv("FIRMWARE"), temp);
     566        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 */
     571        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);
     594        if (op == OP_remove) {
     595            /* Ignoring "remove firmware". It was reported
     596             * to happen and to cause erroneous deletion
     597             * of device nodes. */
     598            if (!fw)
     599                make_device(temp, /*delete:*/ 1);
     600        }
     601        else if (op == OP_add) {
     602            make_device(temp, /*delete:*/ 0);
     603            if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
     604                if (fw)
     605                    load_firmware(fw, temp);
     606            }
     607        }
     608
     609        if (seq) {
     610            xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
    332611        }
    333612    }
    334613
    335     if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp);
    336     return 0;
     614    if (ENABLE_FEATURE_CLEAN_UP)
     615        RELEASE_CONFIG_BUFFER(temp);
     616
     617    return EXIT_SUCCESS;
    337618}
Note: See TracChangeset for help on using the changeset viewer.