/*
- * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2003-2010 Kay Sievers <kay.sievers@vrfy.org>
* Copyright (C) 2008 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
*
* This program is free software: you can redistribute it and/or modify
#include <stddef.h>
#include <limits.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <fnmatch.h>
+#include <time.h>
#include "udev.h"
GL_SOMETHING, /* commonly used "?*" */
};
+enum string_subst_type {
+ SB_UNSET,
+ SB_NONE,
+ SB_FORMAT,
+ SB_SUBSYS,
+};
+
/* tokens of a rule are sorted/handled in this order */
enum token_type {
TK_UNSET,
TK_M_PROGRAM, /* val */
TK_M_IMPORT_FILE, /* val */
TK_M_IMPORT_PROG, /* val */
+ TK_M_IMPORT_DB, /* val */
TK_M_IMPORT_PARENT, /* val */
TK_M_RESULT, /* val */
TK_M_MAX,
- TK_A_IGNORE_DEVICE,
TK_A_STRING_ESCAPE_NONE,
TK_A_STRING_ESCAPE_REPLACE,
TK_A_INOTIFY_WATCH, /* int */
TK_A_ATTR, /* val, attr */
TK_A_RUN, /* val, bool */
TK_A_GOTO, /* size_t */
- TK_A_LAST_RULE,
TK_END,
};
union {
unsigned char type; /* same as in rule and key */
struct {
- unsigned char type;
- unsigned char flags;
+ enum token_type type:8;
+ unsigned int flags:8;
unsigned short token_count;
unsigned int label_off;
unsigned short filename_off;
unsigned short filename_line;
} rule;
struct {
- unsigned char type;
- unsigned char flags;
- unsigned char op;
- unsigned char glob;
+ enum token_type type:8;
+ enum operation_type op:8;
+ enum string_glob_type glob:8;
+ enum string_subst_type subst:4;
+ enum string_subst_type attrsubst:4;
unsigned int value_off;
union {
unsigned int attr_off;
- int ignore_error;
+ int devlink_unique;
+ int fail_on_error;
unsigned int rule_goto;
mode_t mode;
uid_t uid;
gid_t gid;
- int num_fake_part;
int devlink_prio;
int event_timeout;
int watch;
unsigned int token_cur;
};
-#ifdef DEBUG
+#ifdef ENABLE_DEBUG
static const char *operation_str(enum operation_type type)
{
static const char *operation_strs[] = {
[TK_M_PROGRAM] = "M PROGRAM",
[TK_M_IMPORT_FILE] = "M IMPORT_FILE",
[TK_M_IMPORT_PROG] = "M IMPORT_PROG",
- [TK_M_IMPORT_PARENT] = "M MPORT_PARENT",
+ [TK_M_IMPORT_DB] = "M IMPORT_DB",
+ [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT",
[TK_M_RESULT] = "M RESULT",
[TK_M_MAX] = "M MAX",
- [TK_A_IGNORE_DEVICE] = "A IGNORE_DEVICE",
[TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
[TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
[TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH",
[TK_A_ATTR] = "A ATTR",
[TK_A_RUN] = "A RUN",
[TK_A_GOTO] = "A GOTO",
- [TK_A_LAST_RULE] = "A LAST_RULE",
[TK_END] = "END",
};
case TK_M_PROGRAM:
case TK_M_IMPORT_FILE:
case TK_M_IMPORT_PROG:
+ case TK_M_IMPORT_DB:
case TK_M_IMPORT_PARENT:
case TK_M_RESULT:
case TK_A_NAME:
dbg(rules->udev, "%s %s '%s' '%s'(%s)\n",
token_str(type), operation_str(op), attr, value, string_glob_str(glob));
break;
- case TK_A_IGNORE_DEVICE:
case TK_A_STRING_ESCAPE_NONE:
case TK_A_STRING_ESCAPE_REPLACE:
- case TK_A_LAST_RULE:
case TK_A_IGNORE_REMOVE:
dbg(rules->udev, "%s\n", token_str(type));
break;
case TK_A_INOTIFY_WATCH:
dbg(rules->udev, "%s %u\n", token_str(type), token->key.watch);
break;
- case TK_A_NUM_FAKE_PART:
- dbg(rules->udev, "%s %u\n", token_str(type), token->key.num_fake_part);
- break;
case TK_A_DEVLINK_PRIO:
dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.devlink_prio);
break;
static inline const char *token_str(enum token_type type) { return NULL; }
static inline void dump_token(struct udev_rules *rules, struct token *token) {}
static inline void dump_rules(struct udev_rules *rules) {}
-#endif /* DEBUG */
+#endif /* ENABLE_DEBUG */
static int add_new_string(struct udev_rules *rules, const char *str, size_t bytes)
{
struct udev_list_entry *entry;
entry = udev_device_add_property(dev, key, val);
- /* store in db */
- udev_list_entry_set_flag(entry, 1);
+ /* store in db, skip private keys */
+ if (key[0] != '.')
+ udev_list_entry_set_flags(entry, 1);
}
return 0;
}
char *line;
envp = udev_device_get_properties_envp(dev);
- if (util_run_program(udev, program, envp, result, sizeof(result), &reslen) != 0)
+ if (util_run_program(udev, program, envp, result, sizeof(result), &reslen, NULL) != 0)
return -1;
line = result;
dbg(udev, "import key '%s=%s'\n", key, val);
entry = udev_device_add_property(dev, key, val);
- /* store in db */
- udev_list_entry_set_flag(entry, 1);
+ /* store in db, skip private keys */
+ if (key[0] != '.')
+ udev_list_entry_set_flags(entry, 1);
}
}
return 0;
dbg(udev, "will wait %i sec for '%s'\n", timeout, file);
while (--loop) {
+ const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND };
+
/* lookup file */
if (stat(file, &stats) == 0) {
info(udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1);
return -2;
}
info(udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND);
- usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
+ nanosleep(&duration, NULL);
}
info(udev, "waiting for '%s' failed\n", file);
return -1;
char *temp;
linepos = *line;
- if (linepos == NULL && linepos[0] == '\0')
+ if (linepos == NULL || linepos[0] == '\0')
return -1;
/* skip whitespace */
const char *value, const void *data)
{
struct token *token = &rule_tmp->token[rule_tmp->token_cur];
- const char *attr = data;
- enum string_glob_type glob;
+ const char *attr = NULL;
memset(token, 0x00, sizeof(struct token));
case TK_M_PROGRAM:
case TK_M_IMPORT_FILE:
case TK_M_IMPORT_PROG:
+ case TK_M_IMPORT_DB:
case TK_M_IMPORT_PARENT:
case TK_M_RESULT:
case TK_A_OWNER:
case TK_A_GROUP:
case TK_A_MODE:
case TK_A_NAME:
- case TK_A_DEVLINK:
case TK_A_GOTO:
token->key.value_off = add_string(rule_tmp->rules, value);
break;
case TK_M_ATTRS:
case TK_A_ATTR:
case TK_A_ENV:
+ attr = data;
token->key.value_off = add_string(rule_tmp->rules, value);
token->key.attr_off = add_string(rule_tmp->rules, attr);
break;
+ case TK_A_DEVLINK:
+ token->key.value_off = add_string(rule_tmp->rules, value);
+ token->key.devlink_unique = *(int *)data;
+ break;
case TK_M_TEST:
token->key.value_off = add_string(rule_tmp->rules, value);
if (data != NULL)
token->key.mode = *(mode_t *)data;
break;
- case TK_A_IGNORE_DEVICE:
case TK_A_STRING_ESCAPE_NONE:
case TK_A_STRING_ESCAPE_REPLACE:
case TK_A_IGNORE_REMOVE:
- case TK_A_LAST_RULE:
break;
case TK_A_RUN:
token->key.value_off = add_string(rule_tmp->rules, value);
- token->key.ignore_error = *(int *)data;
+ token->key.fail_on_error = *(int *)data;
break;
case TK_A_INOTIFY_WATCH:
case TK_A_NUM_FAKE_PART:
return -1;
}
- glob = GL_PLAIN;
if (value != NULL && type < TK_M_MAX) {
/* check if we need to split or call fnmatch() while matching rules */
+ enum string_glob_type glob;
int has_split;
int has_glob;
has_split = (strchr(value, '|') != NULL);
- has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL ||
- strchr(value, '[') != NULL || strchr(value, ']') != NULL);
+ has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL);
if (has_split && has_glob) {
glob = GL_SPLIT_GLOB;
} else if (has_split) {
glob = GL_SOMETHING;
else
glob = GL_GLOB;
+ } else {
+ glob = GL_PLAIN;
}
+ token->key.glob = glob;
+ }
+
+ if (value != NULL && type > TK_M_MAX) {
+ /* check if assigned value has substitution chars */
+ if (value[0] == '[')
+ token->key.subst = SB_SUBSYS;
+ else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL)
+ token->key.subst = SB_FORMAT;
+ else
+ token->key.subst = SB_NONE;
+ }
+
+ if (attr != NULL) {
+ /* check if property/attribut name has substitution chars */
+ if (attr[0] == '[')
+ token->key.attrsubst = SB_SUBSYS;
+ else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL)
+ token->key.attrsubst = SB_FORMAT;
+ else
+ token->key.attrsubst = SB_NONE;
}
token->key.type = type;
token->key.op = op;
- token->key.glob = glob;
rule_tmp->token_cur++;
if (rule_tmp->token_cur >= ARRAY_SIZE(rule_tmp->token)) {
err(rule_tmp->rules->udev, "temporary rule array too small\n");
for (i = 0; i < rule_tmp->token_cur; i++) {
enum token_type next_val = TK_UNSET;
- unsigned int next_idx;
+ unsigned int next_idx = 0;
unsigned int j;
/* find smallest value */
char *linepos;
char *attr;
struct rule_tmp rule_tmp;
+ bool bus_warn = false;
+ bool sysfs_warn = false;
+ bool id_warn = false;
memset(&rule_tmp, 0x00, sizeof(struct rule_tmp));
rule_tmp.rules = rules;
if (get_key(rules->udev, &linepos, &key, &op, &value) != 0)
break;
- if (strcasecmp(key, "ACTION") == 0) {
+ if (strcmp(key, "ACTION") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid ACTION operation\n");
goto invalid;
continue;
}
- if (strcasecmp(key, "DEVPATH") == 0) {
+ if (strcmp(key, "DEVPATH") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid DEVPATH operation\n");
goto invalid;
continue;
}
- if (strcasecmp(key, "KERNEL") == 0) {
+ if (strcmp(key, "KERNEL") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid KERNEL operation\n");
goto invalid;
continue;
}
- if (strcasecmp(key, "SUBSYSTEM") == 0) {
+ if (strcmp(key, "SUBSYSTEM") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid SUBSYSTEM operation\n");
goto invalid;
continue;
}
- if (strcasecmp(key, "DRIVER") == 0) {
+ if (strcmp(key, "DRIVER") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid DRIVER operation\n");
goto invalid;
continue;
}
- if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) {
+ if (strncmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) {
attr = get_key_attribute(rules->udev, key + sizeof("ATTR")-1);
if (attr == NULL) {
err(rules->udev, "error parsing ATTR attribute\n");
continue;
}
- if (strcasecmp(key, "KERNELS") == 0 ||
- strcasecmp(key, "ID") == 0) {
+ if (strcmp(key, "KERNELS") == 0) {
+ if (op > OP_MATCH_MAX) {
+ err(rules->udev, "invalid KERNELS operation\n");
+ goto invalid;
+ }
+ rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL);
+ continue;
+ }
+
+ if (strcmp(key, "ID") == 0) {
+ if (!id_warn) {
+ id_warn = true;
+ err(rules->udev, "ID= will be removed in a future udev version, "
+ "please use KERNEL= to match the event device, or KERNELS= "
+ "to match a parent device, in %s:%u\n", filename, lineno);
+ }
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid KERNELS operation\n");
goto invalid;
continue;
}
- if (strcasecmp(key, "SUBSYSTEMS") == 0 ||
- strcasecmp(key, "BUS") == 0) {
+ if (strcmp(key, "SUBSYSTEMS") == 0) {
+ if (op > OP_MATCH_MAX) {
+ err(rules->udev, "invalid SUBSYSTEMS operation\n");
+ goto invalid;
+ }
+ rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL);
+ continue;
+ }
+
+ if (strcmp(key, "BUS") == 0) {
+ if (!bus_warn) {
+ bus_warn = true;
+ err(rules->udev, "BUS= will be removed in a future udev version, "
+ "please use SUBSYSTEM= to match the event device, or SUBSYSTEMS= "
+ "to match a parent device, in %s:%u\n", filename, lineno);
+ }
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid SUBSYSTEMS operation\n");
goto invalid;
continue;
}
- if (strcasecmp(key, "DRIVERS") == 0) {
+ if (strcmp(key, "DRIVERS") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid DRIVERS operation\n");
goto invalid;
continue;
}
- if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 ||
- strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) {
+ if (strncmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid ATTRS operation\n");
goto invalid;
continue;
}
- if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) {
+ if (strncmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) {
+ if (!sysfs_warn) {
+ sysfs_warn = true;
+ err(rules->udev, "SYSFS{}= will be removed in a future udev version, "
+ "please use ATTR{}= to match the event device, or ATTRS{}= "
+ "to match a parent device, in %s:%u\n", filename, lineno);
+ }
+ if (op > OP_MATCH_MAX) {
+ err(rules->udev, "invalid ATTRS operation\n");
+ goto invalid;
+ }
+ attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1);
+ if (attr == NULL) {
+ err(rules->udev, "error parsing ATTRS attribute\n");
+ goto invalid;
+ }
+ rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
+ continue;
+ }
+
+ if (strncmp(key, "ENV{", sizeof("ENV{")-1) == 0) {
attr = get_key_attribute(rules->udev, key + sizeof("ENV")-1);
if (attr == NULL) {
err(rules->udev, "error parsing ENV attribute\n");
continue;
}
- if (strcasecmp(key, "PROGRAM") == 0) {
+ if (strcmp(key, "PROGRAM") == 0) {
rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL);
continue;
}
- if (strcasecmp(key, "RESULT") == 0) {
+ if (strcmp(key, "RESULT") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid RESULT operation\n");
goto invalid;
continue;
}
- if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) {
+ if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) {
attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1);
if (attr != NULL && strstr(attr, "program")) {
dbg(rules->udev, "IMPORT will be executed\n");
} else if (attr != NULL && strstr(attr, "file")) {
dbg(rules->udev, "IMPORT will be included as file\n");
rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
+ } else if (attr != NULL && strstr(attr, "db")) {
+ dbg(rules->udev, "IMPORT will include db values\n");
+ rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL);
} else if (attr != NULL && strstr(attr, "parent")) {
dbg(rules->udev, "IMPORT will include the parent values\n");
rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
/* allow programs in /lib/udev called without the path */
if (value[0] != '/')
- util_strscpyl(file, sizeof(file), UDEV_PREFIX "/lib/udev/", value, NULL);
+ util_strscpyl(file, sizeof(file), LIBEXECDIR "/", value, NULL);
else
util_strscpy(file, sizeof(file), value);
pos = strchr(file, ' ');
continue;
}
- if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) {
+ if (strncmp(key, "TEST", sizeof("TEST")-1) == 0) {
mode_t mode = 0;
if (op > OP_MATCH_MAX) {
continue;
}
- if (strncasecmp(key, "RUN", sizeof("RUN")-1) == 0) {
+ if (strncmp(key, "RUN", sizeof("RUN")-1) == 0) {
int flag = 0;
attr = get_key_attribute(rules->udev, key + sizeof("RUN")-1);
- if (attr != NULL && strstr(attr, "ignore_error"))
+ if (attr != NULL && strstr(attr, "fail_event_on_error"))
flag = 1;
rule_add_key(&rule_tmp, TK_A_RUN, op, value, &flag);
continue;
}
- if (strcasecmp(key, "WAIT_FOR") == 0 || strcasecmp(key, "WAIT_FOR_SYSFS") == 0) {
+ if (strcmp(key, "WAIT_FOR") == 0 || strcmp(key, "WAIT_FOR_SYSFS") == 0) {
rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL);
continue;
}
- if (strcasecmp(key, "LABEL") == 0) {
+ if (strcmp(key, "LABEL") == 0) {
rule_tmp.rule.rule.label_off = add_string(rules, value);
continue;
}
- if (strcasecmp(key, "GOTO") == 0) {
+ if (strcmp(key, "GOTO") == 0) {
rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL);
continue;
}
- if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) {
+ if (strncmp(key, "NAME", sizeof("NAME")-1) == 0) {
if (op < OP_MATCH_MAX) {
rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL);
} else {
if (value[0] == '\0')
info(rules->udev, "name empty, node creation suppressed\n");
+ else if (strcmp(value, "%k") == 0)
+ err(rules->udev, "NAME=\"%%k\" is superfluous and breaks "
+ "kernel supplied names, please remove it from %s:%u\n", filename, lineno);
rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL);
- attr = get_key_attribute(rules->udev, key + sizeof("NAME")-1);
- if (attr != NULL) {
- if (strstr(attr, "all_partitions") != NULL) {
- int num = DEFAULT_FAKE_PARTITIONS_COUNT;
-
- dbg(rules->udev, "creation of partition nodes requested\n");
- rule_add_key(&rule_tmp, TK_A_NUM_FAKE_PART, 0, NULL, &num);
- }
- if (strstr(attr, "ignore_remove") != NULL) {
- dbg(rules->udev, "remove event should be ignored\n");
- rule_add_key(&rule_tmp, TK_A_IGNORE_REMOVE, 0, NULL, NULL);
- }
- }
}
rule_tmp.rule.rule.flags = 1;
continue;
}
- if (strcasecmp(key, "SYMLINK") == 0) {
- if (op < OP_MATCH_MAX)
+ if (strncmp(key, "SYMLINK", sizeof("SYMLINK")-1) == 0) {
+ if (op < OP_MATCH_MAX) {
rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL);
- else
- rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL);
+ } else {
+ int flag = 0;
+
+ attr = get_key_attribute(rules->udev, key + sizeof("SYMLINK")-1);
+ if (attr != NULL && strstr(attr, "unique") != NULL)
+ flag = 1;
+ rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, &flag);
+ }
rule_tmp.rule.rule.flags = 1;
continue;
}
- if (strcasecmp(key, "OWNER") == 0) {
+ if (strcmp(key, "OWNER") == 0) {
uid_t uid;
char *endptr;
continue;
}
- if (strcasecmp(key, "GROUP") == 0) {
+ if (strcmp(key, "GROUP") == 0) {
gid_t gid;
char *endptr;
continue;
}
- if (strcasecmp(key, "MODE") == 0) {
+ if (strcmp(key, "MODE") == 0) {
mode_t mode;
char *endptr;
continue;
}
- if (strcasecmp(key, "OPTIONS") == 0) {
+ if (strcmp(key, "OPTIONS") == 0) {
const char *pos;
- if (strstr(value, "last_rule") != NULL) {
- dbg(rules->udev, "last rule to be applied\n");
- rule_add_key(&rule_tmp, TK_A_LAST_RULE, 0, NULL, NULL);
- }
- if (strstr(value, "ignore_device") != NULL) {
- dbg(rules->udev, "device should be ignored\n");
- rule_add_key(&rule_tmp, TK_A_IGNORE_DEVICE, 0, NULL, NULL);
- }
- if (strstr(value, "ignore_remove") != NULL) {
- dbg(rules->udev, "remove event should be ignored\n");
- rule_add_key(&rule_tmp, TK_A_IGNORE_REMOVE, 0, NULL, NULL);
- }
pos = strstr(value, "link_priority=");
if (pos != NULL) {
int prio = atoi(&pos[strlen("link_priority=")]);
int tout = atoi(&pos[strlen("event_timeout=")]);
rule_add_key(&rule_tmp, TK_A_EVENT_TIMEOUT, 0, NULL, &tout);
- dbg(rules->udev, "event timout=%i\n", tout);
+ dbg(rules->udev, "event timeout=%i\n", tout);
}
pos = strstr(value, "string_escape=");
if (pos != NULL) {
else if (strncmp(pos, "replace", strlen("replace")) == 0)
rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, 0, NULL, NULL);
}
- if (strstr(value, "all_partitions") != NULL) {
- int num = DEFAULT_FAKE_PARTITIONS_COUNT;
-
- rule_add_key(&rule_tmp, TK_A_NUM_FAKE_PART, 0, NULL, &num);
- dbg(rules->udev, "creation of partition nodes requested\n");
- }
pos = strstr(value, "nowatch");
if (pos != NULL) {
const int off = 0;
first_token = rules->token_cur;
- while(fgets(line, sizeof(line), f) != NULL) {
+ while (fgets(line, sizeof(line), f) != NULL) {
char *key;
size_t len;
while (line[len-2] == '\\') {
if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL)
break;
+ if (strlen(&line[len-2]) < 2)
+ break;
line_nr++;
len = strlen(line);
}
/* read dynamic/temporary rules */
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL);
- if (stat(filename, &statbuf) != 0) {
- util_create_path(udev, filename);
- udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755);
- mkdir(filename, 0755);
- udev_selinux_resetfscreatecon(udev);
- }
udev_list_init(&sort_list);
add_matching_files(udev, &sort_list, filename, ".rules");
/* read default rules */
- add_matching_files(udev, &sort_list, UDEV_PREFIX "/lib/udev/rules.d", ".rules");
+ add_matching_files(udev, &sort_list, LIBEXECDIR "/rules.d", ".rules");
/* sort all rules files by basename into list of files */
udev_list_entry_foreach_safe(sort_loop, sort_tmp, udev_list_get_entry(&sort_list)) {
filename_off = add_string(rules, filename);
/* the offset in the rule is limited to unsigned short */
if (filename_off < USHRT_MAX)
- udev_list_entry_set_flag(file_loop, filename_off);
+ udev_list_entry_set_flags(file_loop, filename_off);
}
/* parse list of files */
udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
const char *filename = udev_list_entry_get_name(file_loop);
- unsigned int filename_off = udev_list_entry_get_flag(file_loop);
+ unsigned int filename_off = udev_list_entry_get_flags(file_loop);
if (stat(filename, &statbuf) == 0 && statbuf.st_size > 0)
parse_file(rules, filename, filename_off);
else
- info(udev, "can not read '%s'\n", filename);
+ err(udev, "can not read '%s'\n", filename);
udev_list_entry_delete(file_loop);
}
static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur)
{
- const char *key_name = &rules->buf[cur->key.attr_off];
- const char *key_value = &rules->buf[cur->key.value_off];
- char value[UTIL_NAME_SIZE];
+ const char *name;
+ char nbuf[UTIL_NAME_SIZE];
+ const char *value;
+ char vbuf[UTIL_NAME_SIZE];
size_t len;
- value[0] = '\0';
- if (key_name[0] == '[') {
- char attr[UTIL_PATH_SIZE];
-
- util_strscpy(attr, sizeof(attr), key_name);
- util_resolve_subsys_kernel(event->udev, attr, value, sizeof(value), 1);
- }
- if (value[0] == '\0') {
- const char *val;
-
- val = udev_device_get_sysattr_value(dev, key_name);
- if (val == NULL)
+ name = &rules->buf[cur->key.attr_off];
+ switch (cur->key.attrsubst) {
+ case SB_FORMAT:
+ udev_event_apply_format(event, name, nbuf, sizeof(nbuf));
+ name = nbuf;
+ /* fall through */
+ case SB_NONE:
+ value = udev_device_get_sysattr_value(dev, name);
+ if (value == NULL)
+ return -1;
+ break;
+ case SB_SUBSYS:
+ if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0)
return -1;
- util_strscpy(value, sizeof(value), val);
+ value = vbuf;
+ break;
+ default:
+ return -1;
}
- /* strip trailing whitespace of value, if not asked to match for it */
- len = strlen(key_value);
- if (len > 0 && !isspace(key_value[len-1])) {
- len = strlen(value);
- while (len > 0 && isspace(value[--len]))
- value[len] = '\0';
- dbg(rules->udev, "removed trailing whitespace from '%s'\n", value);
+ /* remove trailing whitespace, if not asked to match for it */
+ len = strlen(value);
+ if (len > 0 && isspace(value[len-1])) {
+ const char *key_value;
+ size_t klen;
+
+ key_value = &rules->buf[cur->key.value_off];
+ klen = strlen(key_value);
+ if (klen > 0 && !isspace(key_value[klen-1])) {
+ if (value != vbuf) {
+ util_strscpy(vbuf, sizeof(vbuf), value);
+ value = vbuf;
+ }
+ while (len > 0 && isspace(vbuf[--len]))
+ vbuf[len] = '\0';
+ dbg(rules->udev, "removed trailing whitespace from '%s'\n", value);
+ }
}
+
return match_key(rules, cur, value);
}
struct token *cur;
struct token *rule;
enum escape_type esc = ESCAPE_UNSET;
- int can_set_name;
+ bool can_set_name;
if (rules->tokens == NULL)
return -1;
- can_set_name = ((strcmp(udev_device_get_action(event->dev), "add") == 0 ||
- strcmp(udev_device_get_action(event->dev), "change") == 0) &&
+ can_set_name = ((strcmp(udev_device_get_action(event->dev), "remove") != 0) &&
(major(udev_device_get_devnum(event->dev)) > 0 ||
strcmp(udev_device_get_subsystem(event->dev), "net") == 0));
program,
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
- if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL) != 0) {
+ if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL, NULL) != 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
goto nomatch;
break;
}
+ case TK_M_IMPORT_DB:
+ {
+ const char *key = &rules->buf[cur->key.value_off];
+ const char *value;
+
+ value = udev_device_get_property_value(event->dev_db, key);
+ if (value != NULL) {
+ struct udev_list_entry *entry;
+
+ entry = udev_device_add_property(event->dev, key, value);
+ udev_list_entry_set_flags(entry, 1);
+ } else {
+ if (cur->key.op != OP_NOMATCH)
+ goto nomatch;
+ }
+ break;
+ }
case TK_M_IMPORT_PARENT:
{
char import[UTIL_PATH_SIZE];
if (match_key(rules, cur, event->program_result) != 0)
goto nomatch;
break;
-
- case TK_A_IGNORE_DEVICE:
- event->ignore_device = 1;
- return 0;
- break;
case TK_A_STRING_ESCAPE_NONE:
esc = ESCAPE_NONE;
break;
case TK_A_STRING_ESCAPE_REPLACE:
esc = ESCAPE_REPLACE;
break;
- case TK_A_NUM_FAKE_PART:
- if (strcmp(udev_device_get_subsystem(event->dev), "block") != 0)
- break;
- if (udev_device_get_sysnum(event->dev) != NULL)
- break;
- udev_device_set_num_fake_partitions(event->dev, cur->key.num_fake_part);
- break;
case TK_A_INOTIFY_WATCH:
event->inotify_watch = cur->key.watch;
break;
udev_event_apply_format(event, value, temp_value, sizeof(temp_value));
entry = udev_device_add_property(event->dev, name, temp_value);
- /* store in db */
- udev_list_entry_set_flag(entry, 1);
+ /* store in db, skip private keys */
+ if (name[0] != '.')
+ udev_list_entry_set_flags(entry, 1);
} else {
udev_device_add_property(event->dev, name, NULL);
}
while (isspace(pos[0]))
pos++;
next = strchr(pos, ' ');
- while (next) {
+ while (next != NULL) {
next[0] = '\0';
- info(event->udev, "LINK '%s' %s:%u\n",
- pos,
- &rules->buf[rule->rule.filename_off],
- rule->rule.filename_line);
+ info(event->udev, "LINK '%s' %s:%u\n", pos,
+ &rules->buf[rule->rule.filename_off], rule->rule.filename_line);
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL);
- udev_device_add_devlink(event->dev, filename);
+ udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
while (isspace(next[1]))
next++;
pos = &next[1];
next = strchr(pos, ' ');
}
if (pos[0] != '\0') {
- info(event->udev, "LINK '%s' %s:%u\n",
- pos,
- &rules->buf[rule->rule.filename_off],
- rule->rule.filename_line);
+ info(event->udev, "LINK '%s' %s:%u\n", pos,
+ &rules->buf[rule->rule.filename_off], rule->rule.filename_line);
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL);
- udev_device_add_devlink(event->dev, filename);
+ udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
}
}
break;
case TK_A_EVENT_TIMEOUT:
udev_device_set_event_timeout(event->dev, cur->key.event_timeout);
break;
- case TK_A_IGNORE_REMOVE:
- udev_device_set_ignore_remove(event->dev, 1);
- break;
case TK_A_ATTR:
{
const char *key_name = &rules->buf[cur->key.attr_off];
rule->rule.filename_line);
list_entry = udev_list_entry_add(event->udev, &event->run_list,
&rules->buf[cur->key.value_off], NULL, 1, 0);
- if (cur->key.ignore_error)
- udev_list_entry_set_flag(list_entry, 1);
+ if (cur->key.fail_on_error)
+ udev_list_entry_set_flags(list_entry, 1);
break;
}
case TK_A_GOTO:
break;
cur = &rules->tokens[cur->key.rule_goto];
continue;
- case TK_A_LAST_RULE:
case TK_END:
return 0;