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 {
manager_run_jobs(m);
+ manager_loop(m);
+
retval = 0;
finish:
#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;
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:
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);
}
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);
+ }
+ }
}
}
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);
void manager_clear_jobs(Manager *m);
void manager_run_jobs(Manager *m);
+int manager_loop(Manager *m);
#endif
#include <assert.h>
#include <errno.h>
#include <string.h>
+#include <sys/epoll.h>
#include "set.h"
#include "name.h"
#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,
[NAME_SNAPSHOT] = &snapshot_vtable
};
-#define NAME_VTABLE(n) name_vtable[(n)->meta.type]
-
NameType name_type_from_string(const char *n) {
NameType t;
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));
+}
* 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) { \
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
assert(a);
assert(s);
- memset(a, 0, sizeof(*a));
+ zero(*a);
a->type = SOCK_STREAM;
if (*s == '[') {
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
+#include <sys/poll.h>
#include "name.h"
#include "socket.h"
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;
}
}
goto rollback;
}
}
+
+ if ((r = name_watch_fd(n, p->fd, POLLIN)) < 0)
+ goto rollback;
}
socket_set_state(s, SOCKET_LISTENING);
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);
.active_state = socket_active_state,
+ .fd_event = socket_fd_event,
+
.free_hook = socket_free_hook
};