- device_name = strrchr(path, '/') + 1;
- type = path[5]=='c' ? S_IFCHR : S_IFBLK;
-
- /* If we have a config file, look up permissions for this device */
-
- if (ENABLE_FEATURE_MDEV_CONF) {
- char *conf, *pos, *end;
-
- /* mmap the config file */
- if (-1 != (fd=open("/etc/mdev.conf",O_RDONLY))) {
- len = lseek(fd, 0, SEEK_END);
- conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
- if (conf) {
- int line = 0;
-
- /* Loop through lines in mmaped file*/
- for (pos=conf; pos-conf<len;) {
- int field;
- char *end2;
-
- line++;
- /* find end of this line */
- for(end=pos; end-conf<len && *end!='\n'; end++)
- ;
-
- /* Three fields: regex, uid:gid, mode */
- for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC);
- field++)
- {
- /* Skip whitespace */
- while (pos<end && isspace(*pos)) pos++;
- if (pos==end || *pos=='#') break;
- for (end2=pos;
- end2<end && !isspace(*end2) && *end2!='#'; end2++)
- ;
-
- if (!field) {
- /* Regex to match this device */
-
- char *regex = strndupa(pos, end2-pos);
- regex_t match;
- regmatch_t off;
- int result;
-
- /* Is this it? */
- xregcomp(&match,regex, REG_EXTENDED);
- result = regexec(&match, device_name, 1, &off, 0);
- regfree(&match);
-
- /* If not this device, skip rest of line */
- if (result || off.rm_so
- || off.rm_eo != strlen(device_name))
- break;
-
- } else if (field == 1) {
- /* uid:gid */
-
- char *s, *s2;
-
- /* Find : */
- for(s=pos; s<end2 && *s!=':'; s++)
- ;
- if (s == end2) break;
-
- /* Parse UID */
- uid = strtoul(pos,&s2,10);
- if (s != s2) {
- struct passwd *pass;
- pass = getpwnam(strndupa(pos, s-pos));
- if (!pass) break;
- uid = pass->pw_uid;
+ /* 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;
+ char aliaslink = aliaslink; /* for compiler */
+
+ /* 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 = '=';
+ }
+ /* 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 */
+ }
+ }
+ 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], 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 = -1;
+ }
+ 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++;
+ }