chiark / gitweb /
move rules dirs to udev context; replace inotify with time-controlled stat()
authorKay Sievers <kay.sievers@vrfy.org>
Tue, 27 Dec 2011 02:49:43 +0000 (03:49 +0100)
committerKay Sievers <kay.sievers@vrfy.org>
Tue, 27 Dec 2011 02:49:43 +0000 (03:49 +0100)
NEWS
libudev/libudev-private.h
libudev/libudev-util.c
libudev/libudev.c
udev/udev-builtin-kmod.c
udev/udev-builtin.c
udev/udev-rules.c
udev/udev.h
udev/udevd.c

diff --git a/NEWS b/NEWS
index c01841003c5ea97691db77546e5f52647bd3d3db..b052257c332ace211fa3fe1f35cf27199f1ab47a 100644 (file)
--- 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
index ffc82cbc6d7accfb80dfc757755c181fd50c68c6..fed5863988eeeab8006d4fcfc3cf3ff42adf7d73 100644 (file)
@@ -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 */
index e08349e0fa345d051a30d66d4d7a03f3b06592ea..5369fe734e546b49444aee9b51a596d41778ff17 100644 (file)
@@ -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);
 }
index 3b3a8e9af537a129720e13c075e986c22f563b63..93f2d8f1b5d6d9f58553629e4e9d1ae898430224 100644 (file)
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <string.h>
 #include <ctype.h>
+#include <time.h>
 
 #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) {
index f5b41b1ab498cfd8a67f525202041cab56b742e9..7f2faee261995fd1ee74769d8964960028afb2e3 100644 (file)
@@ -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,
 };
index cf62d57e23283b07e244abe624cb1f9046c9db9e..9804c50faecc8cff4abdc4920c24232639cabbf2 100644 (file)
@@ -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)
index aa750219daaa8dc5c513c0f3323e90a417bfe936..411b9d88eb5a7ab7d948a6edc00057315855ce62 100644 (file)
@@ -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)) {
index 46dafd68e3566d44b538a9fec8a8fe2a57837225..a289f35c4d8ba897534a32d4f20b2999319fa697 100644 (file)
@@ -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 */
index 37eb4ba4b13c1b6ab23fb3df0be8fc08d9c71d8e..f810b9431c6410bcdcd0ecf4bf774abd6300608f 100644 (file)
@@ -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;