From acbb02252a38214ecba3aa8a5c9b3669f9c9317e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jan 2010 04:31:52 +0100 Subject: [PATCH] yay, we can start socket units --- execute.c | 3 ++ fixme | 2 + load-fragment.c | 30 +++++++++++++++ main.c | 5 ++- manager.c | 88 +++++++++++++++++++++++-------------------- manager.h | 25 ++++++++---- service.c | 66 ++++++++++++++++---------------- service.h | 2 +- socket-util.c | 41 +++++++++++++------- socket-util.h | 3 +- socket.c | 86 ++++++++++++++++++++++++------------------ socket.h | 6 ++- test1/postfix.socket | 1 + test1/syslog.socket | 2 + unit.c | 90 ++++++++++++++++++++++++++------------------ unit.h | 12 +++--- 16 files changed, 285 insertions(+), 177 deletions(-) diff --git a/execute.c b/execute.c index 518d7a6b..f3b4df93 100644 --- a/execute.c +++ b/execute.c @@ -11,6 +11,7 @@ #include "strv.h" #include "macro.h" #include "util.h" +#include "log.h" static int close_fds(int except[], unsigned n_except) { DIR *d; @@ -111,6 +112,8 @@ int exec_spawn(const ExecCommand *command, const ExecContext *context, int *fds, assert(ret); assert(fds || n_fds <= 0); + log_debug("About to execute %s", command->path); + if ((pid = fork()) < 0) return -errno; diff --git a/fixme b/fixme index f3f723e3..800801c4 100644 --- a/fixme +++ b/fixme @@ -40,3 +40,5 @@ - verify fragment data after loading: refuse cycles on yourself, service units contradicting, more than one Start executable, ... - rate limit startups + +- automatically delete stale unix sockets diff --git a/load-fragment.c b/load-fragment.c index ef1e1de9..8bb29b2d 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -450,6 +450,35 @@ static int config_parse_service_restart( return 0; } +int config_parse_bindtodevice( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + Socket *s = data; + char *n; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (rvalue[0] && !streq(rvalue, "*")) { + if (!(n = strdup(rvalue))) + return -ENOMEM; + } else + n = NULL; + + free(s->bind_to_device); + s->bind_to_device = n; + + return 0; +} + #define FOLLOW_MAX 8 static char *build_path(const char *path, const char *filename) { @@ -599,6 +628,7 @@ static int load_from_path(Unit *u, const char *path) { { "ListenFIFO", config_parse_listen, &u->socket, "Socket" }, { "BindIPv6Only", config_parse_socket_bind, &u->socket, "Socket" }, { "Backlog", config_parse_unsigned, &u->socket.backlog, "Socket" }, + { "BindToDevice", config_parse_bindtodevice, &u->socket, "Socket" }, { "ExecStartPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_PRE, "Socket" }, { "ExecStartPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_POST, "Socket" }, { "ExecStopPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_PRE, "Socket" }, diff --git a/main.c b/main.c index 052d5029..5e262b45 100644 --- a/main.c +++ b/main.c @@ -37,7 +37,10 @@ int main(int argc, char *argv[]) { printf("→ By jobs:\n"); manager_dump_jobs(m, stdout, "\t"); - /* manager_loop(m); */ + if ((r = manager_loop(m)) < 0) { + log_error("Failed to run mainloop: %s", strerror(-r)); + goto finish; + } retval = 0; diff --git a/manager.c b/manager.c index 3430b97b..e9ae40ca 100644 --- a/manager.c +++ b/manager.c @@ -24,7 +24,7 @@ Manager* manager_new(void) { if (!(m = new0(Manager, 1))) return NULL; - m->signal_fd = m->epoll_fd = -1; + m->signal_watch.fd = m->epoll_fd = -1; if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) goto fail; @@ -45,14 +45,15 @@ Manager* manager_new(void) { 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) + m->signal_watch.type = WATCH_SIGNAL_FD; + if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) goto fail; zero(ev); ev.events = EPOLLIN; - ev.data.fd = m->signal_fd; + ev.data.ptr = &m->signal_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_fd, &ev) < 0) + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0) goto fail; return m; @@ -81,8 +82,8 @@ void manager_free(Manager *m) { if (m->epoll_fd >= 0) close_nointr(m->epoll_fd); - if (m->signal_fd >= 0) - close_nointr(m->signal_fd); + if (m->signal_watch.fd >= 0) + close_nointr(m->signal_watch.fd); free(m); } @@ -555,6 +556,7 @@ static int transaction_apply(Manager *m, JobMode mode) { assert(!j->transaction_next); assert(!j->transaction_prev); + job_schedule_run(j); } /* As last step, kill all remaining job dependencies. */ @@ -943,13 +945,20 @@ void manager_dispatch_run_queue(Manager *m) { static int manager_dispatch_sigchld(Manager *m) { assert(m); + log_debug("dispatching SIGCHLD"); + for (;;) { siginfo_t si; Unit *u; zero(si); - if (waitid(P_ALL, 0, &si, WNOHANG) < 0) + if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG) < 0) { + + if (errno == ECHILD) + break; + return -errno; + } if (si.si_pid == 0) break; @@ -957,6 +966,8 @@ static int manager_dispatch_sigchld(Manager *m) { if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED) continue; + log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code(si.si_code), si.si_status); + if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) continue; @@ -974,13 +985,13 @@ static int manager_process_signal_fd(Manager *m) { assert(m); for (;;) { - if ((n = read(m->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { + if ((n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { if (n >= 0) return -EIO; if (errno == EAGAIN) - return 0; + break; return -errno; } @@ -997,54 +1008,51 @@ static int manager_process_signal_fd(Manager *m) { static int process_event(Manager *m, struct epoll_event *ev) { int r; + Watch *w; assert(m); assert(ev); - switch (ev->data.u32) { + assert(w = ev->data.ptr); - case MANAGER_SIGNAL: - assert(ev->data.fd == m->signal_fd); + switch (w->type) { - /* An incoming signal? */ - if (ev->events != POLLIN) - return -EINVAL; + case WATCH_SIGNAL_FD: - if ((r = manager_process_signal_fd(m)) < 0) - return -r; + /* An incoming signal? */ + if (ev->events != POLLIN) + return -EINVAL; - break; + if ((r = manager_process_signal_fd(m)) < 0) + return r; - case MANAGER_FD: { - Unit *u; + break; - /* Some fd event, to be dispatched to the units */ - assert_se(u = ev->data.ptr); - UNIT_VTABLE(u)->fd_event(u, ev->data.fd, ev->events); - break; - } + case WATCH_FD: - case MANAGER_TIMER: { - Unit *u; - uint64_t v; - ssize_t k; + /* Some fd event, to be dispatched to the units */ + UNIT_VTABLE(w->unit)->fd_event(w->unit, w->fd, ev->events, w); + break; - /* Some timer event, to be dispatched to the units */ - if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) { + case WATCH_TIMER: { + uint64_t v; + ssize_t k; - if (k < 0 && (errno == EINTR || errno == EAGAIN)) - break; + /* Some timer event, to be dispatched to the units */ + if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) { - return k < 0 ? -errno : -EIO; - } + if (k < 0 && (errno == EINTR || errno == EAGAIN)) + break; - assert_se(u = ev->data.ptr); - UNIT_VTABLE(u)->timer_event(u, ev->data.fd, v); - break; + return k < 0 ? -errno : -EIO; } - default: - assert_not_reached("Unknown epoll event type."); + UNIT_VTABLE(w->unit)->timer_event(w->unit, v, w); + break; + } + + default: + assert_not_reached("Unknown epoll event type."); } return 0; diff --git a/manager.h b/manager.h index a17a6c2f..303201b7 100644 --- a/manager.h +++ b/manager.h @@ -8,7 +8,21 @@ #include typedef struct Manager Manager; -typedef enum ManagerEventType ManagerEventType; +typedef enum WatchType WatchType; +typedef struct Watch Watch; + +enum WatchType { + WATCH_INVALID, + WATCH_SIGNAL_FD, + WATCH_FD, + WATCH_TIMER +}; + +struct Watch { + int fd; + WatchType type; + union Unit *unit; +}; #include "unit.h" #include "job.h" @@ -16,12 +30,6 @@ typedef enum ManagerEventType ManagerEventType; #include "list.h" #include "set.h" -enum ManagerEventType { - MANAGER_SIGNAL, - MANAGER_FD, - MANAGER_TIMER -}; - struct Manager { uint32_t current_job_id; @@ -49,7 +57,8 @@ struct Manager { Hashmap *watch_pids; /* pid => Unit object n:1 */ int epoll_fd; - int signal_fd; + + Watch signal_watch; }; Manager* manager_new(void); diff --git a/service.c b/service.c index ef10cdbf..fce42045 100644 --- a/service.c +++ b/service.c @@ -9,7 +9,7 @@ #include "load-dropin.h" #include "log.h" -static const UnitActiveState state_table[_SERVICE_STATE_MAX] = { +static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_DEAD] = UNIT_INACTIVE, [SERVICE_START_PRE] = UNIT_ACTIVATING, [SERVICE_START] = UNIT_ACTIVATING, @@ -26,6 +26,23 @@ static const UnitActiveState state_table[_SERVICE_STATE_MAX] = { [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING, }; +static const char* const state_string_table[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = "dead", + [SERVICE_START_PRE] = "start-pre", + [SERVICE_START] = "start", + [SERVICE_START_POST] = "post", + [SERVICE_RUNNING] = "running", + [SERVICE_RELOAD] = "reload", + [SERVICE_STOP] = "stop", + [SERVICE_STOP_SIGTERM] = "stop-sigterm", + [SERVICE_STOP_SIGKILL] = "stop-sigkill", + [SERVICE_STOP_POST] = "stop-post", + [SERVICE_FINAL_SIGTERM] = "final-sigterm", + [SERVICE_FINAL_SIGKILL] = "final-sigkill", + [SERVICE_MAINTAINANCE] = "maintainance", + [SERVICE_AUTO_RESTART] = "auto-restart", +}; + static void service_done(Unit *u) { Service *s = SERVICE(u); @@ -50,7 +67,7 @@ static void service_done(Unit *u) { s->control_pid = 0; } - unit_unwatch_timer(u, &s->timer_id); + unit_unwatch_timer(u, &s->timer_watch); } static int service_load_sysv(Service *s) { @@ -79,7 +96,7 @@ static int service_init(Unit *u) { exec_context_init(&s->exec_context); - s->timer_id = -1; + s->timer_watch.type = WATCH_INVALID; s->state = SERVICE_DEAD; @@ -106,23 +123,6 @@ static int service_init(Unit *u) { static void service_dump(Unit *u, FILE *f, const char *prefix) { - static const char* const state_table[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = "dead", - [SERVICE_START_PRE] = "start-pre", - [SERVICE_START] = "start", - [SERVICE_START_POST] = "post", - [SERVICE_RUNNING] = "running", - [SERVICE_RELOAD] = "reload", - [SERVICE_STOP] = "stop", - [SERVICE_STOP_SIGTERM] = "stop-sigterm", - [SERVICE_STOP_SIGKILL] = "stop-sigkill", - [SERVICE_STOP_POST] = "stop-post", - [SERVICE_FINAL_SIGTERM] = "final-sigterm", - [SERVICE_FINAL_SIGKILL] = "final-sigkill", - [SERVICE_MAINTAINANCE] = "maintainance", - [SERVICE_AUTO_RESTART] = "auto-restart", - }; - static const char* const command_table[_SERVICE_EXEC_MAX] = { [SERVICE_EXEC_START_PRE] = "ExecStartPre", [SERVICE_EXEC_START] = "ExecStart", @@ -144,7 +144,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sService State: %s\n", - prefix, state_table[s->state]); + prefix, state_string_table[s->state]); if (s->pid_file) fprintf(f, @@ -216,7 +216,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_FINAL_SIGTERM && state != SERVICE_FINAL_SIGKILL && state != SERVICE_AUTO_RESTART) - unit_unwatch_timer(UNIT(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_watch); if (state != SERVICE_START_POST && state != SERVICE_RUNNING && @@ -224,7 +224,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_STOP && state != SERVICE_STOP_SIGTERM && state != SERVICE_STOP_SIGKILL) - if (s->main_pid >= 0) { + if (s->main_pid > 0) { unit_unwatch_pid(UNIT(s), s->main_pid); s->main_pid = 0; } @@ -239,7 +239,7 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_STOP_POST && state != SERVICE_FINAL_SIGTERM && state != SERVICE_FINAL_SIGKILL) - if (s->control_pid >= 0) { + if (s->control_pid > 0) { unit_unwatch_pid(UNIT(s), s->control_pid); s->control_pid = 0; } @@ -252,7 +252,9 @@ static void service_set_state(Service *s, ServiceState state) { state != SERVICE_STOP_POST) s->control_command = NULL; - unit_notify(UNIT(s), state_table[old_state], state_table[s->state]); + log_debug("%s changing %s → %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]); + + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); } static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { @@ -335,10 +337,10 @@ static int service_spawn(Service *s, ExecCommand *c, bool timeout, bool pass_fds goto fail; if (timeout) { - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_id)) < 0) + if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) goto fail; } else - unit_unwatch_timer(UNIT(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_watch); if ((r = exec_spawn(c, &s->exec_context, fds, n_fds, &pid)) < 0) goto fail; @@ -356,7 +358,7 @@ fail: free(fds); if (timeout) - unit_unwatch_timer(UNIT(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_watch); return r; } @@ -372,7 +374,7 @@ static void service_enter_dead(Service *s, bool success, bool allow_restart) { (s->restart == SERVICE_RESTART_ALWAYS || (s->restart == SERVICE_RESTART_ON_SUCCESS && !s->failure))) { - if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_id)) < 0) + if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0) goto fail; service_set_state(s, SERVICE_AUTO_RESTART); @@ -703,7 +705,7 @@ static bool service_can_reload(Unit *u) { static UnitActiveState service_active_state(Unit *u) { assert(u); - return state_table[SERVICE(u)->state]; + return state_translation_table[SERVICE(u)->state]; } static int main_pid_good(Service *s) { @@ -888,13 +890,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert_not_reached("Got SIGCHLD for unkown PID"); } -static void service_timer_event(Unit *u, int id, uint64_t elapsed) { +static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { Service *s = SERVICE(u); assert(s); assert(elapsed == 1); - assert(s->timer_id == id); + assert(w == &s->timer_watch); switch (s->state) { diff --git a/service.h b/service.h index 17bcd2aa..f9dbfea5 100644 --- a/service.h +++ b/service.h @@ -70,7 +70,7 @@ struct Service { bool main_pid_known:1; bool failure:1; /* if we shut down, remember why */ - int timer_id; + Watch timer_watch; }; const UnitVTable service_vtable; diff --git a/socket-util.c b/socket-util.c index 77b80d01..46a6e91b 100644 --- a/socket-util.c +++ b/socket-util.c @@ -111,6 +111,11 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else { unsigned idx; + if (strlen(n) > IF_NAMESIZE-1) { + free(n); + return -EINVAL; + } + /* Uh, our last resort, an interface name */ idx = if_nametoindex(n); free(n); @@ -123,6 +128,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->sockaddr.in6.sin6_scope_id = idx; a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); + } } else { @@ -274,8 +280,8 @@ int socket_address_print(const SocketAddress *a, char **p) { } } -int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, int *ret) { - int r, fd; +int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, const char *bind_to_device, int *ret) { + int r, fd, one; assert(a); assert(ret); @@ -288,23 +294,30 @@ int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBind if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { int flag = only == SOCKET_ADDRESS_IPV6_ONLY; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) { - close_nointr(fd); - return -errno; - } + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) + goto fail; } - if (bind(fd, &a->sockaddr.sa, a->size) < 0) { - close_nointr(fd); - return -errno; - } + if (bind_to_device) + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0) + goto fail; + + one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) + goto fail; + + if (bind(fd, &a->sockaddr.sa, a->size) < 0) + goto fail; if (a->type == SOCK_STREAM) - if (listen(fd, backlog) < 0) { - close_nointr(fd); - return -errno; - } + if (listen(fd, backlog) < 0) + goto fail; *ret = fd; return 0; + +fail: + r = -errno; + close_nointr(fd); + return r; } diff --git a/socket-util.h b/socket-util.h index 1f939ae2..7dd832ea 100644 --- a/socket-util.h +++ b/socket-util.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "macro.h" #include "util.h" @@ -38,6 +39,6 @@ typedef enum SocketAddressBindIPv6Only { int socket_address_parse(SocketAddress *a, const char *s); int socket_address_print(const SocketAddress *a, char **p); int socket_address_verify(const SocketAddress *a); -int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, int *ret); +int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, const char *bind_to_device, int *ret); #endif diff --git a/socket.c b/socket.c index 1b234838..cbddcaa7 100644 --- a/socket.c +++ b/socket.c @@ -12,7 +12,7 @@ #include "socket.h" #include "log.h" -static const UnitActiveState state_table[_SOCKET_STATE_MAX] = { +static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, [SOCKET_START_PRE] = UNIT_ACTIVATING, [SOCKET_START_POST] = UNIT_ACTIVATING, @@ -27,6 +27,21 @@ static const UnitActiveState state_table[_SOCKET_STATE_MAX] = { [SOCKET_MAINTAINANCE] = UNIT_INACTIVE, }; +static const char* const state_string_table[_SOCKET_STATE_MAX] = { + [SOCKET_DEAD] = "dead", + [SOCKET_START_PRE] = "start-pre", + [SOCKET_START_POST] = "start-post", + [SOCKET_LISTENING] = "listening", + [SOCKET_RUNNING] = "running", + [SOCKET_STOP_PRE] = "stop-pre", + [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", + [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", + [SOCKET_STOP_POST] = "stop-post", + [SOCKET_STOP_POST_SIGTERM] = "stop-post-sigterm", + [SOCKET_STOP_POST_SIGKILL] = "stop-post-sigkill", + [SOCKET_MAINTAINANCE] = "maintainance" +}; + static void socket_done(Unit *u) { Socket *s = SOCKET(u); SocketPort *p; @@ -53,7 +68,9 @@ static void socket_done(Unit *u) { s->service = NULL; - unit_unwatch_timer(u, &s->timer_id); + free(s->bind_to_device); + + unit_unwatch_timer(u, &s->timer_watch); } static int socket_init(Unit *u) { @@ -65,7 +82,7 @@ static int socket_init(Unit *u) { * reload */ s->state = 0; - s->timer_id = -1; + s->timer_watch.type = WATCH_INVALID; s->bind_ipv6_only = false; s->backlog = SOMAXCONN; s->timeout_usec = DEFAULT_TIMEOUT_USEC; @@ -110,21 +127,6 @@ static const char* listen_lookup(int type) { static void socket_dump(Unit *u, FILE *f, const char *prefix) { - static const char* const state_table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = "dead", - [SOCKET_START_PRE] = "start-pre", - [SOCKET_START_POST] = "start-post", - [SOCKET_LISTENING] = "listening", - [SOCKET_RUNNING] = "running", - [SOCKET_STOP_PRE] = "stop-pre", - [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", - [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", - [SOCKET_STOP_POST] = "stop-post", - [SOCKET_STOP_POST_SIGTERM] = "stop-post-sigterm", - [SOCKET_STOP_POST_SIGKILL] = "stop-post-sigkill", - [SOCKET_MAINTAINANCE] = "maintainance" - }; - static const char* const command_table[_SOCKET_EXEC_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", [SOCKET_EXEC_START_POST] = "StartPost", @@ -147,10 +149,15 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sSocket State: %s\n" "%sBindIPv6Only: %s\n" "%sBacklog: %u\n", - prefix, state_table[s->state], + prefix, state_string_table[s->state], prefix, yes_no(s->bind_ipv6_only), prefix, s->backlog); + if (s->bind_to_device) + fprintf(f, + "%sBindToDevice: %s\n", + prefix, s->bind_to_device); + LIST_FOREACH(port, p, s->ports) { if (p->type == SOCKET_SOCKET) { @@ -193,7 +200,7 @@ static void socket_close_fds(Socket *s) { if (p->fd < 0) continue; - unit_unwatch_fd(UNIT(s), p->fd); + unit_unwatch_fd(UNIT(s), &p->fd_watch); assert_se(close_nointr(p->fd) >= 0); p->fd = -1; @@ -213,7 +220,7 @@ static int socket_open_fds(Socket *s) { if (p->type == SOCKET_SOCKET) { - if ((r = socket_address_listen(&p->address, s->backlog, s->bind_ipv6_only, &p->fd)) < 0) + if ((r = socket_address_listen(&p->address, s->backlog, s->bind_ipv6_only, s->bind_to_device, &p->fd)) < 0) goto rollback; } else { @@ -260,7 +267,7 @@ static void socket_unwatch_fds(Socket *s) { if (p->fd < 0) continue; - unit_unwatch_fd(UNIT(s), p->fd); + unit_unwatch_fd(UNIT(s), &p->fd_watch); } } @@ -274,7 +281,7 @@ static int socket_watch_fds(Socket *s) { if (p->fd < 0) continue; - if ((r = unit_watch_fd(UNIT(s), p->fd, POLLIN)) < 0) + if ((r = unit_watch_fd(UNIT(s), p->fd, POLLIN, &p->fd_watch)) < 0) goto fail; } @@ -300,7 +307,7 @@ static void socket_set_state(Socket *s, SocketState state) { state != SOCKET_STOP_POST && state != SOCKET_STOP_POST_SIGTERM && state != SOCKET_STOP_POST_SIGKILL) - unit_unwatch_timer(UNIT(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_watch); if (state != SOCKET_START_PRE && state != SOCKET_START_POST && @@ -310,7 +317,7 @@ static void socket_set_state(Socket *s, SocketState state) { state != SOCKET_STOP_POST && state != SOCKET_STOP_POST_SIGTERM && state != SOCKET_STOP_POST_SIGKILL) - if (s->control_pid >= 0) { + if (s->control_pid > 0) { unit_unwatch_pid(UNIT(s), s->control_pid); s->control_pid = 0; } @@ -332,7 +339,9 @@ static void socket_set_state(Socket *s, SocketState state) { if (state != SOCKET_LISTENING) socket_unwatch_fds(s); - unit_notify(UNIT(s), state_table[old_state], state_table[s->state]); + log_debug("%s changing %s → %s", unit_id(UNIT(s)), state_string_table[old_state], state_string_table[state]); + + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); } static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) { @@ -344,10 +353,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) { assert(_pid); if (timeout) { - if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_id)) < 0) + if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) goto fail; } else - unit_unwatch_timer(UNIT(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_watch); if ((r = exec_spawn(c, &s->exec_context, NULL, 0, &pid)) < 0) goto fail; @@ -362,7 +371,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) { fail: if (timeout) - unit_unwatch_timer(UNIT(s), &s->timer_id); + unit_unwatch_timer(UNIT(s), &s->timer_watch); return r; } @@ -594,10 +603,10 @@ static int socket_stop(Unit *u) { static UnitActiveState socket_active_state(Unit *u) { assert(u); - return state_table[SOCKET(u)->state]; + return state_translation_table[SOCKET(u)->state]; } -static void socket_fd_event(Unit *u, int fd, uint32_t events) { +static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { Socket *s = SOCKET(u); assert(s); @@ -626,20 +635,23 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { exec_status_fill(&s->control_command->exec_status, pid, code, status); s->control_pid = 0; - log_debug("%s: control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status); + log_debug("%s control process exited, code=%s status=%i", unit_id(u), sigchld_code(code), status); if (s->control_command->command_next && - (success || (s->state == SOCKET_EXEC_STOP_PRE || s->state == SOCKET_EXEC_STOP_POST))) + (success || (s->state == SOCKET_EXEC_STOP_PRE || s->state == SOCKET_EXEC_STOP_POST))) { + log_debug("%s running next command for the state %s", unit_id(u), state_string_table[s->state]); socket_run_next(s, success); - else { + } else { /* No further commands for this step, so let's figure * out what to do next */ + log_debug("%s finished with state %s", unit_id(u), state_string_table[s->state]); + switch (s->state) { case SOCKET_START_PRE: if (success) - socket_enter_start_pre(s); + socket_enter_start_post(s); else socket_enter_stop_pre(s, false); break; @@ -669,13 +681,13 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { } } -static void socket_timer_event(Unit *u, int id, uint64_t elapsed) { +static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) { Socket *s = SOCKET(u); assert(s); assert(elapsed == 1); - assert(s->timer_id == id); + assert(w == &s->timer_watch); switch (s->state) { diff --git a/socket.h b/socket.h index c34689b8..2c8a69f6 100644 --- a/socket.h +++ b/socket.h @@ -5,6 +5,7 @@ typedef struct Socket Socket; +#include "manager.h" #include "unit.h" #include "socket-util.h" @@ -46,6 +47,7 @@ struct SocketPort { char *path; int fd; + Watch fd_watch; LIST_FIELDS(SocketPort, port); }; @@ -71,8 +73,10 @@ struct Socket { ExecCommand* control_command; pid_t control_pid; + char *bind_to_device; + bool failure; - int timer_id; + Watch timer_watch; }; /* Called from the service code when collecting fds */ diff --git a/test1/postfix.socket b/test1/postfix.socket index 927ea3fd..9d650a15 100644 --- a/test1/postfix.socket +++ b/test1/postfix.socket @@ -10,3 +10,4 @@ ExecStartPost=/bin/echo "Created sockets..." ExecStopPre=/bin/echo "About to delete sockets..." ExecStopPost=/bin/echo "Deleted sockets..." ExecStopPost=/bin/echo "Deleted sockets... 2" +#BindToDevice=eth0 diff --git a/test1/syslog.socket b/test1/syslog.socket index 56c903c4..1662990d 100644 --- a/test1/syslog.socket +++ b/test1/syslog.socket @@ -4,3 +4,5 @@ Description=Syslog Socket [Socket] ListenDatagram=/tmp/systemd-syslog-socket ListenStream=eth0:3456 +ExecStartPre=/bin/rm -f /tmp/systemd-syslog-socket +ExecStopPost=/bin/rm -f /tmp/systemd-syslog-socket diff --git a/unit.c b/unit.c index c722717f..e6e55743 100644 --- a/unit.c +++ b/unit.c @@ -627,33 +627,44 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { retroactively_stop_dependencies(u); } -int unit_watch_fd(Unit *u, int fd, uint32_t events) { +int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) { struct epoll_event ev; assert(u); assert(fd >= 0); + assert(w); + assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->unit == u)); zero(ev); - ev.data.fd = fd; - ev.data.ptr = u; - ev.data.u32 = MANAGER_FD; + ev.data.ptr = w; ev.events = events; - if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) >= 0) - return 0; + if (epoll_ctl(u->meta.manager->epoll_fd, + w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, + fd, + &ev) < 0) + return -errno; - if (errno == EEXIST) - if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_MOD, fd, &ev) >= 0) - return 0; + w->fd = fd; + w->type = WATCH_FD; + w->unit = u; - return -errno; + return 0; } -void unit_unwatch_fd(Unit *u, int fd) { +void unit_unwatch_fd(Unit *u, Watch *w) { assert(u); - assert(fd >= 0); + assert(w); - assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, fd, NULL) >= 0 || errno == ENOENT); + if (w->type == WATCH_INVALID) + return; + + assert(w->type == WATCH_FD && w->unit == u); + assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); + + w->fd = -1; + w->type = WATCH_INVALID; + w->unit = NULL; } int unit_watch_pid(Unit *u, pid_t pid) { @@ -670,25 +681,22 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { hashmap_remove(u->meta.manager->watch_pids, UINT32_TO_PTR(pid)); } -int unit_watch_timer(Unit *u, usec_t delay, int *id) { - struct epoll_event ev; - int fd; +int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { struct itimerspec its; - int flags; + int flags, fd; bool ours; assert(u); - assert(id); + assert(w); + assert(w->type == WATCH_INVALID || (w->type == WATCH_TIMER && w->unit == u)); /* This will try to reuse the old timer if there is one */ - if (*id >= 0) { + if (w->type == WATCH_TIMER) { ours = false; - fd = *id; - + fd = w->fd; } else { ours = true; - if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) return -errno; } @@ -711,16 +719,21 @@ int unit_watch_timer(Unit *u, usec_t delay, int *id) { if (timerfd_settime(fd, flags, &its, NULL) < 0) goto fail; - zero(ev); - ev.data.fd = fd; - ev.data.ptr = u; - ev.data.u32 = MANAGER_TIMER; - ev.events = POLLIN; + if (w->type == WATCH_INVALID) { + struct epoll_event ev; - if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) - goto fail; + zero(ev); + ev.data.ptr = w; + ev.events = POLLIN; + + if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) + goto fail; + } + + w->fd = fd; + w->type = WATCH_TIMER; + w->unit = u; - *id = fd; return 0; fail: @@ -730,16 +743,21 @@ fail: return -errno; } -void unit_unwatch_timer(Unit *u, int *id) { +void unit_unwatch_timer(Unit *u, Watch *w) { assert(u); - assert(id); + assert(w); - if (*id < 0) + if (w->type == WATCH_INVALID) return; - assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, *id, NULL) >= 0); - assert_se(close_nointr(*id) == 0); - *id = -1; + assert(w->type == WATCH_TIMER && w->unit == u); + + assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0); + assert_se(close_nointr(w->fd) == 0); + + w->fd = -1; + w->type = WATCH_INVALID; + w->unit = NULL; } bool unit_job_is_applicable(Unit *u, JobType j) { diff --git a/unit.h b/unit.h index cabf2301..46b3c45b 100644 --- a/unit.h +++ b/unit.h @@ -157,9 +157,9 @@ struct UnitVTable { * a simpler one that the engine can understand */ UnitActiveState (*active_state)(Unit *u); - void (*fd_event)(Unit *u, int fd, uint32_t events); + void (*fd_event)(Unit *u, int fd, uint32_t events, Watch *w); void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); - void (*timer_event)(Unit *u, int id, uint64_t n_elapsed); + void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w); void (*retry)(Unit *u); }; @@ -222,14 +222,14 @@ int unit_reload(Unit *u); void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns); -int unit_watch_fd(Unit *u, int fd, uint32_t events); -void unit_unwatch_fd(Unit *u, int fd); +int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w); +void unit_unwatch_fd(Unit *u, Watch *w); int unit_watch_pid(Unit *u, pid_t pid); void unit_unwatch_pid(Unit *u, pid_t pid); -int unit_watch_timer(Unit *u, usec_t delay, int *id); -void unit_unwatch_timer(Unit *u, int *id); +int unit_watch_timer(Unit *u, usec_t delay, Watch *w); +void unit_unwatch_timer(Unit *u, Watch *w); bool unit_job_is_applicable(Unit *u, JobType j); -- 2.39.5