mdev: add support to run as daemon
[oweals/busybox.git] / util-linux / mdev.c
1 /* 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 GPLv2, see file LICENSE in this source tree.
9  */
10 //config:config MDEV
11 //config:       bool "mdev (17 kb)"
12 //config:       default y
13 //config:       select PLATFORM_LINUX
14 //config:       help
15 //config:       mdev is a mini-udev implementation for dynamically creating device
16 //config:       nodes in the /dev directory.
17 //config:
18 //config:       For more information, please see docs/mdev.txt
19 //config:
20 //config:config FEATURE_MDEV_CONF
21 //config:       bool "Support /etc/mdev.conf"
22 //config:       default y
23 //config:       depends on MDEV
24 //config:       help
25 //config:       Add support for the mdev config file to control ownership and
26 //config:       permissions of the device nodes.
27 //config:
28 //config:       For more information, please see docs/mdev.txt
29 //config:
30 //config:config FEATURE_MDEV_RENAME
31 //config:       bool "Support subdirs/symlinks"
32 //config:       default y
33 //config:       depends on FEATURE_MDEV_CONF
34 //config:       help
35 //config:       Add support for renaming devices and creating symlinks.
36 //config:
37 //config:       For more information, please see docs/mdev.txt
38 //config:
39 //config:config FEATURE_MDEV_RENAME_REGEXP
40 //config:       bool "Support regular expressions substitutions when renaming device"
41 //config:       default y
42 //config:       depends on FEATURE_MDEV_RENAME
43 //config:       help
44 //config:       Add support for regular expressions substitutions when renaming
45 //config:       device.
46 //config:
47 //config:config FEATURE_MDEV_EXEC
48 //config:       bool "Support command execution at device addition/removal"
49 //config:       default y
50 //config:       depends on FEATURE_MDEV_CONF
51 //config:       help
52 //config:       This adds support for an optional field to /etc/mdev.conf for
53 //config:       executing commands when devices are created/removed.
54 //config:
55 //config:       For more information, please see docs/mdev.txt
56 //config:
57 //config:config FEATURE_MDEV_LOAD_FIRMWARE
58 //config:       bool "Support loading of firmware"
59 //config:       default y
60 //config:       depends on MDEV
61 //config:       help
62 //config:       Some devices need to load firmware before they can be usable.
63 //config:
64 //config:       These devices will request userspace look up the files in
65 //config:       /lib/firmware/ and if it exists, send it to the kernel for
66 //config:       loading into the hardware.
67 //config:
68 //config:config FEATURE_MDEV_DAEMON
69 //config:       bool "Support daemon mode"
70 //config:       default y
71 //config:       depends on MDEV
72 //config:       help
73 //config:       Adds the -d option to run mdev in daemon mode handling hotplug
74 //config:       events from the kernel like udev. If the system generates many
75 //config:       hotplug events this mode of operation will consume less
76 //config:       resources than registering mdev as hotplug helper or using the
77 //config:       uevent applet.
78
79 //applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
80
81 //kbuild:lib-$(CONFIG_MDEV) += mdev.o
82
83 //usage:#define mdev_trivial_usage
84 //usage:       "[-s]" IF_FEATURE_MDEV_DAEMON(" | [-df]")
85 //usage:#define mdev_full_usage "\n\n"
86 //usage:       "mdev -s is to be run during boot to scan /sys and populate /dev.\n"
87 //usage:        IF_FEATURE_MDEV_DAEMON(
88 //usage:       "mdev -d[f]: daemon, listen on netlink.\n"
89 //usage:       "        -f: stay in foreground.\n"
90 //usage:        )
91 //usage:       "\n"
92 //usage:       "Bare mdev is a kernel hotplug helper. To activate it:\n"
93 //usage:       "        echo /sbin/mdev >/proc/sys/kernel/hotplug\n"
94 //usage:        IF_FEATURE_MDEV_CONF(
95 //usage:       "\n"
96 //usage:       "It uses /etc/mdev.conf with lines\n"
97 //usage:       "        [-][ENV=regex;]...DEVNAME UID:GID PERM"
98 //usage:                        IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
99 //usage:                        IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
100 //usage:       "\n"
101 //usage:       "where DEVNAME is device name regex, @major,minor[-minor2], or\n"
102 //usage:       "environment variable regex. A common use of the latter is\n"
103 //usage:       "to load modules for hotplugged devices:\n"
104 //usage:       "        $MODALIAS=.* 0:0 660 @modprobe \"$MODALIAS\"\n"
105 //usage:        )
106 //usage:       "\n"
107 //usage:       "If /dev/mdev.seq file exists, mdev will wait for its value\n"
108 //usage:       "to match $SEQNUM variable. This prevents plug/unplug races.\n"
109 //usage:       "To activate this feature, create empty /dev/mdev.seq at boot.\n"
110 //usage:       "\n"
111 //usage:       "If /dev/mdev.log file exists, debug log will be appended to it."
112
113 #include "libbb.h"
114 #include "common_bufsiz.h"
115 #include "xregex.h"
116 #include <linux/netlink.h>
117
118 /* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
119  * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev
120  * contains "4:0\n". Directory name is taken as device name, path component
121  * directly after /sys/class/ as subsystem. In this example, "tty0" and "tty".
122  * Then mdev creates the /dev/device_name node.
123  * If /sys/class/.../dev file does not exist, mdev still may act
124  * on this device: see "@|$|*command args..." parameter in config file.
125  *
126  * mdev w/o parameters is called as hotplug helper. It takes device
127  * and subsystem names from $DEVPATH and $SUBSYSTEM, extracts
128  * maj,min from "/sys/$DEVPATH/dev" and also examines
129  * $ACTION ("add"/"delete") and $FIRMWARE.
130  *
131  * If action is "add", mdev creates /dev/device_name similarly to mdev -s.
132  * (todo: explain "delete" and $FIRMWARE)
133  *
134  * If /etc/mdev.conf exists, it may modify /dev/device_name's properties.
135  *
136  * Leading minus in 1st field means "don't stop on this line", otherwise
137  * search is stopped after the matching line is encountered.
138  *
139  * $envvar=regex format is useful for loading modules for hot-plugged devices
140  * which do not have driver loaded yet. In this case /sys/class/.../dev
141  * does not exist, but $MODALIAS is set to needed module's name
142  * (actually, an alias to it) by kernel. This rule instructs mdev
143  * to load the module and exit:
144  *    $MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"
145  * The kernel will generate another hotplug event when /sys/class/.../dev
146  * file appears.
147  *
148  * When line matches, the device node is created, chmod'ed and chown'ed,
149  * moved to path, and if >path, a symlink to moved node is created,
150  * all this if /sys/class/.../dev exists.
151  *    Examples:
152  *    =loop/      - moves to /dev/loop
153  *    >disk/sda%1 - moves to /dev/disk/sdaN, makes /dev/sdaN a symlink
154  *
155  * Then "command args..." is executed (via sh -c 'command args...').
156  * @:execute on creation, $:on deletion, *:on both.
157  * This happens regardless of /sys/class/.../dev existence.
158  */
159
160 /* Kernel's hotplug environment constantly changes.
161  * Here are new cases I observed on 3.1.0:
162  *
163  * Case with $DEVNAME and $DEVICE, not just $DEVPATH:
164  * ACTION=add
165  * BUSNUM=001
166  * DEVICE=/proc/bus/usb/001/003
167  * DEVNAME=bus/usb/001/003
168  * DEVNUM=003
169  * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5
170  * DEVTYPE=usb_device
171  * MAJOR=189
172  * MINOR=2
173  * PRODUCT=18d1/4e12/227
174  * SUBSYSTEM=usb
175  * TYPE=0/0/0
176  *
177  * Case with $DEVICE, but no $DEVNAME - apparenty, usb iface notification?
178  * "Please load me a module" thing?
179  * ACTION=add
180  * DEVICE=/proc/bus/usb/001/003
181  * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0
182  * DEVTYPE=usb_interface
183  * INTERFACE=8/6/80
184  * MODALIAS=usb:v18D1p4E12d0227dc00dsc00dp00ic08isc06ip50
185  * PRODUCT=18d1/4e12/227
186  * SUBSYSTEM=usb
187  * TYPE=0/0/0
188  *
189  * ACTION=add
190  * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5
191  * DEVTYPE=scsi_host
192  * SUBSYSTEM=scsi
193  *
194  * ACTION=add
195  * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/scsi_host/host5
196  * SUBSYSTEM=scsi_host
197  *
198  * ACTION=add
199  * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0
200  * DEVTYPE=scsi_target
201  * SUBSYSTEM=scsi
202  *
203  * Case with strange $MODALIAS:
204  * ACTION=add
205  * DEVPATH=/devices/pci0000:00/0000:00:02.1/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0
206  * DEVTYPE=scsi_device
207  * MODALIAS=scsi:t-0x00
208  * SUBSYSTEM=scsi
209  *
210  * ACTION=add
211  * 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
212  * SUBSYSTEM=scsi_disk
213  *
214  * ACTION=add
215  * 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
216  * SUBSYSTEM=scsi_device
217  *
218  * Case with explicit $MAJOR/$MINOR (no need to read /sys/$DEVPATH/dev?):
219  * ACTION=add
220  * DEVNAME=bsg/5:0:0:0
221  * 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
222  * MAJOR=253
223  * MINOR=1
224  * SUBSYSTEM=bsg
225  *
226  * ACTION=add
227  * DEVPATH=/devices/virtual/bdi/8:16
228  * SUBSYSTEM=bdi
229  *
230  * ACTION=add
231  * DEVNAME=sdb
232  * DEVPATH=/block/sdb
233  * DEVTYPE=disk
234  * MAJOR=8
235  * MINOR=16
236  * SUBSYSTEM=block
237  *
238  * Case with ACTION=change:
239  * ACTION=change
240  * DEVNAME=sdb
241  * DEVPATH=/block/sdb
242  * DEVTYPE=disk
243  * DISK_MEDIA_CHANGE=1
244  * MAJOR=8
245  * MINOR=16
246  * SUBSYSTEM=block
247  */
248
249 #define DEBUG_LVL 2
250
251 #if DEBUG_LVL >= 1
252 # define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
253 #else
254 # define dbg1(...) ((void)0)
255 #endif
256 #if DEBUG_LVL >= 2
257 # define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
258 #else
259 # define dbg2(...) ((void)0)
260 #endif
261 #if DEBUG_LVL >= 3
262 # define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
263 #else
264 # define dbg3(...) ((void)0)
265 #endif
266
267
268 #ifndef SO_RCVBUFFORCE
269 #define SO_RCVBUFFORCE 33
270 #endif
271 static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
272 enum { OP_add, OP_remove };
273
274 struct envmatch {
275         struct envmatch *next;
276         char *envname;
277         regex_t match;
278 };
279
280 struct rule {
281         bool keep_matching;
282         bool regex_compiled;
283         mode_t mode;
284         int maj, min0, min1;
285         struct bb_uidgid_t ugid;
286         char *envvar;
287         char *ren_mov;
288         IF_FEATURE_MDEV_EXEC(char *r_cmd;)
289         regex_t match;
290         struct envmatch *envmatch;
291 };
292
293 struct globals {
294         int root_major, root_minor;
295         smallint verbose;
296         char *subsystem;
297         char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
298 #if ENABLE_FEATURE_MDEV_CONF
299         const char *filename;
300         parser_t *parser;
301         struct rule **rule_vec;
302         unsigned rule_idx;
303 #endif
304         struct rule cur_rule;
305         char timestr[sizeof("HH:MM:SS.123456")];
306 } FIX_ALIASING;
307 #define G (*(struct globals*)bb_common_bufsiz1)
308 #define INIT_G() do { \
309         setup_common_bufsiz(); \
310         IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \
311         IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \
312 } while (0)
313
314
315 /* Prevent infinite loops in /sys symlinks */
316 #define MAX_SYSFS_DEPTH 3
317
318 /* We use additional bytes in make_device() */
319 #define SCRATCH_SIZE 128
320
321 #if ENABLE_FEATURE_MDEV_CONF
322
323 static void make_default_cur_rule(void)
324 {
325         memset(&G.cur_rule, 0, sizeof(G.cur_rule));
326         G.cur_rule.maj = -1; /* "not a @major,minor rule" */
327         G.cur_rule.mode = 0660;
328 }
329
330 static void clean_up_cur_rule(void)
331 {
332         struct envmatch *e;
333
334         free(G.cur_rule.envvar);
335         free(G.cur_rule.ren_mov);
336         if (G.cur_rule.regex_compiled)
337                 regfree(&G.cur_rule.match);
338         IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
339         e = G.cur_rule.envmatch;
340         while (e) {
341                 free(e->envname);
342                 regfree(&e->match);
343                 e = e->next;
344         }
345         make_default_cur_rule();
346 }
347
348 static char *parse_envmatch_pfx(char *val)
349 {
350         struct envmatch **nextp = &G.cur_rule.envmatch;
351
352         for (;;) {
353                 struct envmatch *e;
354                 char *semicolon;
355                 char *eq = strchr(val, '=');
356                 if (!eq /* || eq == val? */)
357                         return val;
358                 if (endofname(val) != eq)
359                         return val;
360                 semicolon = strchr(eq, ';');
361                 if (!semicolon)
362                         return val;
363                 /* ENVVAR=regex;... */
364                 *nextp = e = xzalloc(sizeof(*e));
365                 nextp = &e->next;
366                 e->envname = xstrndup(val, eq - val);
367                 *semicolon = '\0';
368                 xregcomp(&e->match, eq + 1, REG_EXTENDED);
369                 *semicolon = ';';
370                 val = semicolon + 1;
371         }
372 }
373
374 static void parse_next_rule(void)
375 {
376         /* Note: on entry, G.cur_rule is set to default */
377         while (1) {
378                 char *tokens[4];
379                 char *val;
380
381                 /* No PARSE_EOL_COMMENTS, because command may contain '#' chars */
382                 if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS))
383                         break;
384
385                 /* Fields: [-]regex uid:gid mode [alias] [cmd] */
386                 dbg3("token1:'%s'", tokens[1]);
387
388                 /* 1st field */
389                 val = tokens[0];
390                 G.cur_rule.keep_matching = ('-' == val[0]);
391                 val += G.cur_rule.keep_matching; /* swallow leading dash */
392                 val = parse_envmatch_pfx(val);
393                 if (val[0] == '@') {
394                         /* @major,minor[-minor2] */
395                         /* (useful when name is ambiguous:
396                          * "/sys/class/usb/lp0" and
397                          * "/sys/class/printer/lp0")
398                          */
399                         int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1);
400                         if (sc < 2 || G.cur_rule.maj < 0) {
401                                 bb_error_msg("bad @maj,min on line %d", G.parser->lineno);
402                                 goto next_rule;
403                         }
404                         if (sc == 2)
405                                 G.cur_rule.min1 = G.cur_rule.min0;
406                 } else {
407                         char *eq = strchr(val, '=');
408                         if (val[0] == '$') {
409                                 /* $ENVVAR=regex ... */
410                                 val++;
411                                 if (!eq) {
412                                         bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
413                                         goto next_rule;
414                                 }
415                                 G.cur_rule.envvar = xstrndup(val, eq - val);
416                                 val = eq + 1;
417                         }
418                         xregcomp(&G.cur_rule.match, val, REG_EXTENDED);
419                         G.cur_rule.regex_compiled = 1;
420                 }
421
422                 /* 2nd field: uid:gid - device ownership */
423                 if (get_uidgid(&G.cur_rule.ugid, tokens[1]) == 0) {
424                         bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
425                         goto next_rule;
426                 }
427
428                 /* 3rd field: mode - device permissions */
429                 G.cur_rule.mode = bb_parse_mode(tokens[2], G.cur_rule.mode);
430
431                 /* 4th field (opt): ">|=alias" or "!" to not create the node */
432                 val = tokens[3];
433                 if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) {
434                         char *s = skip_non_whitespace(val);
435                         G.cur_rule.ren_mov = xstrndup(val, s - val);
436                         val = skip_whitespace(s);
437                 }
438
439                 if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) {
440                         const char *s = "$@*";
441                         const char *s2 = strchr(s, val[0]);
442                         if (!s2) {
443                                 bb_error_msg("bad line %u", G.parser->lineno);
444                                 goto next_rule;
445                         }
446                         IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);)
447                 }
448
449                 return;
450  next_rule:
451                 clean_up_cur_rule();
452         } /* while (config_read) */
453
454         dbg3("config_close(G.parser)");
455         config_close(G.parser);
456         G.parser = NULL;
457
458         return;
459 }
460
461 /* If mdev -s, we remember rules in G.rule_vec[].
462  * Otherwise, there is no point in doing it, and we just
463  * save only one parsed rule in G.cur_rule.
464  */
465 static const struct rule *next_rule(void)
466 {
467         struct rule *rule;
468
469         /* Open conf file if we didn't do it yet */
470         if (!G.parser && G.filename) {
471                 dbg3("config_open('%s')", G.filename);
472                 G.parser = config_open2(G.filename, fopen_for_read);
473                 G.filename = NULL;
474         }
475
476         if (G.rule_vec) {
477                 /* mdev -s */
478                 /* Do we have rule parsed already? */
479                 if (G.rule_vec[G.rule_idx]) {
480                         dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
481                         return G.rule_vec[G.rule_idx++];
482                 }
483                 make_default_cur_rule();
484         } else {
485                 /* not mdev -s */
486                 clean_up_cur_rule();
487         }
488
489         /* Parse one more rule if file isn't fully read */
490         rule = &G.cur_rule;
491         if (G.parser) {
492                 parse_next_rule();
493                 if (G.rule_vec) { /* mdev -s */
494                         rule = xmemdup(&G.cur_rule, sizeof(G.cur_rule));
495                         G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
496                         G.rule_vec[G.rule_idx++] = rule;
497                         dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
498                 }
499         }
500
501         return rule;
502 }
503
504 static int env_matches(struct envmatch *e)
505 {
506         while (e) {
507                 int r;
508                 char *val = getenv(e->envname);
509                 if (!val)
510                         return 0;
511                 r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
512                 if (r != 0) /* no match */
513                         return 0;
514                 e = e->next;
515         }
516         return 1;
517 }
518
519 #else
520
521 # define next_rule() (&G.cur_rule)
522
523 #endif
524
525 static void mkdir_recursive(char *name)
526 {
527         /* if name has many levels ("dir1/dir2"),
528          * bb_make_directory() will create dir1 according to umask,
529          * not according to its "mode" parameter.
530          * Since we run with umask=0, need to temporarily switch it.
531          */
532         umask(022); /* "dir1" (if any) will be 0755 too */
533         bb_make_directory(name, 0755, FILEUTILS_RECUR);
534         umask(0);
535 }
536
537 /* Builds an alias path.
538  * This function potentionally reallocates the alias parameter.
539  * Only used for ENABLE_FEATURE_MDEV_RENAME
540  */
541 static char *build_alias(char *alias, const char *device_name)
542 {
543         char *dest;
544
545         /* ">bar/": rename to bar/device_name */
546         /* ">bar[/]baz": rename to bar[/]baz */
547         dest = strrchr(alias, '/');
548         if (dest) { /* ">bar/[baz]" ? */
549                 *dest = '\0'; /* mkdir bar */
550                 mkdir_recursive(alias);
551                 *dest = '/';
552                 if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
553                         dest = alias;
554                         alias = concat_path_file(alias, device_name);
555                         free(dest);
556                 }
557         }
558
559         return alias;
560 }
561
562 /* mknod in /dev based on a path like "/sys/block/hda/hda1"
563  * NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
564  * after NUL, but we promise to not mangle it (IOW: to restore NUL if needed).
565  * NB2: "mdev -s" may call us many times, do not leak memory/fds!
566  *
567  * device_name = $DEVNAME (may be NULL)
568  * path        = /sys/$DEVPATH
569  */
570 static void make_device(char *device_name, char *path, int operation)
571 {
572         int major, minor, type, len;
573         char *path_end = path + strlen(path);
574
575         /* Try to read major/minor string.  Note that the kernel puts \n after
576          * the data, so we don't need to worry about null terminating the string
577          * because sscanf() will stop at the first nondigit, which \n is.
578          * We also depend on path having writeable space after it.
579          */
580         major = -1;
581         if (operation == OP_add) {
582                 strcpy(path_end, "/dev");
583                 len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
584                 *path_end = '\0';
585                 if (len < 1) {
586                         if (!ENABLE_FEATURE_MDEV_EXEC)
587                                 return;
588                         /* no "dev" file, but we can still run scripts
589                          * based on device name */
590                 } else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) {
591                         dbg1("dev %u,%u", major, minor);
592                 } else {
593                         major = -1;
594                 }
595         }
596         /* else: for delete, -1 still deletes the node, but < -1 suppresses that */
597
598         /* Determine device name */
599         if (!device_name) {
600                 /*
601                  * There was no $DEVNAME envvar (for example, mdev -s never has).
602                  * But it is very useful: it contains the *path*, not only basename,
603                  * Thankfully, uevent file has it.
604                  * Example of .../sound/card0/controlC0/uevent file on Linux-3.7.7:
605                  * MAJOR=116
606                  * MINOR=7
607                  * DEVNAME=snd/controlC0
608                  */
609                 strcpy(path_end, "/uevent");
610                 len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
611                 if (len < 0)
612                         len = 0;
613                 *path_end = '\0';
614                 path_end[1 + len] = '\0';
615                 device_name = strstr(path_end + 1, "\nDEVNAME=");
616                 if (device_name) {
617                         device_name += sizeof("\nDEVNAME=")-1;
618                         strchrnul(device_name, '\n')[0] = '\0';
619                 } else {
620                         /* Fall back to just basename */
621                         device_name = (char*) bb_basename(path);
622                 }
623         }
624         /* Determine device type */
625         /*
626          * http://kernel.org/doc/pending/hotplug.txt says that only
627          * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
628          * But since 2.6.25 block devices are also in /sys/class/block.
629          * We use strstr("/block/") to forestall future surprises.
630          */
631         type = S_IFCHR;
632         if (strstr(path, "/block/") || (G.subsystem && is_prefixed_with(G.subsystem, "block")))
633                 type = S_IFBLK;
634
635 #if ENABLE_FEATURE_MDEV_CONF
636         G.rule_idx = 0; /* restart from the beginning (think mdev -s) */
637 #endif
638         for (;;) {
639                 const char *str_to_match;
640                 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
641                 char *command;
642                 char *alias;
643                 char aliaslink = aliaslink; /* for compiler */
644                 char *node_name;
645                 const struct rule *rule;
646
647                 str_to_match = device_name;
648
649                 rule = next_rule();
650
651 #if ENABLE_FEATURE_MDEV_CONF
652                 if (!env_matches(rule->envmatch))
653                         continue;
654                 if (rule->maj >= 0) {  /* @maj,min rule */
655                         if (major != rule->maj)
656                                 continue;
657                         if (minor < rule->min0 || minor > rule->min1)
658                                 continue;
659                         memset(off, 0, sizeof(off));
660                         goto rule_matches;
661                 }
662                 if (rule->envvar) { /* $envvar=regex rule */
663                         str_to_match = getenv(rule->envvar);
664                         dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
665                         if (!str_to_match)
666                                 continue;
667                 }
668                 /* else: str_to_match = device_name */
669
670                 if (rule->regex_compiled) {
671                         int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
672                         dbg3("regex_match for '%s':%d", str_to_match, regex_match);
673                         //bb_error_msg("matches:");
674                         //for (int i = 0; i < ARRAY_SIZE(off); i++) {
675                         //      if (off[i].rm_so < 0) continue;
676                         //      bb_error_msg("match %d: '%.*s'\n", i,
677                         //              (int)(off[i].rm_eo - off[i].rm_so),
678                         //              device_name + off[i].rm_so);
679                         //}
680
681                         if (regex_match != 0
682                         /* regexec returns whole pattern as "range" 0 */
683                          || off[0].rm_so != 0
684                          || (int)off[0].rm_eo != (int)strlen(str_to_match)
685                         ) {
686                                 continue; /* this rule doesn't match */
687                         }
688                 }
689                 /* else: it's final implicit "match-all" rule */
690  rule_matches:
691                 dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
692 #endif
693                 /* Build alias name */
694                 alias = NULL;
695                 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
696                         aliaslink = rule->ren_mov[0];
697                         if (aliaslink == '!') {
698                                 /* "!": suppress node creation/deletion */
699                                 major = -2;
700                         }
701                         else if (aliaslink == '>' || aliaslink == '=') {
702                                 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
703                                         char *s;
704                                         char *p;
705                                         unsigned n;
706
707                                         /* substitute %1..9 with off[1..9], if any */
708                                         n = 0;
709                                         s = rule->ren_mov;
710                                         while (*s)
711                                                 if (*s++ == '%')
712                                                         n++;
713
714                                         p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match));
715                                         s = rule->ren_mov + 1;
716                                         while (*s) {
717                                                 *p = *s;
718                                                 if ('%' == *s) {
719                                                         unsigned i = (s[1] - '0');
720                                                         if (i <= 9 && off[i].rm_so >= 0) {
721                                                                 n = off[i].rm_eo - off[i].rm_so;
722                                                                 strncpy(p, str_to_match + off[i].rm_so, n);
723                                                                 p += n - 1;
724                                                                 s++;
725                                                         }
726                                                 }
727                                                 p++;
728                                                 s++;
729                                         }
730                                 } else {
731                                         alias = xstrdup(rule->ren_mov + 1);
732                                 }
733                         }
734                 }
735                 dbg3("alias:'%s'", alias);
736
737                 command = NULL;
738                 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
739                 if (command) {
740                         /* Are we running this command now?
741                          * Run @cmd on create, $cmd on delete, *cmd on any
742                          */
743                         if ((command[0] == '@' && operation == OP_add)
744                          || (command[0] == '$' && operation == OP_remove)
745                          || (command[0] == '*')
746                         ) {
747                                 command++;
748                         } else {
749                                 command = NULL;
750                         }
751                 }
752                 dbg3("command:'%s'", command);
753
754                 /* "Execute" the line we found */
755                 node_name = device_name;
756                 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
757                         node_name = alias = build_alias(alias, device_name);
758                         dbg3("alias2:'%s'", alias);
759                 }
760
761                 if (operation == OP_add && major >= 0) {
762                         char *slash = strrchr(node_name, '/');
763                         if (slash) {
764                                 *slash = '\0';
765                                 mkdir_recursive(node_name);
766                                 *slash = '/';
767                         }
768                         if (ENABLE_FEATURE_MDEV_CONF) {
769                                 dbg1("mknod %s (%d,%d) %o"
770                                         " %u:%u",
771                                         node_name, major, minor, rule->mode | type,
772                                         rule->ugid.uid, rule->ugid.gid
773                                 );
774                         } else {
775                                 dbg1("mknod %s (%d,%d) %o",
776                                         node_name, major, minor, rule->mode | type
777                                 );
778                         }
779                         if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
780                                 bb_perror_msg("can't create '%s'", node_name);
781                         if (ENABLE_FEATURE_MDEV_CONF) {
782                                 chmod(node_name, rule->mode);
783                                 chown(node_name, rule->ugid.uid, rule->ugid.gid);
784                         }
785                         if (major == G.root_major && minor == G.root_minor)
786                                 symlink(node_name, "root");
787                         if (ENABLE_FEATURE_MDEV_RENAME && alias) {
788                                 if (aliaslink == '>') {
789 //TODO: on devtmpfs, device_name already exists and symlink() fails.
790 //End result is that instead of symlink, we have two nodes.
791 //What should be done?
792                                         dbg1("symlink: %s", device_name);
793                                         symlink(node_name, device_name);
794                                 }
795                         }
796                 }
797
798                 if (ENABLE_FEATURE_MDEV_EXEC && command) {
799                         /* setenv will leak memory, use putenv/unsetenv/free */
800                         char *s = xasprintf("%s=%s", "MDEV", node_name);
801                         putenv(s);
802                         dbg1("running: %s", command);
803                         if (system(command) == -1)
804                                 bb_perror_msg("can't run '%s'", command);
805                         bb_unsetenv_and_free(s);
806                 }
807
808                 if (operation == OP_remove && major >= -1) {
809                         if (ENABLE_FEATURE_MDEV_RENAME && alias) {
810                                 if (aliaslink == '>') {
811                                         dbg1("unlink: %s", device_name);
812                                         unlink(device_name);
813                                 }
814                         }
815                         dbg1("unlink: %s", node_name);
816                         unlink(node_name);
817                 }
818
819                 if (ENABLE_FEATURE_MDEV_RENAME)
820                         free(alias);
821
822                 /* We found matching line.
823                  * Stop unless it was prefixed with '-'
824                  */
825                 if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
826                         break;
827         } /* for (;;) */
828 }
829
830 static ssize_t readlink2(char *buf, size_t bufsize)
831 {
832         // Grr... gcc 8.1.1:
833         // "passing argument 2 to restrict-qualified parameter aliases with argument 1"
834         // dance around that...
835         char *obuf FIX_ALIASING;
836         obuf = buf;
837         return readlink(buf, obuf, bufsize);
838 }
839
840 /* File callback for /sys/ traversal.
841  * We act only on "/sys/.../dev" (pseudo)file
842  */
843 static int FAST_FUNC fileAction(const char *fileName,
844                 struct stat *statbuf UNUSED_PARAM,
845                 void *userData,
846                 int depth UNUSED_PARAM)
847 {
848         size_t len = strlen(fileName) - 4; /* can't underflow */
849         char *path = userData;  /* char array[PATH_MAX + SCRATCH_SIZE] */
850         char subsys[PATH_MAX];
851         int res;
852
853         /* Is it a ".../dev" file? (len check is for paranoid reasons) */
854         if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
855                 return FALSE; /* not .../dev */
856
857         strcpy(path, fileName);
858         path[len] = '\0';
859
860         /* Read ".../subsystem" symlink in the same directory where ".../dev" is */
861         strcpy(subsys, path);
862         strcpy(subsys + len, "/subsystem");
863         res = readlink2(subsys, sizeof(subsys)-1);
864         if (res > 0) {
865                 subsys[res] = '\0';
866                 free(G.subsystem);
867                 if (G.subsys_env) {
868                         bb_unsetenv_and_free(G.subsys_env);
869                         G.subsys_env = NULL;
870                 }
871                 /* Set G.subsystem and $SUBSYSTEM from symlink's last component */
872                 G.subsystem = strrchr(subsys, '/');
873                 if (G.subsystem) {
874                         G.subsystem = xstrdup(G.subsystem + 1);
875                         G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
876                         putenv(G.subsys_env);
877                 }
878         }
879
880         make_device(/*DEVNAME:*/ NULL, path, OP_add);
881
882         return TRUE;
883 }
884
885 /* Directory callback for /sys/ traversal */
886 static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
887                 struct stat *statbuf UNUSED_PARAM,
888                 void *userData UNUSED_PARAM,
889                 int depth)
890 {
891         return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
892 }
893
894 /* For the full gory details, see linux/Documentation/firmware_class/README
895  *
896  * Firmware loading works like this:
897  * - kernel sets FIRMWARE env var
898  * - userspace checks /lib/firmware/$FIRMWARE
899  * - userspace waits for /sys/$DEVPATH/loading to appear
900  * - userspace writes "1" to /sys/$DEVPATH/loading
901  * - userspace copies /lib/firmware/$FIRMWARE into /sys/$DEVPATH/data
902  * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading
903  * - kernel loads firmware into device
904  */
905 static void load_firmware(const char *firmware, const char *sysfs_path)
906 {
907         int cnt;
908         int firmware_fd, loading_fd;
909
910         /* check for /lib/firmware/$FIRMWARE */
911         firmware_fd = -1;
912         if (chdir("/lib/firmware") == 0)
913                 firmware_fd = open(firmware, O_RDONLY); /* can fail */
914
915         /* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
916         xchdir(sysfs_path);
917         for (cnt = 0; cnt < 30; ++cnt) {
918                 loading_fd = open("loading", O_WRONLY);
919                 if (loading_fd >= 0)
920                         goto loading;
921                 sleep(1);
922         }
923         goto out;
924
925  loading:
926         cnt = 0;
927         if (firmware_fd >= 0) {
928                 int data_fd;
929
930                 /* tell kernel we're loading by "echo 1 > /sys/$DEVPATH/loading" */
931                 if (full_write(loading_fd, "1", 1) != 1)
932                         goto out;
933
934                 /* load firmware into /sys/$DEVPATH/data */
935                 data_fd = open("data", O_WRONLY);
936                 if (data_fd < 0)
937                         goto out;
938                 cnt = bb_copyfd_eof(firmware_fd, data_fd);
939                 if (ENABLE_FEATURE_CLEAN_UP)
940                         close(data_fd);
941         }
942
943         /* Tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading"
944          * Note: we emit -1 also if firmware file wasn't found.
945          * There are cases when otherwise kernel would wait for minutes
946          * before timing out.
947          */
948         if (cnt > 0)
949                 full_write(loading_fd, "0", 1);
950         else
951                 full_write(loading_fd, "-1", 2);
952
953  out:
954         xchdir("/dev");
955         if (ENABLE_FEATURE_CLEAN_UP) {
956                 close(firmware_fd);
957                 close(loading_fd);
958         }
959 }
960
961 static char *curtime(void)
962 {
963         struct timeval tv;
964         gettimeofday(&tv, NULL);
965         sprintf(
966                 strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec),
967                 ".%06u",
968                 (unsigned)tv.tv_usec
969         );
970         return G.timestr;
971 }
972
973 static void open_mdev_log(const char *seq, unsigned my_pid)
974 {
975         int logfd = open("mdev.log", O_WRONLY | O_APPEND);
976         if (logfd >= 0) {
977                 xmove_fd(logfd, STDERR_FILENO);
978                 G.verbose = 2;
979                 applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
980         }
981 }
982
983 /* If it exists, does /dev/mdev.seq match $SEQNUM?
984  * If it does not match, earlier mdev is running
985  * in parallel, and we need to wait.
986  * Active mdev pokes us with SIGCHLD to check the new file.
987  */
988 static int
989 wait_for_seqfile(unsigned expected_seq)
990 {
991         /* We time out after 2 sec */
992         static const struct timespec ts = { 0, 32*1000*1000 };
993         int timeout = 2000 / 32;
994         int seq_fd = -1;
995         int do_once = 1;
996         sigset_t set_CHLD;
997
998         sigemptyset(&set_CHLD);
999         sigaddset(&set_CHLD, SIGCHLD);
1000         sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
1001
1002         for (;;) {
1003                 int seqlen;
1004                 char seqbuf[sizeof(long)*3 + 2];
1005                 unsigned seqbufnum;
1006
1007                 if (seq_fd < 0) {
1008                         seq_fd = open("mdev.seq", O_RDWR);
1009                         if (seq_fd < 0)
1010                                 break;
1011                         close_on_exec_on(seq_fd);
1012                 }
1013                 seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
1014                 if (seqlen < 0) {
1015                         close(seq_fd);
1016                         seq_fd = -1;
1017                         break;
1018                 }
1019                 seqbuf[seqlen] = '\0';
1020                 if (seqbuf[0] == '\n' || seqbuf[0] == '\0') {
1021                         /* seed file: write out seq ASAP */
1022                         xwrite_str(seq_fd, utoa(expected_seq));
1023                         xlseek(seq_fd, 0, SEEK_SET);
1024                         dbg2("first seq written");
1025                         break;
1026                 }
1027                 seqbufnum = atoll(seqbuf);
1028                 if (seqbufnum == expected_seq) {
1029                         /* correct idx */
1030                         break;
1031                 }
1032                 if (seqbufnum > expected_seq) {
1033                         /* a later mdev runs already (this was seen by users to happen) */
1034                         /* do not overwrite seqfile on exit */
1035                         close(seq_fd);
1036                         seq_fd = -1;
1037                         break;
1038                 }
1039                 if (do_once) {
1040                         dbg2("%s mdev.seq='%s', need '%u'", curtime(), seqbuf, expected_seq);
1041                         do_once = 0;
1042                 }
1043                 if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
1044                         dbg3("woken up");
1045                         continue; /* don't decrement timeout! */
1046                 }
1047                 if (--timeout == 0) {
1048                         dbg1("%s mdev.seq='%s'", "timed out", seqbuf);
1049                         break;
1050                 }
1051         }
1052         sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
1053         return seq_fd;
1054 }
1055
1056 static void signal_mdevs(unsigned my_pid)
1057 {
1058         procps_status_t* p = NULL;
1059         while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
1060                 if (p->pid != my_pid
1061                  && p->argv0
1062                  && strcmp(bb_basename(p->argv0), "mdev") == 0
1063                 ) {
1064                         kill(p->pid, SIGCHLD);
1065                 }
1066         }
1067 }
1068
1069 static void process_action(char *temp, unsigned my_pid)
1070 {
1071         char *fw;
1072         char *seq;
1073         char *action;
1074         char *env_devname;
1075         char *env_devpath;
1076         unsigned seqnum = seqnum; /* for compiler */
1077         int seq_fd;
1078         smalluint op;
1079
1080         /* Hotplug:
1081          * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
1082          * ACTION can be "add", "remove", "change"
1083          * DEVPATH is like "/block/sda" or "/class/input/mice"
1084          */
1085         env_devname = getenv("DEVNAME"); /* can be NULL */
1086         G.subsystem = getenv("SUBSYSTEM");
1087         action = getenv("ACTION");
1088         env_devpath = getenv("DEVPATH");
1089         if (!action || !env_devpath /*|| !G.subsystem*/)
1090                 bb_show_usage();
1091         fw = getenv("FIRMWARE");
1092         seq = getenv("SEQNUM");
1093         op = index_in_strings(keywords, action);
1094
1095         if (my_pid)
1096                 open_mdev_log(seq, my_pid);
1097
1098         seq_fd = -1;
1099         if (my_pid && seq) {
1100                 seqnum = atoll(seq);
1101                 seq_fd = wait_for_seqfile(seqnum);
1102         }
1103
1104         dbg1("%s "
1105                 "ACTION:%s SEQNUM:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
1106                 "%s%s",
1107                 curtime(),
1108                 action, seq, G.subsystem, env_devname, env_devpath,
1109                 fw ? " FW:" : "", fw ? fw : ""
1110         );
1111
1112         snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
1113         if (op == OP_remove) {
1114                 /* Ignoring "remove firmware". It was reported
1115                  * to happen and to cause erroneous deletion
1116                  * of device nodes. */
1117                 if (!fw)
1118                         make_device(env_devname, temp, op);
1119         }
1120         else {
1121                 make_device(env_devname, temp, op);
1122                 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
1123                         if (op == OP_add && fw)
1124                                 load_firmware(fw, temp);
1125                 }
1126         }
1127
1128         if (seq_fd >= 0) {
1129                 xwrite_str(seq_fd, utoa(seqnum + 1));
1130                 signal_mdevs(my_pid);
1131         }
1132 }
1133
1134 static void initial_scan(char *temp)
1135 {
1136         struct stat st;
1137
1138         xstat("/", &st);
1139         G.root_major = major(st.st_dev);
1140         G.root_minor = minor(st.st_dev);
1141
1142         putenv((char*)"ACTION=add");
1143
1144         /* Create all devices from /sys/dev hierarchy */
1145         recursive_action("/sys/dev",
1146                          ACTION_RECURSE | ACTION_FOLLOWLINKS,
1147                          fileAction, dirAction, temp, 0);
1148 }
1149
1150 #if ENABLE_FEATURE_MDEV_DAEMON
1151
1152 /* uevent applet uses 16k buffer, and mmaps it before every read */
1153 # define BUFFER_SIZE (2 * 1024)
1154 # define RCVBUF (2 * 1024 * 1024)
1155 # define MAX_ENV 32
1156
1157 static void daemon_loop(char *temp, int fd)
1158 {
1159         for (;;) {
1160                 char netbuf[BUFFER_SIZE];
1161                 char *env[MAX_ENV];
1162                 char *s, *end;
1163                 ssize_t len;
1164                 int idx;
1165
1166                 len = safe_read(fd, netbuf, sizeof(netbuf) - 1);
1167                 if (len < 0) {
1168                         bb_perror_msg_and_die("read");
1169                 }
1170                 end = netbuf + len;
1171                 *end = '\0';
1172
1173                 idx = 0;
1174                 s = netbuf;
1175                 while (s < end && idx < MAX_ENV) {
1176                         if (endofname(s)[0] == '=') {
1177                                 env[idx++] = s;
1178                                 putenv(s);
1179                         }
1180                         s += strlen(s) + 1;
1181                 }
1182
1183                 process_action(temp, 0);
1184
1185                 while (idx)
1186                         bb_unsetenv(env[--idx]);
1187         }
1188 }
1189 #endif
1190
1191 int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1192 int mdev_main(int argc UNUSED_PARAM, char **argv)
1193 {
1194         enum {
1195                 MDEV_OPT_SCAN       = 1 << 0,
1196                 MDEV_OPT_DAEMON     = 1 << 1,
1197                 MDEV_OPT_FOREGROUND = 1 << 2,
1198         };
1199         int opt;
1200         RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
1201
1202         INIT_G();
1203
1204         /* We can be called as hotplug helper */
1205         /* Kernel cannot provide suitable stdio fds for us, do it ourself */
1206         bb_sanitize_stdio();
1207
1208         /* Force the configuration file settings exactly */
1209         umask(0);
1210
1211         xchdir("/dev");
1212
1213         opt = getopt32(argv, "s" IF_FEATURE_MDEV_DAEMON("df"));
1214
1215 #if ENABLE_FEATURE_MDEV_CONF
1216         G.filename = "/etc/mdev.conf";
1217         if (opt & (MDEV_OPT_SCAN|MDEV_OPT_DAEMON)) {
1218                 /* Same as xrealloc_vector(NULL, 4, 0): */
1219                 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
1220         }
1221 #endif
1222
1223 #if ENABLE_FEATURE_MDEV_DAEMON
1224         if (opt & MDEV_OPT_DAEMON) {
1225                 /*
1226                  * Daemon mode listening on uevent netlink socket.
1227                  */
1228                 struct sockaddr_nl sa;
1229                 int fd;
1230
1231 //TODO: reuse same code in uevent
1232                 // Subscribe for UEVENT kernel messages
1233                 sa.nl_family = AF_NETLINK;
1234                 sa.nl_pad = 0;
1235                 sa.nl_pid = getpid();
1236                 sa.nl_groups = 1 << 0;
1237                 fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
1238                 xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
1239                 close_on_exec_on(fd);
1240
1241                 // Without a sufficiently big RCVBUF, a ton of simultaneous events
1242                 // can trigger ENOBUFS on read, which is unrecoverable.
1243                 // Reproducer:
1244                 //      mdev -d
1245                 //      find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
1246                 //
1247                 // SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl
1248                 setsockopt_SOL_SOCKET_int(fd, SO_RCVBUF,      RCVBUF);
1249                 setsockopt_SOL_SOCKET_int(fd, SO_RCVBUFFORCE, RCVBUF);
1250
1251                 /*
1252                  * Make inital scan after the uevent socket is alive and
1253                  * _before_ we fork away.
1254                  */
1255                 initial_scan(temp);
1256
1257                 if (!(opt & MDEV_OPT_FOREGROUND))
1258                         bb_daemonize_or_rexec(0, argv);
1259
1260                 open_mdev_log(NULL, getpid());
1261
1262                 daemon_loop(temp, fd);
1263         }
1264 #endif
1265         if (opt & MDEV_OPT_SCAN) {
1266                 /*
1267                  * Scan: mdev -s
1268                  */
1269                 initial_scan(temp);
1270         } else {
1271                 process_action(temp, getpid());
1272
1273                 dbg1("%s exiting", curtime());
1274         }
1275
1276         if (ENABLE_FEATURE_CLEAN_UP)
1277                 RELEASE_CONFIG_BUFFER(temp);
1278
1279         return EXIT_SUCCESS;
1280 }