X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=udev%2Fudevd.c;h=ee3d6f50338600db3608ce4d49a8408bc62b3c19;hb=03733b04c3919920520c8b2ab1e18a44b2691cb5;hp=bc6484b1b0b55a17c6dc51e06313515dc8e1e715;hpb=3210a72ba314dd15e6f7ed85ee22d7a5a0944b7a;p=elogind.git diff --git a/udev/udevd.c b/udev/udevd.c index bc6484b1b..ee3d6f503 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -59,11 +59,12 @@ static void log_fn(struct udev *udev, int priority, } } +static void reap_sigchilds(void); + static int debug_trace; static struct udev_rules *rules; static struct udev_ctrl *udev_ctrl; static struct udev_monitor *kernel_monitor; -static int inotify_fd = -1; static volatile int sigchilds_waiting; static volatile int udev_exit; static volatile int reload_config; @@ -193,8 +194,6 @@ static void event_fork(struct udev_event *event) /* child */ udev_monitor_unref(kernel_monitor); udev_ctrl_unref(udev_ctrl); - if (inotify_fd >= 0) - close(inotify_fd); logging_close(); logging_init("udevd-event"); setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); @@ -227,6 +226,12 @@ static void event_fork(struct udev_event *event) if (err == 0 && !event->ignore_device && udev_get_run(event->udev)) udev_event_execute_run(event); + /* apply/restore inotify watch */ + if (err == 0 && event->inotify_watch) { + udev_watch_begin(event->udev, event->dev); + udev_device_update_db(event->dev); + } + info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err); logging_close(); if (err != 0) @@ -403,6 +408,7 @@ static void event_queue_manager(struct udev *udev) struct udev_list_node *loop; struct udev_list_node *tmp; +start_over: if (udev_list_is_empty(&event_list)) { if (childs > 0) { err(udev, "event list empty, but childs count is %i", childs); @@ -432,6 +438,13 @@ static void event_queue_manager(struct udev *udev) event_fork(loop_event); dbg(udev, "moved seq %llu to running list\n", udev_device_get_seqnum(loop_event->dev)); + + /* retry if events finished in the meantime */ + if (sigchilds_waiting) { + sigchilds_waiting = 0; + reap_sigchilds(); + goto start_over; + } } } @@ -503,6 +516,63 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl) udev_ctrl_msg_unref(ctrl_msg); } +/* read inotify messages */ +static int handle_inotify(struct udev *udev) +{ + int nbytes, pos; + char *buf; + struct inotify_event *ev; + + if ((ioctl(inotify_fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) + return 0; + + buf = malloc(nbytes); + if (buf == NULL) { + err(udev, "error getting buffer for inotify, disable watching\n"); + close(inotify_fd); + inotify_fd = -1; + return 0; + } + + read(inotify_fd, buf, nbytes); + + for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { + struct udev_device *dev; + + ev = (struct inotify_event *)(buf + pos); + if (ev->len) { + dbg(udev, "inotify event: %x for %s\n", ev->mask, ev->name); + reload_config = 1; + continue; + } + + dev = udev_watch_lookup(udev, ev->wd); + if (dev != NULL) { + dbg(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev)); + if (ev->mask & IN_CLOSE_WRITE) { + char filename[UTIL_PATH_SIZE]; + int fd; + + info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev)); + util_strlcpy(filename, udev_device_get_syspath(dev), sizeof(filename)); + util_strlcat(filename, "/uevent", sizeof(filename)); + fd = open(filename, O_WRONLY); + if (fd < 0 || write(fd, "change", 6) < 0) + info(udev, "error writing uevent: %m\n"); + close(fd); + } + if (ev->mask & IN_IGNORED) + udev_watch_end(udev, dev); + + udev_device_unref(dev); + } + + } + + free (buf); + return 0; +} + static void asmlinkage sig_handler(int signum) { switch (signum) { @@ -635,12 +705,14 @@ int main(int argc, char *argv[]) struct sigaction act; const char *value; int daemonize = 0; + int resolve_names = 1; static const struct option options[] = { { "daemon", no_argument, NULL, 'd' }, { "debug-trace", no_argument, NULL, 't' }, { "debug", no_argument, NULL, 'D' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, + { "resolve-names", required_argument, NULL, 'N' }, {} }; int rc = 1; @@ -673,8 +745,22 @@ int main(int argc, char *argv[]) if (udev_get_log_priority(udev) < LOG_INFO) udev_set_log_priority(udev, LOG_INFO); break; + case 'N': + if (strcmp (optarg, "early") == 0) { + resolve_names = 1; + } else if (strcmp (optarg, "late") == 0) { + resolve_names = 0; + } else if (strcmp (optarg, "never") == 0) { + resolve_names = -1; + } else { + fprintf(stderr, "resolve-names must be early, late or never\n"); + err(udev, "resolve-names must be early, late or never\n"); + goto exit; + } + break; case 'h': - printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] [--version]\n"); + printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] " + "[--resolve-names=early|late|never] [--version]\n"); goto exit; case 'V': printf("%s\n", VERSION); @@ -726,7 +812,7 @@ int main(int argc, char *argv[]) } udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - rules = udev_rules_new(udev, 1); + rules = udev_rules_new(udev, resolve_names); if (rules == NULL) { err(udev, "error reading rules\n"); goto exit; @@ -805,13 +891,14 @@ int main(int argc, char *argv[]) memset(&act, 0x00, sizeof(struct sigaction)); act.sa_handler = (void (*)(int)) sig_handler; sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGCHLD, &act, NULL); sigaction(SIGHUP, &act, NULL); /* watch rules directory */ - inotify_fd = inotify_init(); + udev_watch_init(udev); if (inotify_fd >= 0) { if (udev_get_rules_path(udev) != NULL) { inotify_add_watch(inotify_fd, udev_get_rules_path(udev), @@ -830,10 +917,9 @@ int main(int argc, char *argv[]) inotify_add_watch(inotify_fd, filename, IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); } - } else if (errno == ENOSYS) - info(udev, "unable to use inotify, udevd will not monitor rule files changes\n"); - else - err(udev, "inotify_init failed: %m\n"); + + udev_watch_restore(udev); + } /* in trace mode run one event after the other */ if (debug_trace) { @@ -853,9 +939,10 @@ int main(int argc, char *argv[]) while (!udev_exit) { sigset_t blocked_mask, orig_mask; + struct pollfd pfd[4]; struct pollfd *ctrl_poll, *monitor_poll, *inotify_poll = NULL; - struct pollfd pfd[10]; - int fdcount, nfds = 0; + int nfds = 0; + int fdcount; sigfillset(&blocked_mask); sigprocmask(SIG_SETMASK, &blocked_mask, &orig_mask); @@ -864,19 +951,22 @@ int main(int argc, char *argv[]) goto handle_signals; } -#define POLL_FOR(__desc, __pollptr) do { \ - pfd[nfds].fd = (__desc); pfd[nfds].events = POLLIN; \ - __pollptr = &pfd[nfds++]; \ -} while (0) - POLL_FOR(udev_ctrl_get_fd(udev_ctrl), ctrl_poll); - POLL_FOR(udev_monitor_get_fd(kernel_monitor), monitor_poll); - if (inotify_fd >= 0) - POLL_FOR(inotify_fd, inotify_poll); -#undef POLL_FOR + ctrl_poll = &pfd[nfds++]; + ctrl_poll->fd = udev_ctrl_get_fd(udev_ctrl); + ctrl_poll->events = POLLIN; + + monitor_poll = &pfd[nfds++]; + monitor_poll->fd = udev_monitor_get_fd(kernel_monitor); + monitor_poll->events = POLLIN; + + if (inotify_fd >= 0) { + inotify_poll = &pfd[nfds++]; + inotify_poll->fd = inotify_fd; + inotify_poll->events = POLLIN; + } fdcount = ppoll(pfd, nfds, NULL, &orig_mask); sigprocmask(SIG_SETMASK, &orig_mask, NULL); - if (fdcount < 0) { if (errno == EINTR) goto handle_signals; @@ -905,24 +995,8 @@ int main(int argc, char *argv[]) } /* rules directory inotify watch */ - if (inotify_poll && (inotify_poll->revents & POLLIN)) { - int nbytes; - - /* discard all possible events, we can just reload the config */ - if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) { - char *buf; - - reload_config = 1; - buf = malloc(nbytes); - if (buf == NULL) { - err(udev, "error getting buffer for inotify, disable watching\n"); - close(inotify_fd); - inotify_fd = -1; - } - read(inotify_fd, buf, nbytes); - free(buf); - } - } + if (inotify_poll && (inotify_poll->revents & POLLIN)) + handle_inotify(udev); handle_signals: signal_received = 0; @@ -932,7 +1006,7 @@ handle_signals: struct udev_rules *rules_new; reload_config = 0; - rules_new = udev_rules_new(udev, 1); + rules_new = udev_rules_new(udev, resolve_names); if (rules_new != NULL) { udev_rules_unref(rules); rules = rules_new;