X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudev-rules.c;h=75636d9bd6ee52685541356a1f5100d8078c6dee;hp=48395e788738ee2028b0c17c09dda302d11a211c;hb=1f5a5100f3bdfdaf9ef71b29931574b678ff0d82;hpb=1eb037a7bf22b6e4f3a778d0fbbe16675b6df712 diff --git a/udev/udev-rules.c b/udev/udev-rules.c index 48395e788..75636d9bd 100644 --- a/udev/udev-rules.c +++ b/udev/udev-rules.c @@ -144,6 +144,7 @@ enum token_type { 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 */ @@ -197,7 +198,6 @@ struct token { union { unsigned int attr_off; int devlink_unique; - int fail_on_error; unsigned int rule_goto; mode_t mode; uid_t uid; @@ -205,6 +205,7 @@ struct token { int devlink_prio; int event_timeout; int watch; + enum udev_builtin_cmd builtin_cmd; }; } key; }; @@ -280,6 +281,7 @@ static const char *token_str(enum token_type type) [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", @@ -361,6 +363,9 @@ static void dump_token(struct udev_rules *rules, struct token *token) 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\n", token_str(type), token->key.builtin_cmd); + break; case TK_M_ATTR: case TK_M_ATTRS: case TK_M_ENV: @@ -1033,6 +1038,9 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, case TK_A_TAG: token->key.value_off = add_string(rule_tmp->rules, value); break; + case TK_M_IMPORT_BUILTIN: + token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; + break; case TK_M_ENV: case TK_M_ATTR: case TK_M_ATTRS: @@ -1057,7 +1065,6 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, 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: @@ -1182,9 +1189,6 @@ static int add_rule(struct udev_rules *rules, char *line, 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; @@ -1278,21 +1282,6 @@ static int add_rule(struct udev_rules *rules, char *line, 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"); @@ -1302,21 +1291,6 @@ static int add_rule(struct udev_rules *rules, char *line, 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"); @@ -1355,26 +1329,6 @@ static int add_rule(struct udev_rules *rules, char *line, 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) { @@ -1385,6 +1339,26 @@ static int add_rule(struct udev_rules *rules, char *line, if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) goto invalid; } else { + static const char *blacklist[] = { + "ACTION", + "SUBSYSTEM", + "DEVTYPE", + "MAJOR", + "MINOR", + "DRIVER", + "IFINDEX", + "DEVNAME", + "DEVLINKS", + "DEVPATH", + "TAGS", + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(blacklist); i++) + if (strcmp(attr, blacklist[i]) == 0) { + err(rules->udev, "invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno); + continue; + } if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) goto invalid; } @@ -1415,43 +1389,50 @@ static int add_rule(struct udev_rules *rules, char *line, 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] != '/') { + char file[UTIL_PATH_SIZE]; + char *pos; + enum udev_builtin_cmd cmd; + + util_strscpy(file, sizeof(file), value); + pos = strchr(file, ' '); + if (pos) + pos[0] = '\0'; + cmd = udev_builtin_lookup(file); + if (cmd < UDEV_BUILTIN_MAX) { + info(rules->udev, "IMPORT found builtin '%s', replacing %s:%u\n", file, filename, lineno); + rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, NULL, &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, NULL, &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; } @@ -1473,13 +1454,8 @@ static int add_rule(struct udev_rules *rules, char *line, 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; } @@ -1729,7 +1705,7 @@ static int parse_file(struct udev_rules *rules, const char *filename, unsigned s 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; @@ -1763,7 +1739,7 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis * 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); @@ -1773,7 +1749,7 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis 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; @@ -1782,7 +1758,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) 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)); @@ -1865,7 +1841,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) } 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; @@ -2295,7 +2271,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event 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); } @@ -2329,6 +2305,35 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event goto nomatch; break; } + case TK_M_IMPORT_BUILTIN: { + /* 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); + 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, 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; @@ -2507,6 +2512,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event 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]; @@ -2528,10 +2534,20 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event } case TK_A_TAG: { char tag[UTIL_PATH_SIZE]; + const char *p; udev_event_apply_format(event, &rules->buf[cur->key.value_off], tag, sizeof(tag)); if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) udev_device_cleanup_tags_list(event->dev); + for (p = tag; *p != '\0'; p++) { + if ((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + *p == '-' || *p == '_') + continue; + err(event->udev, "ignoring invalid tag name '%s'\n", tag); + break; + } udev_device_add_tag(event->dev, tag); break; } @@ -2546,7 +2562,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event 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); } @@ -2576,9 +2592,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event /* 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); @@ -2630,18 +2646,13 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event 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: @@ -2709,6 +2720,7 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules) case TK_A_STATIC_NODE: { char filename[UTIL_PATH_SIZE]; struct stat stats; + /* we assure, that the permissions tokens are sorted before the static token */ if (mode == 0 && uid == 0 && gid == 0) goto next; @@ -2718,14 +2730,24 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules) goto next; if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) goto next; - if (mode != 0 && 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); } + if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { chown(filename, uid, gid); info(rules->udev, "chown '%s' %u %u\n", filename, uid, gid); } + utimensat(AT_FDCWD, filename, NULL, 0); break; }