X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=udev%2Fudevd.c;h=62c643668c8be96f7f26a59e90b8819d221ad619;hb=ca2bf8007da16fea5f49ff97c95669bfb76cd016;hp=c0852022e246dae89f7bcc21aa728ffd1f1cb543;hpb=1e03b754aef576a5cb75f01b1805cdc1f9cc292f;p=elogind.git diff --git a/udev/udevd.c b/udev/udevd.c index c0852022e..62c643668 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,42 @@ #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 sigset_t orig_sigmask; 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 +138,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,11 +166,12 @@ 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); + info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); udev_device_unref(event->dev); free(event); } @@ -170,14 +183,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); } @@ -195,23 +221,32 @@ static void worker_new(struct event *event) /* allow the main daemon netlink address to send devices to the worker */ udev_monitor_allow_unicast_sender(worker_monitor, monitor); udev_monitor_enable_receiving(worker_monitor); + util_set_fd_cloexec(udev_monitor_get_fd(worker_monitor)); 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 */ @@ -222,11 +257,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); @@ -234,11 +270,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); @@ -255,7 +293,8 @@ 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, + &orig_sigmask); /* reset alarm */ alarm(0); @@ -269,23 +308,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 finished 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: @@ -304,6 +357,7 @@ static void worker_new(struct event *event) event->state = EVENT_RUNNING; udev_list_node_append(&worker->node, &worker_list); childs++; + info(event->udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); break; } } @@ -319,17 +373,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; } @@ -371,7 +425,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; @@ -521,7 +575,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; } } @@ -543,22 +599,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); @@ -585,7 +641,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); @@ -606,7 +662,7 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl) /* read inotify messages */ static int handle_inotify(struct udev *udev) { - ssize_t nbytes, pos; + int nbytes, pos; char *buf; struct inotify_event *ev; @@ -615,7 +671,7 @@ static int handle_inotify(struct udev *udev) buf = malloc(nbytes); if (buf == NULL) { - err(udev, "error getting buffer for inotify, disable watching\n"); + err(udev, "error getting buffer for inotify\n"); return -1; } @@ -627,7 +683,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; } @@ -657,19 +713,20 @@ static int handle_inotify(struct udev *udev) return 0; } -static void handle_signal(int signo) +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) { pid_t pid; + int status; struct udev_list_node *loop, *tmp; - pid = waitpid(-1, NULL, WNOHANG); + pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) break; @@ -679,21 +736,24 @@ static void handle_signal(int signo) if (worker->pid != pid) continue; - /* fail event, if worker died unexpectedly */ - if (worker->event != NULL) { - worker->event->exitcode = 127; - 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--; break; } } break; case SIGHUP: - reload_config = 1; + reload_config = true; break; } } @@ -718,7 +778,7 @@ static void startup_log(struct udev *udev) if (f != NULL) fprintf(f, "<3>%s\n", depr_str); err(udev, "%s\n", depr_str); - sleep(3); + sleep(15); } if (f != NULL) @@ -731,7 +791,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' }, @@ -748,7 +808,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); @@ -762,13 +822,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; @@ -854,7 +914,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); @@ -868,7 +928,7 @@ int main(int argc, char *argv[]) /* block and listen to all signals on signalfd */ sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, NULL); + sigprocmask(SIG_SETMASK, &mask, &orig_sigmask); pfd[FD_SIGNAL].fd = signalfd(-1, &mask, 0); if (pfd[FD_SIGNAL].fd < 0) { fprintf(stderr, "error getting signalfd\n"); @@ -885,6 +945,7 @@ int main(int argc, char *argv[]) goto exit; } pfd[FD_WORKER].fd = worker_watch[READ_END]; + util_set_fd_cloexec(worker_watch[WRITE_END]); rules = udev_rules_new(udev, resolve_names); if (rules == NULL) { @@ -935,9 +996,9 @@ int main(int argc, char *argv[]) /* OOM_DISABLE == -17 */ fd = open("/proc/self/oom_adj", O_RDWR); - if (fd < 0) + if (fd < 0) { err(udev, "error disabling OOM: %m\n"); - else { + } else { write(fd, "-17", 3); close(fd); } @@ -979,7 +1040,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) @@ -1007,7 +1068,7 @@ int main(int argc, char *argv[]) size = read(pfd[FD_SIGNAL].fd, &fdsi, sizeof(struct signalfd_siginfo)); if (size == sizeof(struct signalfd_siginfo)) - handle_signal(fdsi.ssi_signo); + handle_signal(udev, fdsi.ssi_signo); } /* device node and rules directory inotify watch */ @@ -1028,7 +1089,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); @@ -1053,6 +1114,6 @@ exit: udev_monitor_unref(monitor); udev_selinux_exit(udev); udev_unref(udev); - logging_close(); + udev_log_close(); return rc; }