linepos++;
/* get the key */
+ temp = strchr(linepos, '=');
+ if (temp == NULL || temp == linepos)
+ return -1;
+ temp[0] = '\0';
*key = linepos;
- while (1) {
- linepos++;
- if (linepos[0] == '\0')
- return -1;
- if (isspace(linepos[0]))
- break;
- if (linepos[0] == '=')
- break;
+ linepos = &temp[1];
+
+ /* get a quoted value */
+ if (linepos[0] == '"' || linepos[0] == '\'') {
+ temp = strchr(&linepos[1], linepos[0]);
+ if (temp != NULL) {
+ temp[0] = '\0';
+ *value = &linepos[1];
+ goto out;
+ }
}
- /* terminate key */
- linepos[0] = '\0';
- linepos++;
-
- /* skip whitespace */
- while (isspace(linepos[0]))
- linepos++;
-
/* get the value*/
- if (linepos[0] == '"') {
- linepos++;
- temp = strchr(linepos, '"');
- if (!temp) {
- dbg("missing closing quote");
- return -1;
- }
- dbg("value is quoted");
+ temp = strchr(linepos, '\n');
+ if (temp != NULL)
temp[0] = '\0';
- } else if (linepos[0] == '\'') {
- linepos++;
- temp = strchr(linepos, '\'');
- if (!temp) {
- dbg("missing closing quote");
- return -1;
- }
- dbg("value is quoted");
- temp[0] = '\0';
- } else if (linepos[0] == '\0') {
- dbg("value is empty");
- } else {
- temp = linepos;
- while (temp[0] && !isspace(temp[0]))
- temp++;
- temp[0] = '\0';
- }
*value = linepos;
-
+out:
return 0;
}
char result[2048];
size_t reslen;
- if (run_program(program, udev->dev->subsystem, result, sizeof(result), &reslen, (udev_log_priority >= LOG_INFO)) != 0)
+ if (run_program(program, udev->dev->subsystem, result, sizeof(result), &reslen) != 0)
return -1;
return import_keys_into_env(udev, result, reslen);
}
info("wait for '%s' for %i mseconds", filepath, 1000 / WAIT_LOOP_PER_SECOND);
usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
}
- err("waiting for '%s' failed", filepath);
+ info("waiting for '%s' failed", filepath);
return -1;
}
+/* handle "[$SUBSYSTEM/$KERNEL]<attribute>" lookup */
+static int attr_get_by_subsys_id(const char *attrstr, char *devpath, size_t len, char **attr)
+{
+ char subsys[NAME_SIZE];
+ char *attrib;
+ char *id;
+ int found = 0;
+
+ if (attrstr[0] != '[')
+ goto out;
+
+ strlcpy(subsys, &attrstr[1], sizeof(subsys));
+
+ attrib = strchr(subsys, ']');
+ if (attrib == NULL)
+ goto out;
+ attrib[0] = '\0';
+ attrib = &attrib[1];
+
+ id = strchr(subsys, '/');
+ if (id == NULL)
+ goto out;
+ id[0] = '\0';
+ id = &id[1];
+
+ if (sysfs_lookup_devpath_by_subsys_id(devpath, len, subsys, id)) {
+ if (attr != NULL) {
+ if (attrib[0] != '\0')
+ *attr = attrib;
+ else
+ *attr = NULL;
+ }
+ found = 1;
+ }
+out:
+ return found;
+}
+
void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
{
char temp[PATH_SIZE];
SUBST_PARENT,
SUBST_TEMP_NODE,
SUBST_ROOT,
+ SUBST_SYS,
SUBST_ENV,
};
static const struct subst_map {
{ .name = "parent", .fmt = 'P', .type = SUBST_PARENT },
{ .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE },
{ .name = "root", .fmt = 'r', .type = SUBST_ROOT },
+ { .name = "sys", .fmt = 'S', .type = SUBST_SYS },
{ .name = "env", .fmt = 'E', .type = SUBST_ENV },
{ NULL, '\0', 0 }
};
if (attr == NULL)
err("missing file parameter for attr");
else {
+ char devpath[PATH_SIZE];
+ char *attrib;
const char *value = NULL;
size_t size;
- /* first try the current device, other matches may have selected */
- if (udev->dev_parent != NULL && udev->dev_parent != udev->dev)
+ if (attr_get_by_subsys_id(attr, devpath, sizeof(devpath), &attrib)) {
+ if (attrib != NULL)
+ value = sysfs_attr_get_value(devpath, attrib);
+ else
+ break;
+ }
+
+ /* try the current device, other matches may have selected */
+ if (value == NULL && udev->dev_parent != NULL && udev->dev_parent != udev->dev)
value = sysfs_attr_get_value(udev->dev_parent->devpath, attr);
/* look at all devices along the chain of parents */
if (value == NULL)
break;
- /* strip trailing whitespace and replace untrusted characters of sysfs value */
+ /* strip trailing whitespace, and replace unwanted characters */
size = strlcpy(temp2, value, sizeof(temp2));
if (size >= sizeof(temp2))
size = sizeof(temp2)-1;
while (size > 0 && isspace(temp2[size-1]))
temp2[--size] = '\0';
- count = replace_untrusted_chars(temp2);
+ count = replace_chars(temp2, ALLOWED_CHARS_INPUT);
if (count > 0)
- info("%i untrusted character(s) replaced" , count);
+ info("%i character(s) replaced" , count);
strlcat(string, temp2, maxsize);
dbg("substitute sysfs value '%s'", temp2);
}
strlcat(string, udev_root, maxsize);
dbg("substitute udev_root '%s'", udev_root);
break;
+ case SUBST_SYS:
+ strlcat(string, sysfs_path, maxsize);
+ dbg("substitute sysfs_path '%s'", sysfs_path);
+ break;
case SUBST_ENV:
if (attr == NULL) {
dbg("missing attribute");
}
}
+ if (rule->test.operation != KEY_OP_UNSET) {
+ char filename[PATH_SIZE];
+ char devpath[PATH_SIZE];
+ char *attr;
+ struct stat statbuf;
+ int match;
+
+ strlcpy(filename, key_val(rule, &rule->test), sizeof(filename));
+ udev_rules_apply_format(udev, filename, sizeof(filename));
+
+ if (attr_get_by_subsys_id(filename, devpath, sizeof(devpath), &attr)) {
+ strlcpy(filename, sysfs_path, sizeof(filename));
+ strlcat(filename, devpath, sizeof(filename));
+ if (attr != NULL) {
+ strlcat(filename, "/", sizeof(filename));
+ strlcat(filename, attr, sizeof(filename));
+ }
+ }
+
+ match = (stat(filename, &statbuf) == 0);
+ info("'%s' %s", filename, match ? "exists" : "does not exist");
+ if (match && rule->test_mode_mask > 0) {
+ match = ((statbuf.st_mode & rule->test_mode_mask) > 0);
+ info("'%s' has mode=%#o and %s %#o", filename, statbuf.st_mode,
+ match ? "matches" : "does not match",
+ rule->test_mode_mask);
+ }
+ if (match && rule->test.operation == KEY_OP_NOMATCH)
+ goto nomatch;
+ if (!match && rule->test.operation == KEY_OP_MATCH)
+ goto nomatch;
+ dbg("TEST key is true");
+ }
+
if (rule->wait_for_sysfs.operation != KEY_OP_UNSET) {
int found;
pair->key.operation == KEY_OP_NOMATCH) {
const char *key_name = key_pair_name(rule, pair);
const char *key_value = key_val(rule, &pair->key);
- const char *value;
+ char devpath[PATH_SIZE];
+ char *attrib;
+ const char *value = NULL;
char val[VALUE_SIZE];
size_t len;
- value = sysfs_attr_get_value(udev->dev->devpath, key_name);
+ if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) {
+ if (attrib != NULL)
+ value = sysfs_attr_get_value(devpath, attrib);
+ else
+ goto nomatch;
+ }
+ if (value == NULL)
+ value = sysfs_attr_get_value(udev->dev->devpath, key_name);
if (value == NULL)
goto nomatch;
strlcpy(val, value, sizeof(val));
strlcpy(program, key_val(rule, &rule->program), sizeof(program));
udev_rules_apply_format(udev, program, sizeof(program));
- if (run_program(program, udev->dev->subsystem, result, sizeof(result), NULL, (udev_log_priority >= LOG_INFO)) != 0) {
+ if (run_program(program, udev->dev->subsystem, result, sizeof(result), NULL) != 0) {
dbg("PROGRAM is false");
udev->program_result[0] = '\0';
if (rule->program.operation != KEY_OP_NOMATCH)
dbg("PROGRAM matches");
remove_trailing_chars(result, '\n');
- count = replace_untrusted_chars(result);
- if (count)
- info("%i untrusted character(s) replaced" , count);
+ if (rule->string_escape == ESCAPE_UNSET ||
+ rule->string_escape == ESCAPE_REPLACE) {
+ count = replace_chars(result, ALLOWED_CHARS_INPUT);
+ if (count > 0)
+ info("%i character(s) replaced" , count);
+ }
dbg("result is '%s'", result);
strlcpy(udev->program_result, result, sizeof(udev->program_result));
dbg("PROGRAM returned successful");
unsetenv(key_name);
info("unset ENV '%s'", key_name);
} else {
- char *key_value = name_list_key_add(&udev->env_list, key_name, temp_value);
+ struct name_entry *entry;
- if (key_value == NULL)
+ entry = name_list_key_add(&udev->env_list, key_name, temp_value);
+ if (entry == NULL)
break;
- putenv(key_value);
- info("set ENV '%s'", key_value);
+ putenv(entry->name);
+ info("set ENV '%s'", entry->name);
}
}
}
if (pair->key.operation == KEY_OP_ASSIGN) {
const char *key_name = key_pair_name(rule, pair);
- char attr[PATH_SIZE];
+ char devpath[PATH_SIZE];
+ char *attrib;
+ char attr[PATH_SIZE] = "";
char value[NAME_SIZE];
FILE *f;
- strlcpy(attr, sysfs_path, sizeof(attr));
- strlcat(attr, udev->dev->devpath, sizeof(attr));
- strlcat(attr, "/", sizeof(attr));
- strlcat(attr, key_name, sizeof(attr));
+ if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) {
+ if (attrib != NULL) {
+ strlcpy(attr, sysfs_path, sizeof(attr));
+ strlcat(attr, devpath, sizeof(attr));
+ strlcat(attr, "/", sizeof(attr));
+ strlcat(attr, attrib, sizeof(attr));
+ }
+ }
+
+ if (attr[0] == '\0') {
+ strlcpy(attr, sysfs_path, sizeof(attr));
+ strlcat(attr, udev->dev->devpath, sizeof(attr));
+ strlcat(attr, "/", sizeof(attr));
+ strlcat(attr, key_name, sizeof(attr));
+ }
+
strlcpy(value, key_val(rule, &pair->key), sizeof(value));
udev_rules_apply_format(udev, value, sizeof(value));
info("writing '%s' to sysfs file '%s'", value, attr);
info("reset symlink list");
name_list_cleanup(&udev->symlink_list);
}
+ /* allow multiple symlinks separated by spaces */
strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp));
udev_rules_apply_format(udev, temp, sizeof(temp));
- count = replace_untrusted_chars(temp);
- if (count)
- info("%i untrusted character(s) replaced" , count);
+ if (rule->string_escape == ESCAPE_UNSET ||
+ rule->string_escape == ESCAPE_REPLACE) {
+ count = replace_chars(temp, ALLOWED_CHARS_FILE " ");
+ if (count > 0)
+ info("%i character(s) replaced" , count);
+ }
dbg("rule applied, added symlink(s) '%s'", temp);
-
- /* add multiple symlinks separated by spaces */
pos = temp;
while (isspace(pos[0]))
pos++;
name_set = 1;
strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name));
udev_rules_apply_format(udev, udev->name, sizeof(udev->name));
- count = replace_untrusted_chars(udev->name);
- if (count)
- info("%i untrusted character(s) replaced", count);
+ if (rule->string_escape == ESCAPE_UNSET ||
+ rule->string_escape == ESCAPE_REPLACE) {
+ count = replace_chars(udev->name, ALLOWED_CHARS_FILE);
+ if (count > 0)
+ info("%i character(s) replaced", count);
+ }
info("rule applied, '%s' becomes '%s'", udev->dev->kernel, udev->name);
if (strcmp(udev->dev->subsystem, "net") != 0)
}
if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) {
+ struct name_entry *entry;
+
if (rule->run.operation == KEY_OP_ASSIGN_FINAL)
udev->run_final = 1;
if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) {
name_list_cleanup(&udev->run_list);
}
dbg("add run '%s'", key_val(rule, &rule->run));
- name_list_add(&udev->run_list, key_val(rule, &rule->run), 0);
+ entry = name_list_add(&udev->run_list, key_val(rule, &rule->run), 0);
+ if (rule->run_ignore_error)
+ entry->ignore_error = 1;
}
if (rule->last_rule) {
dbg("process rule");
if (rule->name.operation != KEY_OP_UNSET || rule->symlink.operation != KEY_OP_UNSET ||
- rule->mode_operation != KEY_OP_UNSET || rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) {
+ rule->mode_operation != KEY_OP_UNSET || rule->owner.operation != KEY_OP_UNSET ||
+ rule->group.operation != KEY_OP_UNSET) {
dbg("skip rule that names a device");
continue;
}
}
if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) {
- if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) {
+ struct name_entry *entry;
+
+ if (rule->run.operation == KEY_OP_ASSIGN ||
+ rule->run.operation == KEY_OP_ASSIGN_FINAL) {
info("reset run list");
name_list_cleanup(&udev->run_list);
}
dbg("add run '%s'", key_val(rule, &rule->run));
- name_list_add(&udev->run_list, key_val(rule, &rule->run), 0);
+ entry = name_list_add(&udev->run_list, key_val(rule, &rule->run), 0);
+ if (rule->run_ignore_error)
+ entry->ignore_error = 1;
if (rule->run.operation == KEY_OP_ASSIGN_FINAL)
break;
}