diff options
-rw-r--r-- | execute.h | 3 | ||||
-rw-r--r-- | main.c | 2 | ||||
-rw-r--r-- | manager.c | 128 | ||||
-rw-r--r-- | manager.h | 6 | ||||
-rw-r--r-- | name.c | 43 | ||||
-rw-r--r-- | name.h | 13 | ||||
-rw-r--r-- | socket-util.c | 2 | ||||
-rw-r--r-- | socket.c | 27 |
8 files changed, 217 insertions, 7 deletions
@@ -18,7 +18,8 @@ typedef struct ExecContext ExecContext; struct ExecStatus { pid_t pid; time_t timestamp; - int status; /* as in wait() */ + int code; /* as in siginfo_t::si_code */ + int status; /* as in sigingo_t::si_status */ }; struct ExecCommand { @@ -44,6 +44,8 @@ int main(int argc, char *argv[]) { manager_run_jobs(m); + manager_loop(m); + retval = 0; finish: @@ -3,6 +3,12 @@ #include <assert.h> #include <errno.h> #include <string.h> +#include <sys/epoll.h> +#include <signal.h> +#include <sys/signalfd.h> +#include <sys/wait.h> +#include <unistd.h> +#include <sys/poll.h> #include "manager.h" #include "hashmap.h" @@ -12,10 +18,14 @@ Manager* manager_new(void) { Manager *m; + sigset_t mask; + struct epoll_event ev; if (!(m = new0(Manager, 1))) return NULL; + m->signal_fd = m->epoll_fd = -1; + if (!(m->names = hashmap_new(string_hash_func, string_compare_func))) goto fail; @@ -25,6 +35,26 @@ Manager* manager_new(void) { if (!(m->transaction_jobs = hashmap_new(trivial_hash_func, trivial_compare_func))) goto fail; + if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func))) + goto fail; + + if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) + goto fail; + + assert_se(sigemptyset(&mask) == 0); + assert_se(sigaddset(&mask, SIGCHLD) == 0); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + + if ((m->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) + goto fail; + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = m->signal_fd; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_fd, &ev) < 0) + goto fail; + return m; fail: @@ -47,6 +77,12 @@ void manager_free(Manager *m) { hashmap_free(m->names); hashmap_free(m->jobs); hashmap_free(m->transaction_jobs); + hashmap_free(m->watch_pids); + + if (m->epoll_fd >= 0) + close_nointr(m->epoll_fd); + if (m->signal_fd >= 0) + close_nointr(m->signal_fd); free(m); } @@ -890,5 +926,97 @@ void manager_run_jobs(Manager *m) { HASHMAP_FOREACH(j, m->jobs, state) { r = job_run_and_invalidate(j); + + /* FIXME... the list of jobs might have changed */ + } +} + +int manager_dispatch_sigchld(Manager *m) { + assert(m); + + for (;;) { + siginfo_t si; + Name *n; + + zero(si); + if (waitid(P_ALL, 0, &si, WNOHANG) < 0) + return -errno; + + if (si.si_pid == 0) + break; + + if (!(n = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) + continue; + + NAME_VTABLE(n)->sigchld_event(n, si.si_pid, si.si_code, si.si_status); + } + + return 0; +} + +int manager_process_signal_fd(Manager *m) { + ssize_t n; + struct signalfd_siginfo sfsi; + bool sigchld = false; + + assert(m); + + for (;;) { + if ((n = read(m->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { + + if (n >= 0) + return -EIO; + + if (errno == EAGAIN) + return 0; + + return -errno; + } + + if (sfsi.ssi_signo == SIGCHLD) + sigchld = true; + } + + if (sigchld) + manager_dispatch_sigchld(m); + + return 0; +} + +int manager_loop(Manager *m) { + int r; + struct epoll_event events[32]; + + assert(m); + + for (;;) { + int n, i; + + if ((n = epoll_wait(m->epoll_fd, events, ELEMENTSOF(events), -1)) < 0) { + + if (errno == -EINTR) + continue; + + return -errno; + } + + for (i = 0; i < n; i++) { + + if (events[i].data.fd == m->signal_fd) { + + /* An incoming signal? */ + if (events[i].events != POLLIN) + return -EINVAL; + + if ((r = manager_process_signal_fd(m)) < 0) + return -r; + } else { + Name *n; + + /* Some other fd event, to be dispatched to the names */ + assert_se(n = events[i].data.ptr); + NAME_VTABLE(n)->fd_event(n, events[i].data.fd, events[i].events); + } + } } } @@ -35,7 +35,10 @@ struct Manager { bool dispatching_load_queue:1; - Hashmap *pids; /* pid => Name object n:1 */ + Hashmap *watch_pids; /* pid => Name object n:1 */ + + int epoll_fd; + int signal_fd; }; Manager* manager_new(void); @@ -55,5 +58,6 @@ void manager_transaction_unlink_job(Manager *m, Job *j); void manager_clear_jobs(Manager *m); void manager_run_jobs(Manager *m); +int manager_loop(Manager *m); #endif @@ -3,6 +3,7 @@ #include <assert.h> #include <errno.h> #include <string.h> +#include <sys/epoll.h> #include "set.h" #include "name.h" @@ -11,7 +12,7 @@ #include "load-fragment.h" #include "load-dropin.h" -static const NameVTable * const name_vtable[_NAME_TYPE_MAX] = { +const NameVTable * const name_vtable[_NAME_TYPE_MAX] = { [NAME_SERVICE] = &service_vtable, [NAME_TIMER] = &timer_vtable, [NAME_SOCKET] = &socket_vtable, @@ -22,8 +23,6 @@ static const NameVTable * const name_vtable[_NAME_TYPE_MAX] = { [NAME_SNAPSHOT] = &snapshot_vtable }; -#define NAME_VTABLE(n) name_vtable[(n)->meta.type] - NameType name_type_from_string(const char *n) { NameType t; @@ -700,3 +699,41 @@ void name_notify(Name *n, NameActiveState os, NameActiveState ns) { else if (NAME_IS_ACTIVE_OR_ACTIVATING(os) && NAME_IS_INACTIVE_OR_DEACTIVATING(ns)) retroactively_stop_dependencies(n); } + +int name_watch_fd(Name *n, int fd, uint32_t events) { + struct epoll_event ev; + + assert(n); + assert(fd >= 0); + + zero(ev); + ev.data.fd = fd; + ev.data.ptr = n; + ev.events = events; + + if (epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) + return -errno; + + return 0; +} + +void name_unwatch_fd(Name *n, int fd) { + assert(n); + assert(fd >= 0); + + assert_se(epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_DEL, fd, NULL) >= 0 || errno == ENOENT); +} + +int name_watch_pid(Name *n, pid_t pid) { + assert(n); + assert(pid >= 1); + + return hashmap_put(n->meta.manager->watch_pids, UINT32_TO_PTR(pid), n); +} + +void name_unwatch_pid(Name *n, pid_t pid) { + assert(n); + assert(pid >= 1); + + hashmap_remove(n->meta.manager->watch_pids, UINT32_TO_PTR(pid)); +} @@ -140,9 +140,16 @@ struct NameVTable { * a simpler one that the engine can understand */ NameActiveState (*active_state)(Name *n); + void (*fd_event)(Name *n, int fd, uint32_t events); + void (*sigchld_event)(Name *n, pid_t pid, int code, int status); + void (*free_hook)(Name *n); }; +extern const NameVTable * const name_vtable[_NAME_TYPE_MAX]; + +#define NAME_VTABLE(n) name_vtable[(n)->meta.type] + /* For casting a name into the various name types */ #define DEFINE_CAST(UPPERCASE, MixedCase) \ static inline MixedCase* UPPERCASE(Name *name) { \ @@ -191,4 +198,10 @@ int name_reload(Name *n); void name_notify(Name *n, NameActiveState os, NameActiveState ns); +int name_watch_fd(Name *n, int fd, uint32_t events); +void name_unwatch_fd(Name *n, int fd); + +int name_watch_pid(Name *n, pid_t pid); +void name_unwatch_pid(Name *n, pid_t pid); + #endif diff --git a/socket-util.c b/socket-util.c index 1024ecbed3..77b80d0127 100644 --- a/socket-util.c +++ b/socket-util.c @@ -21,7 +21,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { assert(a); assert(s); - memset(a, 0, sizeof(*a)); + zero(*a); a->type = SOCK_STREAM; if (*s == '[') { @@ -5,6 +5,7 @@ #include <unistd.h> #include <errno.h> #include <fcntl.h> +#include <sys/poll.h> #include "name.h" #include "socket.h" @@ -124,7 +125,9 @@ static void close_fds(Socket *s) { if (p->fd < 0) continue; - close_nointr(p->fd); + name_unwatch_fd(NAME(s), p->fd); + assert_se(close_nointr(p->fd) >= 0); + p->fd = -1; } } @@ -185,6 +188,9 @@ static int socket_start(Name *n) { goto rollback; } } + + if ((r = name_watch_fd(n, p->fd, POLLIN)) < 0) + goto rollback; } socket_set_state(s, SOCKET_LISTENING); @@ -231,6 +237,23 @@ static NameActiveState socket_active_state(Name *n) { return state_table[SOCKET(n)->state]; } +static void socket_fd_event(Name *n, int fd, uint32_t events) { + Socket *s = SOCKET(n); + + assert(n); + + if (events != POLLIN) + goto fail; + + log_info("POLLIN on %s", name_id(n)); + + return; + +fail: + close_fds(s); + socket_set_state(s, SOCKET_MAINTAINANCE); +} + static void socket_free_hook(Name *n) { SocketExecCommand c; Socket *s = SOCKET(n); @@ -268,5 +291,7 @@ const NameVTable socket_vtable = { .active_state = socket_active_state, + .fd_event = socket_fd_event, + .free_hook = socket_free_hook }; |