X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=udev%2Fudevd.c;h=4024460d4b1de82faa486dbd0da8e0d9a85dcae7;hb=f7c5b04f69e004912327f082be8645b65bb1ff5d;hp=d63fa7f48646ee7911c9d9ddc6221e3e801bd3f5;hpb=12bc9c54f23a37b2dc1d2965ee452d76bd2b6a37;p=elogind.git diff --git a/udev/udevd.c b/udev/udevd.c index d63fa7f48..4024460d4 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -24,12 +24,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -45,34 +47,41 @@ #define UDEVD_PRIORITY -4 #define UDEV_PRIORITY -2 -static int debug; +static bool debug; static void log_fn(struct udev *udev, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { if (debug) { - fprintf(stderr, "[%d] %s: ", (int) getpid(), fn); - vfprintf(stderr, format, args); + char buf[1024]; + struct timeval tv; + struct timezone tz; + + vsnprintf(buf, sizeof(buf), format, args); + gettimeofday(&tv, &tz); + fprintf(stderr, "%llu.%06u [%u] %s: %s", + (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec, + (int) getpid(), fn, buf); } else { vsyslog(priority, format, args); } } -static int debug_trace; +static bool debug_trace; static struct udev_rules *rules; static struct udev_queue_export *udev_queue_export; static struct udev_ctrl *udev_ctrl; static struct udev_monitor *monitor; static int worker_watch[2]; static pid_t settle_pid; -static int stop_exec_queue; -static int reload_config; +static bool stop_exec_queue; +static bool reload_config; static int max_childs; static int childs; static struct udev_list_node event_list; static struct udev_list_node worker_list; -static int udev_exit; +static bool udev_exit; static volatile sig_atomic_t worker_exit; enum poll_fd { @@ -128,6 +137,8 @@ enum worker_state { struct worker { struct udev_list_node node; + struct udev *udev; + int refcount; pid_t pid; struct udev_monitor *monitor; enum worker_state state; @@ -154,7 +165,7 @@ static void event_queue_delete(struct event *event) udev_list_node_remove(&event->node); /* mark as failed, if "add" event returns non-zero */ - if (event->exitcode && strcmp(udev_device_get_action(event->dev), "add") == 0) + if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "add") == 0) udev_queue_export_device_failed(udev_queue_export, event->dev); else udev_queue_export_device_finished(udev_queue_export, event->dev); @@ -171,14 +182,27 @@ static void event_sig_handler(int signum) _exit(1); break; case SIGTERM: - worker_exit = 1; + worker_exit = true; break; } } +static struct worker *worker_ref(struct worker *worker) +{ + worker->refcount++; + return worker; +} + static void worker_unref(struct worker *worker) { + worker->refcount--; + if (worker->refcount > 0) + return; + + udev_list_node_remove(&worker->node); udev_monitor_unref(worker->monitor); + childs--; + info(worker->udev, "worker [%u] cleaned up\n", worker->pid); free(worker); } @@ -201,20 +225,27 @@ static void worker_new(struct event *event) worker = calloc(1, sizeof(struct worker)); if (worker == NULL) return; + /* worker + event reference */ + worker->refcount = 2; + worker->udev = event->udev; pid = fork(); switch (pid) { case 0: { - sigset_t mask; + sigset_t sigmask; struct udev_device *dev; + struct pollfd pmon = { + .fd = udev_monitor_get_fd(worker_monitor), + .events = POLLIN, + }; udev_queue_export_unref(udev_queue_export); udev_monitor_unref(monitor); udev_ctrl_unref(udev_ctrl); close(pfd[FD_SIGNAL].fd); close(worker_watch[READ_END]); - logging_close(); - logging_init("udevd-work"); + udev_log_close(); + udev_log_init("udevd-work"); setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); /* set signal handlers */ @@ -225,11 +256,12 @@ static void worker_new(struct event *event) sigaction(SIGTERM, &act, NULL); sigaction(SIGALRM, &act, NULL); - /* unblock signals */ - sigfillset(&mask); - sigdelset(&mask, SIGTERM); - sigdelset(&mask, SIGALRM); - sigprocmask(SIG_SETMASK, &mask, NULL); + /* unblock SIGALRM */ + sigfillset(&sigmask); + sigdelset(&sigmask, SIGALRM); + sigprocmask(SIG_SETMASK, &sigmask, NULL); + /* SIGTERM is unblocked in ppoll() */ + sigdelset(&sigmask, SIGTERM); /* request TERM signal if parent exits */ prctl(PR_SET_PDEATHSIG, SIGTERM); @@ -237,11 +269,13 @@ static void worker_new(struct event *event) /* initial device */ dev = event->dev; - while (!worker_exit) { + do { struct udev_event *udev_event; - struct worker_message msg; + struct worker_message msg = {}; int err; + int failed = 0; + info(event->udev, "seq %llu running\n", udev_device_get_seqnum(dev)); udev_event = udev_event_new(dev); if (udev_event == NULL) _exit(3); @@ -258,7 +292,7 @@ static void worker_new(struct event *event) /* execute RUN= */ if (err == 0 && !udev_event->ignore_device && udev_get_run(udev_event->udev)) - udev_event_execute_run(udev_event); + failed = udev_event_execute_run(udev_event); /* reset alarm */ alarm(0); @@ -272,23 +306,37 @@ static void worker_new(struct event *event) /* send processed event back to libudev listeners */ udev_monitor_send_device(worker_monitor, NULL, dev); - info(event->udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); - udev_device_unref(dev); - udev_event_unref(udev_event); - /* send back the result of the event execution */ - msg.exitcode = err; + if (err != 0) + msg.exitcode = err; + else if (failed != 0) + msg.exitcode = failed; msg.pid = getpid(); send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); - /* wait for more device messages from udevd */ - do - dev = udev_monitor_receive_device(worker_monitor); - while (!worker_exit && dev == NULL); - } + info(event->udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); + udev_event_unref(udev_event); + udev_device_unref(dev); + dev = NULL; + + /* wait for more device messages or signal from udevd */ + while (!worker_exit) { + int fdcount; + + fdcount = ppoll(&pmon, 1, NULL, &sigmask); + if (fdcount < 0) + continue; + + if (pmon.revents & POLLIN) { + dev = udev_monitor_receive_device(worker_monitor); + if (dev != NULL) + break; + } + } + } while (dev != NULL); udev_monitor_unref(worker_monitor); - logging_close(); + udev_log_close(); exit(0); } case -1: @@ -323,17 +371,17 @@ static void event_run(struct event *event) if (worker->state != WORKER_IDLE) continue; - worker->event = event; - worker->state = WORKER_RUNNING; - event->state = EVENT_RUNNING; count = udev_monitor_send_device(monitor, worker->monitor, event->dev); if (count < 0) { - err(event->udev, "worker [%u] did not accept message, kill it\n", worker->pid); - event->state = EVENT_QUEUED; - worker->state = WORKER_KILLED; + err(event->udev, "worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count); kill(worker->pid, SIGKILL); + worker->state = WORKER_KILLED; continue; } + worker_ref(worker); + worker->event = event; + worker->state = WORKER_RUNNING; + event->state = EVENT_RUNNING; return; } @@ -375,7 +423,7 @@ static void event_queue_insert(struct udev_device *dev) } } -static void worker_kill(int retain) +static void worker_kill(struct udev *udev, int retain) { struct udev_list_node *loop; int max; @@ -525,7 +573,9 @@ static void worker_returned(void) worker->event->exitcode = msg.exitcode; event_queue_delete(worker->event); worker->event = NULL; - worker->state = WORKER_IDLE; + if (worker->state != WORKER_KILLED) + worker->state = WORKER_IDLE; + worker_unref(worker); break; } } @@ -547,22 +597,22 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl) if (i >= 0) { info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); udev_set_log_priority(udev, i); - worker_kill(0); + worker_kill(udev, 0); } if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); - stop_exec_queue = 1; + stop_exec_queue = true; } if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { info(udev, "udevd message (START_EXEC_QUEUE) received\n"); - stop_exec_queue = 0; + stop_exec_queue = false; } if (udev_ctrl_get_reload_rules(ctrl_msg) > 0) { info(udev, "udevd message (RELOAD_RULES) received\n"); - reload_config = 1; + reload_config = true; } str = udev_ctrl_get_set_env(ctrl_msg); @@ -589,7 +639,7 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl) } free(key); } - worker_kill(0); + worker_kill(udev, 0); } i = udev_ctrl_get_set_max_childs(ctrl_msg); @@ -631,7 +681,7 @@ static int handle_inotify(struct udev *udev) ev = (struct inotify_event *)(buf + pos); if (ev->len) { dbg(udev, "inotify event: %x for %s\n", ev->mask, ev->name); - reload_config = 1; + reload_config = true; continue; } @@ -666,7 +716,7 @@ static void handle_signal(struct udev *udev, int signo) switch (signo) { case SIGINT: case SIGTERM: - udev_exit = 1; + udev_exit = true; break; case SIGCHLD: while (1) { @@ -684,31 +734,24 @@ static void handle_signal(struct udev *udev, int signo) if (worker->pid != pid) continue; - /* fail event, if worker died unexpectedly */ - if (worker->event != NULL) { - int exitcode; - - if (WIFEXITED(status)) - exitcode = WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - exitcode = WTERMSIG(status) + 128; - else - exitcode = 0; - worker->event->exitcode = exitcode; - err(udev, "worker [%u] unexpectedly returned with %i\n", pid, exitcode); - event_queue_delete(worker->event); + info(udev, "worker [%u] exit\n", pid); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + err(udev, "worker [%u] unexpectedly returned with status 0x%04x\n", pid, status); + if (worker->event != NULL) { + err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath); + worker->event->exitcode = -32; + event_queue_delete(worker->event); + /* drop reference from running event */ + worker_unref(worker); + } } - - udev_list_node_remove(&worker->node); worker_unref(worker); - childs--; - info(udev, "worker [%u] exit\n", pid); break; } } break; case SIGHUP: - reload_config = 1; + reload_config = true; break; } } @@ -746,7 +789,7 @@ int main(int argc, char *argv[]) int fd; sigset_t mask; const char *value; - int daemonize = 0; + int daemonize = false; int resolve_names = 1; static const struct option options[] = { { "daemon", no_argument, NULL, 'd' }, @@ -763,7 +806,7 @@ int main(int argc, char *argv[]) if (udev == NULL) goto exit; - logging_init("udevd"); + udev_log_init("udevd"); udev_set_log_fn(udev, log_fn); info(udev, "version %s\n", VERSION); udev_selinux_init(udev); @@ -777,13 +820,13 @@ int main(int argc, char *argv[]) switch (option) { case 'd': - daemonize = 1; + daemonize = true; break; case 't': - debug_trace = 1; + debug_trace = true; break; case 'D': - debug = 1; + debug = true; if (udev_get_log_priority(udev) < LOG_INFO) udev_set_log_priority(udev, LOG_INFO); break; @@ -869,7 +912,7 @@ int main(int argc, char *argv[]) } else { char filename[UTIL_PATH_SIZE]; - inotify_add_watch(pfd[FD_INOTIFY].fd, UDEV_PREFIX "/lib/udev/rules.d", + inotify_add_watch(pfd[FD_INOTIFY].fd, LIBEXECDIR "/rules.d", IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); inotify_add_watch(pfd[FD_INOTIFY].fd, SYSCONFDIR "/udev/rules.d", IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); @@ -995,7 +1038,7 @@ int main(int argc, char *argv[]) /* timeout - kill idle workers */ if (fdcount == 0) - worker_kill(2); + worker_kill(udev, 2); /* event has finished */ if (pfd[FD_WORKER].revents & POLLIN) @@ -1044,7 +1087,7 @@ int main(int argc, char *argv[]) if (reload_config) { struct udev_rules *rules_new; - worker_kill(0); + worker_kill(udev, 0); rules_new = udev_rules_new(udev, resolve_names); if (rules_new != NULL) { udev_rules_unref(rules); @@ -1069,6 +1112,6 @@ exit: udev_monitor_unref(monitor); udev_selinux_exit(udev); udev_unref(udev); - logging_close(); + udev_log_close(); return rc; }