Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 984 - (hide annotations) (download)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 17008 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * mdev - Mini udev for busybox
4     *
5     * Copyright 2005 Rob Landley <rob@landley.net>
6     * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
7     *
8     * Licensed under GPL version 2, see file LICENSE in this tarball for details.
9     */
10 niro 816 #include "libbb.h"
11 niro 532 #include "xregex.h"
12    
13 niro 984 /* "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 niro 816 struct globals {
65     int root_major, root_minor;
66 niro 984 char *subsystem;
67 niro 816 };
68     #define G (*(struct globals*)&bb_common_bufsiz1)
69     #define root_major (G.root_major)
70     #define root_minor (G.root_minor)
71 niro 984 #define subsystem (G.subsystem )
72 niro 532
73 niro 816 /* Prevent infinite loops in /sys symlinks */
74     #define MAX_SYSFS_DEPTH 3
75    
76     /* We use additional 64+ bytes in make_device() */
77     #define SCRATCH_SIZE 80
78    
79     /* Builds an alias path.
80     * This function potentionally reallocates the alias parameter.
81 niro 984 * Only used for ENABLE_FEATURE_MDEV_RENAME
82 niro 816 */
83     static char *build_alias(char *alias, const char *device_name)
84 niro 532 {
85 niro 816 char *dest;
86 niro 532
87 niro 816 /* ">bar/": rename to bar/device_name */
88     /* ">bar[/]baz": rename to bar[/]baz */
89     dest = strrchr(alias, '/');
90     if (dest) { /* ">bar/[baz]" ? */
91     *dest = '\0'; /* mkdir bar */
92     bb_make_directory(alias, 0755, FILEUTILS_RECUR);
93     *dest = '/';
94     if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
95     dest = alias;
96     alias = concat_path_file(alias, device_name);
97     free(dest);
98     }
99     }
100 niro 532
101 niro 816 return alias;
102     }
103    
104 niro 984 /* mknod in /dev based on a path like "/sys/block/hda/hda1"
105     * 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 niro 532 static void make_device(char *path, int delete)
111     {
112 niro 984 char *device_name;
113 niro 532 int major, minor, type, len;
114 niro 984 mode_t mode;
115 niro 816 parser_t *parser;
116 niro 532
117     /* 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
119 niro 816 * because sscanf() will stop at the first nondigit, which \n is.
120     * We also depend on path having writeable space after it.
121     */
122     major = -1;
123 niro 532 if (!delete) {
124 niro 984 char *dev_maj_min = path + strlen(path);
125    
126 niro 816 strcpy(dev_maj_min, "/dev");
127     len = open_read_close(path, dev_maj_min + 1, 64);
128 niro 984 *dev_maj_min = '\0';
129 niro 816 if (len < 1) {
130     if (!ENABLE_FEATURE_MDEV_EXEC)
131     return;
132 niro 984 /* no "dev" file, but we can still run scripts
133     * based on device name */
134     } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) {
135 niro 816 major = -1;
136     }
137 niro 532 }
138    
139     /* Determine device name, type, major and minor */
140 niro 984 device_name = (char*) bb_basename(path);
141 niro 816 /* http://kernel.org/doc/pending/hotplug.txt says that only
142     * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
143 niro 984 * But since 2.6.25 block devices are also in /sys/class/block,
144     * we use strstr("/block/") to forestall future surprises. */
145 niro 816 type = S_IFCHR;
146     if (strstr(path, "/block/"))
147     type = S_IFBLK;
148 niro 532
149 niro 984 /* Make path point to "subsystem/device_name" */
150     if (path[5] == 'b') /* legacy /sys/block? */
151     path += sizeof("/sys/") - 1;
152     else
153     path += sizeof("/sys/class/") - 1;
154 niro 532
155 niro 816 /* If we have config file, look up user settings */
156 niro 984 if (ENABLE_FEATURE_MDEV_CONF)
157     parser = config_open2("/etc/mdev.conf", fopen_for_read);
158 niro 532
159 niro 984 do {
160     int keep_matching;
161     struct bb_uidgid_t ugid;
162     char *tokens[4];
163     char *command = NULL;
164     char *alias = NULL;
165     char aliaslink = aliaslink; /* for compiler */
166 niro 532
167 niro 984 /* Defaults in case we won't match any line */
168     ugid.uid = ugid.gid = 0;
169     keep_matching = 0;
170     mode = 0660;
171    
172     if (ENABLE_FEATURE_MDEV_CONF
173     && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)
174     ) {
175     char *val;
176     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 niro 816 }
206 niro 984 if (val[0] == '$') {
207     /* regex to match an environment variable */
208     char *eq = strchr(++val, '=');
209     if (!eq)
210     continue;
211     *eq = '\0';
212     str_to_match = getenv(val);
213     if (!str_to_match)
214     continue;
215     str_to_match -= strlen(val) + 1;
216     *eq = '=';
217     }
218     /* else: regex to match [subsystem/]device_name */
219 niro 532
220 niro 984 {
221     regex_t match;
222     int result;
223 niro 532
224 niro 984 xregcomp(&match, val, REG_EXTENDED);
225     result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0);
226     regfree(&match);
227     //bb_error_msg("matches:");
228     //for (int i = 0; i < ARRAY_SIZE(off); i++) {
229     // if (off[i].rm_so < 0) continue;
230     // bb_error_msg("match %d: '%.*s'\n", i,
231     // (int)(off[i].rm_eo - off[i].rm_so),
232     // device_name + off[i].rm_so);
233     //}
234 niro 532
235 niro 984 /* If no match, skip rest of line */
236     /* (regexec returns whole pattern as "range" 0) */
237     if (result || off[0].rm_so
238     || ((int)off[0].rm_eo != (int)strlen(str_to_match))
239     ) {
240     continue; /* this line doesn't match */
241     }
242 niro 816 }
243 niro 984 line_matches:
244     /* This line matches. Stop parsing after parsing
245     * the rest the line unless keep_matching == 1 */
246 niro 532
247 niro 984 /* 2nd field: uid:gid - device ownership */
248     if (get_uidgid(&ugid, tokens[1], 1) == 0)
249     bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno);
250 niro 532
251 niro 984 /* 3rd field: mode - device permissions */
252     /* mode = strtoul(tokens[2], NULL, 8); */
253     bb_parse_mode(tokens[2], &mode);
254 niro 532
255 niro 984 val = tokens[3];
256     /* 4th field (opt): >|=alias */
257 niro 532
258 niro 984 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 niro 532
265 niro 984 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 niro 532 }
300     }
301 niro 816 }
302 niro 532
303 niro 984 if (ENABLE_FEATURE_MDEV_EXEC && val) {
304     const char *s = "$@*";
305     const char *s2 = strchr(s, val[0]);
306 niro 532
307 niro 984 if (!s2) {
308     bb_error_msg("bad line %u", parser->lineno);
309     if (ENABLE_FEATURE_MDEV_RENAME)
310     free(alias);
311     continue;
312     }
313 niro 816
314 niro 984 /* Are we running this command now?
315     * Run $cmd on delete, @cmd on create, *cmd on both
316     */
317     if (s2-s != delete)
318     command = xstrdup(val + 1);
319 niro 532 }
320 niro 816 }
321 niro 532
322 niro 984 /* End of field parsing */
323 niro 532
324 niro 984 /* "Execute" the line we found */
325     {
326     const char *node_name;
327 niro 532
328 niro 984 node_name = device_name;
329     if (ENABLE_FEATURE_MDEV_RENAME && alias)
330     node_name = alias = build_alias(alias, device_name);
331 niro 532
332 niro 984 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 niro 532
347 niro 984 if (ENABLE_FEATURE_MDEV_EXEC && command) {
348     /* setenv will leak memory, use putenv/unsetenv/free */
349     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 niro 532
362 niro 984 if (delete) {
363     if (ENABLE_FEATURE_MDEV_RENAME && alias) {
364     if (aliaslink == '>')
365     unlink(device_name);
366     }
367     unlink(node_name);
368     }
369 niro 816
370 niro 984 if (ENABLE_FEATURE_MDEV_RENAME)
371     free(alias);
372     }
373 niro 816
374 niro 984 /* We found matching line.
375     * Stop unless it was prefixed with '-' */
376     if (ENABLE_FEATURE_MDEV_CONF && !keep_matching)
377     break;
378 niro 816
379 niro 984 /* end of "while line is read from /etc/mdev.conf" */
380     } while (ENABLE_FEATURE_MDEV_CONF);
381 niro 816
382 niro 984 if (ENABLE_FEATURE_MDEV_CONF)
383     config_close(parser);
384 niro 532 }
385    
386 niro 816 /* File callback for /sys/ traversal */
387     static int FAST_FUNC fileAction(const char *fileName,
388     struct stat *statbuf UNUSED_PARAM,
389     void *userData,
390     int depth UNUSED_PARAM)
391     {
392     size_t len = strlen(fileName) - 4; /* can't underflow */
393     char *scratch = userData;
394 niro 532
395 niro 816 /* len check is for paranoid reasons */
396     if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
397     return FALSE;
398    
399     strcpy(scratch, fileName);
400     scratch[len] = '\0';
401     make_device(scratch, 0);
402    
403     return TRUE;
404     }
405    
406     /* Directory callback for /sys/ traversal */
407     static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
408     struct stat *statbuf UNUSED_PARAM,
409     void *userData UNUSED_PARAM,
410     int depth)
411 niro 532 {
412 niro 984 /* 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 niro 816 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
422     }
423 niro 532
424 niro 816 /* For the full gory details, see linux/Documentation/firmware_class/README
425     *
426     * Firmware loading works like this:
427     * - kernel sets FIRMWARE env var
428     * - userspace checks /lib/firmware/$FIRMWARE
429     * - userspace waits for /sys/$DEVPATH/loading to appear
430     * - userspace writes "1" to /sys/$DEVPATH/loading
431     * - userspace copies /lib/firmware/$FIRMWARE into /sys/$DEVPATH/data
432     * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading
433     * - kernel loads firmware into device
434     */
435 niro 984 static void load_firmware(const char *firmware, const char *sysfs_path)
436 niro 816 {
437     int cnt;
438     int firmware_fd, loading_fd, data_fd;
439 niro 532
440 niro 816 /* check for /lib/firmware/$FIRMWARE */
441     xchdir("/lib/firmware");
442     firmware_fd = xopen(firmware, O_RDONLY);
443 niro 532
444 niro 816 /* in case we goto out ... */
445     data_fd = -1;
446 niro 532
447 niro 816 /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
448     xchdir(sysfs_path);
449     for (cnt = 0; cnt < 30; ++cnt) {
450     loading_fd = open("loading", O_WRONLY);
451     if (loading_fd != -1)
452     goto loading;
453     sleep(1);
454     }
455     goto out;
456 niro 532
457 niro 816 loading:
458 niro 984 /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
459 niro 816 if (full_write(loading_fd, "1", 1) != 1)
460     goto out;
461 niro 532
462 niro 984 /* load firmware into /sys/$DEVPATH/data */
463 niro 816 data_fd = open("data", O_WRONLY);
464     if (data_fd == -1)
465     goto out;
466     cnt = bb_copyfd_eof(firmware_fd, data_fd);
467 niro 532
468 niro 984 /* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */
469 niro 816 if (cnt > 0)
470     full_write(loading_fd, "0", 1);
471     else
472     full_write(loading_fd, "-1", 2);
473 niro 532
474 niro 816 out:
475     if (ENABLE_FEATURE_CLEAN_UP) {
476     close(firmware_fd);
477     close(loading_fd);
478     close(data_fd);
479 niro 532 }
480     }
481    
482 niro 816 int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
483     int mdev_main(int argc UNUSED_PARAM, char **argv)
484 niro 532 {
485 niro 816 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
486 niro 532
487 niro 816 /* We can be called as hotplug helper */
488     /* Kernel cannot provide suitable stdio fds for us, do it ourself */
489     bb_sanitize_stdio();
490 niro 532
491 niro 984 /* Force the configuration file settings exactly */
492     umask(0);
493    
494 niro 816 xchdir("/dev");
495 niro 532
496 niro 984 if (argv[1] && strcmp(argv[1], "-s") == 0) {
497 niro 816 /* Scan:
498     * mdev -s
499     */
500 niro 532 struct stat st;
501    
502     xstat("/", &st);
503 niro 816 root_major = major(st.st_dev);
504     root_minor = minor(st.st_dev);
505 niro 532
506 niro 816 /* ACTION_FOLLOWLINKS is needed since in newer kernels
507     * /sys/block/loop* (for example) are symlinks to dirs,
508     * not real directories.
509     * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
510 niro 984 * but we can't enforce that on users)
511     */
512     if (access("/sys/class/block", F_OK) != 0) {
513     /* 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 niro 816 recursive_action("/sys/class",
523     ACTION_RECURSE | ACTION_FOLLOWLINKS,
524     fileAction, dirAction, temp, 0);
525     } else {
526 niro 984 char *fw;
527 niro 816 char *seq;
528     char *action;
529     char *env_path;
530 niro 984 static const char keywords[] ALIGN1 = "remove\0add\0";
531     enum { OP_remove = 0, OP_add };
532     smalluint op;
533 niro 532
534 niro 816 /* Hotplug:
535 niro 984 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
536 niro 816 * ACTION can be "add" or "remove"
537     * DEVPATH is like "/block/sda" or "/class/input/mice"
538     */
539 niro 532 action = getenv("ACTION");
540     env_path = getenv("DEVPATH");
541 niro 984 subsystem = getenv("SUBSYSTEM");
542     if (!action || !env_path /*|| !subsystem*/)
543 niro 532 bb_show_usage();
544 niro 984 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 niro 816 seq = getenv("SEQNUM");
550     if (seq) {
551 niro 984 int timeout = 2000 / 32; /* 2000 msec */
552 niro 816 do {
553 niro 984 int seqlen;
554     char seqbuf[sizeof(int)*3 + 2];
555    
556 niro 816 seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));
557 niro 984 if (seqlen < 0) {
558     seq = NULL;
559 niro 816 break;
560 niro 984 }
561 niro 816 seqbuf[seqlen] = '\0';
562     if (seqbuf[0] == '\n' /* seed file? */
563     || strcmp(seq, seqbuf) == 0 /* correct idx? */
564     ) {
565     break;
566     }
567     usleep(32*1000);
568     } while (--timeout);
569     }
570    
571     snprintf(temp, PATH_MAX, "/sys%s", env_path);
572 niro 984 if (op == OP_remove) {
573     /* Ignoring "remove firmware". It was reported
574     * 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 niro 816 make_device(temp, 0);
581     if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
582     if (fw)
583     load_firmware(fw, temp);
584     }
585     }
586    
587 niro 984 if (seq) {
588 niro 816 xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
589     }
590 niro 532 }
591    
592 niro 816 if (ENABLE_FEATURE_CLEAN_UP)
593     RELEASE_CONFIG_BUFFER(temp);
594    
595 niro 984 return EXIT_SUCCESS;
596 niro 532 }