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