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