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
114#include "libbb.h"
115#include "common_bufsiz.h"
116#include "xregex.h"
117#include <linux/netlink.h>
118#include <syslog.h>
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
250
251#define DEBUG_LVL 2
252
253#if DEBUG_LVL >= 1
254# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
255# define dbg1s(msg) do { if (G.verbose) bb_simple_error_msg(msg); } while(0)
256#else
257# define dbg1(...) ((void)0)
258# define dbg1s(msg) ((void)0)
259#endif
260#if DEBUG_LVL >= 2
261# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
262# define dbg2s(msg) do { if (G.verbose >= 2) bb_simple_error_msg(msg); } while(0)
263#else
264# define dbg2(...) ((void)0)
265# define dbg2s(msg) ((void)0)
266#endif
267#if DEBUG_LVL >= 3
268# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
269# define dbg3s(msg) do { if (G.verbose >= 3) bb_simple_error_msg(msg); } while(0)
270#else
271# define dbg3(...) ((void)0)
272# define dbg3s(msg) ((void)0)
273#endif
274
275static const char keywords[] ALIGN1 = "add\0remove\0";
276enum { OP_add, OP_remove };
277
278struct envmatch {
279 struct envmatch *next;
280 char *envname;
281 regex_t match;
282};
283
284struct rule {
285 bool keep_matching;
286 bool regex_compiled;
287 mode_t mode;
288 int maj, min0, min1;
289 struct bb_uidgid_t ugid;
290 char *envvar;
291 char *ren_mov;
292 IF_FEATURE_MDEV_EXEC(char *r_cmd;)
293 regex_t match;
294 struct envmatch *envmatch;
295};
296
297struct globals {
298 int root_major, root_minor;
299 int verbose;
300 char *subsystem;
301 char *subsys_env;
302#if ENABLE_FEATURE_MDEV_CONF
303 const char *filename;
304 parser_t *parser;
305 struct rule **rule_vec;
306 unsigned rule_idx;
307#endif
308 struct rule cur_rule;
309 char timestr[sizeof("HH:MM:SS.123456")];
310} FIX_ALIASING;
311#define G (*(struct globals*)bb_common_bufsiz1)
312#define INIT_G() do { \
313 setup_common_bufsiz(); \
314 IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \
315 IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \
316} while (0)
317
318
319
320#define MAX_SYSFS_DEPTH 3
321
322
323#define SCRATCH_SIZE 128
324
325#if ENABLE_FEATURE_MDEV_CONF
326
327static void make_default_cur_rule(void)
328{
329 memset(&G.cur_rule, 0, sizeof(G.cur_rule));
330 G.cur_rule.maj = -1;
331 G.cur_rule.mode = 0660;
332}
333
334static void clean_up_cur_rule(void)
335{
336 struct envmatch *e;
337
338 free(G.cur_rule.envvar);
339 free(G.cur_rule.ren_mov);
340 if (G.cur_rule.regex_compiled)
341 regfree(&G.cur_rule.match);
342 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
343 e = G.cur_rule.envmatch;
344 while (e) {
345 free(e->envname);
346 regfree(&e->match);
347 e = e->next;
348 }
349 make_default_cur_rule();
350}
351
352static char *parse_envmatch_pfx(char *val)
353{
354 struct envmatch **nextp = &G.cur_rule.envmatch;
355
356 for (;;) {
357 struct envmatch *e;
358 char *semicolon;
359 char *eq = strchr(val, '=');
360 if (!eq )
361 return val;
362 if (endofname(val) != eq)
363 return val;
364 semicolon = strchr(eq, ';');
365 if (!semicolon)
366 return val;
367
368 *nextp = e = xzalloc(sizeof(*e));
369 nextp = &e->next;
370 e->envname = xstrndup(val, eq - val);
371 *semicolon = '\0';
372 xregcomp(&e->match, eq + 1, REG_EXTENDED);
373 *semicolon = ';';
374 val = semicolon + 1;
375 }
376}
377
378static void parse_next_rule(void)
379{
380
381 while (1) {
382 char *tokens[4];
383 char *val;
384
385
386 if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL & ~PARSE_EOL_COMMENTS))
387 break;
388
389
390 dbg3("token1:'%s'", tokens[1]);
391
392
393 val = tokens[0];
394 G.cur_rule.keep_matching = ('-' == val[0]);
395 val += G.cur_rule.keep_matching;
396 val = parse_envmatch_pfx(val);
397 if (val[0] == '@') {
398
399
400
401
402
403 int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1);
404 if (sc < 2 || G.cur_rule.maj < 0) {
405 bb_error_msg("bad @maj,min on line %d", G.parser->lineno);
406 goto next_rule;
407 }
408 if (sc == 2)
409 G.cur_rule.min1 = G.cur_rule.min0;
410 } else {
411 char *eq = strchr(val, '=');
412 if (val[0] == '$') {
413
414 val++;
415 if (!eq) {
416 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
417 goto next_rule;
418 }
419 G.cur_rule.envvar = xstrndup(val, eq - val);
420 val = eq + 1;
421 }
422 xregcomp(&G.cur_rule.match, val, REG_EXTENDED);
423 G.cur_rule.regex_compiled = 1;
424 }
425
426
427 if (get_uidgid(&G.cur_rule.ugid, tokens[1]) == 0) {
428 bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
429 goto next_rule;
430 }
431
432
433 G.cur_rule.mode = bb_parse_mode(tokens[2], G.cur_rule.mode);
434
435
436 val = tokens[3];
437 if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) {
438 char *s = skip_non_whitespace(val);
439 G.cur_rule.ren_mov = xstrndup(val, s - val);
440 val = skip_whitespace(s);
441 }
442
443 if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) {
444 const char *s = "$@*";
445 const char *s2 = strchr(s, val[0]);
446 if (!s2) {
447 bb_error_msg("bad line %u", G.parser->lineno);
448 goto next_rule;
449 }
450 IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);)
451 }
452
453 return;
454 next_rule:
455 clean_up_cur_rule();
456 }
457
458 dbg3("config_close(G.parser)");
459 config_close(G.parser);
460 G.parser = NULL;
461
462 return;
463}
464
465
466
467
468
469static const struct rule *next_rule(void)
470{
471 struct rule *rule;
472
473
474 if (!G.parser && G.filename) {
475 dbg3("config_open('%s')", G.filename);
476 G.parser = config_open2(G.filename, fopen_for_read);
477 G.filename = NULL;
478 }
479
480 if (G.rule_vec) {
481
482
483 if (G.rule_vec[G.rule_idx]) {
484 dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
485 return G.rule_vec[G.rule_idx++];
486 }
487 make_default_cur_rule();
488 } else {
489
490 clean_up_cur_rule();
491 }
492
493
494 rule = &G.cur_rule;
495 if (G.parser) {
496 parse_next_rule();
497 if (G.rule_vec) {
498 rule = xmemdup(&G.cur_rule, sizeof(G.cur_rule));
499 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
500 G.rule_vec[G.rule_idx++] = rule;
501 dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
502 }
503 }
504
505 return rule;
506}
507
508static int env_matches(struct envmatch *e)
509{
510 while (e) {
511 int r;
512 char *val = getenv(e->envname);
513 if (!val)
514 return 0;
515 r = regexec(&e->match, val, 0, NULL, 0);
516 if (r != 0)
517 return 0;
518 e = e->next;
519 }
520 return 1;
521}
522
523#else
524
525# define next_rule() (&G.cur_rule)
526
527#endif
528
529static void mkdir_recursive(char *name)
530{
531
532
533
534
535
536 umask(022);
537 bb_make_directory(name, 0755, FILEUTILS_RECUR);
538 umask(0);
539}
540
541
542
543
544
545static char *build_alias(char *alias, const char *device_name)
546{
547 char *dest;
548
549
550
551 dest = strrchr(alias, '/');
552 if (dest) {
553 *dest = '\0';
554 mkdir_recursive(alias);
555 *dest = '/';
556 if (dest[1] == '\0') {
557 dest = alias;
558 alias = concat_path_file(alias, device_name);
559 free(dest);
560 }
561 }
562
563 return alias;
564}
565
566
567
568
569
570
571
572
573
574static void make_device(char *device_name, char *path, int operation)
575{
576 int major, minor, type, len;
577 char *path_end = path + strlen(path);
578
579
580
581
582
583
584 major = -1;
585 if (operation == OP_add) {
586 strcpy(path_end, "/dev");
587 len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
588 *path_end = '\0';
589 if (len < 1) {
590 if (!ENABLE_FEATURE_MDEV_EXEC)
591 return;
592
593
594 } else if (sscanf(path_end + 1, "%u:%u", &major, &minor) == 2) {
595 dbg1("dev %u,%u", major, minor);
596 } else {
597 major = -1;
598 }
599 }
600
601
602
603 if (!device_name) {
604
605
606
607
608
609
610
611
612
613 strcpy(path_end, "/uevent");
614 len = open_read_close(path, path_end + 1, SCRATCH_SIZE - 1);
615 if (len < 0)
616 len = 0;
617 *path_end = '\0';
618 path_end[1 + len] = '\0';
619 device_name = strstr(path_end + 1, "\nDEVNAME=");
620 if (device_name) {
621 device_name += sizeof("\nDEVNAME=")-1;
622 strchrnul(device_name, '\n')[0] = '\0';
623 } else {
624
625 device_name = (char*) bb_basename(path);
626 }
627 }
628
629
630
631
632
633
634
635 type = S_IFCHR;
636 if (strstr(path, "/block/") || (G.subsystem && is_prefixed_with(G.subsystem, "block")))
637 type = S_IFBLK;
638
639#if ENABLE_FEATURE_MDEV_CONF
640 G.rule_idx = 0;
641#endif
642 for (;;) {
643 const char *str_to_match;
644 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
645 char *command;
646 char *alias;
647 char aliaslink = aliaslink;
648 char *node_name;
649 const struct rule *rule;
650
651 str_to_match = device_name;
652
653 rule = next_rule();
654
655#if ENABLE_FEATURE_MDEV_CONF
656 if (!env_matches(rule->envmatch))
657 continue;
658 if (rule->maj >= 0) {
659 if (major != rule->maj)
660 continue;
661 if (minor < rule->min0 || minor > rule->min1)
662 continue;
663 memset(off, 0, sizeof(off));
664 goto rule_matches;
665 }
666 if (rule->envvar) {
667 str_to_match = getenv(rule->envvar);
668 dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
669 if (!str_to_match)
670 continue;
671 }
672
673
674 if (rule->regex_compiled) {
675 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
676 dbg3("regex_match for '%s':%d", str_to_match, regex_match);
677
678
679
680
681
682
683
684
685 if (regex_match != 0
686
687 || off[0].rm_so != 0
688 || (int)off[0].rm_eo != (int)strlen(str_to_match)
689 ) {
690 continue;
691 }
692 }
693
694 rule_matches:
695 dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
696#endif
697
698 alias = NULL;
699 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
700 aliaslink = rule->ren_mov[0];
701 if (aliaslink == '!') {
702
703 major = -2;
704 }
705 else if (aliaslink == '>' || aliaslink == '=') {
706 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
707 char *s;
708 char *p;
709 unsigned n;
710
711
712 n = 0;
713 s = rule->ren_mov;
714 while (*s)
715 if (*s++ == '%')
716 n++;
717
718 p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match));
719 s = rule->ren_mov + 1;
720 while (*s) {
721 *p = *s;
722 if ('%' == *s) {
723 unsigned i = (s[1] - '0');
724 if (i <= 9 && off[i].rm_so >= 0) {
725 n = off[i].rm_eo - off[i].rm_so;
726 strncpy(p, str_to_match + off[i].rm_so, n);
727 p += n - 1;
728 s++;
729 }
730 }
731 p++;
732 s++;
733 }
734 } else {
735 alias = xstrdup(rule->ren_mov + 1);
736 }
737 }
738 }
739 dbg3("alias:'%s'", alias);
740
741 command = NULL;
742 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
743 if (command) {
744
745
746
747 if ((command[0] == '@' && operation == OP_add)
748 || (command[0] == '$' && operation == OP_remove)
749 || (command[0] == '*')
750 ) {
751 command++;
752 } else {
753 command = NULL;
754 }
755 }
756 dbg3("command:'%s'", command);
757
758
759 node_name = device_name;
760 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
761 node_name = alias = build_alias(alias, device_name);
762 dbg3("alias2:'%s'", alias);
763 }
764
765 if (operation == OP_add && major >= 0) {
766 char *slash = strrchr(node_name, '/');
767 if (slash) {
768 *slash = '\0';
769 mkdir_recursive(node_name);
770 *slash = '/';
771 }
772 if (ENABLE_FEATURE_MDEV_CONF) {
773 dbg1("mknod %s (%d,%d) %o"
774 " %u:%u",
775 node_name, major, minor, rule->mode | type,
776 rule->ugid.uid, rule->ugid.gid
777 );
778 } else {
779 dbg1("mknod %s (%d,%d) %o",
780 node_name, major, minor, rule->mode | type
781 );
782 }
783 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
784 bb_perror_msg("can't create '%s'", node_name);
785 if (ENABLE_FEATURE_MDEV_CONF) {
786 chmod(node_name, rule->mode);
787 chown(node_name, rule->ugid.uid, rule->ugid.gid);
788 }
789 if (major == G.root_major && minor == G.root_minor)
790 symlink(node_name, "root");
791 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
792 if (aliaslink == '>') {
793
794
795
796 dbg1("symlink: %s", device_name);
797 symlink(node_name, device_name);
798 }
799 }
800 }
801
802 if (ENABLE_FEATURE_MDEV_EXEC && command) {
803
804 char *s = xasprintf("%s=%s", "MDEV", node_name);
805 putenv(s);
806 dbg1("running: %s", command);
807 if (system(command) == -1)
808 bb_perror_msg("can't run '%s'", command);
809 bb_unsetenv_and_free(s);
810 }
811
812 if (operation == OP_remove && major >= -1) {
813 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
814 if (aliaslink == '>') {
815 dbg1("unlink: %s", device_name);
816 unlink(device_name);
817 }
818 }
819 dbg1("unlink: %s", node_name);
820 unlink(node_name);
821 }
822
823 if (ENABLE_FEATURE_MDEV_RENAME)
824 free(alias);
825
826
827
828
829 if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
830 break;
831 }
832}
833
834static ssize_t readlink2(char *buf, size_t bufsize)
835{
836
837
838
839 char *obuf FIX_ALIASING;
840 obuf = buf;
841 return readlink(buf, obuf, bufsize);
842}
843
844
845
846
847static int FAST_FUNC fileAction(struct recursive_state *state,
848 const char *fileName,
849 struct stat *statbuf UNUSED_PARAM)
850{
851 size_t len = strlen(fileName) - 4;
852 char *path = state->userData;
853 char subsys[PATH_MAX];
854 int res;
855
856
857 if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
858 return FALSE;
859
860 strcpy(path, fileName);
861 path[len] = '\0';
862
863
864 strcpy(subsys, path);
865 strcpy(subsys + len, "/subsystem");
866 res = readlink2(subsys, sizeof(subsys)-1);
867 if (res > 0) {
868 subsys[res] = '\0';
869 free(G.subsystem);
870 if (G.subsys_env) {
871 bb_unsetenv_and_free(G.subsys_env);
872 G.subsys_env = NULL;
873 }
874
875 G.subsystem = strrchr(subsys, '/');
876 if (G.subsystem) {
877 G.subsystem = xstrdup(G.subsystem + 1);
878 G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
879 putenv(G.subsys_env);
880 }
881 }
882
883 make_device( NULL, path, OP_add);
884
885 return TRUE;
886}
887
888
889static int FAST_FUNC dirAction(struct recursive_state *state,
890 const char *fileName UNUSED_PARAM,
891 struct stat *statbuf UNUSED_PARAM)
892{
893 return (state->depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
894}
895
896
897
898
899
900
901
902
903
904
905
906
907static void load_firmware(const char *firmware, const char *sysfs_path)
908{
909 int cnt;
910 int firmware_fd, loading_fd;
911
912
913 firmware_fd = -1;
914 if (chdir("/lib/firmware") == 0)
915 firmware_fd = open(firmware, O_RDONLY);
916
917
918 xchdir(sysfs_path);
919 for (cnt = 0; cnt < 30; ++cnt) {
920 loading_fd = open("loading", O_WRONLY);
921 if (loading_fd >= 0)
922 goto loading;
923 sleep1();
924 }
925 goto out;
926
927 loading:
928 cnt = 0;
929 if (firmware_fd >= 0) {
930 int data_fd;
931
932
933 if (full_write(loading_fd, "1", 1) != 1)
934 goto out;
935
936
937 data_fd = open("data", O_WRONLY);
938 if (data_fd < 0)
939 goto out;
940 cnt = bb_copyfd_eof(firmware_fd, data_fd);
941 if (ENABLE_FEATURE_CLEAN_UP)
942 close(data_fd);
943 }
944
945
946
947
948
949
950 if (cnt > 0)
951 full_write(loading_fd, "0", 1);
952 else
953 full_write(loading_fd, "-1", 2);
954
955 out:
956 xchdir("/dev");
957 if (ENABLE_FEATURE_CLEAN_UP) {
958 close(firmware_fd);
959 close(loading_fd);
960 }
961}
962
963static char *curtime(void)
964{
965 struct timeval tv;
966 xgettimeofday(&tv);
967 sprintf(
968 strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec),
969 ".%06u",
970 (unsigned)tv.tv_usec
971 );
972 return G.timestr;
973}
974
975static void open_mdev_log(const char *seq, unsigned my_pid)
976{
977 int logfd = open("mdev.log", O_WRONLY | O_APPEND);
978 if (logfd >= 0) {
979 xmove_fd(logfd, STDERR_FILENO);
980 G.verbose = 2;
981 applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
982 }
983}
984
985
986
987
988
989
990static int
991wait_for_seqfile(unsigned expected_seq)
992{
993
994 static const struct timespec ts = { 0, 32*1000*1000 };
995 int timeout = 2000 / 32;
996 int seq_fd = -1;
997 int do_once = 1;
998 sigset_t set_CHLD;
999
1000 sigemptyset(&set_CHLD);
1001 sigaddset(&set_CHLD, SIGCHLD);
1002 sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
1003
1004 for (;;) {
1005 int seqlen;
1006 char seqbuf[sizeof(long)*3 + 2];
1007 unsigned seqbufnum;
1008
1009 if (seq_fd < 0) {
1010 seq_fd = open("mdev.seq", O_RDWR);
1011 if (seq_fd < 0)
1012 break;
1013 close_on_exec_on(seq_fd);
1014 }
1015 seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
1016 if (seqlen < 0) {
1017 close(seq_fd);
1018 seq_fd = -1;
1019 break;
1020 }
1021 seqbuf[seqlen] = '\0';
1022 if (seqbuf[0] == '\n' || seqbuf[0] == '\0') {
1023
1024 xwrite_str(seq_fd, utoa(expected_seq));
1025 xlseek(seq_fd, 0, SEEK_SET);
1026 dbg2s("first seq written");
1027 break;
1028 }
1029 seqbufnum = atoll(seqbuf);
1030 if (seqbufnum == expected_seq) {
1031
1032 break;
1033 }
1034 if (seqbufnum > expected_seq) {
1035
1036
1037 close(seq_fd);
1038 seq_fd = -1;
1039 break;
1040 }
1041 if (do_once) {
1042 dbg2("%s mdev.seq='%s', need '%u'", curtime(), seqbuf, expected_seq);
1043 do_once = 0;
1044 }
1045 if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
1046 dbg3("woken up");
1047 continue;
1048 }
1049 if (--timeout == 0) {
1050 dbg1("%s mdev.seq='%s'", "timed out", seqbuf);
1051 break;
1052 }
1053 }
1054 sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
1055 return seq_fd;
1056}
1057
1058static void signal_mdevs(unsigned my_pid)
1059{
1060 procps_status_t* p = NULL;
1061 while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
1062 if (p->pid != my_pid
1063 && p->argv0
1064 && strcmp(bb_basename(p->argv0), "mdev") == 0
1065 ) {
1066 kill(p->pid, SIGCHLD);
1067 }
1068 }
1069}
1070
1071static void process_action(char *temp, unsigned my_pid)
1072{
1073 char *fw;
1074 char *seq;
1075 char *action;
1076 char *env_devname;
1077 char *env_devpath;
1078 unsigned seqnum = seqnum;
1079 int seq_fd;
1080 smalluint op;
1081
1082
1083
1084
1085
1086
1087 env_devname = getenv("DEVNAME");
1088 G.subsystem = getenv("SUBSYSTEM");
1089 action = getenv("ACTION");
1090 env_devpath = getenv("DEVPATH");
1091 if (!action || !env_devpath )
1092 bb_show_usage();
1093 fw = getenv("FIRMWARE");
1094 seq = getenv("SEQNUM");
1095 op = index_in_strings(keywords, action);
1096
1097 if (my_pid)
1098 open_mdev_log(seq, my_pid);
1099
1100 seq_fd = -1;
1101 if (my_pid && seq) {
1102 seqnum = atoll(seq);
1103 seq_fd = wait_for_seqfile(seqnum);
1104 }
1105
1106 dbg1("%s "
1107 "ACTION:%s SEQNUM:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
1108 "%s%s",
1109 curtime(),
1110 action, seq, G.subsystem, env_devname, env_devpath,
1111 fw ? " FW:" : "", fw ? fw : ""
1112 );
1113
1114 snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
1115 if (op == OP_remove) {
1116
1117
1118
1119 if (!fw)
1120 make_device(env_devname, temp, op);
1121 }
1122 else {
1123 make_device(env_devname, temp, op);
1124 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
1125 if (op == OP_add && fw)
1126 load_firmware(fw, temp);
1127 }
1128 }
1129
1130 if (seq_fd >= 0) {
1131 xwrite_str(seq_fd, utoa(seqnum + 1));
1132 signal_mdevs(my_pid);
1133 }
1134}
1135
1136static void initial_scan(char *temp)
1137{
1138 struct stat st;
1139
1140 xstat("/", &st);
1141 G.root_major = major(st.st_dev);
1142 G.root_minor = minor(st.st_dev);
1143
1144 putenv((char*)"ACTION=add");
1145
1146
1147 recursive_action("/sys/dev",
1148 ACTION_RECURSE | ACTION_FOLLOWLINKS,
1149 fileAction, dirAction, temp);
1150}
1151
1152#if ENABLE_FEATURE_MDEV_DAEMON
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167# define USER_RCVBUF (3 * 1024)
1168# define KERN_RCVBUF (128 * 1024 * 1024)
1169# define MAX_ENV 32
1170
1171static int daemon_init(char *temp)
1172{
1173 int fd;
1174
1175
1176
1177
1178
1179
1180
1181
1182 fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, 1 << 0, KERN_RCVBUF);
1183
1184
1185
1186
1187
1188
1189 initial_scan(temp);
1190
1191 return fd;
1192}
1193
1194static void daemon_loop(char *temp, int fd)
1195{
1196 for (;;) {
1197 char netbuf[USER_RCVBUF];
1198 char *env[MAX_ENV];
1199 char *s, *end;
1200 ssize_t len;
1201 int idx;
1202
1203 len = safe_read(fd, netbuf, sizeof(netbuf) - 1);
1204 if (len < 0) {
1205 if (errno == ENOBUFS) {
1206
1207
1208
1209
1210 dbg1s("uevent overrun, rescanning");
1211 close(fd);
1212 fd = daemon_init(temp);
1213 continue;
1214 }
1215 bb_simple_perror_msg_and_die("read");
1216 }
1217 end = netbuf + len;
1218 *end = '\0';
1219
1220 idx = 0;
1221 s = netbuf;
1222 while (s < end && idx < MAX_ENV) {
1223 if (endofname(s)[0] == '=') {
1224 env[idx++] = s;
1225 putenv(s);
1226 }
1227 s += strlen(s) + 1;
1228 }
1229
1230 process_action(temp, 0);
1231
1232 while (idx)
1233 bb_unsetenv(env[--idx]);
1234 }
1235}
1236#endif
1237
1238int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1239int mdev_main(int argc UNUSED_PARAM, char **argv)
1240{
1241 enum {
1242 MDEV_OPT_SCAN = 1 << 0,
1243 MDEV_OPT_SYSLOG = 1 << 1,
1244 MDEV_OPT_DAEMON = 1 << 2,
1245 MDEV_OPT_FOREGROUND = 1 << 3,
1246 };
1247 int opt;
1248 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
1249
1250 INIT_G();
1251
1252
1253
1254 bb_sanitize_stdio();
1255
1256
1257 umask(0);
1258
1259 xchdir("/dev");
1260
1261 opt = getopt32(argv, "^"
1262 "sS" IF_FEATURE_MDEV_DAEMON("df") "v"
1263 "\0"
1264 "vv",
1265 &G.verbose);
1266
1267#if ENABLE_FEATURE_MDEV_CONF
1268 G.filename = "/etc/mdev.conf";
1269 if (opt & (MDEV_OPT_SCAN|MDEV_OPT_DAEMON)) {
1270
1271 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
1272 }
1273#endif
1274
1275 if (opt & MDEV_OPT_SYSLOG) {
1276 openlog(applet_name, LOG_PID, LOG_DAEMON);
1277 logmode |= LOGMODE_SYSLOG;
1278 }
1279
1280#if ENABLE_FEATURE_MDEV_DAEMON
1281 if (opt & MDEV_OPT_DAEMON) {
1282
1283
1284
1285
1286 int fd = daemon_init(temp);
1287
1288 if (!(opt & MDEV_OPT_FOREGROUND)) {
1289
1290 logmode &= ~LOGMODE_STDIO;
1291 bb_daemonize_or_rexec(0, argv);
1292 }
1293
1294 daemon_loop(temp, fd);
1295 }
1296#endif
1297 if (opt & MDEV_OPT_SCAN) {
1298
1299
1300
1301 initial_scan(temp);
1302 } else {
1303 process_action(temp, getpid());
1304
1305 dbg1("%s exiting", curtime());
1306 }
1307
1308 if (ENABLE_FEATURE_CLEAN_UP)
1309 RELEASE_CONFIG_BUFFER(temp);
1310
1311 return EXIT_SUCCESS;
1312}
1313