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
811static ssize_t readlink2(char *buf, size_t bufsize)
812{
813
814
815
816 char *obuf FIX_ALIASING;
817 obuf = buf;
818 return readlink(buf, obuf, bufsize);
819}
820
821
822
823
824static int FAST_FUNC fileAction(const char *fileName,
825 struct stat *statbuf UNUSED_PARAM,
826 void *userData,
827 int depth UNUSED_PARAM)
828{
829 size_t len = strlen(fileName) - 4;
830 char *path = userData;
831 char subsys[PATH_MAX];
832 int res;
833
834
835 if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
836 return FALSE;
837
838 strcpy(path, fileName);
839 path[len] = '\0';
840
841
842 strcpy(subsys, path);
843 strcpy(subsys + len, "/subsystem");
844 res = readlink2(subsys, sizeof(subsys)-1);
845 if (res > 0) {
846 subsys[res] = '\0';
847 free(G.subsystem);
848 if (G.subsys_env) {
849 bb_unsetenv_and_free(G.subsys_env);
850 G.subsys_env = NULL;
851 }
852
853 G.subsystem = strrchr(subsys, '/');
854 if (G.subsystem) {
855 G.subsystem = xstrdup(G.subsystem + 1);
856 G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
857 putenv(G.subsys_env);
858 }
859 }
860
861 make_device( NULL, path, OP_add);
862
863 return TRUE;
864}
865
866
867static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
868 struct stat *statbuf UNUSED_PARAM,
869 void *userData UNUSED_PARAM,
870 int depth)
871{
872 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
873}
874
875
876
877
878
879
880
881
882
883
884
885
886static void load_firmware(const char *firmware, const char *sysfs_path)
887{
888 int cnt;
889 int firmware_fd, loading_fd;
890
891
892 firmware_fd = -1;
893 if (chdir("/lib/firmware") == 0)
894 firmware_fd = open(firmware, O_RDONLY);
895
896
897 xchdir(sysfs_path);
898 for (cnt = 0; cnt < 30; ++cnt) {
899 loading_fd = open("loading", O_WRONLY);
900 if (loading_fd >= 0)
901 goto loading;
902 sleep(1);
903 }
904 goto out;
905
906 loading:
907 cnt = 0;
908 if (firmware_fd >= 0) {
909 int data_fd;
910
911
912 if (full_write(loading_fd, "1", 1) != 1)
913 goto out;
914
915
916 data_fd = open("data", O_WRONLY);
917 if (data_fd < 0)
918 goto out;
919 cnt = bb_copyfd_eof(firmware_fd, data_fd);
920 if (ENABLE_FEATURE_CLEAN_UP)
921 close(data_fd);
922 }
923
924
925
926
927
928
929 if (cnt > 0)
930 full_write(loading_fd, "0", 1);
931 else
932 full_write(loading_fd, "-1", 2);
933
934 out:
935 xchdir("/dev");
936 if (ENABLE_FEATURE_CLEAN_UP) {
937 close(firmware_fd);
938 close(loading_fd);
939 }
940}
941
942static char *curtime(void)
943{
944 struct timeval tv;
945 gettimeofday(&tv, NULL);
946 sprintf(
947 strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec),
948 ".%06u",
949 (unsigned)tv.tv_usec
950 );
951 return G.timestr;
952}
953
954static void open_mdev_log(const char *seq, unsigned my_pid)
955{
956 int logfd = open("mdev.log", O_WRONLY | O_APPEND);
957 if (logfd >= 0) {
958 xmove_fd(logfd, STDERR_FILENO);
959 G.verbose = 2;
960 applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
961 }
962}
963
964
965
966
967
968
969static int
970wait_for_seqfile(unsigned expected_seq)
971{
972
973 static const struct timespec ts = { 0, 32*1000*1000 };
974 int timeout = 2000 / 32;
975 int seq_fd = -1;
976 int do_once = 1;
977 sigset_t set_CHLD;
978
979 sigemptyset(&set_CHLD);
980 sigaddset(&set_CHLD, SIGCHLD);
981 sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
982
983 for (;;) {
984 int seqlen;
985 char seqbuf[sizeof(long)*3 + 2];
986 unsigned seqbufnum;
987
988 if (seq_fd < 0) {
989 seq_fd = open("mdev.seq", O_RDWR);
990 if (seq_fd < 0)
991 break;
992 close_on_exec_on(seq_fd);
993 }
994 seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
995 if (seqlen < 0) {
996 close(seq_fd);
997 seq_fd = -1;
998 break;
999 }
1000 seqbuf[seqlen] = '\0';
1001 if (seqbuf[0] == '\n' || seqbuf[0] == '\0') {
1002
1003 xwrite_str(seq_fd, utoa(expected_seq));
1004 xlseek(seq_fd, 0, SEEK_SET);
1005 dbg2("first seq written");
1006 break;
1007 }
1008 seqbufnum = atoll(seqbuf);
1009 if (seqbufnum == expected_seq) {
1010
1011 break;
1012 }
1013 if (seqbufnum > expected_seq) {
1014
1015
1016 close(seq_fd);
1017 seq_fd = -1;
1018 break;
1019 }
1020 if (do_once) {
1021 dbg2("%s mdev.seq='%s', need '%u'", curtime(), seqbuf, expected_seq);
1022 do_once = 0;
1023 }
1024 if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
1025 dbg3("woken up");
1026 continue;
1027 }
1028 if (--timeout == 0) {
1029 dbg1("%s mdev.seq='%s'", "timed out", seqbuf);
1030 break;
1031 }
1032 }
1033 sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
1034 return seq_fd;
1035}
1036
1037static void signal_mdevs(unsigned my_pid)
1038{
1039 procps_status_t* p = NULL;
1040 while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
1041 if (p->pid != my_pid
1042 && p->argv0
1043 && strcmp(bb_basename(p->argv0), "mdev") == 0
1044 ) {
1045 kill(p->pid, SIGCHLD);
1046 }
1047 }
1048}
1049
1050int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1051int mdev_main(int argc UNUSED_PARAM, char **argv)
1052{
1053 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
1054
1055 INIT_G();
1056
1057#if ENABLE_FEATURE_MDEV_CONF
1058 G.filename = "/etc/mdev.conf";
1059#endif
1060
1061
1062
1063 bb_sanitize_stdio();
1064
1065
1066 umask(0);
1067
1068 xchdir("/dev");
1069
1070 if (argv[1] && strcmp(argv[1], "-s") == 0) {
1071
1072
1073
1074 struct stat st;
1075
1076#if ENABLE_FEATURE_MDEV_CONF
1077
1078 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
1079#endif
1080 xstat("/", &st);
1081 G.root_major = major(st.st_dev);
1082 G.root_minor = minor(st.st_dev);
1083
1084 putenv((char*)"ACTION=add");
1085
1086
1087 recursive_action("/sys/dev",
1088 ACTION_RECURSE | ACTION_FOLLOWLINKS,
1089 fileAction, dirAction, temp, 0);
1090 } else {
1091 char *fw;
1092 char *seq;
1093 char *action;
1094 char *env_devname;
1095 char *env_devpath;
1096 unsigned my_pid;
1097 unsigned seqnum = seqnum;
1098 int seq_fd;
1099 smalluint op;
1100
1101
1102
1103
1104
1105
1106 env_devname = getenv("DEVNAME");
1107 G.subsystem = getenv("SUBSYSTEM");
1108 action = getenv("ACTION");
1109 env_devpath = getenv("DEVPATH");
1110 if (!action || !env_devpath )
1111 bb_show_usage();
1112 fw = getenv("FIRMWARE");
1113 seq = getenv("SEQNUM");
1114 op = index_in_strings(keywords, action);
1115
1116 my_pid = getpid();
1117 open_mdev_log(seq, my_pid);
1118
1119 seq_fd = -1;
1120 if (seq) {
1121 seqnum = atoll(seq);
1122 seq_fd = wait_for_seqfile(seqnum);
1123 }
1124
1125 dbg1("%s "
1126 "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
1127 "%s%s",
1128 curtime(),
1129 action, G.subsystem, env_devname, env_devpath,
1130 fw ? " FW:" : "", fw ? fw : ""
1131 );
1132
1133 snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
1134 if (op == OP_remove) {
1135
1136
1137
1138 if (!fw)
1139 make_device(env_devname, temp, op);
1140 }
1141 else {
1142 make_device(env_devname, temp, op);
1143 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
1144 if (op == OP_add && fw)
1145 load_firmware(fw, temp);
1146 }
1147 }
1148
1149 dbg1("%s exiting", curtime());
1150 if (seq_fd >= 0) {
1151 xwrite_str(seq_fd, utoa(seqnum + 1));
1152 signal_mdevs(my_pid);
1153 }
1154 }
1155
1156 if (ENABLE_FEATURE_CLEAN_UP)
1157 RELEASE_CONFIG_BUFFER(temp);
1158
1159 return EXIT_SUCCESS;
1160}
1161