1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113#include "libbb.h"
114#include "common_bufsiz.h"
115#include "xregex.h"
116#include <linux/netlink.h>
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
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
271static const char keywords[] ALIGN1 = "add\0remove\0";
272enum { OP_add, OP_remove };
273
274struct envmatch {
275 struct envmatch *next;
276 char *envname;
277 regex_t match;
278};
279
280struct 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
293struct globals {
294 int root_major, root_minor;
295 smallint verbose;
296 char *subsystem;
297 char *subsys_env;
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
316#define MAX_SYSFS_DEPTH 3
317
318
319#define SCRATCH_SIZE 128
320
321#if ENABLE_FEATURE_MDEV_CONF
322
323static void make_default_cur_rule(void)
324{
325 memset(&G.cur_rule, 0, sizeof(G.cur_rule));
326 G.cur_rule.maj = -1;
327 G.cur_rule.mode = 0660;
328}
329
330static 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
348static 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 )
357 return val;
358 if (endofname(val) != eq)
359 return val;
360 semicolon = strchr(eq, ';');
361 if (!semicolon)
362 return val;
363
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
374static void parse_next_rule(void)
375{
376
377 while (1) {
378 char *tokens[4];
379 char *val;
380
381
382 if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS))
383 break;
384
385
386 dbg3("token1:'%s'", tokens[1]);
387
388
389 val = tokens[0];
390 G.cur_rule.keep_matching = ('-' == val[0]);
391 val += G.cur_rule.keep_matching;
392 val = parse_envmatch_pfx(val);
393 if (val[0] == '@') {
394
395
396
397
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
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
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
429 G.cur_rule.mode = bb_parse_mode(tokens[2], G.cur_rule.mode);
430
431
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 }
453
454 dbg3("config_close(G.parser)");
455 config_close(G.parser);
456 G.parser = NULL;
457
458 return;
459}
460
461
462
463
464
465static const struct rule *next_rule(void)
466{
467 struct rule *rule;
468
469
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
478
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
486 clean_up_cur_rule();
487 }
488
489
490 rule = &G.cur_rule;
491 if (G.parser) {
492 parse_next_rule();
493 if (G.rule_vec) {
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
504static 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, 0, NULL, 0);
512 if (r != 0)
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
525static void mkdir_recursive(char *name)
526{
527
528
529
530
531
532 umask(022);
533 bb_make_directory(name, 0755, FILEUTILS_RECUR);
534 umask(0);
535}
536
537
538
539
540
541static char *build_alias(char *alias, const char *device_name)
542{
543 char *dest;
544
545
546
547 dest = strrchr(alias, '/');
548 if (dest) {
549 *dest = '\0';
550 mkdir_recursive(alias);
551 *dest = '/';
552 if (dest[1] == '\0') {
553 dest = alias;
554 alias = concat_path_file(alias, device_name);
555 free(dest);
556 }
557 }
558
559 return alias;
560}
561
562
563
564
565
566
567
568
569
570static 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
576
577
578
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
589
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
597
598
599 if (!device_name) {
600
601
602
603
604
605
606
607
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
621 device_name = (char*) bb_basename(path);
622 }
623 }
624
625
626
627
628
629
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;
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;
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) {
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) {
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
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
674
675
676
677
678
679
680
681 if (regex_match != 0
682
683 || off[0].rm_so != 0
684 || (int)off[0].rm_eo != (int)strlen(str_to_match)
685 ) {
686 continue;
687 }
688 }
689
690 rule_matches:
691 dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
692#endif
693
694 alias = NULL;
695 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
696 aliaslink = rule->ren_mov[0];
697 if (aliaslink == '!') {
698
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
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
741
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
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
790
791
792 dbg1("symlink: %s", device_name);
793 symlink(node_name, device_name);
794 }
795 }
796 }
797
798 if (ENABLE_FEATURE_MDEV_EXEC && command) {
799
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
823
824
825 if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
826 break;
827 }
828}
829
830static ssize_t readlink2(char *buf, size_t bufsize)
831{
832
833
834
835 char *obuf FIX_ALIASING;
836 obuf = buf;
837 return readlink(buf, obuf, bufsize);
838}
839
840
841
842
843static 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;
849 char *path = userData;
850 char subsys[PATH_MAX];
851 int res;
852
853
854 if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
855 return FALSE;
856
857 strcpy(path, fileName);
858 path[len] = '\0';
859
860
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
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( NULL, path, OP_add);
881
882 return TRUE;
883}
884
885
886static 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
895
896
897
898
899
900
901
902
903
904
905static void load_firmware(const char *firmware, const char *sysfs_path)
906{
907 int cnt;
908 int firmware_fd, loading_fd;
909
910
911 firmware_fd = -1;
912 if (chdir("/lib/firmware") == 0)
913 firmware_fd = open(firmware, O_RDONLY);
914
915
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
931 if (full_write(loading_fd, "1", 1) != 1)
932 goto out;
933
934
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
944
945
946
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
961static 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
973static 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
984
985
986
987
988static int
989wait_for_seqfile(unsigned expected_seq)
990{
991
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
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
1030 break;
1031 }
1032 if (seqbufnum > expected_seq) {
1033
1034
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;
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
1056static 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
1069static 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;
1077 int seq_fd;
1078 smalluint op;
1079
1080
1081
1082
1083
1084
1085 env_devname = getenv("DEVNAME");
1086 G.subsystem = getenv("SUBSYSTEM");
1087 action = getenv("ACTION");
1088 env_devpath = getenv("DEVPATH");
1089 if (!action || !env_devpath )
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
1115
1116
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
1134static 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
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
1153# define BUFFER_SIZE (2 * 1024)
1154# define RCVBUF (2 * 1024 * 1024)
1155# define MAX_ENV 32
1156
1157static 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
1191int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1192int 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
1205
1206 bb_sanitize_stdio();
1207
1208
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
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
1227
1228 int fd;
1229
1230
1231
1232
1233
1234
1235
1236
1237 fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, 1 << 0, RCVBUF);
1238
1239
1240
1241
1242
1243 initial_scan(temp);
1244
1245 if (!(opt & MDEV_OPT_FOREGROUND))
1246 bb_daemonize_or_rexec(0, argv);
1247
1248 open_mdev_log(NULL, getpid());
1249
1250 daemon_loop(temp, fd);
1251 }
1252#endif
1253 if (opt & MDEV_OPT_SCAN) {
1254
1255
1256
1257 initial_scan(temp);
1258 } else {
1259 process_action(temp, getpid());
1260
1261 dbg1("%s exiting", curtime());
1262 }
1263
1264 if (ENABLE_FEATURE_CLEAN_UP)
1265 RELEASE_CONFIG_BUFFER(temp);
1266
1267 return EXIT_SUCCESS;
1268}
1269