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