#define PREALLOC_STRBUF 32 * 1024
#define PREALLOC_TRIE 256
+/* configuration directories with last modification timestamp */
+static const char *rules_dirs[] = {
+ TEST_PREFIX UDEVLIBEXECDIR "/rules.d",
+ TEST_PREFIX "/run/udev/rules.d",
+ TEST_PREFIX SYSCONFDIR "/udev/rules.d",
+};
+
struct uid_gid {
unsigned int name_off;
union {
struct udev_rules {
struct udev *udev;
+ unsigned long long dirs_ts_usec[ELEMENTSOF(rules_dirs)];
int resolve_names;
/* every key in the rules file becomes a token */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
TK_A_ATTR, /* val, attr */
- TK_A_RUN, /* val, bool */
+ TK_A_RUN_BUILTIN, /* val, bool */
+ TK_A_RUN_PROGRAM, /* val, bool */
TK_A_GOTO, /* size_t */
TK_END,
[TK_A_NAME] = "A NAME",
[TK_A_DEVLINK] = "A DEVLINK",
[TK_A_ATTR] = "A ATTR",
- [TK_A_RUN] = "A RUN",
+ [TK_A_RUN_BUILTIN] = "A RUN_BUILTIN",
+ [TK_A_RUN_PROGRAM] = "A RUN_PROGRAM",
[TK_A_GOTO] = "A GOTO",
[TK_END] = "END",
case TK_A_OWNER:
case TK_A_GROUP:
case TK_A_MODE:
- case TK_A_RUN:
+ case TK_A_RUN_BUILTIN:
+ case TK_A_RUN_PROGRAM:
log_debug("%s %s '%s'(%s)\n",
token_str(type), operation_str(op), value, string_glob_str(glob));
break;
static int import_property_from_string(struct udev_device *dev, char *line)
{
- struct udev *udev = udev_device_get_udev(dev);
char *key;
char *val;
size_t len;
log_debug("updating devpath from '%s' to '%s'\n",
udev_device_get_devpath(dev), val);
- util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), val, NULL);
+ util_strscpyl(syspath, sizeof(syspath), "/sys", val, NULL);
udev_device_set_syspath(dev, syspath);
} else {
struct udev_list_entry *entry;
static int import_parent_into_properties(struct udev_device *dev, const char *filter)
{
- struct udev *udev = udev_device_get_udev(dev);
struct udev_device *dev_parent;
struct udev_list_entry *list_entry;
#define WAIT_LOOP_PER_SECOND 50
static int wait_for_file(struct udev_device *dev, const char *file, int timeout)
{
- struct udev *udev = udev_device_get_udev(dev);
char filepath[UTIL_PATH_SIZE];
char devicepath[UTIL_PATH_SIZE];
struct stat stats;
/* a relative path is a device attribute */
devicepath[0] = '\0';
if (file[0] != '/') {
- util_strscpyl(devicepath, sizeof(devicepath),
- udev_get_sys_path(udev), udev_device_get_devpath(dev), NULL);
+ util_strscpyl(devicepath, sizeof(devicepath), udev_device_get_syspath(dev), NULL);
util_strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL);
file = filepath;
}
}
/* extract possible KEY{attr} */
-static char *get_key_attribute(struct udev *udev, char *str)
+static const char *get_key_attribute(struct udev *udev, char *str)
{
char *pos;
char *attr;
case TK_A_STRING_ESCAPE_REPLACE:
case TK_A_DB_PERSIST:
break;
- case TK_A_RUN:
+ case TK_A_RUN_BUILTIN:
+ case TK_A_RUN_PROGRAM:
+ token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
token->key.value_off = add_string(rule_tmp->rules, value);
break;
case TK_A_INOTIFY_WATCH:
token->key.type = type;
token->key.op = op;
rule_tmp->token_cur++;
- if (rule_tmp->token_cur >= ARRAY_SIZE(rule_tmp->token)) {
+ if (rule_tmp->token_cur >= ELEMENTSOF(rule_tmp->token)) {
log_error("temporary rule array too small\n");
return -1;
}
const char *filename, unsigned int filename_off, unsigned int lineno)
{
char *linepos;
- char *attr;
+ const char *attr;
struct rule_tmp rule_tmp;
memset(&rule_tmp, 0x00, sizeof(struct rule_tmp));
};
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(blacklist); i++)
+ for (i = 0; i < ELEMENTSOF(blacklist); i++)
if (strcmp(attr, blacklist[i]) == 0) {
log_error("invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno);
continue;
log_error("IMPORT{} type missing, ignoring IMPORT %s:%u\n", filename, lineno);
continue;
}
- if (strstr(attr, "program")) {
+ if (strcmp(attr, "program") == 0) {
/* find known built-in command */
if (value[0] != '/') {
enum udev_builtin_cmd cmd;
}
}
rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
- } else if (strstr(attr, "builtin")) {
+ } else if (strcmp(attr, "builtin") == 0) {
enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
if (cmd < UDEV_BUILTIN_MAX)
rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
else
log_error("IMPORT{builtin}: '%s' unknown %s:%u\n", value, filename, lineno);
- } else if (strstr(attr, "file")) {
+ } else if (strcmp(attr, "file") == 0) {
rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
- } else if (strstr(attr, "db")) {
+ } else if (strcmp(attr, "db") == 0) {
rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL);
- } else if (strstr(attr, "cmdline")) {
+ } else if (strcmp(attr, "cmdline") == 0) {
rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL);
- } else if (strstr(attr, "parent")) {
+ } else if (strcmp(attr, "parent") == 0) {
rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
- }
+ } else
+ log_error("IMPORT{} unknown type, ignoring IMPORT %s:%u\n", filename, lineno);
continue;
}
continue;
}
- if (strcmp(key, "RUN") == 0) {
- if (strncmp(value, "socket:", 7) == 0)
- log_error("RUN+=\"socket:...\" support will be removed from a future udev release. "
- "Please remove it from: %s:%u and use libudev to subscribe to events.\n", filename, lineno);
- rule_add_key(&rule_tmp, TK_A_RUN, op, value, NULL);
+ if (strncmp(key, "RUN", sizeof("RUN")-1) == 0) {
+ attr = get_key_attribute(rules->udev, key + sizeof("RUN")-1);
+ if (attr == NULL)
+ attr = "program";
+
+ if (strcmp(attr, "builtin") == 0) {
+ enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
+
+ if (cmd < UDEV_BUILTIN_MAX)
+ rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
+ else
+ log_error("IMPORT{builtin}: '%s' unknown %s:%u\n", value, filename, lineno);
+ } else if (strcmp(attr, "program") == 0) {
+ enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX;
+
+ rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd);
+ } else {
+ log_error("RUN{} unknown type, ignoring RUN %s:%u\n", filename, lineno);
+ }
+
continue;
}
struct udev_list file_list;
struct udev_list_entry *file_loop;
struct token end_token;
- char **s;
+ unsigned int i;
rules = calloc(1, sizeof(struct udev_rules));
if (rules == NULL)
memset(rules->trie_nodes, 0x00, sizeof(struct trie_node));
rules->trie_nodes_cur = 1;
- for (udev_get_rules_path(udev, &s, NULL); *s != NULL; s++)
- add_matching_files(udev, &file_list, *s, ".rules");
+ for (i = 0; i < ELEMENTSOF(rules_dirs); i++)
+ add_matching_files(udev, &file_list, rules_dirs[i], ".rules");
/* add all filenames to the string buffer */
udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) {
return NULL;
}
+bool udev_rules_check_timestamp(struct udev_rules *rules)
+{
+ unsigned int i;
+ bool changed = false;
+
+ for (i = 0; i < ELEMENTSOF(rules_dirs); i++) {
+ struct stat stats;
+
+ if (stat(rules_dirs[i], &stats) < 0)
+ continue;
+
+ if (rules->dirs_ts_usec[i] == ts_usec(&stats.st_mtim))
+ continue;
+
+ /* first check */
+ if (rules->dirs_ts_usec[i] != 0) {
+ log_debug("reload - timestamp of '%s' changed\n", rules_dirs[i]);
+ changed = true;
+ }
+
+ /* update timestamp */
+ rules->dirs_ts_usec[i] = ts_usec(&stats.st_mtim);
+ }
+
+ return changed;
+}
+
static int match_key(struct udev_rules *rules, struct token *token, const char *val)
{
char *key_value = &rules->buf[token->key.value_off];
goto nomatch;
break;
case TK_M_DEVLINK: {
- size_t devlen = strlen(udev_get_dev_path(event->udev))+1;
struct udev_list_entry *list_entry;
bool match = false;
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) {
const char *devlink;
- devlink = &udev_list_entry_get_name(list_entry)[devlen];
+ devlink = udev_list_entry_get_name(list_entry) + strlen(TEST_PREFIX "/dev/");
if (match_key(rules, cur, devlink) == 0) {
match = true;
break;
if (count > 0)
log_debug("%i character(s) replaced\n", count);
}
- if (major(udev_device_get_devnum(event->dev))) {
- size_t devlen = strlen(udev_get_dev_path(event->udev))+1;
-
- if (strcmp(name_str, &udev_device_get_devnode(event->dev)[devlen]) != 0) {
- log_error("NAME=\"%s\" ignored, kernel device nodes "
- "can not be renamed; please fix it in %s:%u\n", name,
- &rules->buf[rule->rule.filename_off], rule->rule.filename_line);
- break;
- }
+ if (major(udev_device_get_devnum(event->dev)) &&
+ (strcmp(name_str, udev_device_get_devnode(event->dev) + strlen(TEST_PREFIX "/dev/")) != 0)) {
+ log_error("NAME=\"%s\" ignored, kernel device nodes "
+ "can not be renamed; please fix it in %s:%u\n", name,
+ &rules->buf[rule->rule.filename_off], rule->rule.filename_line);
+ break;
}
free(event->name);
event->name = strdup(name_str);
next[0] = '\0';
log_debug("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);
+ util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/dev/", pos, NULL);
udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
while (isspace(next[1]))
next++;
if (pos[0] != '\0') {
log_debug("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);
+ util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/dev/", pos, NULL);
udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
}
break;
}
break;
}
- case TK_A_RUN: {
+ case TK_A_RUN_BUILTIN:
+ case TK_A_RUN_PROGRAM: {
+ struct udev_list_entry *entry;
+
if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
udev_list_cleanup(&event->run_list);
log_debug("RUN '%s' %s:%u\n",
&rules->buf[cur->key.value_off],
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
- udev_list_entry_add(&event->run_list, &rules->buf[cur->key.value_off], NULL);
+ entry = udev_list_entry_add(&event->run_list, &rules->buf[cur->key.value_off], NULL);
+ udev_list_entry_set_num(entry, cur->key.builtin_cmd);
break;
}
case TK_A_GOTO:
/* we assure, that the permissions tokens are sorted before the static token */
if (mode == 0 && uid == 0 && gid == 0)
goto next;
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(rules->udev), "/",
+ util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/dev/",
&rules->buf[cur->key.value_off], NULL);
if (stat(filename, &stats) != 0)
goto next;