1 --- busybox-1.21.0/util-linux/mdev.c
2 +++ busybox-1.21.0-mdev/util-linux/mdev.c
4 //usage: IF_FEATURE_MDEV_CONF(
6 //usage: "It uses /etc/mdev.conf with lines\n"
7 -//usage: " [-]DEVNAME UID:GID PERM"
8 +//usage: " [-][ENV=regex;]...DEVNAME UID:GID PERM"
9 //usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
10 //usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
16 -static const char keywords[] ALIGN1 = "add\0remove\0change\0";
20 +# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
22 +# define dbg1(...) ((void)0)
25 +# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
27 +# define dbg2(...) ((void)0)
30 +# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
32 +# define dbg3(...) ((void)0)
36 +static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
37 enum { OP_add, OP_remove };
40 + struct envmatch *next;
48 @@ -243,12 +268,14 @@ struct rule {
50 IF_FEATURE_MDEV_EXEC(char *r_cmd;)
52 + struct envmatch *envmatch;
56 int root_major, root_minor;
59 + char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
60 #if ENABLE_FEATURE_MDEV_CONF
63 @@ -256,6 +283,7 @@ struct globals {
67 + char timestr[sizeof("60.123456")];
69 #define G (*(struct globals*)&bb_common_bufsiz1)
70 #define INIT_G() do { \
71 @@ -270,13 +298,6 @@ struct globals {
72 /* We use additional 64+ bytes in make_device() */
73 #define SCRATCH_SIZE 80
76 -# define dbg(...) bb_error_msg(__VA_ARGS__)
78 -# define dbg(...) ((void)0)
82 #if ENABLE_FEATURE_MDEV_CONF
84 static void make_default_cur_rule(void)
85 @@ -288,14 +309,65 @@ static void make_default_cur_rule(void)
87 static void clean_up_cur_rule(void)
91 free(G.cur_rule.envvar);
92 + free(G.cur_rule.ren_mov);
93 if (G.cur_rule.regex_compiled)
94 regfree(&G.cur_rule.match);
95 - free(G.cur_rule.ren_mov);
96 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
97 + e = G.cur_rule.envmatch;
100 + regfree(&e->match);
103 make_default_cur_rule();
106 +/* In later versions, endofname is in libbb */
107 +#define endofname mdev_endofname
109 +const char* FAST_FUNC
110 +endofname(const char *name)
112 +#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
113 +#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
114 + if (!is_name(*name))
117 + if (!is_in_name(*name))
123 +static char *parse_envmatch_pfx(char *val)
125 + struct envmatch **nextp = &G.cur_rule.envmatch;
128 + struct envmatch *e;
130 + char *eq = strchr(val, '=');
131 + if (!eq /* || eq == val? */)
133 + if (endofname(val) != eq)
135 + semicolon = strchr(eq, ';');
138 + /* ENVVAR=regex;... */
139 + *nextp = e = xzalloc(sizeof(*e));
141 + e->envname = xstrndup(val, eq - val);
143 + xregcomp(&e->match, eq + 1, REG_EXTENDED);
145 + val = semicolon + 1;
149 static void parse_next_rule(void)
151 /* Note: on entry, G.cur_rule is set to default */
152 @@ -308,12 +380,13 @@ static void parse_next_rule(void)
155 /* Fields: [-]regex uid:gid mode [alias] [cmd] */
156 - dbg("token1:'%s'", tokens[1]);
157 + dbg3("token1:'%s'", tokens[1]);
161 G.cur_rule.keep_matching = ('-' == val[0]);
162 val += G.cur_rule.keep_matching; /* swallow leading dash */
163 + val = parse_envmatch_pfx(val);
165 /* @major,minor[-minor2] */
166 /* (useful when name is ambiguous:
167 @@ -328,8 +401,10 @@ static void parse_next_rule(void)
169 G.cur_rule.min1 = G.cur_rule.min0;
171 + char *eq = strchr(val, '=');
173 - char *eq = strchr(++val, '=');
174 + /* $ENVVAR=regex ... */
177 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
179 @@ -373,7 +448,7 @@ static void parse_next_rule(void)
181 } /* while (config_read) */
183 - dbg("config_close(G.parser)");
184 + dbg3("config_close(G.parser)");
185 config_close(G.parser);
188 @@ -390,7 +465,7 @@ static const struct rule *next_rule(void
190 /* Open conf file if we didn't do it yet */
191 if (!G.parser && G.filename) {
192 - dbg("config_open('%s')", G.filename);
193 + dbg3("config_open('%s')", G.filename);
194 G.parser = config_open2(G.filename, fopen_for_read);
197 @@ -399,7 +474,7 @@ static const struct rule *next_rule(void
199 /* Do we have rule parsed already? */
200 if (G.rule_vec[G.rule_idx]) {
201 - dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
202 + dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
203 return G.rule_vec[G.rule_idx++];
205 make_default_cur_rule();
206 @@ -416,13 +491,28 @@ static const struct rule *next_rule(void
207 rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
208 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
209 G.rule_vec[G.rule_idx++] = rule;
210 - dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
211 + dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
218 +static int env_matches(struct envmatch *e)
222 + char *val = getenv(e->envname);
225 + r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
226 + if (r != 0) /* no match */
235 # define next_rule() (&G.cur_rule)
236 @@ -479,9 +569,6 @@ static void make_device(char *device_nam
238 int major, minor, type, len;
241 - bb_error_msg("device: %s, %s", device_name, path);
243 /* Try to read major/minor string. Note that the kernel puts \n after
244 * the data, so we don't need to worry about null terminating the string
245 * because sscanf() will stop at the first nondigit, which \n is.
246 @@ -500,8 +587,7 @@ static void make_device(char *device_nam
247 /* no "dev" file, but we can still run scripts
248 * based on device name */
249 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) {
251 - bb_error_msg("maj,min: %u,%u", major, minor);
252 + dbg1("dev %u,%u", major, minor);
256 @@ -511,7 +597,8 @@ static void make_device(char *device_nam
257 /* Determine device name, type, major and minor */
259 device_name = (char*) bb_basename(path);
260 - /* http://kernel.org/doc/pending/hotplug.txt says that only
262 + * http://kernel.org/doc/pending/hotplug.txt says that only
263 * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
264 * But since 2.6.25 block devices are also in /sys/class/block.
265 * We use strstr("/block/") to forestall future surprises.
266 @@ -537,6 +624,8 @@ static void make_device(char *device_nam
269 #if ENABLE_FEATURE_MDEV_CONF
270 + if (!env_matches(rule->envmatch))
272 if (rule->maj >= 0) { /* @maj,min rule */
273 if (major != rule->maj)
275 @@ -547,7 +636,7 @@ static void make_device(char *device_nam
277 if (rule->envvar) { /* $envvar=regex rule */
278 str_to_match = getenv(rule->envvar);
279 - dbg("getenv('%s'):'%s'", rule->envvar, str_to_match);
280 + dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
284 @@ -555,7 +644,7 @@ static void make_device(char *device_nam
286 if (rule->regex_compiled) {
287 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
288 - dbg("regex_match for '%s':%d", str_to_match, regex_match);
289 + dbg3("regex_match for '%s':%d", str_to_match, regex_match);
290 //bb_error_msg("matches:");
291 //for (int i = 0; i < ARRAY_SIZE(off); i++) {
292 // if (off[i].rm_so < 0) continue;
293 @@ -574,9 +663,8 @@ static void make_device(char *device_nam
295 /* else: it's final implicit "match-all" rule */
297 + dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
299 - dbg("rule matched");
301 /* Build alias name */
303 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
304 @@ -619,34 +707,30 @@ static void make_device(char *device_nam
308 - dbg("alias:'%s'", alias);
309 + dbg3("alias:'%s'", alias);
312 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
314 - const char *s = "$@*";
315 - const char *s2 = strchr(s, command[0]);
317 /* Are we running this command now?
318 - * Run $cmd on delete, @cmd on create, *cmd on both
319 + * Run @cmd on create, $cmd on delete, *cmd on any
321 - if (s2 - s != (operation == OP_remove) || *s2 == '*') {
322 - /* We are here if: '*',
323 - * or: '@' and delete = 0,
324 - * or: '$' and delete = 1
326 + if ((command[0] == '@' && operation == OP_add)
327 + || (command[0] == '$' && operation == OP_remove)
328 + || (command[0] == '*')
335 - dbg("command:'%s'", command);
336 + dbg3("command:'%s'", command);
338 /* "Execute" the line we found */
339 node_name = device_name;
340 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
341 node_name = alias = build_alias(alias, device_name);
342 - dbg("alias2:'%s'", alias);
343 + dbg3("alias2:'%s'", alias);
346 if (operation == OP_add && major >= 0) {
347 @@ -656,8 +740,17 @@ static void make_device(char *device_nam
348 mkdir_recursive(node_name);
352 - bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type);
353 + if (ENABLE_FEATURE_MDEV_CONF) {
354 + dbg1("mknod %s (%d,%d) %o"
356 + node_name, major, minor, rule->mode | type,
357 + rule->ugid.uid, rule->ugid.gid
360 + dbg1("mknod %s (%d,%d) %o",
361 + node_name, major, minor, rule->mode | type
364 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
365 bb_perror_msg("can't create '%s'", node_name);
366 if (ENABLE_FEATURE_MDEV_CONF) {
367 @@ -671,8 +764,7 @@ static void make_device(char *device_nam
368 //TODO: on devtmpfs, device_name already exists and symlink() fails.
369 //End result is that instead of symlink, we have two nodes.
370 //What should be done?
372 - bb_error_msg("symlink: %s", device_name);
373 + dbg1("symlink: %s", device_name);
374 symlink(node_name, device_name);
377 @@ -681,27 +773,21 @@ static void make_device(char *device_nam
378 if (ENABLE_FEATURE_MDEV_EXEC && command) {
379 /* setenv will leak memory, use putenv/unsetenv/free */
380 char *s = xasprintf("%s=%s", "MDEV", node_name);
381 - char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
385 - bb_error_msg("running: %s", command);
386 + dbg1("running: %s", command);
387 if (system(command) == -1)
388 bb_perror_msg("can't run '%s'", command);
389 - bb_unsetenv_and_free(s1);
390 bb_unsetenv_and_free(s);
393 if (operation == OP_remove && major >= -1) {
394 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
395 if (aliaslink == '>') {
397 - bb_error_msg("unlink: %s", device_name);
398 + dbg1("unlink: %s", device_name);
403 - bb_error_msg("unlink: %s", node_name);
404 + dbg1("unlink: %s", node_name);
408 @@ -746,9 +832,16 @@ static int FAST_FUNC dirAction(const cha
409 * under /sys/class/ */
412 + if (G.subsys_env) {
413 + bb_unsetenv_and_free(G.subsys_env);
414 + G.subsys_env = NULL;
416 G.subsystem = strrchr(fileName, '/');
419 G.subsystem = xstrdup(G.subsystem + 1);
420 + G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
421 + putenv(G.subsys_env);
425 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
426 @@ -813,12 +906,107 @@ static void load_firmware(const char *fi
427 full_write(loading_fd, "-1", 2);
431 if (ENABLE_FEATURE_CLEAN_UP) {
437 +static char *curtime(void)
440 + gettimeofday(&tv, NULL);
441 + sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
445 +static void open_mdev_log(const char *seq, unsigned my_pid)
447 + int logfd = open("mdev.log", O_WRONLY | O_APPEND);
449 + xmove_fd(logfd, STDERR_FILENO);
451 + applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
455 +/* If it exists, does /dev/mdev.seq match $SEQNUM?
456 + * If it does not match, earlier mdev is running
457 + * in parallel, and we need to wait.
458 + * Active mdev pokes us with SIGCHLD to check the new file.
461 +wait_for_seqfile(const char *seq)
463 + /* We time out after 2 sec */
464 + static const struct timespec ts = { 0, 32*1000*1000 };
465 + int timeout = 2000 / 32;
470 + sigemptyset(&set_CHLD);
471 + sigaddset(&set_CHLD, SIGCHLD);
472 + sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
476 + char seqbuf[sizeof(int)*3 + 2];
479 + seq_fd = open("mdev.seq", O_RDWR);
483 + seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
489 + seqbuf[seqlen] = '\0';
490 + if (seqbuf[0] == '\n') {
491 + /* seed file: write out seq ASAP */
492 + xwrite_str(seq_fd, seq);
493 + xlseek(seq_fd, 0, SEEK_SET);
494 + dbg2("first seq written");
497 + if (strcmp(seq, seqbuf) == 0) {
502 + dbg2("%s waiting for '%s'", curtime(), seqbuf);
505 + if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
507 + continue; /* don't decrement timeout! */
509 + if (--timeout == 0) {
510 + dbg1("%s waiting for '%s'", "timed out", seqbuf);
514 + sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
518 +static void signal_mdevs(unsigned my_pid)
520 + procps_status_t* p = NULL;
521 + while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
522 + if (p->pid != my_pid
524 + && strcmp(bb_basename(p->argv0), "mdev") == 0
526 + kill(p->pid, SIGCHLD);
531 int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
532 int mdev_main(int argc UNUSED_PARAM, char **argv)
534 @@ -840,8 +1028,8 @@ int mdev_main(int argc UNUSED_PARAM, cha
537 if (argv[1] && strcmp(argv[1], "-s") == 0) {
545 @@ -853,6 +1041,8 @@ int mdev_main(int argc UNUSED_PARAM, cha
546 G.root_major = major(st.st_dev);
547 G.root_minor = minor(st.st_dev);
549 + putenv((char*)"ACTION=add");
551 /* ACTION_FOLLOWLINKS is needed since in newer kernels
552 * /sys/block/loop* (for example) are symlinks to dirs,
553 * not real directories.
554 @@ -878,11 +1068,13 @@ int mdev_main(int argc UNUSED_PARAM, cha
563 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
564 - * ACTION can be "add" or "remove"
565 + * ACTION can be "add", "remove", "change"
566 * DEVPATH is like "/block/sda" or "/class/input/mice"
568 action = getenv("ACTION");
569 @@ -893,39 +1085,20 @@ int mdev_main(int argc UNUSED_PARAM, cha
570 if (!action || !env_devpath /*|| !G.subsystem*/)
572 fw = getenv("FIRMWARE");
573 - /* If it exists, does /dev/mdev.seq match $SEQNUM?
574 - * If it does not match, earlier mdev is running
575 - * in parallel, and we need to wait */
576 seq = getenv("SEQNUM");
578 - int timeout = 2000 / 32; /* 2000 msec */
581 - char seqbuf[sizeof(int)*3 + 2];
583 - seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf) - 1);
588 - seqbuf[seqlen] = '\0';
589 - if (seqbuf[0] == '\n' /* seed file? */
590 - || strcmp(seq, seqbuf) == 0 /* correct idx? */
595 - } while (--timeout);
599 - int logfd = open("/dev/mdev.log", O_WRONLY | O_APPEND);
601 - xmove_fd(logfd, STDERR_FILENO);
603 - bb_error_msg("seq: %s action: %s", seq, action);
607 + open_mdev_log(seq, my_pid);
609 + seq_fd = seq ? wait_for_seqfile(seq) : -1;
612 + "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
615 + action, G.subsystem, env_devname, env_devpath,
616 + fw ? " FW:" : "", fw ? fw : ""
619 snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
620 if (op == OP_remove) {
621 @@ -935,16 +1108,18 @@ int mdev_main(int argc UNUSED_PARAM, cha
623 make_device(env_devname, temp, op);
625 - else if (op == OP_add) {
627 make_device(env_devname, temp, op);
628 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
630 + if (op == OP_add && fw)
631 load_firmware(fw, temp);
636 - xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
637 + dbg1("%s exiting", curtime());
639 + xwrite_str(seq_fd, utoa(xatou(seq) + 1));
640 + signal_mdevs(my_pid);