X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fudev%2Fudev-rules.c;h=d3b33e4bbf960d597687cfa3a051a426d49fe410;hp=a800ccdc9f3ab1527a45740a8e2380ce5de150f5;hb=b9c26b413497a0014ac2058a0ec04849a83df1ea;hpb=33502ffe2eb7b56cdd018a4fb6830d7828519fad diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index a800ccdc9..d3b33e4bb 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -31,18 +31,13 @@ #include #include "udev.h" +#include "path-util.h" +#include "conf-files.h" #define PREALLOC_TOKEN 2048 #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 { @@ -65,7 +60,8 @@ struct trie_node { struct udev_rules { struct udev *udev; - unsigned long long dirs_ts_usec[ELEMENTSOF(rules_dirs)]; + char **dirs; + unsigned long long *dirs_ts_usec; int resolve_names; /* every key in the rules file becomes a token */ @@ -79,7 +75,7 @@ struct udev_rules { size_t buf_max; unsigned int buf_count; - /* during rule parsing, strings are indexed to find duplicates */ + /* during rule parsing, strings are indexed and de-duplicated */ struct trie_node *trie_nodes; unsigned int trie_nodes_cur; unsigned int trie_nodes_max; @@ -749,7 +745,7 @@ static int import_file_into_properties(struct udev_device *dev, const char *file FILE *f; char line[UTIL_LINE_SIZE]; - f = fopen(filename, "r"); + f = fopen(filename, "re"); if (f == NULL) return -1; while (fgets(line, sizeof(line), f) != NULL) @@ -1188,7 +1184,9 @@ static int add_rule(struct udev_rules *rules, char *line, memset(&rule_tmp, 0x00, sizeof(struct rule_tmp)); rule_tmp.rules = rules; rule_tmp.rule.type = TK_RULE; - rule_tmp.rule.rule.filename_off = filename_off; + /* the offset in the rule is limited to unsigned short */ + if (filename_off < USHRT_MAX) + rule_tmp.rule.rule.filename_off = filename_off; rule_tmp.rule.rule.filename_line = lineno; linepos = line; @@ -1349,11 +1347,12 @@ static int add_rule(struct udev_rules *rules, char *line, }; unsigned int i; - for (i = 0; i < ELEMENTSOF(blacklist); i++) - if (streq(attr, blacklist[i])) { - log_error("invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno); + for (i = 0; i < ELEMENTSOF(blacklist); i++) { + if (!streq(attr, blacklist[i])) continue; - } + log_error("invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno); + goto invalid; + } if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) goto invalid; } @@ -1631,21 +1630,27 @@ invalid: return -1; } -static int parse_file(struct udev_rules *rules, const char *filename, unsigned short filename_off) +static int parse_file(struct udev_rules *rules, const char *filename) { FILE *f; unsigned int first_token; + unsigned int filename_off; char line[UTIL_LINE_SIZE]; int line_nr = 0; unsigned int i; - log_debug("reading '%s' as rules file\n", filename); + if (null_or_empty_path(filename)) { + log_debug("skip empty file: %s\n", filename); + return 0; + } + log_debug("read rules file: %s\n", filename); - f = fopen(filename, "r"); + f = fopen(filename, "re"); if (f == NULL) return -1; first_token = rules->token_cur; + filename_off = add_string(rules, filename); while (fgets(line, sizeof(line), f) != NULL) { char *key; @@ -1706,52 +1711,13 @@ 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 *file_list, const char *dirname, const char *suffix) -{ - DIR *dir; - struct dirent *dent; - char filename[UTIL_PATH_SIZE]; - - dir = opendir(dirname); - if (dir == NULL) { - log_debug("unable to open '%s': %m\n", dirname); - return -1; - } - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - if (dent->d_name[0] == '.') - continue; - - /* look for file matching with specified suffix */ - if (suffix != NULL) { - const char *ext; - - ext = strrchr(dent->d_name, '.'); - if (ext == NULL) - continue; - if (!streq(ext, suffix)) - continue; - } - util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL); - /* - * the basename is the key, the filename the value - * identical basenames from different directories override each other - * entries are sorted after basename - */ - udev_list_entry_add(file_list, dent->d_name, filename); - } - - closedir(dir); - return 0; -} - struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) { struct udev_rules *rules; struct udev_list file_list; - struct udev_list_entry *file_loop; struct token end_token; - unsigned int i; + char **files, **f; + int r; rules = calloc(1, sizeof(struct udev_rules)); if (rules == NULL) @@ -1791,41 +1757,37 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) memset(rules->trie_nodes, 0x00, sizeof(struct trie_node)); rules->trie_nodes_cur = 1; - 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)) { - const char *filename = udev_list_entry_get_value(file_loop); - unsigned int filename_off; - - 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_num(file_loop, filename_off); + rules->dirs = strv_new(SYSCONFDIR "/udev/rules.d", + "/run/udev/rules.d", + UDEVLIBEXECDIR "/rules.d", + NULL); + if (!rules->dirs) { + log_error("failed to build config directory array"); + return NULL; } + if (!path_strv_canonicalize(rules->dirs)) { + log_error("failed to canonicalize config directories\n"); + return NULL; + } + strv_uniq(rules->dirs); + r = conf_files_list_strv(&files, ".rules", (const char **)rules->dirs); + if (r < 0) { + log_error("failed to enumerate rules files: %s\n", strerror(-r)); + return NULL; + } + rules->dirs_ts_usec = calloc(strv_length(rules->dirs), sizeof(long long)); - /* parse all rules files */ - udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { - const char *filename = udev_list_entry_get_value(file_loop); - unsigned int filename_off = udev_list_entry_get_num(file_loop); - struct stat st; + /* + * The offset value in the rules strct is limited; add all + * rules file names to the beginning of the string buffer. + */ + STRV_FOREACH(f, files) + add_string(rules, *f); - if (stat(filename, &st) != 0) { - log_error("can not find '%s': %m\n", filename); - continue; - } - if (S_ISREG(st.st_mode) && st.st_size <= 0) { - log_debug("ignore empty '%s'\n", filename); - continue; - } - if (S_ISCHR(st.st_mode)) { - log_debug("ignore masked '%s'\n", filename); - continue; - } - parse_file(rules, filename, filename_off); - } - udev_list_cleanup(&file_list); + STRV_FOREACH(f, files) + parse_file(rules, *f); + + strv_free(files); memset(&end_token, 0x00, sizeof(struct token)); end_token.type = TK_END; @@ -1885,6 +1847,8 @@ struct udev_rules *udev_rules_unref(struct udev_rules *rules) free(rules->trie_nodes); free(rules->uids); free(rules->gids); + strv_free(rules->dirs); + free(rules->dirs_ts_usec); free(rules); return NULL; } @@ -1894,10 +1858,13 @@ bool udev_rules_check_timestamp(struct udev_rules *rules) unsigned int i; bool changed = false; - for (i = 0; i < ELEMENTSOF(rules_dirs); i++) { + if (rules == NULL) + goto out; + + for (i = 0; rules->dirs[i]; i++) { struct stat stats; - if (stat(rules_dirs[i], &stats) < 0) + if (stat(rules->dirs[i], &stats) < 0) continue; if (rules->dirs_ts_usec[i] == ts_usec(&stats.st_mtim)) @@ -1905,14 +1872,14 @@ bool udev_rules_check_timestamp(struct udev_rules *rules) /* first check */ if (rules->dirs_ts_usec[i] != 0) { - log_debug("reload - timestamp of '%s' changed\n", rules_dirs[i]); + 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); } - +out: return changed; } @@ -2092,7 +2059,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event 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) + strlen(TEST_PREFIX "/dev/"); + devlink = udev_list_entry_get_name(list_entry) + strlen("/dev/"); if (match_key(rules, cur, devlink) == 0) { match = true; break; @@ -2355,7 +2322,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event FILE *f; bool imported = false; - f = fopen("/proc/cmdline", "r"); + f = fopen("/proc/cmdline", "re"); if (f != NULL) { char cmdline[4096]; @@ -2566,7 +2533,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event log_debug("%i character(s) replaced\n", count); } if (major(udev_device_get_devnum(event->dev)) && - (!streq(name_str, udev_device_get_devnode(event->dev) + strlen(TEST_PREFIX "/dev/")))) { + (!streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/")))) { 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); @@ -2611,7 +2578,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event 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), TEST_PREFIX "/dev/", pos, NULL); + util_strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); while (isspace(next[1])) next++; @@ -2621,7 +2588,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event 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), TEST_PREFIX "/dev/", pos, NULL); + util_strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); } break; @@ -2640,7 +2607,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event log_debug("ATTR '%s' writing '%s' %s:%u\n", attr, value, &rules->buf[rule->rule.filename_off], rule->rule.filename_line); - f = fopen(attr, "w"); + f = fopen(attr, "we"); if (f != NULL) { if (fprintf(f, "%s", value) <= 0) log_error("error writing ATTR{%s}: %m\n", attr); @@ -2731,7 +2698,7 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules) /* 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), TEST_PREFIX "/dev/", + util_strscpyl(filename, sizeof(filename), "/dev/", &rules->buf[cur->key.value_off], NULL); if (stat(filename, &stats) != 0) goto next;