#include <unistd.h>
#include <errno.h>
#include <syslog.h>
+#include <dirent.h>
#include <fnmatch.h>
#include <sys/socket.h>
#include <sys/un.h>
cur += count+1;
lineno++;
- if (count >= sizeof(line)) {
- err("line too long, conf line skipped %s, line %d", udev_config_filename, lineno);
- continue;
- }
-
/* eat the whitespace */
while ((count > 0) && isspace(bufline[0])) {
bufline++;
if (bufline[0] == COMMENT_CHARACTER)
continue;
+ if (count >= sizeof(line)) {
+ err("line too long, conf line skipped %s, line %d", udev_config_filename, lineno);
+ continue;
+ }
+
memcpy(line, bufline, count);
line[count] = '\0';
bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath);
bufpos++;
- for (i = 0; environ[i] != NULL && bufpos < sizeof(buf); i++) {
+ for (i = 0; environ[i] != NULL && bufpos < (sizeof(buf)-1); i++) {
bufpos += strlcpy(&buf[bufpos], environ[i], sizeof(buf) - bufpos-1);
bufpos++;
}
+ if (bufpos > sizeof(buf))
+ bufpos = sizeof(buf);
count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, addrlen);
if (count < 0)
return found;
}
+static int attr_subst_subdir(char *attr, size_t len)
+{
+ char *pos;
+ int found = 0;
+
+ pos = strstr(attr, "/*/");
+ if (pos != NULL) {
+ char str[PATH_SIZE];
+ DIR *dir;
+
+ pos[1] = '\0';
+ strlcpy(str, &pos[2], sizeof(str));
+ dir = opendir(attr);
+ if (dir != NULL) {
+ struct dirent *dent;
+
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ struct stat stats;
+
+ if (dent->d_name[0] == '.')
+ continue;
+ strlcat(attr, dent->d_name, len);
+ strlcat(attr, str, len);
+ if (stat(attr, &stats) == 0) {
+ found = 1;
+ break;
+ }
+ pos[1] = '\0';
+ }
+ closedir(dir);
+ }
+ if (!found)
+ strlcat(attr, str, len);
+ }
+
+ return found;
+}
+
void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
{
char temp[PATH_SIZE];
SUBST_KERNEL,
SUBST_KERNEL_NUMBER,
SUBST_ID,
+ SUBST_DRIVER,
SUBST_MAJOR,
SUBST_MINOR,
SUBST_RESULT,
SUBST_ATTR,
SUBST_PARENT,
SUBST_TEMP_NODE,
+ SUBST_NAME,
SUBST_ROOT,
SUBST_SYS,
SUBST_ENV,
{ .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER },
{ .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL },
{ .name = "id", .fmt = 'b', .type = SUBST_ID },
+ { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER },
{ .name = "major", .fmt = 'M', .type = SUBST_MAJOR },
{ .name = "minor", .fmt = 'm', .type = SUBST_MINOR },
{ .name = "result", .fmt = 'c', .type = SUBST_RESULT },
{ .name = "sysfs", .fmt = 's', .type = SUBST_ATTR },
{ .name = "parent", .fmt = 'P', .type = SUBST_PARENT },
{ .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE },
+ { .name = "name", .fmt = 'D', .type = SUBST_NAME },
{ .name = "root", .fmt = 'r', .type = SUBST_ROOT },
{ .name = "sys", .fmt = 'S', .type = SUBST_SYS },
{ .name = "env", .fmt = 'E', .type = SUBST_ENV },
dbg("substitute id '%s'", udev->dev_parent->kernel);
}
break;
+ case SUBST_DRIVER:
+ if (udev->dev_parent != NULL) {
+ strlcat(string, udev->dev_parent->driver, maxsize);
+ dbg("substitute driver '%s'", udev->dev_parent->driver);
+ }
+ break;
case SUBST_MAJOR:
sprintf(temp2, "%d", major(udev->devt));
strlcat(string, temp2, maxsize);
strlcat(string, udev->tmp_node, maxsize);
dbg("substitute temporary device node name '%s'", udev->tmp_node);
break;
+ case SUBST_NAME:
+ strlcat(string, udev->name, maxsize);
+ dbg("substitute udev->name '%s'", udev->name);
+ break;
case SUBST_ROOT:
strlcat(string, udev_root, maxsize);
dbg("substitute udev_root '%s'", udev_root);
if (match_key("DRIVER", rule, &rule->driver, udev->dev->driver))
goto nomatch;
+ /* match NAME against a value assigned by an earlier rule */
if (match_key("NAME", rule, &rule->name, udev->name))
goto nomatch;
}
}
- if (rule->test.operation != KEY_OP_UNSET) {
+ if (rule->test.operation == KEY_OP_MATCH ||
+ rule->test.operation == KEY_OP_NOMATCH) {
char filename[PATH_SIZE];
char devpath[PATH_SIZE];
char *attr;
strlcat(filename, "/", sizeof(filename));
strlcat(filename, attr, sizeof(filename));
}
+ } else if (filename[0] != '/') {
+ char tmp[PATH_SIZE];
+
+ strlcpy(tmp, sysfs_path, sizeof(tmp));
+ strlcat(tmp, udev->dev->devpath, sizeof(tmp));
+ strlcat(tmp, "/", sizeof(tmp));
+ strlcat(tmp, filename, sizeof(tmp));
+ strlcpy(filename, tmp, sizeof(filename));
}
+ attr_subst_subdir(filename, sizeof(filename));
+
match = (stat(filename, &statbuf) == 0);
info("'%s' %s", filename, match ? "exists" : "does not exist");
if (match && rule->test_mode_mask > 0) {
if (rule->wait_for_sysfs.operation != KEY_OP_UNSET) {
int found;
- found = (wait_for_sysfs(udev, key_val(rule, &rule->wait_for_sysfs), 3) == 0);
+ found = (wait_for_sysfs(udev, key_val(rule, &rule->wait_for_sysfs), 10) == 0);
if (!found && (rule->wait_for_sysfs.operation != KEY_OP_NOMATCH))
goto nomatch;
}
strlcat(attr, key_name, sizeof(attr));
}
+ attr_subst_subdir(attr, 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);
dbg("udev->dev->devpath='%s'", udev->dev->devpath);
dbg("udev->dev->kernel='%s'", udev->dev->kernel);
- /* use kernel name as default node name */
- strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name));
-
/* look for a matching rule to apply */
udev_rules_iter_init(rules);
while (1) {
}
}
- if (!name_set)
- info("no node name set, will use kernel name '%s'", udev->name);
+ if (!name_set) {
+ info("no node name set, will use kernel name '%s'", udev->dev->kernel);
+ strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name));
+ }
if (udev->tmp_node[0] != '\0') {
dbg("removing temporary device node");
break;
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) {
+ if (rule->name.operation == KEY_OP_ASSIGN ||
+ rule->name.operation == KEY_OP_ASSIGN_FINAL ||
+ rule->name.operation == KEY_OP_ADD ||
+ rule->symlink.operation == KEY_OP_ASSIGN ||
+ rule->symlink.operation == KEY_OP_ASSIGN_FINAL ||
+ rule->symlink.operation == KEY_OP_ADD ||
+ 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;
}
udev->ignore_device = 1;
return 0;
}
+ if (rule->ignore_remove) {
+ udev->ignore_remove = 1;
+ dbg("remove event should be ignored");
+ }
if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) {
struct name_entry *entry;