X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udevd.c;h=154c37058958f39c94711b73b365fce8f34edca9;hp=ffd2b1ff3b4e78c45b48b72b0a14b23fe5153ced;hb=287814b23cd564043391ce449f27e6ccd9d244e4;hpb=3f9f8de4de49135476644297585bd910b14d0ea7 diff --git a/udevd.c b/udevd.c index ffd2b1ff3..154c37058 100644 --- a/udevd.c +++ b/udevd.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -46,29 +47,31 @@ #include "udev_libc_wrapper.h" #include "udev.h" #include "udev_version.h" +#include "udev_rules.h" #include "udev_utils.h" #include "udevd.h" #include "logging.h" /* global variables*/ +struct udev_rules rules; static int udevd_sock; static int uevent_netlink_sock; +static int inotify_fd; static pid_t sid; -static int pipefds[2] = {-1, -1}; +static int signal_pipe[2] = {-1, -1}; static volatile int sigchilds_waiting; static volatile int run_msg_q; -static volatile int sig_flag; static volatile int udev_exit; +static volatile int reload_config; static int init_phase = 1; static int run_exec_q; static int stop_exec_q; -static char *udev_bin; static int event_timeout; static int max_childs; static int max_childs_running; static unsigned long long expected_seqnum; -static char log[32]; +static char udev_log[32]; static LIST_HEAD(msg_list); static LIST_HEAD(exec_list); @@ -76,7 +79,7 @@ static LIST_HEAD(running_list); #ifdef USE_LOG -void log_message (int priority, const char *format, ...) +void log_message(int priority, const char *format, ...) { va_list args; @@ -113,7 +116,7 @@ static void msg_queue_insert(struct uevent_msg *msg) if (msg->seqnum == 0) { dbg("no SEQNUM, move straight to the exec queue"); - list_add(&msg->node, &exec_list); + list_add_tail(&msg->node, &exec_list); run_exec_q = 1; return; } @@ -132,15 +135,6 @@ static void msg_queue_insert(struct uevent_msg *msg) init_phase = 0; } - /* don't delay messages with timeout set */ - if (msg->timeout) { - info("seq %llu with timeout %u seconds will be execute without queuing, '%s' '%s'", - msg->seqnum, msg->timeout, msg->action, msg->devpath); - list_add(&msg->node, &exec_list); - run_exec_q = 1; - return; - } - /* sort message by sequence number into list */ list_for_each_entry_reverse(loop_msg, &msg_list, node) { if (loop_msg->seqnum < msg->seqnum) @@ -161,10 +155,55 @@ static void msg_queue_insert(struct uevent_msg *msg) return; } -/* forks event and removes event from run queue when finished */ +static void asmlinkage udev_event_sig_handler(int signum) +{ + if (signum == SIGALRM) + exit(1); +} + +static int udev_event_process(struct uevent_msg *msg) +{ + struct sigaction act; + struct udevice udev; + struct name_entry *name_loop; + 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); + + /* trigger timeout to prevent hanging processes */ + alarm(UDEV_ALARM_TIMEOUT); + + /* reconstruct env from message */ + for (i = 0; msg->envp[i]; i++) + putenv(msg->envp[i]); + + udev_init_device(&udev, msg->devpath, msg->subsystem, msg->action); + retval = udev_process_event(&rules, &udev); + + /* run programs collected by RUN-key*/ + if (!retval) { + list_for_each_entry(name_loop, &udev.run_list, node) { + if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0) + pass_env_to_socket(&name_loop->name[strlen("socket:")], msg->devpath, msg->action); + else + run_program(name_loop->name, udev.subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO)); + } + } + + udev_cleanup_device(&udev); + + return 0; +} + +/* runs event and removes event from run queue when finished */ static void udev_event_run(struct uevent_msg *msg) { - char *const argv[] = { "udev", msg->subsystem, NULL }; pid_t pid; struct sysinfo info; @@ -172,14 +211,21 @@ static void udev_event_run(struct uevent_msg *msg) switch (pid) { case 0: /* child */ - if (uevent_netlink_sock != -1) - close(uevent_netlink_sock); + close(uevent_netlink_sock); close(udevd_sock); + 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); - execve(udev_bin, argv, msg->envp); - err("exec of child failed"); - _exit(1); + udev_event_process(msg); + info("seq %llu finished", msg->seqnum); + + logging_close(); + exit(0); case -1: err("fork of child failed"); msg_queue_delete(msg); @@ -205,7 +251,7 @@ static int running_processes(void) if (f == -1) return -1; - len = read(f, buf, sizeof(buf)); + len = read(f, buf, sizeof(buf)-1); close(f); if (len <= 0) @@ -254,7 +300,7 @@ static int running_processes_in_session(pid_t session, int limit) if (f == -1) continue; - len = read(f, line, sizeof(line)); + len = read(f, line, sizeof(line)-1); close(f); if (len <= 0) @@ -420,8 +466,9 @@ recheck: /* limit timeout during initialization phase */ if (init_phase) { - timeout = UDEVD_INIT_EVENT_TIMEOUT; - dbg("initialization phase, limit timeout to %i seconds", UDEVD_INIT_EVENT_TIMEOUT); + if (timeout > UDEVD_INIT_EVENT_TIMEOUT) + timeout = UDEVD_INIT_EVENT_TIMEOUT; + dbg("initialization phase, timeout %i seconds", timeout); } /* move event with expired timeout to the exec list */ @@ -430,9 +477,8 @@ recheck: if (msg_age >= timeout) { msg_move_exec(loop_msg); goto recheck; - } else { + } else break; - } } msg_dump_queue(); @@ -440,7 +486,7 @@ recheck: /* set timeout for remaining queued events */ if (!list_empty(&msg_list)) { struct itimerval itv = {{0, 0}, {timeout - msg_age, 0}}; - dbg("next event expires in %li seconds", timeout - msg_age); + info("next event expires in %li seconds", timeout - msg_age); setitimer(ITIMER_REAL, &itv, NULL); } } @@ -523,31 +569,31 @@ static struct uevent_msg *get_udevd_msg(void) size = recvmsg(udevd_sock, &smsg, 0); if (size < 0) { if (errno != EINTR) - dbg("unable to receive udevd message"); + err("unable to receive udevd message"); return NULL; } cmsg = CMSG_FIRSTHDR(&smsg); cred = (struct ucred *) CMSG_DATA(cmsg); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - info("no sender credentials received, message ignored"); + err("no sender credentials received, message ignored"); return NULL; } if (cred->uid != 0) { - info("sender uid=%i, message ignored", cred->uid); + err("sender uid=%i, message ignored", cred->uid); return NULL; } if (strncmp(usend_msg.magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) { - info("message magic '%s' doesn't match, ignore it", usend_msg.magic); + err("message magic '%s' doesn't match, ignore it", usend_msg.magic); return NULL; } switch (usend_msg.type) { case UDEVD_UEVENT_UDEVSEND: case UDEVD_UEVENT_INITSEND: - dbg("udevd event message received"); + info("udevd event message received"); envbuf_size = size - offsetof(struct udevd_msg, envbuf); dbg("envbuf_size=%i", envbuf_size); msg = get_msg_from_envbuf(usend_msg.envbuf, envbuf_size); @@ -568,14 +614,18 @@ static struct uevent_msg *get_udevd_msg(void) intval = (int *) usend_msg.envbuf; info("udevd message (SET_LOG_PRIORITY) received, udev_log_priority=%i", *intval); udev_log_priority = *intval; - sprintf(log, "UDEV_LOG=%i", udev_log_priority); - putenv(log); + sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); + putenv(udev_log); break; case UDEVD_SET_MAX_CHILDS: intval = (int *) usend_msg.envbuf; info("udevd message (UDEVD_SET_MAX_CHILDS) received, max_childs=%i", *intval); max_childs = *intval; break; + case UDEVD_RELOAD_RULES: + info("udevd message (RELOAD_RULES) received"); + reload_config = 1; + break; default: dbg("unknown message type"); } @@ -594,7 +644,7 @@ static struct uevent_msg *get_netlink_msg(void) size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0); if (size < 0) { if (errno != EINTR) - dbg("unable to receive udevd message"); + err("unable to receive udevd message"); return NULL; } @@ -613,20 +663,20 @@ static struct uevent_msg *get_netlink_msg(void) /* validate message */ pos = strchr(buffer, '@'); if (pos == NULL) { - dbg("invalid uevent '%s'", buffer); + err("invalid uevent '%s'", buffer); free(msg); return NULL; } pos[0] = '\0'; if (msg->action == NULL) { - dbg("no ACTION in payload found, skip event '%s'", buffer); + info("no ACTION in payload found, skip event '%s'", buffer); free(msg); return NULL; } if (strcmp(msg->action, buffer) != 0) { - dbg("ACTION in payload does not match uevent, skip event '%s'", buffer); + err("ACTION in payload does not match uevent, skip event '%s'", buffer); free(msg); return NULL; } @@ -636,8 +686,6 @@ static struct uevent_msg *get_netlink_msg(void) static void asmlinkage sig_handler(int signum) { - int rc; - switch (signum) { case SIGINT: case SIGTERM: @@ -651,16 +699,13 @@ static void asmlinkage sig_handler(int signum) /* set flag, then write to pipe if needed */ sigchilds_waiting = 1; break; + case SIGHUP: + reload_config = 1; + break; } - /* if pipe is empty, write to pipe to force select to return, - * which will wakeup our mainloop - */ - if (!sig_flag) { - rc = write(pipefds[1], &signum, sizeof(signum)); - if (rc >= 0) - sig_flag = 1; - } + /* write to pipe, which will wakeup select() in our mainloop */ + write(signal_pipe[WRITE_END], "", 1); } static void udev_done(int pid) @@ -672,7 +717,10 @@ static void udev_done(int pid) list_for_each_entry(msg, &running_list, node) { if (msg->pid == pid) { sysinfo(&info); - info("seq %llu exit, %ld seconds old", msg->seqnum, info.uptime - msg->queue_time); + if (msg->queue_time) + info("seq %llu, pid [%d] exit, %ld seconds old", msg->seqnum, msg->pid, info.uptime - msg->queue_time); + else + info("seq 0, pid [%d] exit", msg->pid); msg_queue_delete(msg); /* we want to run the exec queue manager since there may @@ -700,7 +748,7 @@ static void reap_sigchilds(void) static int init_udevd_socket(void) { struct sockaddr_un saddr; - const int buffersize = 1024 * 1024; + const int buffersize = 16 * 1024 * 1024; socklen_t addrlen; const int feature_on = 1; int retval; @@ -718,13 +766,12 @@ static int init_udevd_socket(void) } /* set receive buffersize */ - setsockopt(udevd_sock, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)); + setsockopt(udevd_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); /* 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", strerror(errno)); - close(udevd_sock); return -1; } @@ -737,7 +784,7 @@ static int init_udevd_socket(void) static int init_uevent_netlink_sock(void) { struct sockaddr_nl snl; - const int buffersize = 1024 * 1024; + const int buffersize = 16 * 1024 * 1024; int retval; memset(&snl, 0x00, sizeof(struct sockaddr_nl)); @@ -747,51 +794,50 @@ static int init_uevent_netlink_sock(void) uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (uevent_netlink_sock == -1) { - dbg("error getting socket, %s", strerror(errno)); + err("error getting socket, %s", strerror(errno)); return -1; } /* set receive buffersize */ - setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize)); + setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); - retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, - sizeof(struct sockaddr_nl)); + retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); if (retval < 0) { - dbg("bind failed, %s", strerror(errno)); + err("bind failed, %s", strerror(errno)); close(uevent_netlink_sock); uevent_netlink_sock = -1; return -1; } - return 0; } int main(int argc, char *argv[], char *envp[]) { int retval; - int devnull; + int fd; struct sigaction act; fd_set readfds; const char *value; int uevent_netlink_active = 0; int daemonize = 0; int i; - - /* set std fd's to /dev/null, if the kernel forks us, we don't have them at all */ - devnull = open("/dev/null", O_RDWR); - if (devnull >= 0) { - if (devnull != STDIN_FILENO) - dup2(devnull, STDIN_FILENO); - if (devnull != STDOUT_FILENO) - dup2(devnull, STDOUT_FILENO); - if (devnull != STDERR_FILENO) - dup2(devnull, STDERR_FILENO); - if (devnull > STDERR_FILENO) - close(devnull); + int rc = 0; + + /* redirect std fd's, if the kernel forks us, we don't have them at all */ + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + if (fd != STDIN_FILENO) + dup2(fd, STDIN_FILENO); + if (fd != STDOUT_FILENO) + dup2(fd, STDOUT_FILENO); + if (fd != STDERR_FILENO) + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); } logging_init("udevd"); - if (devnull < 0) + if (fd < 0) err("fatal, could not open /dev/null"); udev_init_config(); @@ -802,6 +848,7 @@ int main(int argc, char *argv[], char *envp[]) goto exit; } + /* parse commandline options */ for (i = 1 ; i < argc; i++) { char *arg = argv[i]; if (strcmp(arg, "--daemon") == 0 || strcmp(arg, "-d") == 0) { @@ -814,55 +861,80 @@ int main(int argc, char *argv[], char *envp[]) } } + /* init sockets to receive events */ + if (init_udevd_socket() < 0) { + if (errno == EADDRINUSE) { + dbg("another udevd running, exit"); + rc = 1; + } else { + dbg("error initializing udevd socket: %s", strerror(errno)); + rc = 2; + } + goto exit; + } + + if (init_uevent_netlink_sock() < 0) { + err("uevent socket not available"); + rc = 3; + goto exit; + } + + /* parse the rules and keep it in memory */ + udev_rules_init(&rules, 1); + if (daemonize) { pid_t pid; pid = fork(); switch (pid) { case 0: - dbg("damonized fork running"); + dbg("daemonized fork running"); break; case -1: err("fork of daemon failed"); + rc = 4; goto exit; default: - logging_close(); - exit(0); + dbg("child [%u] running, parent exits", pid); + goto exit; } } + /* set scheduling priority for the daemon */ + setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); + + chdir("/"); + umask(077); + /* become session leader */ sid = setsid(); dbg("our session is %d", sid); - chdir("/"); - umask(umask(077) | 022); - - /* set a reasonable scheduling priority for the daemon */ - setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); + /* OOM_DISABLE == -17 */ + fd = open("/proc/self/oom_adj", O_RDWR); + if (fd < 0) + err("error disabling OOM"); + else { + write(fd, "-17", 3); + close(fd); + } /* setup signal handler pipe */ - retval = pipe(pipefds); + retval = pipe(signal_pipe); if (retval < 0) { err("error getting pipes: %s", strerror(errno)); goto exit; } - retval = fcntl(pipefds[0], F_SETFL, O_NONBLOCK); + retval = fcntl(signal_pipe[READ_END], F_SETFL, O_NONBLOCK); if (retval < 0) { err("error fcntl on read pipe: %s", strerror(errno)); goto exit; } - retval = fcntl(pipefds[0], F_SETFD, FD_CLOEXEC); - if (retval < 0) - err("error fcntl on read pipe: %s", strerror(errno)); - retval = fcntl(pipefds[1], F_SETFL, O_NONBLOCK); + retval = fcntl(signal_pipe[WRITE_END], F_SETFL, O_NONBLOCK); if (retval < 0) { err("error fcntl on write pipe: %s", strerror(errno)); goto exit; } - retval = fcntl(pipefds[1], F_SETFD, FD_CLOEXEC); - if (retval < 0) - err("error fcntl on write pipe: %s", strerror(errno)); /* set signal handlers */ memset(&act, 0x00, sizeof(struct sigaction)); @@ -875,24 +947,10 @@ int main(int argc, char *argv[], char *envp[]) sigaction(SIGCHLD, &act, NULL); sigaction(SIGHUP, &act, NULL); - if (init_udevd_socket() < 0) { - if (errno == EADDRINUSE) - dbg("another udevd running, exit"); - else - dbg("error initialising udevd socket: %s", strerror(errno)); - - goto exit; - } - - if (init_uevent_netlink_sock() < 0) - info("uevent socket not available"); - - /* override of forked udev binary, used for testing */ - udev_bin = getenv("UDEV_BIN"); - if (udev_bin != NULL) - info("udev binary is set to '%s'", udev_bin); - else - udev_bin = UDEV_BIN; + /* watch rules directory */ + inotify_fd = inotify_init(); + if (inotify_fd > 0) + inotify_add_watch(inotify_fd, udev_rules_filename, IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); /* init of expected_seqnum value */ value = getenv("UDEVD_EXPECTED_SEQNUM"); @@ -925,21 +983,25 @@ int main(int argc, char *argv[], char *envp[]) max_childs_running = UDEVD_MAX_CHILDS_RUNNING; info("initialize max_childs_running to %u", max_childs_running); + /* clear environment for forked event processes */ + clearenv(); + /* export log_priority , as called programs may want to follow that setting */ - sprintf(log, "UDEV_LOG=%i", udev_log_priority); - putenv(log); + sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); + putenv(udev_log); while (!udev_exit) { struct uevent_msg *msg; int fdcount; FD_ZERO(&readfds); - FD_SET(pipefds[0], &readfds); + FD_SET(signal_pipe[READ_END], &readfds); FD_SET(udevd_sock, &readfds); - if (uevent_netlink_sock > 0) - FD_SET(uevent_netlink_sock, &readfds); + FD_SET(uevent_netlink_sock, &readfds); + if (inotify_fd > 0) + FD_SET(inotify_fd, &readfds); - fdcount = select(UDEV_MAX(udevd_sock, uevent_netlink_sock)+1, &readfds, NULL, NULL, NULL); + fdcount = select(UDEV_MAX(uevent_netlink_sock, inotify_fd)+1, &readfds, NULL, NULL, NULL); if (fdcount < 0) { if (errno != EINTR) dbg("error in select: %s", strerror(errno)); @@ -952,16 +1014,15 @@ int main(int argc, char *argv[], char *envp[]) if (msg) { /* discard kernel messages if netlink is active */ if (uevent_netlink_active && msg->type == UDEVD_UEVENT_UDEVSEND && msg->seqnum != 0) { - dbg("skip uevent_helper message, netlink is active"); + info("skip uevent_helper message with SEQNUM, netlink is active"); free(msg); - continue; - } - msg_queue_insert(msg); + } else + msg_queue_insert(msg); } } /* get kernel netlink message */ - if ((uevent_netlink_sock > 0) && FD_ISSET(uevent_netlink_sock, &readfds)) { + if (FD_ISSET(uevent_netlink_sock, &readfds)) { msg = get_netlink_msg(); if (msg) { msg_queue_insert(msg); @@ -974,16 +1035,37 @@ int main(int argc, char *argv[], char *envp[]) } /* received a signal, clear our notification pipe */ - if (FD_ISSET(pipefds[0], &readfds)) { - int sig; - ssize_t rlen; - - while(1) { - rlen = read(pipefds[0], &sig, sizeof(sig)); - if (rlen <= 0) - break; + 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) { + char *buf; + + reload_config = 1; + buf = malloc(nbytes); + if (!buf) { + err("error getting buffer for inotify, disable watching"); + close(inotify_fd); + inotify_fd = -1; + } + read(inotify_fd, buf, nbytes); + free(buf); } - sig_flag = 0; + } + + /* rules changed, set by inotify or a signal*/ + if (reload_config) { + reload_config = 0; + udev_rules_close(&rules); + udev_rules_init(&rules, 1); } /* forked child have returned */ @@ -1011,17 +1093,21 @@ int main(int argc, char *argv[], char *envp[]) } exit: - if (pipefds[0] > 0) - close(pipefds[0]); - if (pipefds[1] > 0) - close(pipefds[1]); + udev_rules_close(&rules); + + 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); + if (inotify_fd > 0) + close(inotify_fd); if (uevent_netlink_sock > 0) close(uevent_netlink_sock); logging_close(); - return 0; + return rc; }