chiark / gitweb /
add simple event loop
authorLennart Poettering <lennart@poettering.net>
Sat, 23 Jan 2010 23:39:29 +0000 (00:39 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 23 Jan 2010 23:39:29 +0000 (00:39 +0100)
execute.h
main.c
manager.c
manager.h
name.c
name.h
socket-util.c
socket.c

index fb952d1..552e4a9 100644 (file)
--- a/execute.h
+++ b/execute.h
@@ -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 {
diff --git a/main.c b/main.c
index f073c8f..47292bb 100644 (file)
--- a/main.c
+++ b/main.c
@@ -44,6 +44,8 @@ int main(int argc, char *argv[]) {
 
         manager_run_jobs(m);
 
+        manager_loop(m);
+
         retval = 0;
 
 finish:
index 7941d89..0cc4d26 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -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"
 
 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);
+                        }
+                }
         }
 }
index 68f0dd9..6e0b500 100644 (file)
--- a/manager.h
+++ b/manager.h
@@ -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
diff --git a/name.c b/name.c
index b6e62e9..e922428 100644 (file)
--- a/name.c
+++ b/name.c
@@ -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));
+}
diff --git a/name.h b/name.h
index 8c526ba..59f3b16 100644 (file)
--- a/name.h
+++ b/name.h
@@ -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
index 1024ecb..77b80d0 100644 (file)
@@ -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 == '[') {
index ed14db5..ac0ea12 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -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
 };