TK_M_PROGRAM, /* val */
TK_M_IMPORT_FILE, /* val */
TK_M_IMPORT_PROG, /* val */
+ TK_M_IMPORT_BUILTIN, /* val */
TK_M_IMPORT_DB, /* val */
TK_M_IMPORT_CMDLINE, /* val */
TK_M_IMPORT_PARENT, /* val */
union {
unsigned int attr_off;
int devlink_unique;
- int fail_on_error;
unsigned int rule_goto;
mode_t mode;
uid_t uid;
int devlink_prio;
int event_timeout;
int watch;
+ enum udev_builtin_cmd builtin_cmd;
};
} key;
};
[TK_M_PROGRAM] = "M PROGRAM",
[TK_M_IMPORT_FILE] = "M IMPORT_FILE",
[TK_M_IMPORT_PROG] = "M IMPORT_PROG",
+ [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN",
[TK_M_IMPORT_DB] = "M IMPORT_DB",
[TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE",
[TK_M_IMPORT_PARENT] = "M IMPORT_PARENT",
dbg(rules->udev, "%s %s '%s'(%s)\n",
token_str(type), operation_str(op), value, string_glob_str(glob));
break;
+ case TK_M_IMPORT_BUILTIN:
+ dbg(rules->udev, "%s %i '%s'\n", token_str(type), token->key.builtin_cmd, value);
+ break;
case TK_M_ATTR:
case TK_M_ATTRS:
case TK_M_ENV:
case TK_A_TAG:
token->key.value_off = add_string(rule_tmp->rules, value);
break;
+ case TK_M_IMPORT_BUILTIN:
+ token->key.value_off = add_string(rule_tmp->rules, value);
+ token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
+ break;
case TK_M_ENV:
case TK_M_ATTR:
case TK_M_ATTRS:
break;
case TK_A_RUN:
token->key.value_off = add_string(rule_tmp->rules, value);
- token->key.fail_on_error = *(int *)data;
break;
case TK_A_INOTIFY_WATCH:
case TK_A_DEVLINK_PRIO:
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;
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;
- }
- rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL);
- continue;
- }
-
if (strcmp(key, "SUBSYSTEMS") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid SUBSYSTEMS operation\n");
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;
- }
- rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL);
- continue;
- }
-
if (strcmp(key, "DRIVERS") == 0) {
if (op > OP_MATCH_MAX) {
err(rules->udev, "invalid DRIVERS operation\n");
continue;
}
- 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) {
if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) {
attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1);
- if (attr != NULL && strstr(attr, "program")) {
+ if (attr == NULL) {
+ err(rules->udev, "IMPORT{} type missing, ignoring IMPORT %s:%u\n", filename, lineno);
+ continue;
+ }
+ if (strstr(attr, "program")) {
+ /* find known built-in command */
+ if (value[0] != '/') {
+ enum udev_builtin_cmd cmd;
+
+ cmd = udev_builtin_lookup(value);
+ if (cmd < UDEV_BUILTIN_MAX) {
+ info(rules->udev, "IMPORT found builtin '%s', replacing %s:%u\n",
+ value, filename, lineno);
+ rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
+ continue;
+ }
+ }
dbg(rules->udev, "IMPORT will be executed\n");
rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
- } else if (attr != NULL && strstr(attr, "file")) {
+ } else if (strstr(attr, "builtin")) {
+ enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
+
+ dbg(rules->udev, "IMPORT execute builtin\n");
+ if (cmd < UDEV_BUILTIN_MAX)
+ rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
+ else
+ err(rules->udev, "IMPORT{builtin}: '%s' unknown %s:%u\n", value, filename, lineno);
+ } else if (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")) {
+ } else if (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, "cmdline")) {
+ } else if (strstr(attr, "cmdline")) {
dbg(rules->udev, "IMPORT will include db values\n");
rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL);
- } else if (attr != NULL && strstr(attr, "parent")) {
+ } else if (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);
- } else {
- /* figure it out if it is executable */
- char file[UTIL_PATH_SIZE];
- char *pos;
- struct stat statbuf;
-
- /* allow programs in /lib/udev called without the path */
- if (value[0] != '/')
- util_strscpyl(file, sizeof(file), LIBEXECDIR "/", value, NULL);
- else
- util_strscpy(file, sizeof(file), value);
- pos = strchr(file, ' ');
- if (pos)
- pos[0] = '\0';
- dbg(rules->udev, "IMPORT auto mode for '%s'\n", file);
- if (stat(file, &statbuf) == 0 && (statbuf.st_mode & S_IXUSR)) {
- dbg(rules->udev, "IMPORT will be executed (autotype)\n");
- rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
- } else {
- dbg(rules->udev, "IMPORT will be included as file (autotype)\n");
- rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
- }
}
continue;
}
continue;
}
- 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, "fail_event_on_error"))
- flag = 1;
- rule_add_key(&rule_tmp, TK_A_RUN, op, value, &flag);
+ if (strcmp(key, "RUN") == 0) {
+ rule_add_key(&rule_tmp, TK_A_RUN, op, value, NULL);
continue;
}
continue;
}
+
err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno);
+ goto invalid;
}
/* add rule token */
return 0;
}
-static int add_matching_files(struct udev *udev, struct udev_list_node *file_list, const char *dirname, const char *suffix)
+static int add_matching_files(struct udev *udev, struct udev_list *file_list, const char *dirname, const char *suffix)
{
DIR *dir;
struct dirent *dent;
* identical basenames from different directories overwrite each other
* entries are sorted after basename
*/
- udev_list_entry_add(udev, file_list, dent->d_name, filename, UDEV_LIST_UNIQUE|UDEV_LIST_SORT);
+ udev_list_entry_add(file_list, dent->d_name, filename);
}
closedir(dir);
struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
{
struct udev_rules *rules;
- struct udev_list_node file_list;
+ struct udev_list file_list;
struct udev_list_entry *file_loop;
struct token end_token;
return NULL;
rules->udev = udev;
rules->resolve_names = resolve_names;
- udev_list_init(&file_list);
+ udev_list_init(udev, &file_list, true);
/* init token array and string buffer */
rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token));
if (udev_get_rules_path(udev) == NULL) {
char filename[UTIL_PATH_SIZE];
- /* /lib/udev -- default/package rules */
+ /* /usr/lib/udev -- system rules */
add_matching_files(udev, &file_list, LIBEXECDIR "/rules.d", ".rules");
- /* /etc/udev -- system-specific/user/admin rules */
+ /* /etc/udev -- local administration rules */
add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".rules");
- /* /run/udev -- throw-away/temporary rules */
+ /* /run/udev -- runtime rules */
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
add_matching_files(udev, &file_list, filename, ".rules");
} else {
}
parse_file(rules, filename, filename_off);
}
- udev_list_cleanup_entries(udev, &file_list);
+ udev_list_cleanup(&file_list);
memset(&end_token, 0x00, sizeof(struct token));
end_token.type = TK_END;
util_remove_trailing_chars(result, '\n');
if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) {
- count = udev_util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
+ count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
if (count > 0)
info(event->udev, "%i character(s) replaced\n" , count);
}
goto nomatch;
break;
}
+ case TK_M_IMPORT_BUILTIN: {
+ char command[UTIL_PATH_SIZE];
+
+ if (udev_builtin_run_once(cur->key.builtin_cmd)) {
+ /* check if we ran already */
+ if (event->builtin_run & (1 << cur->key.builtin_cmd)) {
+ info(event->udev, "IMPORT builtin skip '%s' %s:%u\n",
+ udev_builtin_name(cur->key.builtin_cmd),
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+ /* return the result from earlier run */
+ if (event->builtin_ret & (1 << cur->key.builtin_cmd))
+ if (cur->key.op != OP_NOMATCH)
+ goto nomatch;
+ break;
+ }
+ /* mark as ran */
+ event->builtin_run |= (1 << cur->key.builtin_cmd);
+ }
+
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], command, sizeof(command));
+ info(event->udev, "IMPORT builtin '%s' %s:%u\n",
+ udev_builtin_name(cur->key.builtin_cmd),
+ &rules->buf[rule->rule.filename_off],
+ rule->rule.filename_line);
+
+ if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) {
+ /* remember failure */
+ info(rules->udev, "IMPORT builtin '%s' returned non-zero\n",
+ udev_builtin_name(cur->key.builtin_cmd));
+ event->builtin_ret |= (1 << cur->key.builtin_cmd);
+ if (cur->key.op != OP_NOMATCH)
+ goto nomatch;
+ }
+ break;
+ }
case TK_M_IMPORT_DB: {
const char *key = &rules->buf[cur->key.value_off];
const char *value;
rule->rule.filename_line);
break;
case TK_A_STATIC_NODE:
+ event->static_node = true;
break;
case TK_A_ENV: {
const char *name = &rules->buf[cur->key.attr_off];
event->name_final = true;
udev_event_apply_format(event, name, name_str, sizeof(name_str));
if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) {
- count = udev_util_replace_chars(name_str, "/");
+ count = util_replace_chars(name_str, "/");
if (count > 0)
info(event->udev, "%i character(s) replaced\n", count);
}
/* allow multiple symlinks separated by spaces */
udev_event_apply_format(event, &rules->buf[cur->key.value_off], temp, sizeof(temp));
if (esc == ESCAPE_UNSET)
- count = udev_util_replace_chars(temp, "/ ");
+ count = util_replace_chars(temp, "/ ");
else if (esc == ESCAPE_REPLACE)
- count = udev_util_replace_chars(temp, "/");
+ count = util_replace_chars(temp, "/");
if (count > 0)
info(event->udev, "%i character(s) replaced\n" , count);
dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp);
break;
}
case TK_A_RUN: {
- struct udev_list_entry *list_entry;
-
if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
- udev_list_cleanup_entries(event->udev, &event->run_list);
+ udev_list_cleanup(&event->run_list);
info(event->udev, "RUN '%s' %s:%u\n",
&rules->buf[cur->key.value_off],
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
- list_entry = udev_list_entry_add(event->udev, &event->run_list,
- &rules->buf[cur->key.value_off], NULL, UDEV_LIST_UNIQUE);
- if (cur->key.fail_on_error)
- udev_list_entry_set_num(list_entry, true);
+ udev_list_entry_add(&event->run_list, &rules->buf[cur->key.value_off], NULL);
break;
}
case TK_A_GOTO:
goto next;
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
goto next;
-
- if (mode == 0 && gid > 0)
- mode = 0660;
- if (mode != (stats.st_mode & 0777)) {
+ if (mode == 0) {
+ if (gid > 0)
+ mode = 0660;
+ else
+ mode = 0600;
+ }
+ /* set sticky bit, so we do not remove the node on module unload */
+ mode |= 01000;
+ if (mode != (stats.st_mode & 01777)) {
chmod(filename, mode);
info(rules->udev, "chmod '%s' %#o\n", filename, mode);
}