Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/util-linux/mdev.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 983 by niro, Fri Apr 24 18:33:46 2009 UTC revision 984 by niro, Sun May 30 11:32:42 2010 UTC
# Line 1  Line 1 
1  /* vi: set sw=4 ts=4: */  /* vi: set sw=4 ts=4: */
2  /*  /*
  *  
3   * mdev - Mini udev for busybox   * mdev - Mini udev for busybox
4   *   *
5   * Copyright 2005 Rob Landley <rob@landley.net>   * Copyright 2005 Rob Landley <rob@landley.net>
# Line 8  Line 7 
7   *   *
8   * Licensed under GPL version 2, see file LICENSE in this tarball for details.   * Licensed under GPL version 2, see file LICENSE in this tarball for details.
9   */   */
   
10  #include "libbb.h"  #include "libbb.h"
11  #include "xregex.h"  #include "xregex.h"
12    
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    
64  struct globals {  struct globals {
65   int root_major, root_minor;   int root_major, root_minor;
66     char *subsystem;
67  };  };
68  #define G (*(struct globals*)&bb_common_bufsiz1)  #define G (*(struct globals*)&bb_common_bufsiz1)
69  #define root_major (G.root_major)  #define root_major (G.root_major)
70  #define root_minor (G.root_minor)  #define root_minor (G.root_minor)
71    #define subsystem  (G.subsystem )
72    
73  /* Prevent infinite loops in /sys symlinks */  /* Prevent infinite loops in /sys symlinks */
74  #define MAX_SYSFS_DEPTH 3  #define MAX_SYSFS_DEPTH 3
# Line 25  struct globals { Line 76  struct globals {
76  /* We use additional 64+ bytes in make_device() */  /* We use additional 64+ bytes in make_device() */
77  #define SCRATCH_SIZE 80  #define SCRATCH_SIZE 80
78    
 #if ENABLE_FEATURE_MDEV_RENAME  
79  /* Builds an alias path.  /* Builds an alias path.
80   * This function potentionally reallocates the alias parameter.   * This function potentionally reallocates the alias parameter.
81     * Only used for ENABLE_FEATURE_MDEV_RENAME
82   */   */
83  static char *build_alias(char *alias, const char *device_name)  static char *build_alias(char *alias, const char *device_name)
84  {  {
# Line 49  static char *build_alias(char *alias, co Line 100  static char *build_alias(char *alias, co
100    
101   return alias;   return alias;
102  }  }
 #endif  
103    
104  /* mknod in /dev based on a path like "/sys/block/hda/hda1" */  /* mknod in /dev based on a path like "/sys/block/hda/hda1"
105  /* NB: "mdev -s" may call us many times, do not leak memory/fds! */   * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
106     * after NUL, but we promise to not mangle (IOW: to restore if needed)
107     * path string.
108     * NB2: "mdev -s" may call us many times, do not leak memory/fds!
109     */
110  static void make_device(char *path, int delete)  static void make_device(char *path, int delete)
111  {  {
112   const char *device_name;   char *device_name;
113   int major, minor, type, len;   int major, minor, type, len;
114   int mode = 0660;   mode_t mode;
 #if ENABLE_FEATURE_MDEV_CONF  
  struct bb_uidgid_t ugid = { 0, 0 };  
115   parser_t *parser;   parser_t *parser;
  char *tokens[5];  
 #endif  
 #if ENABLE_FEATURE_MDEV_EXEC  
  char *command = NULL;  
 #endif  
 #if ENABLE_FEATURE_MDEV_RENAME  
  char *alias = NULL;  
  char aliaslink = aliaslink; /* for compiler */  
 #endif  
  char *dev_maj_min = path + strlen(path);  
   
  /* Force the configuration file settings exactly. */  
  umask(0);  
116    
117   /* Try to read major/minor string.  Note that the kernel puts \n after   /* Try to read major/minor string.  Note that the kernel puts \n after
118   * the data, so we don't need to worry about null terminating the string   * the data, so we don't need to worry about null terminating the string
# Line 82  static void make_device(char *path, int Line 121  static void make_device(char *path, int
121   */   */
122   major = -1;   major = -1;
123   if (!delete) {   if (!delete) {
124     char *dev_maj_min = path + strlen(path);
125    
126   strcpy(dev_maj_min, "/dev");   strcpy(dev_maj_min, "/dev");
127   len = open_read_close(path, dev_maj_min + 1, 64);   len = open_read_close(path, dev_maj_min + 1, 64);
128   *dev_maj_min++ = '\0';   *dev_maj_min = '\0';
129   if (len < 1) {   if (len < 1) {
130   if (!ENABLE_FEATURE_MDEV_EXEC)   if (!ENABLE_FEATURE_MDEV_EXEC)
131   return;   return;
132   /* no "dev" file, so just try to run script */   /* no "dev" file, but we can still run scripts
133   *dev_maj_min = '\0';   * based on device name */
134   } else if (sscanf(dev_maj_min, "%u:%u", &major, &minor) != 2) {   } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) {
135   major = -1;   major = -1;
136   }   }
137   }   }
138    
139   /* Determine device name, type, major and minor */   /* Determine device name, type, major and minor */
140   device_name = bb_basename(path);   device_name = (char*) bb_basename(path);
141   /* http://kernel.org/doc/pending/hotplug.txt says that only   /* http://kernel.org/doc/pending/hotplug.txt says that only
142   * "/sys/block/..." is for block devices. "/sys/bus" etc is not.   * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
143   * But since 2.6.25 block devices are also in /sys/class/block.   * But since 2.6.25 block devices are also in /sys/class/block,
144   * We use strstr("/block/") to forestall future surprises. */   * we use strstr("/block/") to forestall future surprises. */
145   type = S_IFCHR;   type = S_IFCHR;
146   if (strstr(path, "/block/"))   if (strstr(path, "/block/"))
147   type = S_IFBLK;   type = S_IFBLK;
148    
149  #if ENABLE_FEATURE_MDEV_CONF   /* Make path point to "subsystem/device_name" */
150   parser = config_open2("/etc/mdev.conf", fopen_for_read);   if (path[5] == 'b') /* legacy /sys/block? */
151     path += sizeof("/sys/") - 1;
152     else
153     path += sizeof("/sys/class/") - 1;
154    
155   /* If we have config file, look up user settings */   /* If we have config file, look up user settings */
156   while (config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)) {   if (ENABLE_FEATURE_MDEV_CONF)
157   regmatch_t off[1 + 9*ENABLE_FEATURE_MDEV_RENAME_REGEXP];   parser = config_open2("/etc/mdev.conf", fopen_for_read);
158   char *val;  
159     do {
160   /* Fields: regex uid:gid mode [alias] [cmd] */   int keep_matching;
161     struct bb_uidgid_t ugid;
162   /* 1st field: @<numeric maj,min>... */   char *tokens[4];
163   if (tokens[0][0] == '@') {   char *command = NULL;
164   /* @major,minor[-last] */   char *alias = NULL;
165   /* (useful when name is ambiguous:   char aliaslink = aliaslink; /* for compiler */
166   * "/sys/class/usb/lp0" and  
167   * "/sys/class/printer/lp0") */   /* Defaults in case we won't match any line */
168   int cmaj, cmin0, cmin1, sc;   ugid.uid = ugid.gid = 0;
169   if (major < 0)   keep_matching = 0;
170   continue; /* no dev, no match */   mode = 0660;
171   sc = sscanf(tokens[0], "@%u,%u-%u", &cmaj, &cmin0, &cmin1);  
172   if (sc < 1 || major != cmaj   if (ENABLE_FEATURE_MDEV_CONF
173   || (sc == 2 && minor != cmin0)   && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)
174   || (sc == 3 && (minor < cmin0 || minor > cmin1))   ) {
175   ) {   char *val;
176   continue; /* no match */   char *str_to_match;
177     regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
178    
179     val = tokens[0];
180     keep_matching = ('-' == val[0]);
181     val += keep_matching; /* swallow leading dash */
182    
183     /* Match against either "subsystem/device_name"
184     * or "device_name" alone */
185     str_to_match = strchr(val, '/') ? path : device_name;
186    
187     /* Fields: regex uid:gid mode [alias] [cmd] */
188    
189     if (val[0] == '@') {
190     /* @major,minor[-minor2] */
191     /* (useful when name is ambiguous:
192     * "/sys/class/usb/lp0" and
193     * "/sys/class/printer/lp0") */
194     int cmaj, cmin0, cmin1, sc;
195     if (major < 0)
196     continue; /* no dev, no match */
197     sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1);
198     if (sc < 1 || major != cmaj
199     || (sc == 2 && minor != cmin0)
200     || (sc == 3 && (minor < cmin0 || minor > cmin1))
201     ) {
202     continue; /* this line doesn't match */
203     }
204     goto line_matches;
205   }   }
206   } else { /* ... or regex to match device name */   if (val[0] == '$') {
207   regex_t match;   /* regex to match an environment variable */
208   int result;   char *eq = strchr(++val, '=');
209     if (!eq)
210   /* Is this it? */   continue;
211   xregcomp(&match, tokens[0], REG_EXTENDED);   *eq = '\0';
212   result = regexec(&match, device_name, ARRAY_SIZE(off), off, 0);   str_to_match = getenv(val);
213   regfree(&match);   if (!str_to_match)
214     continue;
215   //bb_error_msg("matches:");   str_to_match -= strlen(val) + 1;
216   //for (int i = 0; i < ARRAY_SIZE(off); i++) {   *eq = '=';
  // if (off[i].rm_so < 0) continue;  
  // bb_error_msg("match %d: '%.*s'\n", i,  
  // (int)(off[i].rm_eo - off[i].rm_so),  
  // device_name + off[i].rm_so);  
  //}  
   
  /* If not this device, skip rest of line */  
  /* (regexec returns whole pattern as "range" 0) */  
  if (result || off[0].rm_so  
  || ((int)off[0].rm_eo != (int)strlen(device_name))  
  ) {  
  continue;  
217   }   }
218   }   /* else: regex to match [subsystem/]device_name */
   
  /* This line matches: stop parsing the file  
  * after parsing the rest of fields */  
   
  /* 2nd field: uid:gid - device ownership */  
  parse_chown_usergroup_or_die(&ugid, tokens[1]);  
   
  /* 3rd field: mode - device permissions */  
  mode = strtoul(tokens[2], NULL, 8);  
219    
220   val = tokens[3];   {
221   /* 4th field (opt): >alias */   regex_t match;
222  #if ENABLE_FEATURE_MDEV_RENAME   int result;
223   if (!val)  
224   break;   xregcomp(&match, val, REG_EXTENDED);
225   aliaslink = *val;   result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0);
226   if (aliaslink == '>' || aliaslink == '=') {   regfree(&match);
227   char *s;   //bb_error_msg("matches:");
228  #if ENABLE_FEATURE_MDEV_RENAME_REGEXP   //for (int i = 0; i < ARRAY_SIZE(off); i++) {
229   char *p;   // if (off[i].rm_so < 0) continue;
230   unsigned i, n;   // bb_error_msg("match %d: '%.*s'\n", i,
231  #endif   // (int)(off[i].rm_eo - off[i].rm_so),
232   char *a = val;   // device_name + off[i].rm_so);
233   s = strchrnul(val, ' ');   //}
234   val = (s[0] && s[1]) ? s+1 : NULL;  
235   s[0] = '\0';   /* If no match, skip rest of line */
236  #if ENABLE_FEATURE_MDEV_RENAME_REGEXP   /* (regexec returns whole pattern as "range" 0) */
237   /* substitute %1..9 with off[1..9], if any */   if (result || off[0].rm_so
238   n = 0;   || ((int)off[0].rm_eo != (int)strlen(str_to_match))
239   s = a;   ) {
240   while (*s)   continue; /* this line doesn't match */
241   if (*s++ == '%')   }
242   n++;   }
243     line_matches:
244   p = alias = xzalloc(strlen(a) + n * strlen(device_name));   /* This line matches. Stop parsing after parsing
245   s = a + 1;   * the rest the line unless keep_matching == 1 */
246   while (*s) {  
247   *p = *s;   /* 2nd field: uid:gid - device ownership */
248   if ('%' == *s) {   if (get_uidgid(&ugid, tokens[1], 1) == 0)
249   i = (s[1] - '0');   bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno);
250   if (i <= 9 && off[i].rm_so >= 0) {  
251   n = off[i].rm_eo - off[i].rm_so;   /* 3rd field: mode - device permissions */
252   strncpy(p, device_name + off[i].rm_so, n);   /* mode = strtoul(tokens[2], NULL, 8); */
253   p += n - 1;   bb_parse_mode(tokens[2], &mode);
254   s++;  
255     val = tokens[3];
256     /* 4th field (opt): >|=alias */
257    
258     if (ENABLE_FEATURE_MDEV_RENAME && val) {
259     aliaslink = val[0];
260     if (aliaslink == '>' || aliaslink == '=') {
261     char *a, *s, *st;
262     char *p;
263     unsigned i, n;
264    
265     a = val;
266     s = strchrnul(val, ' ');
267     st = strchrnul(val, '\t');
268     if (st < s)
269     s = st;
270     val = (s[0] && s[1]) ? s+1 : NULL;
271     s[0] = '\0';
272    
273     if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
274     /* substitute %1..9 with off[1..9], if any */
275     n = 0;
276     s = a;
277     while (*s)
278     if (*s++ == '%')
279     n++;
280    
281     p = alias = xzalloc(strlen(a) + n * strlen(str_to_match));
282     s = a + 1;
283     while (*s) {
284     *p = *s;
285     if ('%' == *s) {
286     i = (s[1] - '0');
287     if (i <= 9 && off[i].rm_so >= 0) {
288     n = off[i].rm_eo - off[i].rm_so;
289     strncpy(p, str_to_match + off[i].rm_so, n);
290     p += n - 1;
291     s++;
292     }
293     }
294     p++;
295     s++;
296     }
297     } else {
298     alias = xstrdup(a + 1);
299   }   }
300   }   }
  p++;  
  s++;  
301   }   }
 #else  
  alias = xstrdup(a + 1);  
 #endif  
  }  
 #endif /* ENABLE_FEATURE_MDEV_RENAME */  
   
 #if ENABLE_FEATURE_MDEV_EXEC  
  /* The rest (opt): command to run */  
  if (!val)  
  break;  
  {  
  const char *s = "@$*";  
  const char *s2 = strchr(s, *val);  
302    
303   if (!s2)   if (ENABLE_FEATURE_MDEV_EXEC && val) {
304   bb_error_msg_and_die("bad line %u", parser->lineno);   const char *s = "$@*";
305     const char *s2 = strchr(s, val[0]);
306    
307     if (!s2) {
308     bb_error_msg("bad line %u", parser->lineno);
309     if (ENABLE_FEATURE_MDEV_RENAME)
310     free(alias);
311     continue;
312     }
313    
314   /* Correlate the position in the "@$*" with the delete   /* Are we running this command now?
315   * step so that we get the proper behavior:   * Run $cmd on delete, @cmd on create, *cmd on both
316   * @cmd: run on create   */
317   * $cmd: run on delete   if (s2-s != delete)
318   * *cmd: run on both   command = xstrdup(val + 1);
  */  
  if ((s2 - s + 1) /*1/2/3*/ & /*1/2*/ (1 + delete)) {  
  command = xstrdup(val + 1);  
319   }   }
320   }   }
 #endif  
  /* end of field parsing */  
  break; /* we found matching line, stop */  
  } /* end of "while line is read from /etc/mdev.conf" */  
   
  config_close(parser);  
 #endif /* ENABLE_FEATURE_MDEV_CONF */  
   
  if (!delete && major >= 0) {  
   
  if (ENABLE_FEATURE_MDEV_RENAME)  
  unlink(device_name);  
321    
322   if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)   /* End of field parsing */
  bb_perror_msg_and_die("mknod %s", device_name);  
323    
324   if (major == root_major && minor == root_minor)   /* "Execute" the line we found */
325   symlink(device_name, "root");   {
326     const char *node_name;
327    
328  #if ENABLE_FEATURE_MDEV_CONF   node_name = device_name;
329   chown(device_name, ugid.uid, ugid.gid);   if (ENABLE_FEATURE_MDEV_RENAME && alias)
330     node_name = alias = build_alias(alias, device_name);
331    
332     if (!delete && major >= 0) {
333     if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST)
334     bb_perror_msg("can't create %s", node_name);
335     if (major == root_major && minor == root_minor)
336     symlink(node_name, "root");
337     if (ENABLE_FEATURE_MDEV_CONF) {
338     chmod(node_name, mode);
339     chown(node_name, ugid.uid, ugid.gid);
340     }
341     if (ENABLE_FEATURE_MDEV_RENAME && alias) {
342     if (aliaslink == '>')
343     symlink(node_name, device_name);
344     }
345     }
346    
347  #if ENABLE_FEATURE_MDEV_RENAME   if (ENABLE_FEATURE_MDEV_EXEC && command) {
348   if (alias) {   /* setenv will leak memory, use putenv/unsetenv/free */
349   alias = build_alias(alias, device_name);   char *s = xasprintf("%s=%s", "MDEV", node_name);
350     char *s1 = xasprintf("%s=%s", "SUBSYSTEM", subsystem);
351     putenv(s);
352     putenv(s1);
353     if (system(command) == -1)
354     bb_perror_msg("can't run '%s'", command);
355     unsetenv("SUBSYSTEM");
356     free(s1);
357     unsetenv("MDEV");
358     free(s);
359     free(command);
360     }
361    
362   /* move the device, and optionally   if (delete) {
363   * make a symlink to moved device node */   if (ENABLE_FEATURE_MDEV_RENAME && alias) {
364   if (rename(device_name, alias) == 0 && aliaslink == '>')   if (aliaslink == '>')
365   symlink(alias, device_name);   unlink(device_name);
366     }
367     unlink(node_name);
368     }
369    
370   free(alias);   if (ENABLE_FEATURE_MDEV_RENAME)
371     free(alias);
372   }   }
 #endif  
 #endif  
  }  
373    
374  #if ENABLE_FEATURE_MDEV_EXEC   /* We found matching line.
375   if (command) {   * Stop unless it was prefixed with '-' */
376   /* setenv will leak memory, use putenv/unsetenv/free */   if (ENABLE_FEATURE_MDEV_CONF && !keep_matching)
377   char *s = xasprintf("MDEV=%s", device_name);   break;
  putenv(s);  
  if (system(command) == -1)  
  bb_perror_msg_and_die("can't run '%s'", command);  
  s[4] = '\0';  
  unsetenv(s);  
  free(s);  
  free(command);  
  }  
 #endif  
378    
379   if (delete) {   /* end of "while line is read from /etc/mdev.conf" */
380   unlink(device_name);   } while (ENABLE_FEATURE_MDEV_CONF);
381   /* At creation time, device might have been moved  
382   * and a symlink might have been created. Undo that. */   if (ENABLE_FEATURE_MDEV_CONF)
383  #if ENABLE_FEATURE_MDEV_RENAME   config_close(parser);
  if (alias) {  
  alias = build_alias(alias, device_name);  
  unlink(alias);  
  free(alias);  
  }  
 #endif  
  }  
384  }  }
385    
386  /* File callback for /sys/ traversal */  /* File callback for /sys/ traversal */
# Line 324  static int FAST_FUNC dirAction(const cha Line 409  static int FAST_FUNC dirAction(const cha
409   void *userData UNUSED_PARAM,   void *userData UNUSED_PARAM,
410   int depth)   int depth)
411  {  {
412     /* Extract device subsystem -- the name of the directory
413     * under /sys/class/ */
414     if (1 == depth) {
415     free(subsystem);
416     subsystem = strrchr(fileName, '/');
417     if (subsystem)
418     subsystem = xstrdup(subsystem + 1);
419     }
420    
421   return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);   return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
422  }  }
423    
# Line 338  static int FAST_FUNC dirAction(const cha Line 432  static int FAST_FUNC dirAction(const cha
432   * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading   * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading
433   * - kernel loads firmware into device   * - kernel loads firmware into device
434   */   */
435  static void load_firmware(const char *const firmware, const char *const sysfs_path)  static void load_firmware(const char *firmware, const char *sysfs_path)
436  {  {
437   int cnt;   int cnt;
438   int firmware_fd, loading_fd, data_fd;   int firmware_fd, loading_fd, data_fd;
# Line 361  static void load_firmware(const char *co Line 455  static void load_firmware(const char *co
455   goto out;   goto out;
456    
457   loading:   loading:
458   /* tell kernel we're loading by `echo 1 > /sys/$DEVPATH/loading` */   /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
459   if (full_write(loading_fd, "1", 1) != 1)   if (full_write(loading_fd, "1", 1) != 1)
460   goto out;   goto out;
461    
462   /* load firmware by `cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data */   /* load firmware into /sys/$DEVPATH/data */
463   data_fd = open("data", O_WRONLY);   data_fd = open("data", O_WRONLY);
464   if (data_fd == -1)   if (data_fd == -1)
465   goto out;   goto out;
466   cnt = bb_copyfd_eof(firmware_fd, data_fd);   cnt = bb_copyfd_eof(firmware_fd, data_fd);
467    
468   /* tell kernel result by `echo [0|-1] > /sys/$DEVPATH/loading` */   /* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */
469   if (cnt > 0)   if (cnt > 0)
470   full_write(loading_fd, "0", 1);   full_write(loading_fd, "0", 1);
471   else   else
# Line 392  int mdev_main(int argc UNUSED_PARAM, cha Line 486  int mdev_main(int argc UNUSED_PARAM, cha
486    
487   /* We can be called as hotplug helper */   /* We can be called as hotplug helper */
488   /* Kernel cannot provide suitable stdio fds for us, do it ourself */   /* Kernel cannot provide suitable stdio fds for us, do it ourself */
 #if 1  
489   bb_sanitize_stdio();   bb_sanitize_stdio();
490  #else  
491   /* Debug code */   /* Force the configuration file settings exactly */
492   /* Replace LOGFILE by other file or device name if you need */   umask(0);
 #define LOGFILE "/dev/console"  
  /* Just making sure fd 0 is not closed,  
  * we don't really intend to read from it */  
  xmove_fd(xopen("/", O_RDONLY), STDIN_FILENO);  
  xmove_fd(xopen(LOGFILE, O_WRONLY|O_APPEND), STDOUT_FILENO);  
  xmove_fd(xopen(LOGFILE, O_WRONLY|O_APPEND), STDERR_FILENO);  
 #endif  
493    
494   xchdir("/dev");   xchdir("/dev");
495    
496   if (argv[1] && !strcmp(argv[1], "-s")) {   if (argv[1] && strcmp(argv[1], "-s") == 0) {
497   /* Scan:   /* Scan:
498   * mdev -s   * mdev -s
499   */   */
# Line 421  int mdev_main(int argc UNUSED_PARAM, cha Line 507  int mdev_main(int argc UNUSED_PARAM, cha
507   * /sys/block/loop* (for example) are symlinks to dirs,   * /sys/block/loop* (for example) are symlinks to dirs,
508   * not real directories.   * not real directories.
509   * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,   * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
510   * but we can't enforce that on users) */   * but we can't enforce that on users)
511   recursive_action("/sys/block",   */
512   ACTION_RECURSE | ACTION_FOLLOWLINKS,   if (access("/sys/class/block", F_OK) != 0) {
513   fileAction, dirAction, temp, 0);   /* Scan obsolete /sys/block only if /sys/class/block
514     * doesn't exist. Otherwise we'll have dupes.
515     * Also, do not complain if it doesn't exist.
516     * Some people configure kernel to have no blockdevs.
517     */
518     recursive_action("/sys/block",
519     ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
520     fileAction, dirAction, temp, 0);
521     }
522   recursive_action("/sys/class",   recursive_action("/sys/class",
523   ACTION_RECURSE | ACTION_FOLLOWLINKS,   ACTION_RECURSE | ACTION_FOLLOWLINKS,
524   fileAction, dirAction, temp, 0);   fileAction, dirAction, temp, 0);
525   } else {   } else {
526     char *fw;
527   char *seq;   char *seq;
528   char *action;   char *action;
529   char *env_path;   char *env_path;
530   char seqbuf[sizeof(int)*3 + 2];   static const char keywords[] ALIGN1 = "remove\0add\0";
531   int seqlen = seqlen; /* for compiler */   enum { OP_remove = 0, OP_add };
532     smalluint op;
533    
534   /* Hotplug:   /* Hotplug:
535   * env ACTION=... DEVPATH=... [SEQNUM=...] mdev   * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
536   * ACTION can be "add" or "remove"   * ACTION can be "add" or "remove"
537   * DEVPATH is like "/block/sda" or "/class/input/mice"   * DEVPATH is like "/block/sda" or "/class/input/mice"
538   */   */
539   action = getenv("ACTION");   action = getenv("ACTION");
540   env_path = getenv("DEVPATH");   env_path = getenv("DEVPATH");
541   if (!action || !env_path)   subsystem = getenv("SUBSYSTEM");
542     if (!action || !env_path /*|| !subsystem*/)
543   bb_show_usage();   bb_show_usage();
544     fw = getenv("FIRMWARE");
545     op = index_in_strings(keywords, action);
546     /* If it exists, does /dev/mdev.seq match $SEQNUM?
547     * If it does not match, earlier mdev is running
548     * in parallel, and we need to wait */
549   seq = getenv("SEQNUM");   seq = getenv("SEQNUM");
550   if (seq) {   if (seq) {
551   int timeout = 2000 / 32;   int timeout = 2000 / 32; /* 2000 msec */
552   do {   do {
553     int seqlen;
554     char seqbuf[sizeof(int)*3 + 2];
555    
556   seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));   seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));
557   if (seqlen < 0)   if (seqlen < 0) {
558     seq = NULL;
559   break;   break;
560     }
561   seqbuf[seqlen] = '\0';   seqbuf[seqlen] = '\0';
562   if (seqbuf[0] == '\n' /* seed file? */   if (seqbuf[0] == '\n' /* seed file? */
563   || strcmp(seq, seqbuf) == 0 /* correct idx? */   || strcmp(seq, seqbuf) == 0 /* correct idx? */
# Line 463  int mdev_main(int argc UNUSED_PARAM, cha Line 569  int mdev_main(int argc UNUSED_PARAM, cha
569   }   }
570    
571   snprintf(temp, PATH_MAX, "/sys%s", env_path);   snprintf(temp, PATH_MAX, "/sys%s", env_path);
572   if (!strcmp(action, "remove"))   if (op == OP_remove) {
573   make_device(temp, 1);   /* Ignoring "remove firmware". It was reported
574   else if (!strcmp(action, "add")) {   * to happen and to cause erroneous deletion
575     * of device nodes. */
576     if (!fw)
577     make_device(temp, 1);
578     }
579     else if (op == OP_add) {
580   make_device(temp, 0);   make_device(temp, 0);
   
581   if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {   if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
  char *fw = getenv("FIRMWARE");  
582   if (fw)   if (fw)
583   load_firmware(fw, temp);   load_firmware(fw, temp);
584   }   }
585   }   }
586    
587   if (seq && seqlen >= 0) {   if (seq) {
588   xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));   xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
589   }   }
590   }   }
# Line 483  int mdev_main(int argc UNUSED_PARAM, cha Line 592  int mdev_main(int argc UNUSED_PARAM, cha
592   if (ENABLE_FEATURE_CLEAN_UP)   if (ENABLE_FEATURE_CLEAN_UP)
593   RELEASE_CONFIG_BUFFER(temp);   RELEASE_CONFIG_BUFFER(temp);
594    
595   return 0;   return EXIT_SUCCESS;
596  }  }

Legend:
Removed from v.983  
changed lines
  Added in v.984