mdev: don't reparse rules on -s
No binary (build failed)
—
(log yok)
| @@ -168,9 +168,29 @@ | |
| * This happens regardless of /sys/class/.../dev existence. | |
| */ | |
| + | struct rule { |
| + | bool keep_matching; |
| + | bool regex_compiled; |
| + | bool regex_has_slash; |
| + | mode_t mode; |
| + | int maj, min0, min1; |
| + | struct bb_uidgid_t ugid; |
| + | char *envvar; |
| + | char *ren_mov; |
| + | IF_FEATURE_MDEV_EXEC(char *r_cmd;) |
| + | regex_t match; |
| + | }; |
| + | |
| struct globals { | |
| int root_major, root_minor; | |
| char *subsystem; | |
| + | #if ENABLE_FEATURE_MDEV_CONF |
| + | const char *filename; |
| + | parser_t *parser; |
| + | struct rule **rule_vec; |
| + | unsigned rule_idx; |
| + | #endif |
| + | struct rule cur_rule; |
| } FIX_ALIASING; | |
| #define G (*(struct globals*)&bb_common_bufsiz1) | |
| #define INIT_G() do { } while (0) | |
| @@ -182,6 +202,164 @@ struct globals { | |
| /* We use additional 64+ bytes in make_device() */ | |
| #define SCRATCH_SIZE 80 | |
| + | #if 0 |
| + | # define dbg(...) bb_error_msg(__VA_ARGS__) |
| + | #else |
| + | # define dbg(...) ((void)0) |
| + | #endif |
| + | |
| + | |
| + | #if ENABLE_FEATURE_MDEV_CONF |
| + | |
| + | static void make_default_cur_rule(void) |
| + | { |
| + | memset(&G.cur_rule, 0, sizeof(G.cur_rule)); |
| + | G.cur_rule.maj = -1; /* "not a @major,minor rule" */ |
| + | G.cur_rule.mode = 0660; |
| + | } |
| + | |
| + | static void clean_up_cur_rule(void) |
| + | { |
| + | free(G.cur_rule.envvar); |
| + | if (G.cur_rule.regex_compiled) |
| + | regfree(&G.cur_rule.match); |
| + | free(G.cur_rule.ren_mov); |
| + | IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);) |
| + | make_default_cur_rule(); |
| + | } |
| + | |
| + | static void parse_next_rule(void) |
| + | { |
| + | /* Note: on entry, G.cur_rule is set to default */ |
| + | while (1) { |
| + | char *tokens[4]; |
| + | char *val; |
| + | |
| + | if (!config_read(G.parser, tokens, 4, 3, "# \t", PARSE_NORMAL)) |
| + | break; |
| + | |
| + | /* Fields: [-]regex uid:gid mode [alias] [cmd] */ |
| + | dbg("token1:'%s'", tokens[1]); |
| + | |
| + | /* 1st field */ |
| + | val = tokens[0]; |
| + | G.cur_rule.keep_matching = ('-' == val[0]); |
| + | val += G.cur_rule.keep_matching; /* swallow leading dash */ |
| + | if (val[0] == '@') { |
| + | /* @major,minor[-minor2] */ |
| + | /* (useful when name is ambiguous: |
| + | * "/sys/class/usb/lp0" and |
| + | * "/sys/class/printer/lp0") |
| + | */ |
| + | int sc = sscanf(val, "@%u,%u-%u", &G.cur_rule.maj, &G.cur_rule.min0, &G.cur_rule.min1); |
| + | if (sc < 2 || G.cur_rule.maj < 0) { |
| + | bb_error_msg("bad @maj,min on line %d", G.parser->lineno); |
| + | goto next_rule; |
| + | } |
| + | if (sc == 2) |
| + | G.cur_rule.min1 = G.cur_rule.min0; |
| + | } else { |
| + | if (val[0] == '$') { |
| + | char *eq = strchr(++val, '='); |
| + | if (!eq) { |
| + | bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno); |
| + | goto next_rule; |
| + | } |
| + | G.cur_rule.envvar = xstrndup(val, eq - val); |
| + | val = eq + 1; |
| + | } |
| + | xregcomp(&G.cur_rule.match, val, REG_EXTENDED); |
| + | G.cur_rule.regex_compiled = 1; |
| + | G.cur_rule.regex_has_slash = (strchr(val, '/') != NULL); |
| + | } |
| + | |
| + | /* 2nd field: uid:gid - device ownership */ |
| + | if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) { |
| + | bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno); |
| + | goto next_rule; |
| + | } |
| + | |
| + | /* 3rd field: mode - device permissions */ |
| + | bb_parse_mode(tokens[2], &G.cur_rule.mode); |
| + | |
| + | /* 4th field (opt): ">|=alias" or "!" to not create the node */ |
| + | val = tokens[3]; |
| + | if (ENABLE_FEATURE_MDEV_RENAME && val && strchr(">=!", val[0])) { |
| + | char *s = skip_non_whitespace(val); |
| + | G.cur_rule.ren_mov = xstrndup(val, s - val); |
| + | val = skip_whitespace(s); |
| + | } |
| + | |
| + | if (ENABLE_FEATURE_MDEV_EXEC && val && val[0]) { |
| + | const char *s = "$@*"; |
| + | const char *s2 = strchr(s, val[0]); |
| + | if (!s2) { |
| + | bb_error_msg("bad line %u", G.parser->lineno); |
| + | goto next_rule; |
| + | } |
| + | IF_FEATURE_MDEV_EXEC(G.cur_rule.r_cmd = xstrdup(val);) |
| + | } |
| + | |
| + | return; |
| + | next_rule: |
| + | clean_up_cur_rule(); |
| + | } /* while (config_read) */ |
| + | |
| + | dbg("config_close(G.parser)"); |
| + | config_close(G.parser); |
| + | G.parser = NULL; |
| + | |
| + | return; |
| + | } |
| + | |
| + | /* If mdev -s, we remember rules in G.rule_vec[]. |
| + | * Otherwise, there is no point in doing it, and we just |
| + | * save only one parsed rule in G.cur_rule. |
| + | */ |
| + | static const struct rule *next_rule(void) |
| + | { |
| + | struct rule *rule; |
| + | |
| + | /* Open conf file if we didn't do it yet */ |
| + | if (!G.parser && G.filename) { |
| + | dbg("config_open('%s')", G.filename); |
| + | G.parser = config_open2(G.filename, fopen_for_read); |
| + | G.filename = NULL; |
| + | } |
| + | |
| + | if (G.rule_vec) { |
| + | /* mdev -s */ |
| + | /* Do we have rule parsed already? */ |
| + | if (G.rule_vec[G.rule_idx]) { |
| + | dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); |
| + | return G.rule_vec[G.rule_idx++]; |
| + | } |
| + | make_default_cur_rule(); |
| + | } else { |
| + | /* not mdev -s */ |
| + | clean_up_cur_rule(); |
| + | } |
| + | |
| + | /* Parse one more rule if file isn't fully read */ |
| + | rule = &G.cur_rule; |
| + | if (G.parser) { |
| + | parse_next_rule(); |
| + | if (G.rule_vec) { /* mdev -s */ |
| + | rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule)); |
| + | G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx); |
| + | G.rule_vec[G.rule_idx++] = rule; |
| + | dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); |
| + | } |
| + | } |
| + | |
| + | return rule; |
| + | } |
| + | |
| + | #else |
| + | |
| + | # define next_rule() (&G.cur_rule) |
| + | |
| + | #endif |
| /* Builds an alias path. | |
| * This function potentionally reallocates the alias parameter. | |
| @@ -218,8 +396,8 @@ static void make_device(char *path, int delete) | |
| { | |
| char *device_name, *subsystem_slash_devname; | |
| int major, minor, type, len; | |
| - | mode_t mode; |
| - | parser_t *parser; |
| + | |
| + | dbg("%s('%s', delete:%d)", __func__, path, delete); |
| /* Try to read major/minor string. Note that the kernel puts \n after | |
| * the data, so we don't need to worry about null terminating the string | |
| @@ -272,246 +450,184 @@ static void make_device(char *path, int delete) | |
| path = subsystem_slash_devname; | |
| } | |
| - | /* If we have config file, look up user settings */ |
| - | if (ENABLE_FEATURE_MDEV_CONF) |
| - | parser = config_open2("/etc/mdev.conf", fopen_for_read); |
| - | |
| - | do { |
| - | int keep_matching; |
| - | struct bb_uidgid_t ugid; |
| - | char *tokens[4]; |
| - | char *command = NULL; |
| - | char *alias = NULL; |
| + | #if ENABLE_FEATURE_MDEV_CONF |
| + | G.rule_idx = 0; /* restart from the beginning (think mdev -s) */ |
| + | #endif |
| + | for (;;) { |
| + | const char *str_to_match; |
| + | regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP]; |
| + | char *command; |
| + | char *alias; |
| char aliaslink = aliaslink; /* for compiler */ | |
| + | const char *node_name; |
| + | const struct rule *rule; |
| - | /* Defaults in case we won't match any line */ |
| - | ugid.uid = ugid.gid = 0; |
| - | keep_matching = 0; |
| - | mode = 0660; |
| - | |
| - | if (ENABLE_FEATURE_MDEV_CONF |
| - | && config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL) |
| - | ) { |
| - | char *val; |
| - | char *str_to_match; |
| - | regmatch_t off[1 + 9 * ENABLE_FEATURE_MDEV_RENAME_REGEXP]; |
| - | |
| - | val = tokens[0]; |
| - | keep_matching = ('-' == val[0]); |
| - | val += keep_matching; /* swallow leading dash */ |
| - | |
| - | /* Match against either "subsystem/device_name" |
| - | * or "device_name" alone */ |
| - | str_to_match = strchr(val, '/') ? path : device_name; |
| - | |
| - | /* Fields: regex uid:gid mode [alias] [cmd] */ |
| - | |
| - | if (val[0] == '@') { |
| - | /* @major,minor[-minor2] */ |
| - | /* (useful when name is ambiguous: |
| - | * "/sys/class/usb/lp0" and |
| - | * "/sys/class/printer/lp0") */ |
| - | int cmaj, cmin0, cmin1, sc; |
| - | if (major < 0) |
| - | continue; /* no dev, no match */ |
| - | sc = sscanf(val, "@%u,%u-%u", &cmaj, &cmin0, &cmin1); |
| - | if (sc < 1 |
| - | || major != cmaj |
| - | || (sc == 2 && minor != cmin0) |
| - | || (sc == 3 && (minor < cmin0 || minor > cmin1)) |
| - | ) { |
| - | continue; /* this line doesn't match */ |
| - | } |
| - | goto line_matches; |
| - | } |
| - | if (val[0] == '$') { |
| - | /* regex to match an environment variable */ |
| - | char *eq = strchr(++val, '='); |
| - | if (!eq) |
| - | continue; |
| - | *eq = '\0'; |
| - | str_to_match = getenv(val); |
| - | if (!str_to_match) |
| - | continue; |
| - | str_to_match -= strlen(val) + 1; |
| - | *eq = '='; |
| + | str_to_match = ""; |
| + | |
| + | rule = next_rule(); |
| + | |
| + | #if ENABLE_FEATURE_MDEV_CONF |
| + | if (rule->maj >= 0) { /* @maj,min rule */ |
| + | if (major != rule->maj) |
| + | continue; |
| + | if (minor < rule->min0 || minor > rule->min1) |
| + | continue; |
| + | memset(off, 0, sizeof(off)); |
| + | goto rule_matches; |
| + | } |
| + | if (rule->envvar) { /* $envvar=regex rule */ |
| + | str_to_match = getenv(rule->envvar); |
| + | dbg("getenv('%s'):'%s'", rule->envvar, str_to_match); |
| + | if (!str_to_match) |
| + | continue; |
| + | } else { |
| + | /* regex to match [subsystem/]device_name */ |
| + | str_to_match = (rule->regex_has_slash ? path : device_name); |
| + | } |
| + | |
| + | if (rule->regex_compiled) { |
| + | int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0); |
| + | dbg("regex_match for '%s':%d", str_to_match, regex_match); |
| + | //bb_error_msg("matches:"); |
| + | //for (int i = 0; i < ARRAY_SIZE(off); i++) { |
| + | // if (off[i].rm_so < 0) continue; |
| + | // bb_error_msg("match %d: '%.*s'\n", i, |
| + | // (int)(off[i].rm_eo - off[i].rm_so), |
| + | // device_name + off[i].rm_so); |
| + | //} |
| + | |
| + | if (regex_match != 0 |
| + | /* regexec returns whole pattern as "range" 0 */ |
| + | || off[0].rm_so != 0 |
| + | || (int)off[0].rm_eo != (int)strlen(str_to_match) |
| + | ) { |
| + | continue; /* this rule doesn't match */ |
| } | |
| - | /* else: regex to match [subsystem/]device_name */ |
| - | |
| - | { |
| - | regex_t match; |
| - | int result; |
| - | |
| - | xregcomp(&match, val, REG_EXTENDED); |
| - | result = regexec(&match, str_to_match, ARRAY_SIZE(off), off, 0); |
| - | regfree(&match); |
| - | //bb_error_msg("matches:"); |
| - | //for (int i = 0; i < ARRAY_SIZE(off); i++) { |
| - | // if (off[i].rm_so < 0) continue; |
| - | // bb_error_msg("match %d: '%.*s'\n", i, |
| - | // (int)(off[i].rm_eo - off[i].rm_so), |
| - | // device_name + off[i].rm_so); |
| - | //} |
| - | |
| - | /* If no match, skip rest of line */ |
| - | /* (regexec returns whole pattern as "range" 0) */ |
| - | if (result |
| - | || off[0].rm_so |
| - | || ((int)off[0].rm_eo != (int)strlen(str_to_match)) |
| - | ) { |
| - | continue; /* this line doesn't match */ |
| - | } |
| + | } |
| + | /* else: it's final implicit "match-all" rule */ |
| + | #endif |
| + | |
| + | rule_matches: |
| + | dbg("rule matched"); |
| + | |
| + | /* Build alias name */ |
| + | alias = NULL; |
| + | if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) { |
| + | aliaslink = rule->ren_mov[0]; |
| + | if (aliaslink == '!') { |
| + | /* "!": suppress node creation/deletion */ |
| + | major = -2; |
| } | |
| - | line_matches: |
| - | /* This line matches. Stop parsing after parsing |
| - | * the rest the line unless keep_matching == 1 */ |
| - | |
| - | /* 2nd field: uid:gid - device ownership */ |
| - | if (get_uidgid(&ugid, tokens[1], /*allow_numeric:*/ 1) == 0) |
| - | bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno); |
| - | |
| - | /* 3rd field: mode - device permissions */ |
| - | bb_parse_mode(tokens[2], &mode); |
| - | |
| - | val = tokens[3]; |
| - | /* 4th field (opt): ">|=alias" or "!" to not create the node */ |
| - | |
| - | if (ENABLE_FEATURE_MDEV_RENAME && val) { |
| - | char *a, *s, *st; |
| - | |
| - | a = val; |
| - | s = strchrnul(val, ' '); |
| - | st = strchrnul(val, '\t'); |
| - | if (st < s) |
| - | s = st; |
| - | st = (s[0] && s[1]) ? s+1 : NULL; |
| - | |
| - | aliaslink = a[0]; |
| - | if (aliaslink == '!' && s == a+1) { |
| - | val = st; |
| - | /* "!": suppress node creation/deletion */ |
| - | major = -2; |
| - | } |
| - | else if (aliaslink == '>' || aliaslink == '=') { |
| - | val = st; |
| - | s[0] = '\0'; |
| - | if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) { |
| - | char *p; |
| - | unsigned i, n; |
| - | |
| - | /* substitute %1..9 with off[1..9], if any */ |
| - | n = 0; |
| - | s = a; |
| - | while (*s) |
| - | if (*s++ == '%') |
| - | n++; |
| - | |
| - | p = alias = xzalloc(strlen(a) + n * strlen(str_to_match)); |
| - | s = a + 1; |
| - | while (*s) { |
| - | *p = *s; |
| - | if ('%' == *s) { |
| - | i = (s[1] - '0'); |
| - | if (i <= 9 && off[i].rm_so >= 0) { |
| - | n = off[i].rm_eo - off[i].rm_so; |
| - | strncpy(p, str_to_match + off[i].rm_so, n); |
| - | p += n - 1; |
| - | s++; |
| - | } |
| + | else if (aliaslink == '>' || aliaslink == '=') { |
| + | if (ENABLE_FEATURE_MDEV_RENAME_REGEXP) { |
| + | char *s; |
| + | char *p; |
| + | unsigned n; |
| + | |
| + | /* substitute %1..9 with off[1..9], if any */ |
| + | n = 0; |
| + | s = rule->ren_mov; |
| + | while (*s) |
| + | if (*s++ == '%') |
| + | n++; |
| + | |
| + | p = alias = xzalloc(strlen(rule->ren_mov) + n * strlen(str_to_match)); |
| + | s = rule->ren_mov + 1; |
| + | while (*s) { |
| + | *p = *s; |
| + | if ('%' == *s) { |
| + | unsigned i = (s[1] - '0'); |
| + | if (i <= 9 && off[i].rm_so >= 0) { |
| + | n = off[i].rm_eo - off[i].rm_so; |
| + | strncpy(p, str_to_match + off[i].rm_so, n); |
| + | p += n - 1; |
| + | s++; |
| } | |
| - | p++; |
| - | s++; |
| } | |
| - | } else { |
| - | alias = xstrdup(a + 1); |
| + | p++; |
| + | s++; |
| } | |
| + | } else { |
| + | alias = xstrdup(rule->ren_mov + 1); |
| } | |
| } | |
| + | } |
| + | dbg("alias:'%s'", alias); |
| - | if (ENABLE_FEATURE_MDEV_EXEC && val) { |
| - | const char *s = "$@*"; |
| - | const char *s2 = strchr(s, val[0]); |
| - | |
| - | if (!s2) { |
| - | bb_error_msg("bad line %u", parser->lineno); |
| - | if (ENABLE_FEATURE_MDEV_RENAME) |
| - | free(alias); |
| - | continue; |
| - | } |
| + | command = NULL; |
| + | IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;) |
| + | if (command) { |
| + | const char *s = "$@*"; |
| + | const char *s2 = strchr(s, command[0]); |
| - | /* Are we running this command now? |
| - | * Run $cmd on delete, @cmd on create, *cmd on both |
| + | /* Are we running this command now? |
| + | * Run $cmd on delete, @cmd on create, *cmd on both |
| + | */ |
| + | if (s2 - s != delete) { |
| + | /* We are here if: '*', |
| + | * or: '@' and delete = 0, |
| + | * or: '$' and delete = 1 |
| */ | |
| - | if (s2 - s != delete) { |
| - | /* We are here if: '*', |
| - | * or: '@' and delete = 0, |
| - | * or: '$' and delete = 1 |
| - | */ |
| - | command = xstrdup(val + 1); |
| - | } |
| + | command++; |
| + | } else { |
| + | command = NULL; |
| } | |
| } | |
| - | |
| - | /* End of field parsing */ |
| + | dbg("command:'%s'", command); |
| /* "Execute" the line we found */ | |
| - | { |
| - | const char *node_name; |
| - | |
| - | node_name = device_name; |
| - | if (ENABLE_FEATURE_MDEV_RENAME && alias) |
| - | node_name = alias = build_alias(alias, device_name); |
| - | |
| - | if (!delete && major >= 0) { |
| - | if (mknod(node_name, mode | type, makedev(major, minor)) && errno != EEXIST) |
| - | bb_perror_msg("can't create '%s'", node_name); |
| - | if (major == G.root_major && minor == G.root_minor) |
| - | symlink(node_name, "root"); |
| - | if (ENABLE_FEATURE_MDEV_CONF) { |
| - | chmod(node_name, mode); |
| - | chown(node_name, ugid.uid, ugid.gid); |
| - | } |
| - | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
| - | if (aliaslink == '>') |
| - | symlink(node_name, device_name); |
| - | } |
| - | } |
| + | node_name = device_name; |
| + | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
| + | node_name = alias = build_alias(alias, device_name); |
| + | dbg("alias2:'%s'", alias); |
| + | } |
| - | if (ENABLE_FEATURE_MDEV_EXEC && command) { |
| - | /* setenv will leak memory, use putenv/unsetenv/free */ |
| - | char *s = xasprintf("%s=%s", "MDEV", node_name); |
| - | char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); |
| - | putenv(s); |
| - | putenv(s1); |
| - | if (system(command) == -1) |
| - | bb_perror_msg("can't run '%s'", command); |
| - | bb_unsetenv_and_free(s1); |
| - | bb_unsetenv_and_free(s); |
| - | free(command); |
| + | if (!delete && major >= 0) { |
| + | dbg("mknod('%s',%o,(%d,%d))", node_name, rule->mode | type, major, minor); |
| + | if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) |
| + | bb_perror_msg("can't create '%s'", node_name); |
| + | if (major == G.root_major && minor == G.root_minor) |
| + | symlink(node_name, "root"); |
| + | if (ENABLE_FEATURE_MDEV_CONF) { |
| + | chmod(node_name, rule->mode); |
| + | chown(node_name, rule->ugid.uid, rule->ugid.gid); |
| } | |
| - | |
| - | if (delete && major >= -1) { |
| - | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
| - | if (aliaslink == '>') |
| - | unlink(device_name); |
| - | } |
| - | unlink(node_name); |
| + | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
| + | if (aliaslink == '>') |
| + | symlink(node_name, device_name); |
| } | |
| + | } |
| - | if (ENABLE_FEATURE_MDEV_RENAME) |
| - | free(alias); |
| + | if (ENABLE_FEATURE_MDEV_EXEC && command) { |
| + | /* setenv will leak memory, use putenv/unsetenv/free */ |
| + | char *s = xasprintf("%s=%s", "MDEV", node_name); |
| + | char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem); |
| + | putenv(s); |
| + | putenv(s1); |
| + | if (system(command) == -1) |
| + | bb_perror_msg("can't run '%s'", command); |
| + | bb_unsetenv_and_free(s1); |
| + | bb_unsetenv_and_free(s); |
| } | |
| + | if (delete && major >= -1) { |
| + | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
| + | if (aliaslink == '>') |
| + | unlink(device_name); |
| + | } |
| + | unlink(node_name); |
| + | } |
| + | |
| + | if (ENABLE_FEATURE_MDEV_RENAME) |
| + | free(alias); |
| + | |
| /* We found matching line. | |
| - | * Stop unless it was prefixed with '-' */ |
| - | if (ENABLE_FEATURE_MDEV_CONF && !keep_matching) |
| + | * Stop unless it was prefixed with '-' |
| + | */ |
| + | if (!ENABLE_FEATURE_MDEV_CONF || !rule->keep_matching) |
| break; | |
| + | } /* for (;;) */ |
| - | /* end of "while line is read from /etc/mdev.conf" */ |
| - | } while (ENABLE_FEATURE_MDEV_CONF); |
| - | |
| - | if (ENABLE_FEATURE_MDEV_CONF) |
| - | config_close(parser); |
| free(subsystem_slash_devname); | |
| } | |
| @@ -618,6 +734,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |
| INIT_G(); | |
| + | #if ENABLE_FEATURE_MDEV_CONF |
| + | G.filename = "/etc/mdev.conf"; |
| + | #endif |
| + | |
| /* We can be called as hotplug helper */ | |
| /* Kernel cannot provide suitable stdio fds for us, do it ourself */ | |
| bb_sanitize_stdio(); | |
| @@ -633,6 +753,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |
| */ | |
| struct stat st; | |
| + | #if ENABLE_FEATURE_MDEV_CONF |
| + | /* Same as xrealloc_vector(NULL, 4, 0): */ |
| + | G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec)); |
| + | #endif |
| xstat("/", &st); | |
| G.root_major = major(st.st_dev); | |
| G.root_minor = minor(st.st_dev); | |