X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudevd.c;h=419c486ddd4fca09a341fb5d551337090c7e95ce;hp=918825ae506194765e9431503991ee0a2a7eb88a;hb=619b97ff2b9e29c79dedd9aff425c7a11764c300;hpb=d7ddce186c8a0171eb98862b6f8ff72d64261b33 diff --git a/udev/udevd.c b/udev/udevd.c index 918825ae5..419c486dd 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -1,6 +1,8 @@ /* * Copyright (C) 2004-2008 Kay Sievers * Copyright (C) 2004 Chris Friesen + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +29,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -57,22 +61,22 @@ 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 int signal_pipe[2] = {-1, -1}; -static volatile int sigchilds_waiting; -static volatile int udev_exit; -static volatile int reload_config; +static volatile sig_atomic_t sigchilds_waiting; +static volatile sig_atomic_t udev_exit; +static volatile sig_atomic_t reload_config; +static volatile sig_atomic_t signal_received; +static volatile pid_t settle_pid; static int run_exec_q; static int stop_exec_q; static int max_childs; - -static struct udev_list_node exec_list; -static struct udev_list_node running_list; +static int childs; +static struct udev_list_node event_list; enum event_state { EVENT_QUEUED, @@ -108,9 +112,9 @@ static void export_event_state(struct udev_event *event, enum event_state state) switch (state) { case EVENT_QUEUED: - unlink(filename_failed); - delete_path(event->udev, filename_failed); - create_path(event->udev, filename); + if(unlink(filename_failed) == 0) + util_delete_path(event->udev, filename_failed); + util_create_path(event->udev, filename); udev_selinux_setfscreatecon(event->udev, filename, S_IFLNK); symlink(udev_device_get_devpath(event->dev), filename); udev_selinux_resetfscreatecon(event->udev); @@ -130,20 +134,24 @@ static void export_event_state(struct udev_event *event, enum event_state state) info(event->udev, "renamed devpath, moved failed state of '%s' to %s'\n", udev_device_get_devpath_old(event->dev), udev_device_get_devpath(event->dev)); } else { - unlink(filename_failed); - delete_path(event->udev, filename_failed); + if (unlink(filename_failed) == 0) + util_delete_path(event->udev, filename_failed); } unlink(filename); - delete_path(event->udev, filename); + + /* clean up possibly empty queue directory */ + if (udev_list_is_empty(&event_list)) + util_delete_path(event->udev, filename); break; case EVENT_FAILED: /* move failed event to the failed directory */ - create_path(event->udev, filename_failed); + util_create_path(event->udev, filename_failed); rename(filename, filename_failed); /* clean up possibly empty queue directory */ - delete_path(event->udev, filename); + if (udev_list_is_empty(&event_list)) + util_delete_path(event->udev, filename); break; } @@ -164,10 +172,10 @@ static void event_queue_delete(struct udev_event *event) udev_event_unref(event); } -static void asmlinkage event_sig_handler(int signum) +static void event_sig_handler(int signum) { if (signum == SIGALRM) - exit(1); + _exit(1); } static void event_fork(struct udev_event *event) @@ -176,23 +184,25 @@ static void event_fork(struct udev_event *event) struct sigaction act; int err; + if (debug_trace) { + event->trace = 1; + fprintf(stderr, "fork %s (%llu)\n", + udev_device_get_syspath(event->dev), + udev_device_get_seqnum(event->dev)); + } + pid = fork(); switch (pid) { case 0: /* child */ - udev_monitor_unref(kernel_monitor); udev_ctrl_unref(udev_ctrl); - if (inotify_fd >= 0) - close(inotify_fd); - close(signal_pipe[READ_END]); - close(signal_pipe[WRITE_END]); logging_close(); logging_init("udevd-event"); setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); /* set signal handlers */ memset(&act, 0x00, sizeof(act)); - act.sa_handler = (void (*)(int)) event_sig_handler; + act.sa_handler = event_sig_handler; sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM, &act, NULL); @@ -208,7 +218,7 @@ static void event_fork(struct udev_event *event) alarm(UDEV_EVENT_TIMEOUT); /* apply rules, create node, symlinks */ - err = udev_event_run(event, rules); + err = udev_event_execute_rules(event, rules); /* rules may change/disable the timeout */ if (udev_device_get_event_timeout(event->dev) >= 0) @@ -216,7 +226,17 @@ static void event_fork(struct udev_event *event) /* execute RUN= */ if (err == 0 && !event->ignore_device && udev_get_run(event->udev)) - udev_rules_run(event); + 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); + } + + /* send processed event back to the kernel netlink socket */ + udev_monitor_send_device(kernel_monitor, event->dev); + info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err); logging_close(); if (err != 0) @@ -235,6 +255,7 @@ static void event_fork(struct udev_event *event) udev_device_get_subsystem(event->dev), time(NULL) - event->queue_time); event->pid = pid; + childs++; } } @@ -261,29 +282,20 @@ static void event_queue_insert(struct udev_event *event) close(fd); } - /* run one event after the other in debug mode */ - if (debug_trace) { - udev_list_node_append(&event->node, &running_list); - event_fork(event); - waitpid(event->pid, NULL, 0); - event_queue_delete(event); - return; - } + + udev_list_node_append(&event->node, &event_list); + run_exec_q = 1; /* run all events with a timeout set immediately */ if (udev_device_get_timeout(event->dev) > 0) { - udev_list_node_append(&event->node, &running_list); event_fork(event); return; } - - udev_list_node_append(&event->node, &exec_list); - run_exec_q = 1; } static int mem_size_mb(void) { - FILE* f; + FILE *f; char buf[4096]; long int memsize = -1; @@ -306,47 +318,56 @@ static int mem_size_mb(void) static int compare_devpath(const char *running, const char *waiting) { - int i; + int i = 0; - for (i = 0; i < UTIL_PATH_SIZE; i++) { - /* identical device event found */ - if (running[i] == '\0' && waiting[i] == '\0') - return 1; + while (running[i] != '\0' && running[i] == waiting[i]) + i++; - /* parent device event found */ - if (running[i] == '\0' && waiting[i] == '/') - return 2; + /* identical device event found */ + if (running[i] == '\0' && waiting[i] == '\0') + return 1; - /* child device event found */ - if (running[i] == '/' && waiting[i] == '\0') - return 3; + /* parent device event found */ + if (running[i] == '\0' && waiting[i] == '/') + return 2; - /* no matching event */ - if (running[i] != waiting[i]) - break; - } + /* child device event found */ + if (running[i] == '/' && waiting[i] == '\0') + return 3; + /* no matching event */ return 0; } -/* lookup event for identical, parent, child, or physical device */ -static int devpath_busy(struct udev_event *event, int limit) +/* lookup event for identical, parent, child device */ +static int devpath_busy(struct udev_event *event) { struct udev_list_node *loop; - int childs_count = 0; - /* check exec-queue which may still contain delayed events we depend on */ - udev_list_node_foreach(loop, &exec_list) { + if (event->delaying_seqnum > 0) { + } + /* check if queue contains events we depend on */ + udev_list_node_foreach(loop, &event_list) { struct udev_event *loop_event = node_to_event(loop); - /* skip ourself and all later events */ + /* we already found a later event, earlier can not block us, no need to check again */ + if (udev_device_get_seqnum(loop_event->dev) < event->delaying_seqnum) + continue; + + /* event we checked earlier still exists, no need to check again */ + if (udev_device_get_seqnum(loop_event->dev) == event->delaying_seqnum) + return 2; + + /* found ourself, no later event can block us */ if (udev_device_get_seqnum(loop_event->dev) >= udev_device_get_seqnum(event->dev)) break; /* check our old name */ if (udev_device_get_devpath_old(event->dev) != NULL) - if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) - return 2; + if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) { + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 3; + } /* check identical, parent, or child device event */ if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) { @@ -354,57 +375,10 @@ static int devpath_busy(struct udev_event *event, int limit) udev_device_get_seqnum(event->dev), udev_device_get_seqnum(loop_event->dev), udev_device_get_devpath(loop_event->dev)); - return 3; - } - - /* check for our major:minor number */ - if (major(udev_device_get_devnum(event->dev)) > 0 && - udev_device_get_devnum(loop_event->dev) == udev_device_get_devnum(event->dev) && - strcmp(udev_device_get_subsystem(event->dev), udev_device_get_subsystem(loop_event->dev)) == 0) { - dbg(event->udev, "%llu, device event still pending %llu (%d:%d)\n", - udev_device_get_seqnum(event->dev), - udev_device_get_seqnum(loop_event->dev), - major(udev_device_get_devnum(loop_event->dev)), minor(udev_device_get_devnum(loop_event->dev))); + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); return 4; } - /* check physical device event (special case of parent) */ - if (udev_device_get_physdevpath(event->dev) != NULL && - strcmp(udev_device_get_action(event->dev), "add") == 0) - if (compare_devpath(udev_device_get_devpath(loop_event->dev), - udev_device_get_physdevpath(event->dev)) != 0) { - dbg(event->udev, "%llu, physical device event still pending %llu (%s)\n", - udev_device_get_seqnum(event->dev), - udev_device_get_seqnum(loop_event->dev), - udev_device_get_devpath(loop_event->dev)); - return 5; - } - } - - /* check run queue for still running events */ - udev_list_node_foreach(loop, &running_list) { - struct udev_event *loop_event = node_to_event(loop); - - if (childs_count++ >= limit) { - info(event->udev, "%llu, maximum number (%i) of childs reached\n", - udev_device_get_seqnum(event->dev), childs_count); - return 1; - } - - /* check our old name */ - if (udev_device_get_devpath_old(event->dev) != NULL) - if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) - return 2; - - /* check identical, parent, or child device event */ - if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) { - dbg(event->udev, "%llu, device event still running %llu (%s)\n", - udev_device_get_seqnum(event->dev), - udev_device_get_seqnum(loop_event->dev), - udev_device_get_devpath(loop_event->dev)); - return 3; - } - /* check for our major:minor number */ if (major(udev_device_get_devnum(event->dev)) > 0 && udev_device_get_devnum(loop_event->dev) == udev_device_get_devnum(event->dev) && @@ -413,20 +387,9 @@ static int devpath_busy(struct udev_event *event, int limit) udev_device_get_seqnum(event->dev), udev_device_get_seqnum(loop_event->dev), major(udev_device_get_devnum(loop_event->dev)), minor(udev_device_get_devnum(loop_event->dev))); - return 4; + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 5; } - - /* check physical device event (special case of parent) */ - if (udev_device_get_physdevpath(event->dev) != NULL && - strcmp(udev_device_get_action(event->dev), "add") == 0) - if (compare_devpath(udev_device_get_devpath(loop_event->dev), - udev_device_get_physdevpath(event->dev)) != 0) { - dbg(event->udev, "%llu, physical device event still pending %llu (%s)\n", - udev_device_get_seqnum(event->dev), - udev_device_get_seqnum(loop_event->dev), - udev_device_get_devpath(loop_event->dev)); - return 5; - } } return 0; } @@ -437,25 +400,43 @@ static void event_queue_manager(struct udev *udev) struct udev_list_node *loop; struct udev_list_node *tmp; - if (udev_list_is_empty(&exec_list)) +start_over: + if (udev_list_is_empty(&event_list)) { + if (childs > 0) { + err(udev, "event list empty, but childs count is %i", childs); + childs = 0; + } return; + } - udev_list_node_foreach_safe(loop, tmp, &exec_list) { + udev_list_node_foreach_safe(loop, tmp, &event_list) { struct udev_event *loop_event = node_to_event(loop); - /* serialize and wait for parent or child events */ - if (devpath_busy(loop_event, max_childs) != 0) { + if (childs >= max_childs) { + info(udev, "maximum number (%i) of childs reached\n", childs); + break; + } + + if (loop_event->pid != 0) + continue; + + /* do not start event if parent or child event is still running */ + if (devpath_busy(loop_event) != 0) { dbg(udev, "delay seq %llu (%s)\n", udev_device_get_seqnum(loop_event->dev), udev_device_get_devpath(loop_event->dev)); continue; } - /* move event to run list */ - udev_list_node_remove(&loop_event->node); - udev_list_node_append(&loop_event->node, &running_list); 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; + } } } @@ -525,10 +506,71 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl) max_childs = i; } + settle_pid = udev_ctrl_get_settle(ctrl_msg); + if (settle_pid > 0) { + info(udev, "udevd message (SETTLE) received\n"); + } udev_ctrl_msg_unref(ctrl_msg); } -static void asmlinkage sig_handler(int signum) +/* 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 sig_handler(int signum) { switch (signum) { case SIGINT: @@ -544,8 +586,7 @@ static void asmlinkage sig_handler(int signum) break; } - /* write to pipe, which will wakeup select() in our mainloop */ - write(signal_pipe[WRITE_END], "", 1); + signal_received = 1; } static void udev_done(int pid, int exitstatus) @@ -553,7 +594,7 @@ static void udev_done(int pid, int exitstatus) struct udev_list_node *loop; /* find event associated with pid and delete it */ - udev_list_node_foreach(loop, &running_list) { + udev_list_node_foreach(loop, &event_list) { struct udev_event *loop_event = node_to_event(loop); if (loop_event->pid == pid) { @@ -561,9 +602,14 @@ static void udev_done(int pid, int exitstatus) udev_device_get_seqnum(loop_event->dev), loop_event->pid, exitstatus, time(NULL) - loop_event->queue_time); loop_event->exitstatus = exitstatus; + if (debug_trace) + fprintf(stderr, "exit %s (%llu)\n", + udev_device_get_syspath(loop_event->dev), + udev_device_get_seqnum(loop_event->dev)); event_queue_delete(loop_event); + childs--; - /* there may be events waiting with the same devpath */ + /* there may be dependent events waiting */ run_exec_q = 1; return; } @@ -589,6 +635,38 @@ static void reap_sigchilds(void) } } +static void cleanup_queue_dir(struct udev *udev) +{ + char dirname[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; + DIR *dir; + + util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); + util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); + unlink(filename); + + util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname)); + util_strlcat(dirname, "/.udev/queue", sizeof(dirname)); + dir = opendir(dirname); + if (dir != NULL) { + while (1) { + struct dirent *dent; + + dent = readdir(dir); + if (dent == NULL || dent->d_name[0] == '\0') + break; + if (dent->d_name[0] == '.') + continue; + util_strlcpy(filename, dirname, sizeof(filename)); + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, dent->d_name, sizeof(filename)); + unlink(filename); + } + closedir(dir); + rmdir(dirname); + } +} + static void export_initial_seqnum(struct udev *udev) { char filename[UTIL_PATH_SIZE]; @@ -609,7 +687,7 @@ static void export_initial_seqnum(struct udev *udev) } util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); - create_path(udev, filename); + util_create_path(udev, filename); fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); if (fd >= 0) { write(fd, seqnum, len); @@ -617,25 +695,52 @@ static void export_initial_seqnum(struct udev *udev) } } +static void startup_log(struct udev *udev) +{ + FILE *f; + char path[UTIL_PATH_SIZE]; + struct stat statbuf; + + f = fopen("/dev/kmsg", "w"); + if (f != NULL) + fprintf(f, "<6>udev: starting version " VERSION "\n"); + + util_strlcpy(path, udev_get_sys_path(udev), sizeof(path)); + util_strlcat(path, "/class/mem/null", sizeof(path)); + if (lstat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { + const char *depr_str = + "udev: missing sysfs features; please update the kernel " + "or disable the kernel's CONFIG_SYSFS_DEPRECATED option; " + "udev may fail to work correctly"; + + if (f != NULL) + fprintf(f, "<3>%s\n", depr_str); + err(udev, "%s\n", depr_str); + sleep(3); + } + + if (f != NULL) + fclose(f); +} + int main(int argc, char *argv[]) { struct udev *udev; - int err; int fd; struct sigaction act; - fd_set readfds; 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; - int maxfd; udev = udev_new(); if (udev == NULL) @@ -644,7 +749,7 @@ int main(int argc, char *argv[]) logging_init("udevd"); udev_set_log_fn(udev, log_fn); info(udev, "version %s\n", VERSION); - selinux_init(udev); + udev_selinux_init(udev); while (1) { int option; @@ -665,8 +770,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); @@ -688,8 +807,6 @@ int main(int argc, char *argv[]) fprintf(stderr, "cannot open /dev/null\n"); err(udev, "cannot open /dev/null\n"); } - if (fd > STDIN_FILENO) - dup2(fd, STDIN_FILENO); if (write(STDOUT_FILENO, 0, 0) < 0) dup2(fd, STDOUT_FILENO); if (write(STDERR_FILENO, 0, 0) < 0) @@ -711,7 +828,7 @@ int main(int argc, char *argv[]) goto exit; } - kernel_monitor = udev_monitor_new_from_netlink(udev); + kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); if (kernel_monitor == NULL || udev_monitor_enable_receiving(kernel_monitor) < 0) { fprintf(stderr, "error initializing netlink socket\n"); err(udev, "error initializing netlink socket\n"); @@ -720,41 +837,13 @@ int main(int argc, char *argv[]) } udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - err = pipe(signal_pipe); - if (err < 0) { - err(udev, "error getting pipes: %m\n"); - goto exit; - } - - err = fcntl(signal_pipe[READ_END], F_GETFL, 0); - if (err < 0) { - err(udev, "error fcntl on read pipe: %m\n"); - goto exit; - } - err = fcntl(signal_pipe[READ_END], F_SETFL, err | O_NONBLOCK); - if (err < 0) { - err(udev, "error fcntl on read pipe: %m\n"); - goto exit; - } - - err = fcntl(signal_pipe[WRITE_END], F_GETFL, 0); - if (err < 0) { - err(udev, "error fcntl on write pipe: %m\n"); - goto exit; - } - err = fcntl(signal_pipe[WRITE_END], F_SETFL, err | O_NONBLOCK); - if (err < 0) { - err(udev, "error fcntl on write pipe: %m\n"); - goto exit; - } - - rules = udev_rules_new(udev, 1); + rules = udev_rules_new(udev, resolve_names); if (rules == NULL) { err(udev, "error reading rules\n"); goto exit; } - udev_list_init(&running_list); - udev_list_init(&exec_list); + udev_list_init(&event_list); + cleanup_queue_dir(udev); export_initial_seqnum(udev); if (daemonize) { @@ -777,7 +866,8 @@ int main(int argc, char *argv[]) } /* redirect std{out,err} */ - if (!debug) { + if (!debug && !debug_trace) { + dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } @@ -800,30 +890,11 @@ int main(int argc, char *argv[]) close(fd); } - fd = open("/dev/kmsg", O_WRONLY); - if (fd > 0) { - const char *ver_str = "<6>udev: starting version " VERSION "\n"; - char path[UTIL_PATH_SIZE]; - struct stat statbuf; - - write(fd, ver_str, strlen(ver_str)); - util_strlcpy(path, udev_get_sys_path(udev), sizeof(path)); - util_strlcat(path, "/class/mem/null", sizeof(path)); - if (lstat(path, &statbuf) == 0) { - if (S_ISDIR(statbuf.st_mode)) { - const char *depr_str = "<6>udev: deprecated sysfs layout (kernel too old, " - "or CONFIG_SYSFS_DEPRECATED) is unsupported, some " - "udev features may fail\n"; - - write(fd, depr_str, strlen(depr_str)); - } - } - close(fd); - } + startup_log(udev); /* set signal handlers */ memset(&act, 0x00, sizeof(struct sigaction)); - act.sa_handler = (void (*)(int)) sig_handler; + act.sa_handler = sig_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESTART; sigaction(SIGINT, &act, NULL); @@ -832,13 +903,13 @@ int main(int argc, char *argv[]) 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), IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); } else { - char filename[PATH_MAX]; + char filename[UTIL_PATH_SIZE]; inotify_add_watch(inotify_fd, UDEV_PREFIX "/lib/udev/rules.d", IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); @@ -851,50 +922,69 @@ 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) - err(udev, "the kernel does not support inotify, udevd can't monitor rules file changes\n"); - else - err(udev, "inotify_init failed: %m\n"); - /* maximum limit of forked childs */ - value = getenv("UDEVD_MAX_CHILDS"); - if (value) - max_childs = strtoul(value, NULL, 10); - else { + udev_watch_restore(udev); + } + + /* in trace mode run one event after the other */ + if (debug_trace) { + max_childs = 1; + } else { int memsize = mem_size_mb(); if (memsize > 0) max_childs = 128 + (memsize / 4); else max_childs = UDEVD_MAX_CHILDS; } + /* possibly overwrite maximum limit of executed events */ + value = getenv("UDEVD_MAX_CHILDS"); + if (value) + max_childs = strtoul(value, NULL, 10); info(udev, "initialize max_childs to %u\n", max_childs); - maxfd = udev_ctrl_get_fd(udev_ctrl); - maxfd = UDEV_MAX(maxfd, udev_monitor_get_fd(kernel_monitor)); - maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]); - maxfd = UDEV_MAX(maxfd, inotify_fd); while (!udev_exit) { + sigset_t blocked_mask, orig_mask; + struct pollfd pfd[4]; + struct pollfd *ctrl_poll, *monitor_poll, *inotify_poll = NULL; + int nfds = 0; int fdcount; - FD_ZERO(&readfds); - FD_SET(signal_pipe[READ_END], &readfds); - FD_SET(udev_ctrl_get_fd(udev_ctrl), &readfds); - FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds); - if (inotify_fd >= 0) - FD_SET(inotify_fd, &readfds); - fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL); + sigfillset(&blocked_mask); + sigprocmask(SIG_SETMASK, &blocked_mask, &orig_mask); + if (signal_received) { + sigprocmask(SIG_SETMASK, &orig_mask, NULL); + goto handle_signals; + } + + 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) - err(udev, "error in select: %m\n"); + if (errno == EINTR) + goto handle_signals; + err(udev, "error in select: %m\n"); continue; } /* get control message */ - if (FD_ISSET(udev_ctrl_get_fd(udev_ctrl), &readfds)) + if (ctrl_poll->revents & POLLIN) handle_ctrl_msg(udev_ctrl); /* get kernel uevent */ - if (FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) { + if (monitor_poll->revents & POLLIN) { struct udev_device *dev; dev = udev_monitor_receive_device(kernel_monitor); @@ -909,39 +999,19 @@ int main(int argc, char *argv[]) } } - /* received a signal, clear our notification pipe */ - if (FD_ISSET(signal_pipe[READ_END], &readfds)) { - char buf[256]; - - read(signal_pipe[READ_END], &buf, sizeof(buf)); - } - /* rules directory inotify watch */ - if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) { - 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; /* rules changed, set by inotify or a HUP signal */ if (reload_config) { 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; @@ -958,23 +1028,21 @@ int main(int argc, char *argv[]) if (!stop_exec_q) event_queue_manager(udev); } + + if (settle_pid > 0) { + kill(settle_pid, SIGUSR1); + settle_pid = 0; + } } + cleanup_queue_dir(udev); rc = 0; - exit: udev_rules_unref(rules); - - if (signal_pipe[READ_END] >= 0) - close(signal_pipe[READ_END]); - if (signal_pipe[WRITE_END] >= 0) - close(signal_pipe[WRITE_END]); - udev_ctrl_unref(udev_ctrl); if (inotify_fd >= 0) close(inotify_fd); udev_monitor_unref(kernel_monitor); - - selinux_exit(udev); + udev_selinux_exit(udev); udev_unref(udev); logging_close(); return rc;