From: Kay Sievers Date: Tue, 27 Dec 2011 02:49:43 +0000 (+0100) Subject: move rules dirs to udev context; replace inotify with time-controlled stat() X-Git-Tag: 176~29 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=4f1795cca7060030461c8f3429e5a62f143f74a9 move rules dirs to udev context; replace inotify with time-controlled stat() --- diff --git a/NEWS b/NEWS index c01841003..b052257c3 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ udev 175 ======== +A writable /run directory (tmpfs) is required now for a fully functional +udev. + The default 'configure' install locations have changed. Packages for systems with the historic / vs. /usr split need to be adapted, otherwise udev will be installed in /usr and not work properly. Example configuration options are diff --git a/libudev/libudev-private.h b/libudev/libudev-private.h index ffc82cbc6..fed586398 100644 --- a/libudev/libudev-private.h +++ b/libudev/libudev-private.h @@ -62,9 +62,7 @@ void udev_log(struct udev *udev, int priority, const char *file, int line, const char *fn, const char *format, ...) __attribute__((format(printf, 6, 7))); -const char *udev_get_rules_path(struct udev *udev); -const char *udev_get_run_config_path(struct udev *udev); -const char *udev_set_run_path(struct udev *udev, const char *path); +int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *ts_usec[]); struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value); struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev); @@ -195,6 +193,7 @@ uid_t util_lookup_user(struct udev *udev, const char *user); gid_t util_lookup_group(struct udev *udev, const char *group); int util_resolve_subsys_kernel(struct udev *udev, const char *string, char *result, size_t maxsize, int read_value); +unsigned long long ts_usec(const struct timespec *ts); unsigned long long now_usec(void); /* libudev-selinux-private.c */ diff --git a/libudev/libudev-util.c b/libudev/libudev-util.c index e08349e0f..5369fe734 100644 --- a/libudev/libudev-util.c +++ b/libudev/libudev-util.c @@ -552,12 +552,17 @@ uint64_t util_string_bloom64(const char *str) #define USEC_PER_SEC 1000000ULL #define NSEC_PER_USEC 1000ULL +unsigned long long ts_usec(const struct timespec *ts) +{ + return (unsigned long long) ts->tv_sec * USEC_PER_SEC + + (unsigned long long) ts->tv_nsec / NSEC_PER_USEC; +} + unsigned long long now_usec(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return 0; - return (unsigned long long) ts.tv_sec * USEC_PER_SEC + - (unsigned long long) ts.tv_nsec / NSEC_PER_USEC; + return ts_usec(&ts); } diff --git a/libudev/libudev.c b/libudev/libudev.c index 3b3a8e9af..93f2d8f1b 100644 --- a/libudev/libudev.c +++ b/libudev/libudev.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "libudev.h" #include "libudev-private.h" @@ -42,8 +43,9 @@ struct udev { void *userdata; char *sys_path; char *dev_path; - char *rules_path; - char *run_config_path; + char *rules_path[4]; + unsigned long long rules_path_ts[4]; + int rules_path_count; char *run_path; struct udev_list properties_list; int log_priority; @@ -218,11 +220,12 @@ UDEV_EXPORT struct udev *udev_new(void) continue; } if (strcmp(key, "udev_run") == 0) { - set_value(&udev->run_config_path, val); + set_value(&udev->run_path, val); continue; } if (strcmp(key, "udev_rules") == 0) { - set_value(&udev->rules_path, val); + set_value(&udev->rules_path[0], val); + udev->rules_path_count = 1; continue; } } @@ -255,18 +258,35 @@ UDEV_EXPORT struct udev *udev_new(void) if (set_value(&udev->sys_path, "/sys") == NULL) goto err; - if (udev->run_config_path == NULL) - if (set_value(&udev->run_config_path, "/run/udev") == NULL) + if (udev->run_path == NULL) + if (set_value(&udev->run_path, "/run/udev") == NULL) + goto err; + + if (udev->rules_path[0] == NULL) { + /* /usr/lib/udev -- system rules */ + udev->rules_path[0] = strdup(LIBEXECDIR "/rules.d"); + if (!udev->rules_path[0]) + goto err; + + /* /etc/udev -- local administration rules */ + udev->rules_path[1] = strdup(SYSCONFDIR "/udev/rules.d"); + if (!udev->rules_path[1]) goto err; + /* /run/udev -- runtime rules */ + if (asprintf(&udev->rules_path[2], "%s/rules.d", udev->run_path) < 0) + goto err; + + udev->rules_path_count = 3; + } + dbg(udev, "context %p created\n", udev); dbg(udev, "log_priority=%d\n", udev->log_priority); dbg(udev, "config_file='%s'\n", config_file); dbg(udev, "dev_path='%s'\n", udev->dev_path); dbg(udev, "sys_path='%s'\n", udev->sys_path); - dbg(udev, "run_path='%s'\n", udev->run_config_path); - if (udev->rules_path != NULL) - dbg(udev, "rules_path='%s'\n", udev->rules_path); + dbg(udev, "run_path='%s'\n", udev->run_path); + dbg(udev, "rules_path='%s':'%s':'%s'\n", udev->rules_path[0], udev->rules_path[1], udev->rules_path[2]); free(config_file); return udev; err: @@ -310,9 +330,10 @@ UDEV_EXPORT void udev_unref(struct udev *udev) udev_list_cleanup(&udev->properties_list); free(udev->dev_path); free(udev->sys_path); - free(udev->rules_path); + free(udev->rules_path[0]); + free(udev->rules_path[1]); + free(udev->rules_path[2]); free(udev->run_path); - free(udev->run_config_path); dbg(udev, "context %p released\n", udev); free(udev); } @@ -367,9 +388,12 @@ UDEV_EXPORT void udev_set_log_priority(struct udev *udev, int priority) udev_add_property(udev, "UDEV_LOG", num); } -const char *udev_get_rules_path(struct udev *udev) +int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *stamp_usec[]) { - return udev->rules_path; + *path = udev->rules_path; + if (stamp_usec) + *stamp_usec = udev->rules_path_ts; + return udev->rules_path_count; } /** @@ -406,11 +430,6 @@ UDEV_EXPORT const char *udev_get_dev_path(struct udev *udev) return udev->dev_path; } -const char *udev_get_run_config_path(struct udev *udev) -{ - return udev->run_config_path; -} - /** * udev_get_run_path: * @udev: udev library context @@ -421,32 +440,11 @@ const char *udev_get_run_config_path(struct udev *udev) **/ UDEV_EXPORT const char *udev_get_run_path(struct udev *udev) { - if (udev->run_path != NULL) - return udev->run_path; - - /* check if configured path exists */ - if (access(udev->run_config_path, F_OK) < 0) { - char filename[UTIL_PATH_SIZE]; - - /* fall back to /dev/.udev if that exists */ - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev", NULL); - if (access(filename, F_OK) >= 0) - if (set_value(&udev->run_path, filename) != NULL) - return udev->run_path; - } - - /* use default path */ - set_value(&udev->run_path, udev->run_config_path); - if (udev->run_path == NULL) - return udev->run_config_path; + if (udev == NULL) + return NULL; return udev->run_path; } -const char *udev_set_run_path(struct udev *udev, const char *path) -{ - return set_value(&udev->run_path, path); -} - struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value) { if (value == NULL) { diff --git a/udev/udev-builtin-kmod.c b/udev/udev-builtin-kmod.c index f5b41b1ab..7f2faee26 100644 --- a/udev/udev-builtin-kmod.c +++ b/udev/udev-builtin-kmod.c @@ -322,6 +322,7 @@ static void udev_kmod_log(void *data, int priority, const char *file, int line, udev_main_log(data, priority, file, line, fn, format, args); } +/* needs to re-instantiate the context after a reload */ static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) { struct udev *udev = udev_device_get_udev(dev); @@ -350,6 +351,7 @@ static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool te return EXIT_SUCCESS; } +/* called at udev startup */ static int builtin_kmod_init(struct udev *udev) { if (ctx) @@ -365,11 +367,18 @@ static int builtin_kmod_init(struct udev *udev) return 0; } -static int builtin_kmod_exit(struct udev *udev) +/* called on udev shutdown and reload request */ +static void builtin_kmod_exit(struct udev *udev) { ctx = kmod_unref(ctx); info(udev, "unload module index\n"); - return 0; +} + +/* called every couple of seconds during event activity; 'true' if config has changed */ +static bool builtin_kmod_validate(struct udev *udev) +{ + info(udev, "validate module index\n"); + return false; } const struct udev_builtin udev_builtin_kmod = { @@ -377,6 +386,7 @@ const struct udev_builtin udev_builtin_kmod = { .cmd = builtin_kmod, .init = builtin_kmod_init, .exit = builtin_kmod_exit, + .validate = builtin_kmod_validate, .help = "kernel module loader", .run_once = false, }; diff --git a/udev/udev-builtin.c b/udev/udev-builtin.c index cf62d57e2..9804c50fa 100644 --- a/udev/udev-builtin.c +++ b/udev/udev-builtin.c @@ -37,30 +37,45 @@ static const struct udev_builtin *builtins[] = { int udev_builtin_init(struct udev *udev) { unsigned int i; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) - if (builtins[i]->init) - builtins[i]->init(udev); - return 0; + int err; + + for (i = 0; i < ARRAY_SIZE(builtins); i++) { + if (builtins[i]->init) { + err = builtins[i]->init(udev); + if (err < 0) + break; + } + } + return err; } -int udev_builtin_exit(struct udev *udev) +void udev_builtin_exit(struct udev *udev) { unsigned int i; for (i = 0; i < ARRAY_SIZE(builtins); i++) if (builtins[i]->exit) builtins[i]->exit(udev); - return 0; } -int udev_builtin_list(struct udev *udev) +bool udev_builtin_validate(struct udev *udev) +{ + unsigned int i; + bool change = false; + + for (i = 0; i < ARRAY_SIZE(builtins); i++) + if (builtins[i]->validate) + if (builtins[i]->validate(udev)) + change = true; + return change; +} + +void udev_builtin_list(struct udev *udev) { unsigned int i; for (i = 0; i < ARRAY_SIZE(builtins); i++) fprintf(stderr, " %-12s %s\n", builtins[i]->name, builtins[i]->help); - return 0; } const char *udev_builtin_name(enum udev_builtin_cmd cmd) diff --git a/udev/udev-rules.c b/udev/udev-rules.c index aa750219d..411b9d88e 100644 --- a/udev/udev-rules.c +++ b/udev/udev-rules.c @@ -1750,6 +1750,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) struct udev_list file_list; struct udev_list_entry *file_loop; struct token end_token; + char **s; rules = calloc(1, sizeof(struct udev_rules)); if (rules == NULL) @@ -1791,22 +1792,8 @@ 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; - if (udev_get_rules_path(udev) == NULL) { - char filename[UTIL_PATH_SIZE]; - - /* /usr/lib/udev -- system rules */ - add_matching_files(udev, &file_list, LIBEXECDIR "/rules.d", ".rules"); - - /* /etc/udev -- local administration rules */ - add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".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 { - /* custom rules files location for testing */ - add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules"); - } + for (udev_get_rules_path(udev, &s, NULL); *s != NULL; s++) + add_matching_files(udev, &file_list, *s, ".rules"); /* add all filenames to the string buffer */ udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { diff --git a/udev/udev.h b/udev/udev.h index 46dafd68e..a289f35c4 100644 --- a/udev/udev.h +++ b/udev/udev.h @@ -146,7 +146,8 @@ struct udev_builtin { int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); const char *help; int (*init)(struct udev *udev); - int (*exit)(struct udev *udev); + void (*exit)(struct udev *udev); + bool (*validate)(struct udev *udev); bool run_once; }; extern const struct udev_builtin udev_builtin_blkid; @@ -155,13 +156,13 @@ extern const struct udev_builtin udev_builtin_input_id; extern const struct udev_builtin udev_builtin_kmod; extern const struct udev_builtin udev_builtin_path_id; extern const struct udev_builtin udev_builtin_usb_id; -int udev_builtin_load(struct udev *udev); -int udev_builtin_unload(struct udev *udev); +int udev_builtin_init(struct udev *udev); +void udev_builtin_exit(struct udev *udev); enum udev_builtin_cmd udev_builtin_lookup(const char *command); const char *udev_builtin_name(enum udev_builtin_cmd cmd); bool udev_builtin_run_once(enum udev_builtin_cmd cmd); int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); -int udev_builtin_list(struct udev *udev); +void udev_builtin_list(struct udev *udev); int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); /* udev logging */ diff --git a/udev/udevd.c b/udev/udevd.c index 37eb4ba4b..f810b9431 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -736,19 +736,6 @@ static int handle_inotify(struct udev *udev) struct udev_device *dev; ev = (struct inotify_event *)(buf + pos); - if (ev->len) { - const char *s; - - info(udev, "inotify event: %x for %s\n", ev->mask, ev->name); - s = strstr(ev->name, ".rules"); - if (s == NULL) - continue; - if (strlen(s) != strlen(".rules")) - continue; - reload = true; - continue; - } - dev = udev_watch_lookup(udev, ev->wd); if (dev != NULL) { info(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev)); @@ -1169,6 +1156,36 @@ static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) return 0; } +static bool check_rules_timestamp(struct udev *udev) +{ + char **p; + unsigned long long *stamp_usec; + int i, n; + bool changed = false; + + n = udev_get_rules_path(udev, &p, &stamp_usec); + for (i = 0; i < n; i++) { + struct stat stats; + + if (stat(p[i], &stats) < 0) + continue; + + if (stamp_usec[i] == ts_usec(&stats.st_mtim)) + continue; + + /* first check */ + if (stamp_usec[i] != 0) { + info(udev, "reload - timestamp of '%s' changed\n", p[i]); + changed = true; + } + + /* update timestamp */ + stamp_usec[i] = ts_usec(&stats.st_mtim); + } + + return changed; +} + int main(int argc, char *argv[]) { struct udev *udev; @@ -1191,6 +1208,7 @@ int main(int argc, char *argv[]) int fd_worker = -1; struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker; struct udev_ctrl_connection *ctrl_conn = NULL; + char **s; int rc = 1; udev = udev_new(); @@ -1202,31 +1220,6 @@ int main(int argc, char *argv[]) info(udev, "version %s\n", VERSION); udev_selinux_init(udev); - /* make sure, that our runtime dir exists and is writable */ - if (utimensat(AT_FDCWD, udev_get_run_config_path(udev), NULL, 0) < 0) { - /* try to create our own subdirectory, do not create parent directories */ - mkdir(udev_get_run_config_path(udev), 0755); - - if (utimensat(AT_FDCWD, udev_get_run_config_path(udev), NULL, 0) >= 0) { - /* directory seems writable now */ - udev_set_run_path(udev, udev_get_run_config_path(udev)); - } else { - /* fall back to /dev/.udev */ - char filename[UTIL_PATH_SIZE]; - - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev", NULL); - if (udev_set_run_path(udev, filename) == NULL) - goto exit; - mkdir(udev_get_run_path(udev), 0755); - err(udev, "error: runtime directory '%s' not writable, for now falling back to '%s'", - udev_get_run_config_path(udev), udev_get_run_path(udev)); - } - } - /* relabel runtime dir only if it resides below /dev */ - if (strncmp(udev_get_run_path(udev), udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0) - udev_selinux_lsetfilecon(udev, udev_get_run_path(udev), 0755); - info(udev, "runtime dir '%s'\n", udev_get_run_path(udev)); - for (;;) { int option; @@ -1469,28 +1462,6 @@ int main(int argc, char *argv[]) rc = 4; goto exit; } - - if (udev_get_rules_path(udev) != NULL) { - inotify_add_watch(fd_inotify, udev_get_rules_path(udev), - IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - } else { - char filename[UTIL_PATH_SIZE]; - struct stat statbuf; - - inotify_add_watch(fd_inotify, LIBEXECDIR "/rules.d", - IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - inotify_add_watch(fd_inotify, SYSCONFDIR "/udev/rules.d", - IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - - /* watch dynamic rules directory */ - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL); - if (stat(filename, &statbuf) != 0) { - util_create_path(udev, filename); - mkdir(filename, 0755); - } - inotify_add_watch(fd_inotify, filename, - IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - } udev_watch_restore(udev); /* block and listen to all signals on signalfd */ @@ -1575,6 +1546,7 @@ int main(int argc, char *argv[]) udev_list_node_init(&worker_list); for (;;) { + static unsigned long long last_usec; struct epoll_event ev[8]; int fdcount; int timeout; @@ -1642,6 +1614,24 @@ int main(int argc, char *argv[]) is_ctrl = true; } + /* check for changed config, every 3 seconds at most */ + if ((now_usec() - last_usec) > 3 * 1000 * 1000) { + if (check_rules_timestamp(udev)) + reload = true; + if (udev_builtin_validate(udev)) + reload = true; + + last_usec = now_usec(); + } + + /* reload requested, HUP signal received, rules changed, builtin changed */ + if (reload) { + worker_kill(udev, 0); + rules = udev_rules_unref(rules); + udev_builtin_exit(udev); + reload = 0; + } + /* event has finished */ if (is_worker) worker_returned(fd_worker); @@ -1678,7 +1668,7 @@ int main(int argc, char *argv[]) if (udev_exit) continue; - /* device node and rules directory inotify watch */ + /* device node watch */ if (is_inotify) handle_inotify(udev); @@ -1693,14 +1683,6 @@ int main(int argc, char *argv[]) */ if (is_ctrl) ctrl_conn = handle_ctrl_msg(udev_ctrl); - - /* rules changed, set by inotify or a HUP signal */ - if (reload) { - worker_kill(udev, 0); - rules = udev_rules_unref(rules); - udev_builtin_exit(udev); - reload = 0; - } } rc = EXIT_SUCCESS;