aboutsummaryrefslogtreecommitdiff
path: root/util-linux/mdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'util-linux/mdev.c')
-rw-r--r--util-linux/mdev.c676
1 files changed, 424 insertions, 252 deletions
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index 7cabb1df6..c6be1b872 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -8,39 +8,91 @@
8 * Licensed under GPLv2, see file LICENSE in this source tree. 8 * Licensed under GPLv2, see file LICENSE in this source tree.
9 */ 9 */
10 10
11//config:config MDEV
12//config: bool "mdev"
13//config: default y
14//config: select PLATFORM_LINUX
15//config: help
16//config: mdev is a mini-udev implementation for dynamically creating device
17//config: nodes in the /dev directory.
18//config:
19//config: For more information, please see docs/mdev.txt
20//config:
21//config:config FEATURE_MDEV_CONF
22//config: bool "Support /etc/mdev.conf"
23//config: default y
24//config: depends on MDEV
25//config: help
26//config: Add support for the mdev config file to control ownership and
27//config: permissions of the device nodes.
28//config:
29//config: For more information, please see docs/mdev.txt
30//config:
31//config:config FEATURE_MDEV_RENAME
32//config: bool "Support subdirs/symlinks"
33//config: default y
34//config: depends on FEATURE_MDEV_CONF
35//config: help
36//config: Add support for renaming devices and creating symlinks.
37//config:
38//config: For more information, please see docs/mdev.txt
39//config:
40//config:config FEATURE_MDEV_RENAME_REGEXP
41//config: bool "Support regular expressions substitutions when renaming device"
42//config: default y
43//config: depends on FEATURE_MDEV_RENAME
44//config: help
45//config: Add support for regular expressions substitutions when renaming
46//config: device.
47//config:
48//config:config FEATURE_MDEV_EXEC
49//config: bool "Support command execution at device addition/removal"
50//config: default y
51//config: depends on FEATURE_MDEV_CONF
52//config: help
53//config: This adds support for an optional field to /etc/mdev.conf for
54//config: executing commands when devices are created/removed.
55//config:
56//config: For more information, please see docs/mdev.txt
57//config:
58//config:config FEATURE_MDEV_LOAD_FIRMWARE
59//config: bool "Support loading of firmwares"
60//config: default y
61//config: depends on MDEV
62//config: help
63//config: Some devices need to load firmware before they can be usable.
64//config:
65//config: These devices will request userspace look up the files in
66//config: /lib/firmware/ and if it exists, send it to the kernel for
67//config: loading into the hardware.
68
69//applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
70
71//kbuild:lib-$(CONFIG_MDEV) += mdev.o
72
11//usage:#define mdev_trivial_usage 73//usage:#define mdev_trivial_usage
12//usage: "[-s]" 74//usage: "[-s]"
13//usage:#define mdev_full_usage "\n\n" 75//usage:#define mdev_full_usage "\n\n"
14//usage: " -s Scan /sys and populate /dev during system boot\n" 76//usage: "mdev -s is to be run during boot to scan /sys and populate /dev.\n"
15//usage: "\n" 77//usage: "\n"
16//usage: "It can be run by kernel as a hotplug helper. To activate it:\n" 78//usage: "Bare mdev is a kernel hotplug helper. To activate it:\n"
17//usage: " echo /sbin/mdev > /proc/sys/kernel/hotplug\n" 79//usage: " echo /sbin/mdev >/proc/sys/kernel/hotplug\n"
18//usage: IF_FEATURE_MDEV_CONF( 80//usage: IF_FEATURE_MDEV_CONF(
81//usage: "\n"
19//usage: "It uses /etc/mdev.conf with lines\n" 82//usage: "It uses /etc/mdev.conf with lines\n"
20//usage: "[-]DEVNAME UID:GID PERM" 83//usage: " [-]DEVNAME UID:GID PERM"
21//usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]") 84//usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
22//usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]") 85//usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
86//usage: "\n"
87//usage: "where DEVNAME is device name regex, @major,minor[-minor2], or\n"
88//usage: "environment variable regex. A common use of the latter is\n"
89//usage: "to load modules for hotplugged devices:\n"
90//usage: " $MODALIAS=.* 0:0 660 @modprobe \"$MODALIAS\"\n"
23//usage: ) 91//usage: )
24//usage: 92//usage: "\n"
25//usage:#define mdev_notes_usage "" 93//usage: "If /dev/mdev.seq file exists, mdev will wait for its value\n"
26//usage: IF_FEATURE_MDEV_CONFIG( 94//usage: "to match $SEQNUM variable. This prevents plug/unplug races.\n"
27//usage: "The mdev config file contains lines that look like:\n" 95//usage: "To activate this feature, create empty /dev/mdev.seq at boot."
28//usage: " hd[a-z][0-9]* 0:3 660\n\n"
29//usage: "That's device name (with regex match), uid:gid, and permissions.\n\n"
30//usage: IF_FEATURE_MDEV_EXEC(
31//usage: "Optionally, that can be followed (on the same line) by a special character\n"
32//usage: "and a command line to run after creating/before deleting the corresponding\n"
33//usage: "device(s). The environment variable $MDEV indicates the active device node\n"
34//usage: "(which is useful if it's a regex match). For example:\n\n"
35//usage: " hdc root:cdrom 660 *ln -s $MDEV cdrom\n\n"
36//usage: "The special characters are @ (run after creating), $ (run before deleting),\n"
37//usage: "and * (run both after creating and before deleting). The commands run in\n"
38//usage: "the /dev directory, and use system() which calls /bin/sh.\n\n"
39//usage: )
40//usage: "Config file parsing stops on the first matching line. If no config\n"
41//usage: "entry is matched, devices are created with default 0:0 660. (Make\n"
42//usage: "the last line match .* to override this.)\n\n"
43//usage: )
44 96
45#include "libbb.h" 97#include "libbb.h"
46#include "xregex.h" 98#include "xregex.h"
@@ -62,20 +114,11 @@
62 * (todo: explain "delete" and $FIRMWARE) 114 * (todo: explain "delete" and $FIRMWARE)
63 * 115 *
64 * If /etc/mdev.conf exists, it may modify /dev/device_name's properties. 116 * If /etc/mdev.conf exists, it may modify /dev/device_name's properties.
65 * /etc/mdev.conf file format:
66 *
67 * [-][subsystem/]device user:grp mode [>|=path] [@|$|*command args...]
68 * [-]@maj,min[-min2] user:grp mode [>|=path] [@|$|*command args...]
69 * [-]$envvar=val user:grp mode [>|=path] [@|$|*command args...]
70 * 117 *
71 * Leading minus in 1st field means "don't stop on this line", otherwise 118 * Leading minus in 1st field means "don't stop on this line", otherwise
72 * search is stopped after the matching line is encountered. 119 * search is stopped after the matching line is encountered.
73 * 120 *
74 * The device name or "subsystem/device" combo is matched against 1st field 121 * $envvar=regex format is useful for loading modules for hot-plugged devices
75 * (which is a regex), or maj,min is matched against 1st field,
76 * or specified environment variable (as regex) is matched against 1st field.
77 *
78 * $envvar=val format is useful for loading modules for hot-plugged devices
79 * which do not have driver loaded yet. In this case /sys/class/.../dev 122 * which do not have driver loaded yet. In this case /sys/class/.../dev
80 * does not exist, but $MODALIAS is set to needed module's name 123 * does not exist, but $MODALIAS is set to needed module's name
81 * (actually, an alias to it) by kernel. This rule instructs mdev 124 * (actually, an alias to it) by kernel. This rule instructs mdev
@@ -96,11 +139,33 @@
96 * This happens regardless of /sys/class/.../dev existence. 139 * This happens regardless of /sys/class/.../dev existence.
97 */ 140 */
98 141
142struct rule {
143 bool keep_matching;
144 bool regex_compiled;
145 bool regex_has_slash;
146 mode_t mode;
147 int maj, min0, min1;
148 struct bb_uidgid_t ugid;
149 char *envvar;
150 char *ren_mov;
151 IF_FEATURE_MDEV_EXEC(char *r_cmd;)
152 regex_t match;
153};
154
99struct globals { 155struct globals {
100 int root_major, root_minor; 156 int root_major, root_minor;
101 char *subsystem; 157 char *subsystem;
158#if ENABLE_FEATURE_MDEV_CONF
159 const char *filename;
160 parser_t *parser;
161 struct rule **rule_vec;
162 unsigned rule_idx;
163#endif
164 struct rule cur_rule;
102} FIX_ALIASING; 165} FIX_ALIASING;
103#define G (*(struct globals*)&bb_common_bufsiz1) 166#define G (*(struct globals*)&bb_common_bufsiz1)
167#define INIT_G() do { } while (0)
168
104 169
105/* Prevent infinite loops in /sys symlinks */ 170/* Prevent infinite loops in /sys symlinks */
106#define MAX_SYSFS_DEPTH 3 171#define MAX_SYSFS_DEPTH 3
@@ -108,6 +173,165 @@ struct globals {
108/* We use additional 64+ bytes in make_device() */ 173/* We use additional 64+ bytes in make_device() */
109#define SCRATCH_SIZE 80 174#define SCRATCH_SIZE 80
110 175
176#if 0
177# define dbg(...) bb_error_msg(__VA_ARGS__)
178#else
179# define dbg(...) ((void)0)
180#endif
181
182
183#if ENABLE_FEATURE_MDEV_CONF
184
185static void make_default_cur_rule(void)
186{
187 memset(&G.cur_rule, 0, sizeof(G.cur_rule));
188 G.cur_rule.maj = -1; /* "not a @major,minor rule" */
189 G.cur_rule.mode = 0660;
190}
191
192static void clean_up_cur_rule(void)
193{
194 free(G.cur_rule.envvar);
195 if (G.cur_rule.regex_compiled)
196 regfree(&G.cur_rule.match);
197 free(G.cur_rule.ren_mov);
198 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
199 make_default_cur_rule();
200}
201
202static void parse_next_rule(void)
203{
204 /* Note: on entry, G.cur_rule is set to default */
205 while (1) {
206 char *tokens[4];
207 char *val;
208
209 if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL))
210 break;
211
212 /* Fields: [-]regex uid:gid mode [alias] [cmd] */
213 dbg("token1:'%s'", tokens[1]);
214
215 /* 1st field */
216 val = tokens[0];
217 G.cur_rule.keep_matching = ('-' == val[0]);
218 val += G.cur_rule.keep_matching; /* swallow leading dash */
219 if (val[0] == '@') {
220 /* @major,minor[-minor2] */
221 /* (useful when name is ambiguous:
222 * "/sys/class/usb/lp0" and
223 * "/sys/class/printer/lp0")
224 */
225 int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1);
226 if (sc < 2 || G.cur_rule.maj < 0) {
227 bb_error_msg("bad @maj,min on line %d", G.parser->lineno);
228 goto next_rule;
229 }
230 if (sc == 2)
231 G.cur_rule.min1 = G.cur_rule.min0;
232 } else {
233 if (val[0] == '$') {
234 char *eq = strchr(++val, '=');
235 if (!eq) {
236 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
237 goto next_rule;
238 }
239 G.cur_rule.envvar = xstrndup(val, eq - val);
240 val = eq + 1;
241 }
242 xregcomp(&G.cur_rule.match, val, REG_EXTENDED);
243 G.cur_rule.regex_compiled = 1;
244 G.cur_rule.regex_has_slash = (strchr(val, '/') != NULL);
245 }
246
247 /* 2nd field: uid:gid - device ownership */
248 if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) {
249 bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
250 goto next_rule;
251 }
252
253 /* 3rd field: mode - device permissions */
254 bb_parse_mode(tokens[2], &G.cur_rule.mode);
255
256 /* 4th field (opt): ">|=alias" or "!" to not create the node */
257 val = tokens[3];
258 if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) {
259 char *s = skip_non_whitespace(val);
260 G.cur_rule.ren_mov = xstrndup(val, s - val);
261 val = skip_whitespace(s);
262 }
263
264 if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) {
265 const char *s = "$@*";
266 const char *s2 = strchr(s, val[0]);
267 if (!s2) {
268 bb_error_msg("bad line %u", G.parser->lineno);
269 goto next_rule;
270 }
271 IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);)
272 }
273
274 return;
275 next_rule:
276 clean_up_cur_rule();
277 } /* while (config_read) */
278
279 dbg("config_close(G.parser)");
280 config_close(G.parser);
281 G.parser = NULL;
282
283 return;
284}
285
286/* If mdev -s, we remember rules in G.rule_vec[].
287 * Otherwise, there is no point in doing it, and we just
288 * save only one parsed rule in G.cur_rule.
289 */
290static const struct rule *next_rule(void)
291{
292 struct rule *rule;
293
294 /* Open conf file if we didn't do it yet */
295 if (!G.parser && G.filename) {
296 dbg("config_open('%s')", G.filename);
297 G.parser = config_open2(G.filename, fopen_for_read);
298 G.filename = NULL;
299 }
300
301 if (G.rule_vec) {
302 /* mdev -s */
303 /* Do we have rule parsed already? */
304 if (G.rule_vec[G.rule_idx]) {
305 dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
306 return G.rule_vec[G.rule_idx++];
307 }
308 make_default_cur_rule();
309 } else {
310 /* not mdev -s */
311 clean_up_cur_rule();
312 }
313
314 /* Parse one more rule if file isn't fully read */
315 rule = &G.cur_rule;
316 if (G.parser) {
317 parse_next_rule();
318 if (G.rule_vec) { /* mdev -s */
319 rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
320 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
321 G.rule_vec[G.rule_idx++] = rule;
322 dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
323 }
324 }
325
326 return rule;
327}
328
329#else
330
331# define next_rule() (&G.cur_rule)
332
333#endif
334
111/* Builds an alias path. 335/* Builds an alias path.
112 * This function potentionally reallocates the alias parameter. 336 * This function potentionally reallocates the alias parameter.
113 * Only used for ENABLE_FEATURE_MDEV_RENAME 337 * Only used for ENABLE_FEATURE_MDEV_RENAME
@@ -143,8 +367,8 @@ static void make_device(char *path, int delete)
143{ 367{
144 char *device_name, *subsystem_slash_devname; 368 char *device_name, *subsystem_slash_devname;
145 int major, minor, type, len; 369 int major, minor, type, len;
146 mode_t mode; 370
147 parser_t *parser; 371 dbg("%s('%s', delete:%d)", __func__, path, delete);
148 372
149 /* Try to read major/minor string. Note that the kernel puts \n after 373 /* Try to read major/minor string. Note that the kernel puts \n after
150 * the data, so we don't need to worry about null terminating the string 374 * the data, so we don't need to worry about null terminating the string
@@ -197,246 +421,184 @@ static void make_device(char *path, int delete)
197 path = subsystem_slash_devname; 421 path = subsystem_slash_devname;
198 } 422 }
199 423
200 /* If we have config file, look up user settings */ 424#if ENABLE_FEATURE_MDEV_CONF
201 if (ENABLE_FEATURE_MDEV_CONF) 425 G.rule_idx = 0; /* restart from the beginning (think mdev -s) */
202 parser = config_open2("/etc/mdev.conf", fopen_for_read); 426#endif
203 427 for (;;) {
204 do { 428 const char *str_to_match;
205 int keep_matching; 429 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP];
206 struct bb_uidgid_t ugid; 430 char *command;
207 char *tokens[4]; 431 char *alias;
208 char *command = NULL;
209 char *alias = NULL;
210 char aliaslink = aliaslink; /* for compiler */ 432 char aliaslink = aliaslink; /* for compiler */
433 const char *node_name;
434 const struct rule *rule;
211 435
212 /* Defaults in case we won't match any line */ 436 str_to_match = "";
213 ugid.uid = ugid.gid = 0; 437
214 keep_matching = 0; 438 rule = next_rule();
215 mode = 0660; 439
216 440#if ENABLE_FEATURE_MDEV_CONF
217 if (ENABLE_FEATURE_MDEV_CONF 441 if (rule->maj >= 0) { /* @maj,min rule */
218 && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL) 442 if (major != rule->maj)
219 ) { 443 continue;
220 char *val; 444 if (minor < rule->min0 || minor > rule->min1)
221 char *str_to_match; 445 continue;
222 regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP]; 446 memset(off, 0, sizeof(off));
223 447 goto rule_matches;
224 val = tokens[0]; 448 }
225 keep_matching = ('-' == val[0]); 449 if (rule->envvar) { /* $envvar=regex rule */
226 val += keep_matching; /* swallow leading dash */ 450 str_to_match = getenv(rule->envvar);
227 451 dbg("getenv('%s'):'%s'", rule->envvar, str_to_match);
228 /* Match against either "subsystem/device_name" 452 if (!str_to_match)
229 * or "device_name" alone */ 453 continue;
230 str_to_match = strchr(val, '/') ? path : device_name; 454 } else {
231 455 /* regex to match [subsystem/]device_name */
232 /* Fields: regex uid:gid mode [alias] [cmd] */ 456 str_to_match = (rule->regex_has_slash ? path : device_name);
233 457 }
234 if (val[0] == '@') { 458
235 /* @major,minor[-minor2] */ 459 if (rule->regex_compiled) {
236 /* (useful when name is ambiguous: 460 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
237 * "/sys/class/usb/lp0" and 461 dbg("regex_match for '%s':%d", str_to_match, regex_match);
238 * "/sys/class/printer/lp0") */ 462 //bb_error_msg("matches:");
239 int cmaj, cmin0, cmin1, sc; 463 //for (int i = 0; i < ARRAY_SIZE(off); i++) {
240 if (major < 0) 464 // if (off[i].rm_so < 0) continue;
241 continue; /* no dev, no match */ 465 // bb_error_msg("match %d: '%.*s'\n", i,
242 sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1); 466 // (int)(off[i].rm_eo - off[i].rm_so),
243 if (sc < 1 467 // device_name + off[i].rm_so);
244 || major != cmaj 468 //}
245 || (sc == 2 && minor != cmin0) 469
246 || (sc == 3 && (minor < cmin0 || minor > cmin1)) 470 if (regex_match != 0
247 ) { 471 /* regexec returns whole pattern as "range" 0 */
248 continue; /* this line doesn't match */ 472 || off[0].rm_so != 0
249 } 473 || (int)off[0].rm_eo != (int)strlen(str_to_match)
250 goto line_matches; 474 ) {
251 } 475 continue; /* this rule doesn't match */
252 if (val[0] == '$') {
253 /* regex to match an environment variable */
254 char *eq = strchr(++val, '=');
255 if (!eq)
256 continue;
257 *eq = '\0';
258 str_to_match = getenv(val);
259 if (!str_to_match)
260 continue;
261 str_to_match -= strlen(val) + 1;
262 *eq = '=';
263 } 476 }
264 /* else: regex to match [subsystem/]device_name */ 477 }
265 478 /* else: it's final implicit "match-all" rule */
266 { 479#endif
267 regex_t match; 480
268 int result; 481 rule_matches:
269 482 dbg("rule matched");
270 xregcomp(&match, val, REG_EXTENDED); 483
271 result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0); 484 /* Build alias name */
272 regfree(&match); 485 alias = NULL;
273 //bb_error_msg("matches:"); 486 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
274 //for (int i = 0; i < ARRAY_SIZE(off); i++) { 487 aliaslink = rule->ren_mov[0];
275 // if (off[i].rm_so < 0) continue; 488 if (aliaslink == '!') {
276 // bb_error_msg("match %d: '%.*s'\n", i, 489 /* "!": suppress node creation/deletion */
277 // (int)(off[i].rm_eo - off[i].rm_so), 490 major = -2;
278 // device_name + off[i].rm_so);
279 //}
280
281 /* If no match, skip rest of line */
282 /* (regexec returns whole pattern as "range" 0) */
283 if (result
284 || off[0].rm_so
285 || ((int)off[0].rm_eo != (int)strlen(str_to_match))
286 ) {
287 continue; /* this line doesn't match */
288 }
289 } 491 }
290 line_matches: 492 else if (aliaslink == '>' || aliaslink == '=') {
291 /* This line matches. Stop parsing after parsing 493 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
292 * the rest the line unless keep_matching == 1 */ 494 char *s;
293 495 char *p;
294 /* 2nd field: uid:gid - device ownership */ 496 unsigned n;
295 if (get_uidgid(&ugid, tokens[1], /*allow_numeric:*/ 1) == 0) 497
296 bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno); 498 /* substitute %1..9 with off[1..9], if any */
297 499 n = 0;
298 /* 3rd field: mode - device permissions */ 500 s = rule->ren_mov;
299 bb_parse_mode(tokens[2], &mode); 501 while (*s)
300 502 if (*s++ == '%')
301 val = tokens[3]; 503 n++;
302 /* 4th field (opt): ">|=alias" or "!" to not create the node */ 504
303 505 p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match));
304 if (ENABLE_FEATURE_MDEV_RENAME && val) { 506 s = rule->ren_mov + 1;
305 char *a, *s, *st; 507 while (*s) {
306 508 *p = *s;
307 a = val; 509 if ('%' == *s) {
308 s = strchrnul(val, ' '); 510 unsigned i = (s[1] - '0');
309 st = strchrnul(val, '\t'); 511 if (i <= 9 && off[i].rm_so >= 0) {
310 if (st < s) 512 n = off[i].rm_eo - off[i].rm_so;
311 s = st; 513 strncpy(p, str_to_match + off[i].rm_so, n);
312 st = (s[0] && s[1]) ? s+1 : NULL; 514 p += n - 1;
313 515 s++;
314 aliaslink = a[0];
315 if (aliaslink == '!' && s == a+1) {
316 val = st;
317 /* "!": suppress node creation/deletion */
318 major = -2;
319 }
320 else if (aliaslink == '>' || aliaslink == '=') {
321 val = st;
322 s[0] = '\0';
323 if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) {
324 char *p;
325 unsigned i, n;
326
327 /* substitute %1..9 with off[1..9], if any */
328 n = 0;
329 s = a;
330 while (*s)
331 if (*s++ == '%')
332 n++;
333
334 p = alias = xzalloc(strlen(a) + n * strlen(str_to_match));
335 s = a + 1;
336 while (*s) {
337 *p = *s;
338 if ('%' == *s) {
339 i = (s[1] - '0');
340 if (i <= 9 && off[i].rm_so >= 0) {
341 n = off[i].rm_eo - off[i].rm_so;
342 strncpy(p, str_to_match + off[i].rm_so, n);
343 p += n - 1;
344 s++;
345 }
346 } 516 }
347 p++;
348 s++;
349 } 517 }
350 } else { 518 p++;
351 alias = xstrdup(a + 1); 519 s++;
352 } 520 }
521 } else {
522 alias = xstrdup(rule->ren_mov + 1);
353 } 523 }
354 } 524 }
525 }
526 dbg("alias:'%s'", alias);
355 527
356 if (ENABLE_FEATURE_MDEV_EXEC && val) { 528 command = NULL;
357 const char *s = "$@*"; 529 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
358 const char *s2 = strchr(s, val[0]); 530 if (command) {
359 531 const char *s = "$@*";
360 if (!s2) { 532 const char *s2 = strchr(s, command[0]);
361 bb_error_msg("bad line %u", parser->lineno);
362 if (ENABLE_FEATURE_MDEV_RENAME)
363 free(alias);
364 continue;
365 }
366 533
367 /* Are we running this command now? 534 /* Are we running this command now?
368 * Run $cmd on delete, @cmd on create, *cmd on both 535 * Run $cmd on delete, @cmd on create, *cmd on both
536 */
537 if (s2 - s != delete) {
538 /* We are here if: '*',
539 * or: '@' and delete = 0,
540 * or: '$' and delete = 1
369 */ 541 */
370 if (s2 - s != delete) { 542 command++;
371 /* We are here if: '*', 543 } else {
372 * or: '@' and delete = 0, 544 command = NULL;
373 * or: '$' and delete = 1
374 */
375 command = xstrdup(val + 1);
376 }
377 } 545 }
378 } 546 }
379 547 dbg("command:'%s'", command);
380 /* End of field parsing */
381 548
382 /* "Execute" the line we found */ 549 /* "Execute" the line we found */
383 { 550 node_name = device_name;
384 const char *node_name; 551 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
385 552 node_name = alias = build_alias(alias, device_name);
386 node_name = device_name; 553 dbg("alias2:'%s'", alias);
387 if (ENABLE_FEATURE_MDEV_RENAME && alias) 554 }
388 node_name = alias = build_alias(alias, device_name);
389
390 if (!delete && major >= 0) {
391 if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST)
392 bb_perror_msg("can't create '%s'", node_name);
393 if (major == G.root_major && minor == G.root_minor)
394 symlink(node_name, "root");
395 if (ENABLE_FEATURE_MDEV_CONF) {
396 chmod(node_name, mode);
397 chown(node_name, ugid.uid, ugid.gid);
398 }
399 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
400 if (aliaslink == '>')
401 symlink(node_name, device_name);
402 }
403 }
404 555
405 if (ENABLE_FEATURE_MDEV_EXEC && command) { 556 if (!delete && major >= 0) {
406 /* setenv will leak memory, use putenv/unsetenv/free */ 557 dbg("mknod('%s',%o,(%d,%d))", node_name, rule->mode | type, major, minor);
407 char *s = xasprintf("%s=%s", "MDEV", node_name); 558 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
408 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); 559 bb_perror_msg("can't create '%s'", node_name);
409 putenv(s); 560 if (major == G.root_major && minor == G.root_minor)
410 putenv(s1); 561 symlink(node_name, "root");
411 if (system(command) == -1) 562 if (ENABLE_FEATURE_MDEV_CONF) {
412 bb_perror_msg("can't run '%s'", command); 563 chmod(node_name, rule->mode);
413 bb_unsetenv_and_free(s1); 564 chown(node_name, rule->ugid.uid, rule->ugid.gid);
414 bb_unsetenv_and_free(s);
415 free(command);
416 } 565 }
417 566 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
418 if (delete && major >= -1) { 567 if (aliaslink == '>')
419 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 568 symlink(node_name, device_name);
420 if (aliaslink == '>')
421 unlink(device_name);
422 }
423 unlink(node_name);
424 } 569 }
570 }
571
572 if (ENABLE_FEATURE_MDEV_EXEC && command) {
573 /* setenv will leak memory, use putenv/unsetenv/free */
574 char *s = xasprintf("%s=%s", "MDEV", node_name);
575 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
576 putenv(s);
577 putenv(s1);
578 if (system(command) == -1)
579 bb_perror_msg("can't run '%s'", command);
580 bb_unsetenv_and_free(s1);
581 bb_unsetenv_and_free(s);
582 }
425 583
426 if (ENABLE_FEATURE_MDEV_RENAME) 584 if (delete && major >= -1) {
427 free(alias); 585 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
586 if (aliaslink == '>')
587 unlink(device_name);
588 }
589 unlink(node_name);
428 } 590 }
429 591
592 if (ENABLE_FEATURE_MDEV_RENAME)
593 free(alias);
594
430 /* We found matching line. 595 /* We found matching line.
431 * Stop unless it was prefixed with '-' */ 596 * Stop unless it was prefixed with '-'
432 if (ENABLE_FEATURE_MDEV_CONF && !keep_matching) 597 */
598 if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching)
433 break; 599 break;
600 } /* for (;;) */
434 601
435 /* end of "while line is read from /etc/mdev.conf" */
436 } while (ENABLE_FEATURE_MDEV_CONF);
437
438 if (ENABLE_FEATURE_MDEV_CONF)
439 config_close(parser);
440 free(subsystem_slash_devname); 602 free(subsystem_slash_devname);
441} 603}
442 604
@@ -541,6 +703,12 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
541{ 703{
542 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE); 704 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
543 705
706 INIT_G();
707
708#if ENABLE_FEATURE_MDEV_CONF
709 G.filename = "/etc/mdev.conf";
710#endif
711
544 /* We can be called as hotplug helper */ 712 /* We can be called as hotplug helper */
545 /* Kernel cannot provide suitable stdio fds for us, do it ourself */ 713 /* Kernel cannot provide suitable stdio fds for us, do it ourself */
546 bb_sanitize_stdio(); 714 bb_sanitize_stdio();
@@ -556,6 +724,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
556 */ 724 */
557 struct stat st; 725 struct stat st;
558 726
727#if ENABLE_FEATURE_MDEV_CONF
728 /* Same as xrealloc_vector(NULL, 4, 0): */
729 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
730#endif
559 xstat("/", &st); 731 xstat("/", &st);
560 G.root_major = major(st.st_dev); 732 G.root_major = major(st.st_dev);
561 G.root_minor = minor(st.st_dev); 733 G.root_minor = minor(st.st_dev);