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