1
2
3
4
5
6
7
8
9
10#include "libbb.h"
11#include "xregex.h"
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
64struct globals {
65 int root_major, root_minor;
66 char *subsystem;
67} FIX_ALIASING;
68#define G (*(struct globals*)&bb_common_bufsiz1)
69
70
71#define MAX_SYSFS_DEPTH 3
72
73
74#define SCRATCH_SIZE 80
75
76
77
78
79
80static char *build_alias(char *alias, const char *device_name)
81{
82 char *dest;
83
84
85
86 dest = strrchr(alias, '/');
87 if (dest) {
88 *dest = '\0';
89 bb_make_directory(alias, 0755, FILEUTILS_RECUR);
90 *dest = '/';
91 if (dest[1] == '\0') {
92 dest = alias;
93 alias = concat_path_file(alias, device_name);
94 free(dest);
95 }
96 }
97
98 return alias;
99}
100
101
102
103
104
105
106
107static void make_device(char *path, int delete)
108{
109 char *device_name, *subsystem_slash_devname;
110 int major, minor, type, len;
111 mode_t mode;
112 parser_t *parser;
113
114
115
116
117
118
119 major = -1;
120 if (!delete) {
121 char *dev_maj_min = path + strlen(path);
122
123 strcpy(dev_maj_min, "/dev");
124 len = open_read_close(path, dev_maj_min + 1, 64);
125 *dev_maj_min = '\0';
126 if (len < 1) {
127 if (!ENABLE_FEATURE_MDEV_EXEC)
128 return;
129
130
131 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) != 2) {
132 major = -1;
133 }
134 }
135
136
137
138 device_name = (char*) bb_basename(path);
139
140
141
142
143 type = S_IFCHR;
144 if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0))
145 type = S_IFBLK;
146
147
148 subsystem_slash_devname = NULL;
149
150 if (strncmp(path, "/sys/block/", 11) == 0)
151 path += sizeof("/sys/") - 1;
152 else if (strncmp(path, "/sys/class/", 11) == 0)
153 path += sizeof("/sys/class/") - 1;
154 else {
155
156
157
158
159
160
161 subsystem_slash_devname = concat_path_file(G.subsystem, device_name);
162 path = subsystem_slash_devname;
163 }
164
165
166 if (ENABLE_FEATURE_MDEV_CONF)
167 parser = config_open2("/etc/mdev.conf", fopen_for_read);
168
169 do {
170 int keep_matching;
171 struct bb_uidgid_t ugid;
172 char *tokens[4];
173 char *command = NULL;
174 char *alias = NULL;
175 char aliaslink = aliaslink;
176
177
178 ugid.uid = ugid.gid = 0;
179 keep_matching = 0;
180 mode = 0660;
181
182 if (ENABLE_FEATURE_MDEV_CONF
183 && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)
184 ) {
185 char *val;
186 char *str_to_match;
187 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
188
189 val = tokens[0];
190 keep_matching = ('-' == val[0]);
191 val += keep_matching;
192
193
194
195 str_to_match = strchr(val, '/') ? path : device_name;
196
197
198
199 if (val[0] == '@') {
200
201
202
203
204 int cmaj, cmin0, cmin1, sc;
205 if (major < 0)
206 continue;
207 sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1);
208 if (sc < 1
209 || major != cmaj
210 || (sc == 2 && minor != cmin0)
211 || (sc == 3 && (minor < cmin0 || minor > cmin1))
212 ) {
213 continue;
214 }
215 goto line_matches;
216 }
217 if (val[0] == '$') {
218
219 char *eq = strchr(++val, '=');
220 if (!eq)
221 continue;
222 *eq = '\0';
223 str_to_match = getenv(val);
224 if (!str_to_match)
225 continue;
226 str_to_match -= strlen(val) + 1;
227 *eq = '=';
228 }
229
230
231 {
232 regex_t match;
233 int result;
234
235 xregcomp(&match, val, REG_EXTENDED);
236 result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0);
237 regfree(&match);
238
239
240
241
242
243
244
245
246
247
248 if (result
249 || off[0].rm_so
250 || ((int)off[0].rm_eo != (int)strlen(str_to_match))
251 ) {
252 continue;
253 }
254 }
255 line_matches:
256
257
258
259
260 if (get_uidgid(&ugid, tokens[1], 1) == 0)
261 bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno);
262
263
264 bb_parse_mode(tokens[2], &mode);
265
266 val = tokens[3];
267
268
269 if (ENABLE_FEATURE_MDEV_RENAME && val) {
270 char *a, *s, *st;
271
272 a = val;
273 s = strchrnul(val, ' ');
274 st = strchrnul(val, '\t');
275 if (st < s)
276 s = st;
277 st = (s[0] && s[1]) ? s+1 : NULL;
278
279 aliaslink = a[0];
280 if (aliaslink == '!' && s == a+1) {
281 val = st;
282
283 major = -2;
284 }
285 else if (aliaslink == '>' || aliaslink == '=') {
286 val = st;
287 s[0] = '\0';
288 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
289 char *p;
290 unsigned i, n;
291
292
293 n = 0;
294 s = a;
295 while (*s)
296 if (*s++ == '%')
297 n++;
298
299 p = alias = xzalloc(strlen(a) + n * strlen(str_to_match));
300 s = a + 1;
301 while (*s) {
302 *p = *s;
303 if ('%' == *s) {
304 i = (s[1] - '0');
305 if (i <= 9 && off[i].rm_so >= 0) {
306 n = off[i].rm_eo - off[i].rm_so;
307 strncpy(p, str_to_match + off[i].rm_so, n);
308 p += n - 1;
309 s++;
310 }
311 }
312 p++;
313 s++;
314 }
315 } else {
316 alias = xstrdup(a + 1);
317 }
318 }
319 }
320
321 if (ENABLE_FEATURE_MDEV_EXEC && val) {
322 const char *s = "$@*";
323 const char *s2 = strchr(s, val[0]);
324
325 if (!s2) {
326 bb_error_msg("bad line %u", parser->lineno);
327 if (ENABLE_FEATURE_MDEV_RENAME)
328 free(alias);
329 continue;
330 }
331
332
333
334
335 if (s2 - s != delete) {
336
337
338
339
340 command = xstrdup(val + 1);
341 }
342 }
343 }
344
345
346
347
348 {
349 const char *node_name;
350
351 node_name = device_name;
352 if (ENABLE_FEATURE_MDEV_RENAME && alias)
353 node_name = alias = build_alias(alias, device_name);
354
355 if (!delete && major >= 0) {
356 if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST)
357 bb_perror_msg("can't create '%s'", node_name);
358 if (major == G.root_major && minor == G.root_minor)
359 symlink(node_name, "root");
360 if (ENABLE_FEATURE_MDEV_CONF) {
361 chmod(node_name, mode);
362 chown(node_name, ugid.uid, ugid.gid);
363 }
364 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
365 if (aliaslink == '>')
366 symlink(node_name, device_name);
367 }
368 }
369
370 if (ENABLE_FEATURE_MDEV_EXEC && command) {
371
372 char *s = xasprintf("%s=%s", "MDEV", node_name);
373 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
374 putenv(s);
375 putenv(s1);
376 if (system(command) == -1)
377 bb_perror_msg("can't run '%s'", command);
378 bb_unsetenv_and_free(s1);
379 bb_unsetenv_and_free(s);
380 free(command);
381 }
382
383 if (delete && major >= -1) {
384 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
385 if (aliaslink == '>')
386 unlink(device_name);
387 }
388 unlink(node_name);
389 }
390
391 if (ENABLE_FEATURE_MDEV_RENAME)
392 free(alias);
393 }
394
395
396
397 if (ENABLE_FEATURE_MDEV_CONF && !keep_matching)
398 break;
399
400
401 } while (ENABLE_FEATURE_MDEV_CONF);
402
403 if (ENABLE_FEATURE_MDEV_CONF)
404 config_close(parser);
405 free(subsystem_slash_devname);
406}
407
408
409static int FAST_FUNC fileAction(const char *fileName,
410 struct stat *statbuf UNUSED_PARAM,
411 void *userData,
412 int depth UNUSED_PARAM)
413{
414 size_t len = strlen(fileName) - 4;
415 char *scratch = userData;
416
417
418 if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
419 return FALSE;
420
421 strcpy(scratch, fileName);
422 scratch[len] = '\0';
423 make_device(scratch, 0);
424
425 return TRUE;
426}
427
428
429static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
430 struct stat *statbuf UNUSED_PARAM,
431 void *userData UNUSED_PARAM,
432 int depth)
433{
434
435
436 if (1 == depth) {
437 free(G.subsystem);
438 G.subsystem = strrchr(fileName, '/');
439 if (G.subsystem)
440 G.subsystem = xstrdup(G.subsystem + 1);
441 }
442
443 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
444}
445
446
447
448
449
450
451
452
453
454
455
456
457static void load_firmware(const char *firmware, const char *sysfs_path)
458{
459 int cnt;
460 int firmware_fd, loading_fd, data_fd;
461
462
463 xchdir("/lib/firmware");
464 firmware_fd = xopen(firmware, O_RDONLY);
465
466
467 data_fd = -1;
468
469
470 xchdir(sysfs_path);
471 for (cnt = 0; cnt < 30; ++cnt) {
472 loading_fd = open("loading", O_WRONLY);
473 if (loading_fd != -1)
474 goto loading;
475 sleep(1);
476 }
477 goto out;
478
479 loading:
480
481 if (full_write(loading_fd, "1", 1) != 1)
482 goto out;
483
484
485 data_fd = open("data", O_WRONLY);
486 if (data_fd == -1)
487 goto out;
488 cnt = bb_copyfd_eof(firmware_fd, data_fd);
489
490
491 if (cnt > 0)
492 full_write(loading_fd, "0", 1);
493 else
494 full_write(loading_fd, "-1", 2);
495
496 out:
497 if (ENABLE_FEATURE_CLEAN_UP) {
498 close(firmware_fd);
499 close(loading_fd);
500 close(data_fd);
501 }
502}
503
504int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
505int mdev_main(int argc UNUSED_PARAM, char **argv)
506{
507 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
508
509
510
511 bb_sanitize_stdio();
512
513
514 umask(0);
515
516 xchdir("/dev");
517
518 if (argv[1] && strcmp(argv[1], "-s") == 0) {
519
520
521
522 struct stat st;
523
524 xstat("/", &st);
525 G.root_major = major(st.st_dev);
526 G.root_minor = minor(st.st_dev);
527
528
529
530
531
532
533
534 if (access("/sys/class/block", F_OK) != 0) {
535
536
537
538
539
540 recursive_action("/sys/block",
541 ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
542 fileAction, dirAction, temp, 0);
543 }
544 recursive_action("/sys/class",
545 ACTION_RECURSE | ACTION_FOLLOWLINKS,
546 fileAction, dirAction, temp, 0);
547 } else {
548 char *fw;
549 char *seq;
550 char *action;
551 char *env_path;
552 static const char keywords[] ALIGN1 = "remove\0add\0";
553 enum { OP_remove = 0, OP_add };
554 smalluint op;
555
556
557
558
559
560
561 action = getenv("ACTION");
562 env_path = getenv("DEVPATH");
563 G.subsystem = getenv("SUBSYSTEM");
564 if (!action || !env_path )
565 bb_show_usage();
566 fw = getenv("FIRMWARE");
567 op = index_in_strings(keywords, action);
568
569
570
571 seq = getenv("SEQNUM");
572 if (seq) {
573 int timeout = 2000 / 32;
574 do {
575 int seqlen;
576 char seqbuf[sizeof(int)*3 + 2];
577
578 seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf-1));
579 if (seqlen < 0) {
580 seq = NULL;
581 break;
582 }
583 seqbuf[seqlen] = '\0';
584 if (seqbuf[0] == '\n'
585 || strcmp(seq, seqbuf) == 0
586 ) {
587 break;
588 }
589 usleep(32*1000);
590 } while (--timeout);
591 }
592
593 snprintf(temp, PATH_MAX, "/sys%s", env_path);
594 if (op == OP_remove) {
595
596
597
598 if (!fw)
599 make_device(temp, 1);
600 }
601 else if (op == OP_add) {
602 make_device(temp, 0);
603 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
604 if (fw)
605 load_firmware(fw, temp);
606 }
607 }
608
609 if (seq) {
610 xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
611 }
612 }
613
614 if (ENABLE_FEATURE_CLEAN_UP)
615 RELEASE_CONFIG_BUFFER(temp);
616
617 return EXIT_SUCCESS;
618}
619