X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudevd.c;h=1aa50eb74cfbcdd00031aaf0c6d234c1416f91d7;hp=654118e5a935ffc86edfe688cc0671f9fcf88de4;hb=b44b7f884136e1ef4452818ed514a5d7e51567f1;hpb=44aff4cd6d74d230e4a97f8d59f780472b7cad6e diff --git a/udev/udevd.c b/udev/udevd.c index 654118e5a..1aa50eb74 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -1,24 +1,23 @@ /* - * Copyright (C) 2004-2006 Kay Sievers + * 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 the - * Free Software Foundation version 2 of the License. + * 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 + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ -#include "config.h" - #include #include #include @@ -27,126 +26,57 @@ #include #include #include -#include #include -#include #include #include +#include #include +#include #include -#include -#include -#include #include #include -#include -#include #ifdef HAVE_INOTIFY #include #endif #include "udev.h" -#include "udev_rules.h" -#include "udevd.h" -#include "udev_selinux.h" - -static int debug_trace; -static int debug; - -static struct udev_rules rules; -static int udevd_sock = -1; -static int uevent_netlink_sock = -1; -static int inotify_fd = -1; -static pid_t sid; -static int signal_pipe[2] = {-1, -1}; -static volatile int sigchilds_waiting; -static volatile int udev_exit; -static volatile int reload_config; -static int run_exec_q; -static int stop_exec_q; -static int max_childs; -static int max_childs_running; -static char udev_log[32]; +#define UDEVD_PRIORITY -4 +#define UDEV_PRIORITY -2 -static LIST_HEAD(exec_list); -static LIST_HEAD(running_list); +/* maximum limit of forked childs */ +#define UDEVD_MAX_CHILDS 256 +static int debug; -#ifdef USE_LOG -void log_message(int priority, const char *format, ...) +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) { - va_list args; - - if (priority > udev_log_priority) - return; - - va_start(args, format); if (debug) { - printf("[%d] ", (int) getpid()); - vprintf(format, args); - } else + fprintf(stderr, "[%d] %s: ", (int) getpid(), fn); + vfprintf(stderr, format, args); + } else { vsyslog(priority, format, args); - va_end(args); -} - -#endif - -static void asmlinkage udev_event_sig_handler(int signum) -{ - if (signum == SIGALRM) - exit(1); + } } -static int udev_event_process(struct udevd_uevent_msg *msg) -{ - struct sigaction act; - struct udevice *udev; - int i; - int retval; - - /* set signal handlers */ - memset(&act, 0x00, sizeof(act)); - act.sa_handler = (void (*)(int)) udev_event_sig_handler; - sigemptyset (&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGALRM, &act, NULL); - - /* reset to default */ - act.sa_handler = SIG_DFL; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGCHLD, &act, NULL); - sigaction(SIGHUP, &act, NULL); - - /* trigger timeout to prevent hanging processes */ - alarm(UDEV_EVENT_TIMEOUT); - - /* reconstruct event environment from message */ - for (i = 0; msg->envp[i]; i++) - putenv(msg->envp[i]); - - udev = udev_device_init(); - if (udev == NULL) - return -1; - strlcpy(udev->action, msg->action, sizeof(udev->action)); - sysfs_device_set_values(udev->dev, msg->devpath, msg->subsystem, msg->driver); - udev->devpath_old = msg->devpath_old; - udev->devt = msg->devt; - - retval = udev_device_event(&rules, udev); - - /* rules may change/disable the timeout */ - if (udev->event_timeout >= 0) - alarm(udev->event_timeout); +static void reap_sigchilds(void); - /* run programs collected by RUN-key*/ - if (retval == 0 && !udev->ignore_device && udev_run) - retval = udev_rules_run(udev); - - udev_device_cleanup(udev); - return retval; -} +static int debug_trace; +static struct udev_rules *rules; +static struct udev_ctrl *udev_ctrl; +static struct udev_monitor *kernel_monitor; +static volatile int sigchilds_waiting; +static volatile int udev_exit; +static volatile int reload_config; +static volatile int signal_received; +static volatile pid_t settle_pid; +static int run_exec_q; +static int stop_exec_q; +static int max_childs; +static int childs; +static struct udev_list_node event_list; enum event_state { EVENT_QUEUED, @@ -154,164 +84,218 @@ enum event_state { EVENT_FAILED, }; -static void export_event_state(struct udevd_uevent_msg *msg, enum event_state state) +static struct udev_event *node_to_event(struct udev_list_node *node) +{ + char *event; + + event = (char *)node; + event -= offsetof(struct udev_event, node); + return (struct udev_event *)event; +} + +static void export_event_state(struct udev_event *event, enum event_state state) { - char filename[PATH_SIZE]; - char filename_failed[PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; + char filename_failed[UTIL_PATH_SIZE]; size_t start; /* location of queue file */ - snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu", udev_root, msg->seqnum); + snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu", + udev_get_dev_path(event->udev), udev_device_get_seqnum(event->dev)); /* location of failed file */ - strlcpy(filename_failed, udev_root, sizeof(filename_failed)); - strlcat(filename_failed, "/", sizeof(filename_failed)); - start = strlcat(filename_failed, ".udev/failed/", sizeof(filename_failed)); - strlcat(filename_failed, msg->devpath, sizeof(filename_failed)); - path_encode(&filename_failed[start], sizeof(filename_failed) - start); + util_strlcpy(filename_failed, udev_get_dev_path(event->udev), sizeof(filename_failed)); + util_strlcat(filename_failed, "/", sizeof(filename_failed)); + start = util_strlcat(filename_failed, ".udev/failed/", sizeof(filename_failed)); + util_strlcat(filename_failed, udev_device_get_devpath(event->dev), sizeof(filename_failed)); + util_path_encode(&filename_failed[start], sizeof(filename_failed) - start); switch (state) { case EVENT_QUEUED: - unlink(filename_failed); - delete_path(filename_failed); - - create_path(filename); - selinux_setfscreatecon(filename, NULL, S_IFLNK); - symlink(msg->devpath, filename); - selinux_resetfscreatecon(); + 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); break; case EVENT_FINISHED: - if (msg->devpath_old != NULL) { + if (udev_device_get_devpath_old(event->dev) != NULL) { /* "move" event - rename failed file to current name, do not delete failed */ - char filename_failed_old[PATH_SIZE]; + char filename_failed_old[UTIL_PATH_SIZE]; - strlcpy(filename_failed_old, udev_root, sizeof(filename_failed_old)); - strlcat(filename_failed_old, "/", sizeof(filename_failed_old)); - start = strlcat(filename_failed_old, ".udev/failed/", sizeof(filename_failed_old)); - strlcat(filename_failed_old, msg->devpath_old, sizeof(filename_failed_old)); - path_encode(&filename_failed_old[start], sizeof(filename) - start); + util_strlcpy(filename_failed_old, udev_get_dev_path(event->udev), sizeof(filename_failed_old)); + util_strlcat(filename_failed_old, "/", sizeof(filename_failed_old)); + start = util_strlcat(filename_failed_old, ".udev/failed/", sizeof(filename_failed_old)); + util_strlcat(filename_failed_old, udev_device_get_devpath_old(event->dev), sizeof(filename_failed_old)); + util_path_encode(&filename_failed_old[start], sizeof(filename) - start); if (rename(filename_failed_old, filename_failed) == 0) - info("renamed devpath, moved failed state of '%s' to %s'\n", - msg->devpath_old, msg->devpath); + 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(filename_failed); + if (unlink(filename_failed) == 0) + util_delete_path(event->udev, filename_failed); } unlink(filename); - delete_path(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(filename_failed); + util_create_path(event->udev, filename_failed); rename(filename, filename_failed); /* clean up possibly empty queue directory */ - delete_path(filename); + if (udev_list_is_empty(&event_list)) + util_delete_path(event->udev, filename); break; } return; } -static void msg_queue_delete(struct udevd_uevent_msg *msg) +static void event_queue_delete(struct udev_event *event) { - list_del(&msg->node); + udev_list_node_remove(&event->node); /* mark as failed, if "add" event returns non-zero */ - if (msg->exitstatus && strcmp(msg->action, "add") == 0) - export_event_state(msg, EVENT_FAILED); + if (event->exitstatus && strcmp(udev_device_get_action(event->dev), "add") == 0) + export_event_state(event, EVENT_FAILED); else - export_event_state(msg, EVENT_FINISHED); + export_event_state(event, EVENT_FINISHED); - free(msg); + udev_device_unref(event->dev); + udev_event_unref(event); } -static void udev_event_run(struct udevd_uevent_msg *msg) +static void asmlinkage event_sig_handler(int signum) +{ + if (signum == SIGALRM) + exit(1); +} + +static void event_fork(struct udev_event *event) { pid_t pid; - int retval; + 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 */ - close(uevent_netlink_sock); - close(udevd_sock); - if (inotify_fd >= 0) - close(inotify_fd); - close(signal_pipe[READ_END]); - close(signal_pipe[WRITE_END]); + udev_ctrl_unref(udev_ctrl); logging_close(); - logging_init("udevd-event"); setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); - retval = udev_event_process(msg); - info("seq %llu finished with %i\n", msg->seqnum, retval); + /* set signal handlers */ + memset(&act, 0x00, sizeof(act)); + act.sa_handler = (void (*)(int)) event_sig_handler; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGALRM, &act, NULL); + + /* reset to default */ + act.sa_handler = SIG_DFL; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGCHLD, &act, NULL); + sigaction(SIGHUP, &act, NULL); + + /* set timeout to prevent hanging processes */ + alarm(UDEV_EVENT_TIMEOUT); + + /* apply rules, create node, symlinks */ + err = udev_event_execute_rules(event, rules); + + /* rules may change/disable the timeout */ + if (udev_device_get_event_timeout(event->dev) >= 0) + alarm(udev_device_get_event_timeout(event->dev)); + + /* execute RUN= */ + 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); + } + /* 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 (retval) + if (err != 0) exit(1); exit(0); case -1: - err("fork of child failed: %s\n", strerror(errno)); - msg_queue_delete(msg); + err(event->udev, "fork of child failed: %m\n"); + event_queue_delete(event); break; default: /* get SIGCHLD in main loop */ - info("seq %llu forked, pid [%d], '%s' '%s', %ld seconds old\n", - msg->seqnum, pid, msg->action, msg->subsystem, time(NULL) - msg->queue_time); - msg->pid = pid; + info(event->udev, "seq %llu forked, pid [%d], '%s' '%s', %ld seconds old\n", + udev_device_get_seqnum(event->dev), + pid, + udev_device_get_action(event->dev), + udev_device_get_subsystem(event->dev), + time(NULL) - event->queue_time); + event->pid = pid; + childs++; } } -static void msg_queue_insert(struct udevd_uevent_msg *msg) +static void event_queue_insert(struct udev_event *event) { - char filename[PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; int fd; - msg->queue_time = time(NULL); + event->queue_time = time(NULL); - export_event_state(msg, EVENT_QUEUED); - info("seq %llu queued, '%s' '%s'\n", msg->seqnum, msg->action, msg->subsystem); + export_event_state(event, EVENT_QUEUED); + info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(event->dev), + udev_device_get_action(event->dev), udev_device_get_subsystem(event->dev)); - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); + util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); + util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); if (fd >= 0) { char str[32]; int len; - len = sprintf(str, "%llu\n", msg->seqnum); + len = sprintf(str, "%llu\n", udev_device_get_seqnum(event->dev)); write(fd, str, len); close(fd); } - /* run one event after the other in debug mode */ - if (debug_trace) { - list_add_tail(&msg->node, &running_list); - udev_event_run(msg); - waitpid(msg->pid, NULL, 0); - msg_queue_delete(msg); - return; - } + + udev_list_node_append(&event->node, &event_list); + run_exec_q = 1; /* run all events with a timeout set immediately */ - if (msg->timeout != 0) { - list_add_tail(&msg->node, &running_list); - udev_event_run(msg); + if (udev_device_get_timeout(event->dev) > 0) { + event_fork(event); return; } - - list_add_tail(&msg->node, &exec_list); - run_exec_q = 1; } static int mem_size_mb(void) { - FILE* f; + FILE *f; char buf[4096]; long int memsize = -1; @@ -332,475 +316,271 @@ static int mem_size_mb(void) return memsize; } -static int cpu_count(void) +static int compare_devpath(const char *running, const char *waiting) { - FILE* f; - char buf[4096]; - int count = 0; - - f = fopen("/proc/stat", "r"); - if (f == NULL) - return -1; - - while (fgets(buf, sizeof(buf), f) != NULL) { - if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) - count++; - } - - fclose(f); - if (count == 0) - return -1; - return count; -} + int i = 0; -static int running_processes(void) -{ - FILE* f; - char buf[4096]; - int running = -1; + while (running[i] != '\0' && running[i] == waiting[i]) + i++; - f = fopen("/proc/stat", "r"); - if (f == NULL) - return -1; + /* identical device event found */ + if (running[i] == '\0' && waiting[i] == '\0') + return 1; - while (fgets(buf, sizeof(buf), f) != NULL) { - int value; + /* parent device event found */ + if (running[i] == '\0' && waiting[i] == '/') + return 2; - if (sscanf(buf, "procs_running %u", &value) == 1) { - running = value; - break; - } - } + /* child device event found */ + if (running[i] == '/' && waiting[i] == '\0') + return 3; - fclose(f); - return running; + /* no matching event */ + return 0; } -/* return the number of process es in our session, count only until limit */ -static int running_processes_in_session(pid_t session, int limit) +/* lookup event for identical, parent, child, or physical device */ +static int devpath_busy(struct udev_event *event) { - DIR *dir; - struct dirent *dent; - int running = 0; - - dir = opendir("/proc"); - if (!dir) - return -1; - - /* read process info from /proc */ - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - int f; - char procdir[64]; - char line[256]; - const char *pos; - char state; - pid_t ppid, pgrp, sess; - int len; - - if (!isdigit(dent->d_name[0])) - continue; - - snprintf(procdir, sizeof(procdir), "/proc/%s/stat", dent->d_name); - procdir[sizeof(procdir)-1] = '\0'; + struct udev_list_node *loop; - f = open(procdir, O_RDONLY); - if (f == -1) - continue; - - len = read(f, line, sizeof(line)-1); - close(f); - - if (len <= 0) - continue; - else - line[len] = '\0'; - - /* skip ugly program name */ - pos = strrchr(line, ')') + 2; - if (pos == NULL) - continue; - - if (sscanf(pos, "%c %d %d %d ", &state, &ppid, &pgrp, &sess) != 4) - continue; - - /* count only processes in our session */ - if (sess != session) - continue; - - /* count only running, no sleeping processes */ - if (state != 'R') - continue; - - running++; - if (limit > 0 && running >= limit) - break; + if (event->delaying_seqnum > 0) { } - closedir(dir); - - return running; -} + /* check if queue contains events we depend on */ + udev_list_node_foreach(loop, &event_list) { + struct udev_event *loop_event = node_to_event(loop); -static int compare_devpath(const char *running, const char *waiting) -{ - int i; - - for (i = 0; i < PATH_SIZE; i++) { - /* identical device event found */ - if (running[i] == '\0' && waiting[i] == '\0') - return 1; + /* 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; - /* parent device event found */ - if (running[i] == '\0' && waiting[i] == '/') + /* event we checked earlier still exists, no need to check again */ + if (udev_device_get_seqnum(loop_event->dev) == event->delaying_seqnum) return 2; - /* child device event found */ - if (running[i] == '/' && waiting[i] == '\0') - return 3; - - /* no matching event */ - if (running[i] != waiting[i]) - break; - } - - return 0; -} - -/* lookup event for identical, parent, child, or physical device */ -static int devpath_busy(struct udevd_uevent_msg *msg, int limit) -{ - struct udevd_uevent_msg *loop_msg; - int childs_count = 0; - - /* check exec-queue which may still contain delayed events we depend on */ - list_for_each_entry(loop_msg, &exec_list, node) { - /* skip ourself and all later events */ - if (loop_msg->seqnum >= msg->seqnum) + /* 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 (msg->devpath_old != NULL) - if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) - return 2; - - /* check identical, parent, or child device event */ - if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { - dbg("%llu, device event still pending %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 3; - } - - /* check for our major:minor number */ - if (msg->devt && loop_msg->devt == msg->devt && - strcmp(msg->subsystem, loop_msg->subsystem) == 0) { - dbg("%llu, device event still pending %llu (%d:%d)\n", msg->seqnum, - loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); - return 4; - } - - /* check physical device event (special case of parent) */ - if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) - if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { - dbg("%llu, physical device event still pending %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 5; + 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) { + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 3; } - } - - /* check run queue for still running events */ - list_for_each_entry(loop_msg, &running_list, node) { - if (limit && childs_count++ > limit) { - dbg("%llu, maximum number (%i) of childs reached\n", msg->seqnum, childs_count); - return 1; - } - - /* check our old name */ - if (msg->devpath_old != NULL) - if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) - return 2; /* check identical, parent, or child device event */ - if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { - dbg("%llu, device event still running %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 3; + if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) { + dbg(event->udev, "%llu, 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)); + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 4; } /* check for our major:minor number */ - if (msg->devt && loop_msg->devt == msg->devt && - strcmp(msg->subsystem, loop_msg->subsystem) == 0) { - dbg("%llu, device event still running %llu (%d:%d)\n", msg->seqnum, - loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); - return 4; + 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 5; } /* check physical device event (special case of parent) */ - if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) - if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { - dbg("%llu, physical device event still running %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 5; + 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)); + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 6; } } return 0; } /* serializes events for the identical and parent and child devices */ -static void msg_queue_manager(void) +static void event_queue_manager(struct udev *udev) { - struct udevd_uevent_msg *loop_msg; - struct udevd_uevent_msg *tmp_msg; - int running; - - if (list_empty(&exec_list)) + 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); + childs = 0; + } return; + } - running = running_processes(); - dbg("%d processes runnning on system\n", running); - if (running < 0) - running = max_childs_running; - - list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, node) { - /* check running processes in our session and possibly throttle */ - if (running >= max_childs_running) { - running = running_processes_in_session(sid, max_childs_running+10); - dbg("at least %d processes running in session\n", running); - if (running >= max_childs_running) { - dbg("delay seq %llu, too many processes already running\n", loop_msg->seqnum); - return; - } + udev_list_node_foreach_safe(loop, tmp, &event_list) { + struct udev_event *loop_event = node_to_event(loop); + + if (childs >= max_childs) { + info(udev, "maximum number (%i) of childs reached\n", childs); + break; } - /* serialize and wait for parent or child events */ - if (devpath_busy(loop_msg, max_childs) != 0) { - dbg("delay seq %llu (%s)\n", loop_msg->seqnum, loop_msg->devpath); + 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 */ - list_move_tail(&loop_msg->node, &running_list); - udev_event_run(loop_msg); - running++; - dbg("moved seq %llu to running list\n", loop_msg->seqnum); + 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; + } } } -static struct udevd_uevent_msg *get_msg_from_envbuf(const char *buf, int buf_size) +/* receive the udevd message from userspace */ +static void handle_ctrl_msg(struct udev_ctrl *uctrl) { - int bufpos; + struct udev *udev = udev_ctrl_get_udev(uctrl); + struct udev_ctrl_msg *ctrl_msg; + const char *str; int i; - struct udevd_uevent_msg *msg; - char *physdevdriver_key = NULL; - int maj = 0; - int min = 0; - - msg = malloc(sizeof(struct udevd_uevent_msg) + buf_size); - if (msg == NULL) - return NULL; - memset(msg, 0x00, sizeof(struct udevd_uevent_msg) + buf_size); - - /* copy environment buffer and reconstruct envp */ - memcpy(msg->envbuf, buf, buf_size); - bufpos = 0; - for (i = 0; (bufpos < buf_size) && (i < UEVENT_NUM_ENVP-2); i++) { - int keylen; - char *key; - key = &msg->envbuf[bufpos]; - keylen = strlen(key); - msg->envp[i] = key; - bufpos += keylen + 1; - dbg("add '%s' to msg.envp[%i]\n", msg->envp[i], i); - - /* remember some keys for further processing */ - if (strncmp(key, "ACTION=", 7) == 0) - msg->action = &key[7]; - else if (strncmp(key, "DEVPATH=", 8) == 0) - msg->devpath = &key[8]; - else if (strncmp(key, "SUBSYSTEM=", 10) == 0) - msg->subsystem = &key[10]; - else if (strncmp(key, "DRIVER=", 7) == 0) - msg->driver = &key[7]; - else if (strncmp(key, "SEQNUM=", 7) == 0) - msg->seqnum = strtoull(&key[7], NULL, 10); - else if (strncmp(key, "DEVPATH_OLD=", 12) == 0) - msg->devpath_old = &key[12]; - else if (strncmp(key, "PHYSDEVPATH=", 12) == 0) - msg->physdevpath = &key[12]; - else if (strncmp(key, "PHYSDEVDRIVER=", 14) == 0) - physdevdriver_key = key; - else if (strncmp(key, "MAJOR=", 6) == 0) - maj = strtoull(&key[6], NULL, 10); - else if (strncmp(key, "MINOR=", 6) == 0) - min = strtoull(&key[6], NULL, 10); - else if (strncmp(key, "TIMEOUT=", 8) == 0) - msg->timeout = strtoull(&key[8], NULL, 10); - } - msg->devt = makedev(maj, min); - msg->envp[i++] = "UDEVD_EVENT=1"; + ctrl_msg = udev_ctrl_receive_msg(uctrl); + if (ctrl_msg == NULL) + return; - if (msg->driver == NULL && msg->physdevpath == NULL && physdevdriver_key != NULL) { - /* for older kernels DRIVER is empty for a bus device, export PHYSDEVDRIVER as DRIVER */ - msg->envp[i++] = &physdevdriver_key[7]; - msg->driver = &physdevdriver_key[14]; + i = udev_ctrl_get_set_log_level(ctrl_msg); + if (i >= 0) { + info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); + udev_set_log_priority(udev, i); } - msg->envp[i] = NULL; - - if (msg->devpath == NULL || msg->action == NULL) { - info("DEVPATH or ACTION missing, ignore message\n"); - free(msg); - return NULL; + if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { + info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); + stop_exec_q = 1; } - return msg; -} -/* receive the udevd message from userspace */ -static void get_ctrl_msg(void) -{ - struct udevd_ctrl_msg ctrl_msg; - ssize_t size; - struct msghdr smsg; - struct cmsghdr *cmsg; - struct iovec iov; - struct ucred *cred; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - int *intval; - char *pos; - - memset(&ctrl_msg, 0x00, sizeof(struct udevd_ctrl_msg)); - iov.iov_base = &ctrl_msg; - iov.iov_len = sizeof(struct udevd_ctrl_msg); - - memset(&smsg, 0x00, sizeof(struct msghdr)); - smsg.msg_iov = &iov; - smsg.msg_iovlen = 1; - smsg.msg_control = cred_msg; - smsg.msg_controllen = sizeof(cred_msg); - - size = recvmsg(udevd_sock, &smsg, 0); - if (size < 0) { - if (errno != EINTR) - err("unable to receive user udevd message: %s\n", strerror(errno)); - return; + if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { + info(udev, "udevd message (START_EXEC_QUEUE) received\n"); + stop_exec_q = 0; + event_queue_manager(udev); } - cmsg = CMSG_FIRSTHDR(&smsg); - cred = (struct ucred *) CMSG_DATA(cmsg); - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - err("no sender credentials received, message ignored\n"); - return; + if (udev_ctrl_get_reload_rules(ctrl_msg) > 0) { + info(udev, "udevd message (RELOAD_RULES) received\n"); + reload_config = 1; } - if (cred->uid != 0) { - err("sender uid=%i, message ignored\n", cred->uid); - return; + str = udev_ctrl_get_set_env(ctrl_msg); + if (str != NULL) { + char *key; + + key = strdup(str); + if (key != NULL) { + char *val; + + val = strchr(key, '='); + if (val != NULL) { + val[0] = '\0'; + val = &val[1]; + if (val[0] == '\0') { + info(udev, "udevd message (ENV) received, unset '%s'\n", key); + udev_add_property(udev, key, NULL); + } else { + info(udev, "udevd message (ENV) received, set '%s=%s'\n", key, val); + udev_add_property(udev, key, val); + } + } else { + err(udev, "wrong key format '%s'\n", key); + } + free(key); + } } - if (strncmp(ctrl_msg.magic, UDEVD_CTRL_MAGIC, sizeof(UDEVD_CTRL_MAGIC)) != 0 ) { - err("message magic '%s' doesn't match, ignore it\n", ctrl_msg.magic); - return; + i = udev_ctrl_get_set_max_childs(ctrl_msg); + if (i >= 0) { + info(udev, "udevd message (SET_MAX_CHILDS) received, max_childs=%i\n", i); + max_childs = i; } - switch (ctrl_msg.type) { - case UDEVD_CTRL_ENV: - pos = strchr(ctrl_msg.buf, '='); - if (pos == NULL) { - err("wrong key format '%s'\n", ctrl_msg.buf); - break; - } - pos[0] = '\0'; - if (pos[1] == '\0') { - info("udevd message (ENV) received, unset '%s'\n", ctrl_msg.buf); - unsetenv(ctrl_msg.buf); - } else { - info("udevd message (ENV) received, set '%s=%s'\n", ctrl_msg.buf, &pos[1]); - setenv(ctrl_msg.buf, &pos[1], 1); - } - break; - case UDEVD_CTRL_STOP_EXEC_QUEUE: - info("udevd message (STOP_EXEC_QUEUE) received\n"); - stop_exec_q = 1; - break; - case UDEVD_CTRL_START_EXEC_QUEUE: - info("udevd message (START_EXEC_QUEUE) received\n"); - stop_exec_q = 0; - msg_queue_manager(); - break; - case UDEVD_CTRL_SET_LOG_LEVEL: - intval = (int *) ctrl_msg.buf; - info("udevd message (SET_LOG_PRIORITY) received, udev_log_priority=%i\n", *intval); - udev_log_priority = *intval; - sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); - putenv(udev_log); - break; - case UDEVD_CTRL_SET_MAX_CHILDS: - intval = (int *) ctrl_msg.buf; - info("udevd message (UDEVD_SET_MAX_CHILDS) received, max_childs=%i\n", *intval); - max_childs = *intval; - break; - case UDEVD_CTRL_SET_MAX_CHILDS_RUNNING: - intval = (int *) ctrl_msg.buf; - info("udevd message (UDEVD_SET_MAX_CHILDS_RUNNING) received, max_childs=%i\n", *intval); - max_childs_running = *intval; - break; - case UDEVD_CTRL_RELOAD_RULES: - info("udevd message (RELOAD_RULES) received\n"); - reload_config = 1; - break; - default: - err("unknown control message type\n"); + 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); } -/* receive the kernel user event message and do some sanity checks */ -static struct udevd_uevent_msg *get_netlink_msg(void) +/* read inotify messages */ +static int handle_inotify(struct udev *udev) { - struct udevd_uevent_msg *msg; - int bufpos; - ssize_t size; - static char buffer[UEVENT_BUFFER_SIZE+512]; - char *pos; - - size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0); - if (size < 0) { - if (errno != EINTR) - err("unable to receive kernel netlink message: %s\n", strerror(errno)); - return NULL; - } + int nbytes, pos; + char *buf; + struct inotify_event *ev; - if ((size_t)size > sizeof(buffer)-1) - size = sizeof(buffer)-1; - buffer[size] = '\0'; - dbg("uevent_size=%zi\n", size); - - /* start of event payload */ - bufpos = strlen(buffer)+1; - msg = get_msg_from_envbuf(&buffer[bufpos], size-bufpos); - if (msg == NULL) - return NULL; - - /* validate message */ - pos = strchr(buffer, '@'); - if (pos == NULL) { - err("invalid uevent '%s'\n", buffer); - free(msg); - return NULL; - } - pos[0] = '\0'; + if ((ioctl(inotify_fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) + return 0; - if (msg->action == NULL) { - info("no ACTION in payload found, skip event '%s'\n", buffer); - free(msg); - return NULL; + buf = malloc(nbytes); + if (buf == NULL) { + err(udev, "error getting buffer for inotify, disable watching\n"); + close(inotify_fd); + inotify_fd = -1; + return 0; } - if (strcmp(msg->action, buffer) != 0) { - err("ACTION in payload does not match uevent, skip event '%s'\n", buffer); - free(msg); - return NULL; + 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); + } + } - return msg; + free (buf); + return 0; } static void asmlinkage sig_handler(int signum) @@ -819,23 +599,30 @@ 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) { - /* find msg associated with pid and delete it */ - struct udevd_uevent_msg *msg; - - list_for_each_entry(msg, &running_list, node) { - if (msg->pid == pid) { - info("seq %llu, pid [%d] exit with %i, %ld seconds old\n", msg->seqnum, msg->pid, - exitstatus, time(NULL) - msg->queue_time); - msg->exitstatus = exitstatus; - msg_queue_delete(msg); - - /* there may be events waiting with the same devpath */ + struct udev_list_node *loop; + + /* find event associated with pid and delete it */ + udev_list_node_foreach(loop, &event_list) { + struct udev_event *loop_event = node_to_event(loop); + + if (loop_event->pid == pid) { + info(loop_event->udev, "seq %llu cleanup, pid [%d], status %i, %ld seconds old\n", + 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 dependent events waiting */ run_exec_q = 1; return; } @@ -861,79 +648,47 @@ static void reap_sigchilds(void) } } -static int init_udevd_socket(void) +static void cleanup_queue_dir(struct udev *udev) { - struct sockaddr_un saddr; - socklen_t addrlen; - const int feature_on = 1; - int retval; - - memset(&saddr, 0x00, sizeof(saddr)); - saddr.sun_family = AF_LOCAL; - /* use abstract namespace for socket path */ - strcpy(&saddr.sun_path[1], UDEVD_CTRL_SOCK_PATH); - addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - - udevd_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (udevd_sock == -1) { - err("error getting socket: %s\n", strerror(errno)); - return -1; - } - - /* the bind takes care of ensuring only one copy running */ - retval = bind(udevd_sock, (struct sockaddr *) &saddr, addrlen); - if (retval < 0) { - err("bind failed: %s\n", strerror(errno)); - close(udevd_sock); - udevd_sock = -1; - return -1; - } - - /* enable receiving of the sender credentials */ - setsockopt(udevd_sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); - - return 0; -} - -static int init_uevent_netlink_sock(void) -{ - struct sockaddr_nl snl; - const int buffersize = 16 * 1024 * 1024; - int retval; - - memset(&snl, 0x00, sizeof(struct sockaddr_nl)); - snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); - snl.nl_groups = 1; - - uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if (uevent_netlink_sock == -1) { - err("error getting socket: %s\n", strerror(errno)); - return -1; - } - - /* set receive buffersize */ - setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); + char dirname[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; + DIR *dir; - retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); - if (retval < 0) { - err("bind failed: %s\n", strerror(errno)); - close(uevent_netlink_sock); - uevent_netlink_sock = -1; - return -1; + 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); } - return 0; } -static void export_initial_seqnum(void) +static void export_initial_seqnum(struct udev *udev) { - char filename[PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; int fd; char seqnum[32]; ssize_t len = 0; - strlcpy(filename, sysfs_path, sizeof(filename)); - strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); + util_strlcpy(filename, udev_get_sys_path(udev), sizeof(filename)); + util_strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); fd = open(filename, O_RDONLY); if (fd >= 0) { len = read(fd, seqnum, sizeof(seqnum)-1); @@ -943,9 +698,9 @@ static void export_initial_seqnum(void) strcpy(seqnum, "0\n"); len = 3; } - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); - create_path(filename); + util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); + util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); + util_create_path(udev, filename); fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); if (fd >= 0) { write(fd, seqnum, len); @@ -953,32 +708,65 @@ static void export_initial_seqnum(void) } } -int main(int argc, char *argv[], char *envp[]) +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[]) { - int retval; + struct udev *udev; int fd; struct sigaction act; - fd_set readfds; const char *value; int daemonize = 0; - int option; + int resolve_names = 1; static const struct option options[] = { - { "daemon", 0, NULL, 'd' }, - { "debug-trace", 0, NULL, 't' }, - { "debug", 0, NULL, 'D' }, - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, + { "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) + goto exit; logging_init("udevd"); - udev_config_init(); - selinux_init(); - dbg("version %s\n", VERSION); + udev_set_log_fn(udev, log_fn); + info(udev, "version %s\n", VERSION); + udev_selinux_init(udev); while (1) { + int option; + option = getopt_long(argc, argv, "dDthV", options, NULL); if (option == -1) break; @@ -992,11 +780,25 @@ int main(int argc, char *argv[], char *envp[]) break; case 'D': debug = 1; - if (udev_log_priority < LOG_INFO) - udev_log_priority = LOG_INFO; + 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); @@ -1008,7 +810,7 @@ int main(int argc, char *argv[], char *envp[]) if (getuid() != 0) { fprintf(stderr, "root privileges required\n"); - err("root privileges required\n"); + err(udev, "root privileges required\n"); goto exit; } @@ -1016,70 +818,46 @@ int main(int argc, char *argv[], char *envp[]) fd = open("/dev/null", O_RDWR); if (fd < 0) { fprintf(stderr, "cannot open /dev/null\n"); - err("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) dup2(fd, STDERR_FILENO); - /* init sockets to receive events */ - if (init_udevd_socket() < 0) { - if (errno == EADDRINUSE) { - fprintf(stderr, "another udev daemon already running\n"); - err("another udev daemon already running\n"); - rc = 1; - } else { - fprintf(stderr, "error initializing udevd socket\n"); - err("error initializing udevd socket\n"); - rc = 2; - } + /* init control socket, bind() ensures, that only one udevd instance is running */ + udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH); + if (udev_ctrl == NULL) { + fprintf(stderr, "error initializing control socket"); + err(udev, "error initializing udevd socket"); + rc = 1; goto exit; } - if (init_uevent_netlink_sock() < 0) { - fprintf(stderr, "error initializing netlink socket\n"); - err("error initializing netlink socket\n"); - rc = 3; + if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { + fprintf(stderr, "error binding control socket, seems udevd is already running\n"); + err(udev, "error binding control socket, seems udevd is already running\n"); + rc = 1; goto exit; } - /* setup signal handler pipe */ - retval = pipe(signal_pipe); - if (retval < 0) { - err("error getting pipes: %s\n", strerror(errno)); + 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"); + rc = 3; goto exit; } + udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - retval = fcntl(signal_pipe[READ_END], F_GETFL, 0); - if (retval < 0) { - err("error fcntl on read pipe: %s\n", strerror(errno)); - goto exit; - } - retval = fcntl(signal_pipe[READ_END], F_SETFL, retval | O_NONBLOCK); - if (retval < 0) { - err("error fcntl on read pipe: %s\n", strerror(errno)); + rules = udev_rules_new(udev, resolve_names); + if (rules == NULL) { + err(udev, "error reading rules\n"); goto exit; } - - retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0); - if (retval < 0) { - err("error fcntl on write pipe: %s\n", strerror(errno)); - goto exit; - } - retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK); - if (retval < 0) { - err("error fcntl on write pipe: %s\n", strerror(errno)); - goto exit; - } - - /* parse the rules and keep them in memory */ - sysfs_init(); - udev_rules_init(&rules, 1); - - export_initial_seqnum(); + udev_list_init(&event_list); + cleanup_queue_dir(udev); + export_initial_seqnum(udev); if (daemonize) { pid_t pid; @@ -1087,23 +865,25 @@ int main(int argc, char *argv[], char *envp[]) pid = fork(); switch (pid) { case 0: - dbg("daemonized fork running\n"); + dbg(udev, "daemonized fork running\n"); break; case -1: - err("fork of daemon failed: %s\n", strerror(errno)); + err(udev, "fork of daemon failed: %m\n"); rc = 4; goto exit; default: - dbg("child [%u] running, parent exits\n", pid); + dbg(udev, "child [%u] running, parent exits\n", pid); rc = 0; goto exit; } } - /* redirect std{out,err} fd's */ - if (!debug) + /* redirect std{out,err} */ + if (!debug && !debug_trace) { + dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); + dup2(fd, STDERR_FILENO); + } if (fd > STDERR_FILENO) close(fd); @@ -1112,27 +892,18 @@ int main(int argc, char *argv[], char *envp[]) chdir("/"); umask(022); - - /* become session leader */ - sid = setsid(); - dbg("our session is %d\n", sid); + setsid(); /* OOM_DISABLE == -17 */ fd = open("/proc/self/oom_adj", O_RDWR); if (fd < 0) - err("error disabling OOM: %s\n", strerror(errno)); + err(udev, "error disabling OOM: %m\n"); else { write(fd, "-17", 3); close(fd); } - fd = open("/dev/kmsg", O_WRONLY); - if (fd > 0) { - const char *str = "<6>udevd version " VERSION " started\n"; - - write(fd, str, strlen(str)); - close(fd); - } + startup_log(udev); /* set signal handlers */ memset(&act, 0x00, sizeof(struct sigaction)); @@ -1145,13 +916,13 @@ int main(int argc, char *argv[], char *envp[]) sigaction(SIGHUP, &act, NULL); /* watch rules directory */ - inotify_fd = inotify_init(); + udev_watch_init(udev); if (inotify_fd >= 0) { - if (udev_rules_dir[0] != '\0') { - inotify_add_watch(inotify_fd, udev_rules_dir, + 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); @@ -1159,120 +930,107 @@ int main(int argc, char *argv[], char *envp[]) IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); /* watch dynamic rules directory */ - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/.udev/rules.d", sizeof(filename)); + util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); + util_strlcat(filename, "/.udev/rules.d", sizeof(filename)); inotify_add_watch(inotify_fd, filename, IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); } - } else if (errno == ENOSYS) - err("the kernel does not support inotify, udevd can't monitor rules file changes\n"); - else - err("inotify_init failed: %s\n", strerror(errno)); - /* 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; } - info("initialize max_childs to %u\n", max_childs); - - /* start to throttle forking if maximum number of _running_ childs is reached */ - value = getenv("UDEVD_MAX_CHILDS_RUNNING"); + /* possibly overwrite maximum limit of executed events */ + value = getenv("UDEVD_MAX_CHILDS"); if (value) - max_childs_running = strtoull(value, NULL, 10); - else { - int cpus = cpu_count(); - if (cpus > 0) - max_childs_running = 8 + (8 * cpus); - else - max_childs_running = UDEVD_MAX_CHILDS_RUNNING; - } - info("initialize max_childs_running to %u\n", max_childs_running); + max_childs = strtoul(value, NULL, 10); + info(udev, "initialize max_childs to %u\n", max_childs); - /* clear environment for forked event processes */ - clearenv(); + 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; - /* export log_priority , as called programs may want to follow that setting */ - sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); - putenv(udev_log); - if (debug_trace) - putenv("DEBUG=1"); + sigfillset(&blocked_mask); + sigprocmask(SIG_SETMASK, &blocked_mask, &orig_mask); + if (signal_received) { + sigprocmask(SIG_SETMASK, &orig_mask, NULL); + goto handle_signals; + } - maxfd = udevd_sock; - maxfd = UDEV_MAX(maxfd, uevent_netlink_sock); - maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]); - maxfd = UDEV_MAX(maxfd, inotify_fd); + ctrl_poll = &pfd[nfds++]; + ctrl_poll->fd = udev_ctrl_get_fd(udev_ctrl); + ctrl_poll->events = POLLIN; - while (!udev_exit) { - struct udevd_uevent_msg *msg; - int fdcount; + monitor_poll = &pfd[nfds++]; + monitor_poll->fd = udev_monitor_get_fd(kernel_monitor); + monitor_poll->events = POLLIN; - FD_ZERO(&readfds); - FD_SET(signal_pipe[READ_END], &readfds); - FD_SET(udevd_sock, &readfds); - FD_SET(uevent_netlink_sock, &readfds); - if (inotify_fd >= 0) - FD_SET(inotify_fd, &readfds); + if (inotify_fd >= 0) { + inotify_poll = &pfd[nfds++]; + inotify_poll->fd = inotify_fd; + inotify_poll->events = POLLIN; + } - fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL); + fdcount = ppoll(pfd, nfds, NULL, &orig_mask); + sigprocmask(SIG_SETMASK, &orig_mask, NULL); if (fdcount < 0) { - if (errno != EINTR) - err("error in select: %s\n", strerror(errno)); + if (errno == EINTR) + goto handle_signals; + err(udev, "error in select: %m\n"); continue; } /* get control message */ - if (FD_ISSET(udevd_sock, &readfds)) - get_ctrl_msg(); - - /* get netlink message */ - if (FD_ISSET(uevent_netlink_sock, &readfds)) { - msg = get_netlink_msg(); - if (msg) - msg_queue_insert(msg); - } - - /* 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)); + if (ctrl_poll->revents & POLLIN) + handle_ctrl_msg(udev_ctrl); + + /* get kernel uevent */ + if (monitor_poll->revents & POLLIN) { + struct udev_device *dev; + + dev = udev_monitor_receive_device(kernel_monitor); + if (dev != NULL) { + struct udev_event *event; + + event = udev_event_new(dev); + if (event != NULL) + event_queue_insert(event); + else + udev_device_unref(dev); + } } /* 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("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; - udev_rules_cleanup(&rules); - udev_rules_init(&rules, 1); + rules_new = udev_rules_new(udev, resolve_names); + if (rules_new != NULL) { + udev_rules_unref(rules); + rules = rules_new; + } } - /* forked child has returned */ if (sigchilds_waiting) { sigchilds_waiting = 0; reap_sigchilds(); @@ -1281,29 +1039,24 @@ int main(int argc, char *argv[], char *envp[]) if (run_exec_q) { run_exec_q = 0; if (!stop_exec_q) - msg_queue_manager(); + event_queue_manager(udev); + } + + if (settle_pid > 0) { + kill(settle_pid, SIGUSR1); + settle_pid = 0; } } + cleanup_queue_dir(udev); rc = 0; - exit: - udev_rules_cleanup(&rules); - sysfs_cleanup(); - selinux_exit(); - - if (signal_pipe[READ_END] >= 0) - close(signal_pipe[READ_END]); - if (signal_pipe[WRITE_END] >= 0) - close(signal_pipe[WRITE_END]); - - if (udevd_sock >= 0) - close(udevd_sock); + udev_rules_unref(rules); + udev_ctrl_unref(udev_ctrl); if (inotify_fd >= 0) close(inotify_fd); - if (uevent_netlink_sock >= 0) - close(uevent_netlink_sock); - + udev_monitor_unref(kernel_monitor); + udev_selinux_exit(udev); + udev_unref(udev); logging_close(); - return rc; }