From 4f2d528d3bb25cebf8d3ebe83d8193ab4016cb90 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 Apr 2010 06:19:54 +0200 Subject: socket: optionally call accept() for incoming connections and spawn one service instance per connection --- device.c | 24 +++--- execute.c | 122 +++++++++++++++++++++++------- execute.h | 2 + load-fragment.c | 1 + manager.h | 1 + service.c | 58 +++++++++++++-- service.h | 4 + socket-util.c | 10 ++- socket-util.h | 2 + socket.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++----- socket.h | 3 + unit-name.c | 2 +- unit.c | 15 +++- 13 files changed, 403 insertions(+), 68 deletions(-) diff --git a/device.c b/device.c index 1c4c32931c..1a4edd29a7 100644 --- a/device.c +++ b/device.c @@ -415,21 +415,21 @@ static int device_enumerate(Manager *m) { if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0) return -errno; - /* if (!(e = udev_enumerate_new(m->udev))) { */ - /* r = -ENOMEM; */ - /* goto fail; */ - /* } */ + if (!(e = udev_enumerate_new(m->udev))) { + r = -ENOMEM; + goto fail; + } - /* if (udev_enumerate_scan_devices(e) < 0) { */ - /* r = -EIO; */ - /* goto fail; */ - /* } */ + if (udev_enumerate_scan_devices(e) < 0) { + r = -EIO; + goto fail; + } - /* first = udev_enumerate_get_list_entry(e); */ - /* udev_list_entry_foreach(item, first) */ - /* device_process_path(m, udev_list_entry_get_name(item), false); */ + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) + device_process_path(m, udev_list_entry_get_name(item), false); - /* udev_enumerate_unref(e); */ + udev_enumerate_unref(e); return 0; fail: diff --git a/execute.c b/execute.c index 9b407258ff..dfedb31a53 100644 --- a/execute.c +++ b/execute.c @@ -222,10 +222,41 @@ static bool is_terminal_input(ExecInput i) { i == EXEC_INPUT_TTY_FAIL; } -static int setup_input(const ExecContext *context) { +static int fixup_input(const ExecContext *context, int socket_fd) { assert(context); - switch (context->std_input) { + if (socket_fd < 0 && context->std_input == EXEC_INPUT_SOCKET) + return EXEC_INPUT_NULL; + + return context->std_input; +} + +static int fixup_output(const ExecContext *context, int socket_fd) { + assert(context); + + if (socket_fd < 0 && context->std_output == EXEC_OUTPUT_SOCKET) + return EXEC_OUTPUT_INHERIT; + + return context->std_output; +} + +static int fixup_error(const ExecContext *context, int socket_fd) { + assert(context); + + if (socket_fd < 0 && context->std_error == EXEC_OUTPUT_SOCKET) + return EXEC_OUTPUT_INHERIT; + + return context->std_error; +} + +static int setup_input(const ExecContext *context, int socket_fd) { + ExecInput i; + + assert(context); + + i = fixup_input(context, socket_fd); + + switch (i) { case EXEC_INPUT_NULL: return open_null_as(O_RDONLY, STDIN_FILENO); @@ -237,8 +268,8 @@ static int setup_input(const ExecContext *context) { if ((fd = acquire_terminal( tty_path(context), - context->std_input == EXEC_INPUT_TTY_FAIL, - context->std_input == EXEC_INPUT_TTY_FORCE)) < 0) + i == EXEC_INPUT_TTY_FAIL, + i == EXEC_INPUT_TTY_FORCE)) < 0) return fd; if (fd != STDIN_FILENO) { @@ -250,72 +281,90 @@ static int setup_input(const ExecContext *context) { return r; } + case EXEC_INPUT_SOCKET: + return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; + default: assert_not_reached("Unknown input type"); } } -static int setup_output(const ExecContext *context, const char *ident) { +static int setup_output(const ExecContext *context, int socket_fd, const char *ident) { + ExecOutput o; + ExecInput i; + assert(context); assert(ident); + i = fixup_input(context, socket_fd); + o = fixup_output(context, socket_fd); + /* This expects the input is already set up */ - switch (context->std_output) { + switch (o) { case EXEC_OUTPUT_INHERIT: /* If the input is connected to a terminal, inherit that... */ - if (is_terminal_input(context->std_input)) + if (is_terminal_input(i) || i == EXEC_INPUT_SOCKET) return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; - return 0; + return STDIN_FILENO; case EXEC_OUTPUT_NULL: return open_null_as(O_WRONLY, STDOUT_FILENO); - case EXEC_OUTPUT_TTY: { - if (is_terminal_input(context->std_input)) + case EXEC_OUTPUT_TTY: + if (is_terminal_input(i)) return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; /* We don't reset the terminal if this is just about output */ return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO); - } case EXEC_OUTPUT_SYSLOG: case EXEC_OUTPUT_KERNEL: - return connect_logger_as(context, context->std_output, ident, STDOUT_FILENO); + return connect_logger_as(context, o, ident, STDOUT_FILENO); + + case EXEC_OUTPUT_SOCKET: + assert(socket_fd >= 0); + return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; default: assert_not_reached("Unknown output type"); } } -static int setup_error(const ExecContext *context, const char *ident) { +static int setup_error(const ExecContext *context, int socket_fd, const char *ident) { + ExecOutput o, e; + ExecInput i; + assert(context); assert(ident); + i = fixup_input(context, socket_fd); + o = fixup_output(context, socket_fd); + e = fixup_error(context, socket_fd); + /* This expects the input and output are already set up */ /* Don't change the stderr file descriptor if we inherit all * the way and are not on a tty */ - if (context->std_error == EXEC_OUTPUT_INHERIT && - context->std_output == EXEC_OUTPUT_INHERIT && - !is_terminal_input(context->std_input)) + if (e == EXEC_OUTPUT_INHERIT && + o == EXEC_OUTPUT_INHERIT && + !is_terminal_input(i)) return STDERR_FILENO; /* Duplicate form stdout if possible */ - if (context->std_error == context->std_output || - context->std_error == EXEC_OUTPUT_INHERIT) + if (e == o || e == EXEC_OUTPUT_INHERIT) return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; - switch (context->std_error) { + switch (e) { case EXEC_OUTPUT_NULL: return open_null_as(O_WRONLY, STDERR_FILENO); case EXEC_OUTPUT_TTY: - if (is_terminal_input(context->std_input)) + if (is_terminal_input(i)) return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; /* We don't reset the terminal if this is just about output */ @@ -323,7 +372,11 @@ static int setup_error(const ExecContext *context, const char *ident) { case EXEC_OUTPUT_SYSLOG: case EXEC_OUTPUT_KERNEL: - return connect_logger_as(context, context->std_error, ident, STDERR_FILENO); + return connect_logger_as(context, e, ident, STDERR_FILENO); + + case EXEC_OUTPUT_SOCKET: + assert(socket_fd >= 0); + return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; default: assert_not_reached("Unknown error type"); @@ -677,12 +730,27 @@ int exec_spawn(ExecCommand *command, pid_t pid; int r; char *line; + int socket_fd; assert(command); assert(context); assert(ret); assert(fds || n_fds <= 0); + if (context->std_input == EXEC_INPUT_SOCKET || + context->std_output == EXEC_OUTPUT_SOCKET || + context->std_error == EXEC_OUTPUT_SOCKET) { + + if (n_fds != 1) + return -EINVAL; + + socket_fd = fds[0]; + + fds = NULL; + n_fds = 0; + } else + socket_fd = -1; + if (!argv) argv = command->argv; @@ -760,18 +828,18 @@ int exec_spawn(ExecCommand *command, } if (!keep_stdin) - if (setup_input(context) < 0) { + if (setup_input(context, socket_fd) < 0) { r = EXIT_STDIN; goto fail; } if (!keep_stdout) - if (setup_output(context, file_name_from_path(command->path)) < 0) { + if (setup_output(context, socket_fd, file_name_from_path(command->path)) < 0) { r = EXIT_STDOUT; goto fail; } - if (setup_error(context, file_name_from_path(command->path)) < 0) { + if (setup_error(context, socket_fd, file_name_from_path(command->path)) < 0) { r = EXIT_STDERR; goto fail; } @@ -1501,7 +1569,8 @@ static const char* const exec_input_table[_EXEC_INPUT_MAX] = { [EXEC_INPUT_NULL] = "null", [EXEC_INPUT_TTY] = "tty", [EXEC_INPUT_TTY_FORCE] = "tty-force", - [EXEC_INPUT_TTY_FAIL] = "tty-fail" + [EXEC_INPUT_TTY_FAIL] = "tty-fail", + [EXEC_INPUT_SOCKET] = "socket" }; static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { @@ -1509,7 +1578,8 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { [EXEC_OUTPUT_NULL] = "null", [EXEC_OUTPUT_TTY] = "tty", [EXEC_OUTPUT_SYSLOG] = "syslog", - [EXEC_OUTPUT_KERNEL] = "kernel" + [EXEC_OUTPUT_KERNEL] = "kernel", + [EXEC_OUTPUT_SOCKET] = "socket" }; DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); diff --git a/execute.h b/execute.h index 6d877ff7b7..135c8bab5f 100644 --- a/execute.h +++ b/execute.h @@ -46,6 +46,7 @@ typedef enum ExecInput { EXEC_INPUT_TTY, EXEC_INPUT_TTY_FORCE, EXEC_INPUT_TTY_FAIL, + EXEC_INPUT_SOCKET, _EXEC_INPUT_MAX, _EXEC_INPUT_INVALID = -1 } ExecInput; @@ -56,6 +57,7 @@ typedef enum ExecOutput { EXEC_OUTPUT_TTY, EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_KERNEL, + EXEC_OUTPUT_SOCKET, _EXEC_OUTPUT_MAX, _EXEC_OUTPUT_INVALID = -1 } ExecOutput; diff --git a/load-fragment.c b/load-fragment.c index 5093c6dea9..5e98ae8324 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -1199,6 +1199,7 @@ static int load_from_path(Unit *u, const char *path) { { "DirectoryMode", config_parse_mode, &u->socket.directory_mode, "Socket" }, { "SocketMode", config_parse_mode, &u->socket.socket_mode, "Socket" }, { "KillMode", config_parse_kill_mode, &u->socket.kill_mode, "Socket" }, + { "Accept", config_parse_bool, &u->socket.accept, "Socket" }, EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"), { "What", config_parse_string, &u->mount.parameters_fragment.what, "Mount" }, diff --git a/manager.h b/manager.h index a2b7b7ef8d..ae9cd9fe93 100644 --- a/manager.h +++ b/manager.h @@ -59,6 +59,7 @@ struct Watch { union Unit *unit; DBusWatch *bus_watch; DBusTimeout *bus_timeout; + bool socket_accept; } data; }; diff --git a/service.c b/service.c index 9ab8ec1a43..96e9d83ad9 100644 --- a/service.c +++ b/service.c @@ -85,6 +85,16 @@ static void service_unwatch_main_pid(Service *s) { s->main_pid = 0; } +static void service_close_socket_fd(Service *s) { + assert(s); + + if (s->socket_fd < 0) + return; + + close_nointr_nofail(s->socket_fd); + s->socket_fd = -1; +} + static void service_done(Unit *u) { Service *s = SERVICE(u); @@ -108,6 +118,8 @@ static void service_done(Unit *u) { service_unwatch_main_pid(s); service_unwatch_control_pid(s); + service_close_socket_fd(s); + unit_unwatch_timer(u, &s->timer_watch); } @@ -728,6 +740,8 @@ static void service_init(Unit *u) { s->main_pid_known = false; s->failure = false; + s->socket_fd = -1; + RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); } @@ -944,7 +958,6 @@ fail: return r; } - static int service_notify_sockets_dead(Service *s) { Iterator i; Set *set; @@ -954,7 +967,6 @@ static int service_notify_sockets_dead(Service *s) { assert(s); /* Notifies all our sockets when we die */ - if ((r = service_get_sockets(s, &set)) < 0) return r; @@ -1020,6 +1032,11 @@ static void service_set_state(Service *s, ServiceState state) { state == SERVICE_AUTO_RESTART) service_notify_sockets_dead(s); + if (state != SERVICE_START_PRE && + state != SERVICE_START && + !(state == SERVICE_DEAD && UNIT(s)->meta.job)) + service_close_socket_fd(s); + if (old_state != state) log_debug("%s changed %s → %s", UNIT(s)->meta.id, service_state_to_string(old_state), service_state_to_string(state)); @@ -1106,9 +1123,13 @@ static int service_spawn( assert(c); assert(_pid); - if (pass_fds) - if ((r = service_collect_fds(s, &fds, &n_fds)) < 0) + if (pass_fds) { + if (s->socket_fd >= 0) { + fds = &s->socket_fd; + n_fds = 1; + } else if ((r = service_collect_fds(s, &fds, &n_fds)) < 0) goto fail; + } if (timeout) { if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) @@ -1135,11 +1156,17 @@ static int service_spawn( if (r < 0) goto fail; + if (fds) { + if (s->socket_fd >= 0) + service_close_socket_fd(s); + else + free(fds); + } + if ((r = unit_watch_pid(UNIT(s), pid)) < 0) /* FIXME: we need to do something here */ goto fail; - free(fds); *_pid = pid; return 0; @@ -2019,6 +2046,27 @@ finish: return r; } +int service_set_socket_fd(Service *s, int fd) { + assert(s); + assert(fd >= 0); + + /* This is called by the socket code when instantiating a new + * service for a stream socket and the socket needs to be + * configured. */ + + if (UNIT(s)->meta.load_state != UNIT_LOADED) + return -EINVAL; + + if (s->socket_fd >= 0) + return -EBUSY; + + if (s->state != SERVICE_DEAD) + return -EAGAIN; + + s->socket_fd = fd; + return 0; +} + static const char* const service_state_table[_SERVICE_STATE_MAX] = { [SERVICE_DEAD] = "dead", [SERVICE_START_PRE] = "start-pre", diff --git a/service.h b/service.h index f357fc857e..5ddc180423 100644 --- a/service.h +++ b/service.h @@ -112,11 +112,15 @@ struct Service { RateLimit ratelimit; + int socket_fd; + Watch timer_watch; }; extern const UnitVTable service_vtable; +int service_set_socket_fd(Service *s, int fd); + const char* service_state_to_string(ServiceState i); ServiceState service_state_from_string(const char *s); diff --git a/socket-util.c b/socket-util.c index 96567c6d81..cd5ab82a23 100644 --- a/socket-util.c +++ b/socket-util.c @@ -316,7 +316,7 @@ int socket_address_listen( if ((r = socket_address_verify(a)) < 0) return r; - if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) + if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK, 0)) < 0) return -errno; if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { @@ -373,3 +373,11 @@ fail: close_nointr(fd); return r; } + +bool socket_address_can_accept(const SocketAddress *a) { + assert(a); + + return + a->type == SOCK_STREAM || + a->type == SOCK_SEQPACKET; +} diff --git a/socket-util.h b/socket-util.h index 62a48ac35a..e6e1b30b53 100644 --- a/socket-util.h +++ b/socket-util.h @@ -59,6 +59,8 @@ 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); +bool socket_address_can_accept(const SocketAddress *a); + int socket_address_listen( const SocketAddress *a, int backlog, diff --git a/socket.c b/socket.c index 8e893d4657..91832128f5 100644 --- a/socket.c +++ b/socket.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "unit.h" #include "socket.h" @@ -33,6 +34,7 @@ #include "load-dropin.h" #include "load-fragment.h" #include "strv.h" +#include "unit-name.h" static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, @@ -120,9 +122,40 @@ static void socket_init(Unit *u) { s->failure = false; s->control_pid = 0; s->service = NULL; + s->accept = false; + s->n_accepted = 0; exec_context_init(&s->exec_context); } +static bool have_non_accept_socket(Socket *s) { + SocketPort *p; + + assert(s); + + if (!s->accept) + return true; + + LIST_FOREACH(port, p, s->ports) + if (!socket_address_can_accept(&p->address)) + return true; + + return false; +} + +static int socket_verify(Socket *s) { + assert(s); + + if (UNIT(s)->meta.load_state != UNIT_LOADED) + return 0; + + if (!s->ports) { + log_error("%s lacks Listen setting. Refusing.", UNIT(s)->meta.id); + return -EINVAL; + } + + return 0; +} + static int socket_load(Unit *u) { Socket *s = SOCKET(u); int r; @@ -136,11 +169,13 @@ static int socket_load(Unit *u) { /* This is a new unit? Then let's add in some extras */ if (u->meta.load_state == UNIT_LOADED) { - if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service))) - return r; + if (have_non_accept_socket(s)) { + if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service))) + return r; - if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0) - return r; + if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0) + return r; + } if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) return r; @@ -149,7 +184,7 @@ static int socket_load(Unit *u) { return r; } - return 0; + return socket_verify(s); } static const char* listen_lookup(int type) { @@ -210,6 +245,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sBindToDevice: %s\n", prefix, s->bind_to_device); + if (s->accept) + fprintf(f, + "%sAccepted: %u\n", + prefix, s->n_accepted); + LIST_FOREACH(port, p, s->ports) { if (p->type == SOCKET_SOCKET) { @@ -243,6 +283,87 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { free(p2); } +static int instance_from_socket(int fd, unsigned nr, char **instance) { + socklen_t l; + char *r; + union { + struct sockaddr sa; + struct sockaddr_un un; + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr_storage storage; + } local, remote; + + assert(fd >= 0); + assert(instance); + + l = sizeof(local); + if (getsockname(fd, &local.sa, &l) < 0) + return -errno; + + l = sizeof(remote); + if (getpeername(fd, &remote.sa, &l) < 0) + return -errno; + + switch (local.sa.sa_family) { + + case AF_INET: { + uint32_t + a = ntohl(local.in.sin_addr.s_addr), + b = ntohl(remote.in.sin_addr.s_addr); + + if (asprintf(&r, + "%u-%u.%u.%u.%u-%u-%u.%u.%u.%u-%u", + nr, + a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, + ntohs(local.in.sin_port), + b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF, + ntohs(remote.in.sin_port)) < 0) + return -ENOMEM; + + break; + } + + case AF_INET6: { + char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN]; + + if (asprintf(&r, + "%u-%s-%u-%s-%u", + nr, + inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)), + ntohs(local.in6.sin6_port), + inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)), + ntohs(remote.in6.sin6_port)) < 0) + return -ENOMEM; + + break; + } + + case AF_UNIX: { + struct ucred ucred; + + l = sizeof(ucred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) + return -errno; + + if (asprintf(&r, + "%u-%llu-%llu", + nr, + (unsigned long long) ucred.pid, + (unsigned long long) ucred.uid) < 0) + return -ENOMEM; + + break; + } + + default: + assert_not_reached("Unhandled socket type."); + } + + *instance = r; + return 0; +} + static void socket_close_fds(Socket *s) { SocketPort *p; @@ -342,6 +463,10 @@ static int socket_watch_fds(Socket *s) { if (p->fd < 0) continue; + p->fd_watch.data.socket_accept = + s->accept && + socket_address_can_accept(&p->address); + if ((r = unit_watch_fd(UNIT(s), p->fd, EPOLLIN, &p->fd_watch)) < 0) goto fail; } @@ -607,20 +732,59 @@ fail: socket_enter_dead(s, false); } -static void socket_enter_running(Socket *s) { +static void socket_enter_running(Socket *s, int cfd) { int r; assert(s); - if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s->service), JOB_REPLACE, true, NULL)) < 0) - goto fail; + if (cfd < 0) { + if ((r = manager_add_job(UNIT(s)->meta.manager, JOB_START, UNIT(s->service), JOB_REPLACE, true, NULL)) < 0) + goto fail; + + socket_set_state(s, SOCKET_RUNNING); + } else { + Unit *u; + char *prefix, *instance, *name; + + if ((r = instance_from_socket(cfd, s->n_accepted++, &instance))) + goto fail; + + if (!(prefix = unit_name_to_prefix(UNIT(s)->meta.id))) { + free(instance); + r = -ENOMEM; + goto fail; + } + + name = unit_name_build(prefix, instance, ".service"); + free(prefix); + free(instance); + + if (!name) + r = -ENOMEM; + + r = manager_load_unit(UNIT(s)->meta.manager, name, NULL, &u); + free(name); + + if (r < 0) + goto fail; + + if ((r = service_set_socket_fd(SERVICE(u), cfd) < 0)) + goto fail; + + cfd = -1; + + if ((r = manager_add_job(u->meta.manager, JOB_START, u, JOB_REPLACE, true, NULL)) < 0) + goto fail; + } - socket_set_state(s, SOCKET_RUNNING); return; fail: log_warning("%s failed to queue socket startup job: %s", s->meta.id, strerror(-r)); socket_enter_stop_pre(s, false); + + if (cfd >= 0) + close_nointr_nofail(cfd); } static void socket_run_next(Socket *s, bool success) { @@ -673,15 +837,17 @@ static int socket_start(Unit *u) { return 0; /* Cannot run this without the service being around */ - if (s->service->meta.load_state != UNIT_LOADED) - return -ENOENT; - - /* If the service is alredy actvie we cannot start the - * socket */ - if (s->service->state != SERVICE_DEAD && - s->service->state != SERVICE_MAINTAINANCE && - s->service->state != SERVICE_AUTO_RESTART) - return -EBUSY; + if (s->service) { + if (s->service->meta.load_state != UNIT_LOADED) + return -ENOENT; + + /* If the service is alredy actvie we cannot start the + * socket */ + if (s->service->state != SERVICE_DEAD && + s->service->state != SERVICE_MAINTAINANCE && + s->service->state != SERVICE_AUTO_RESTART) + return -EBUSY; + } assert(s->state == SOCKET_DEAD || s->state == SOCKET_MAINTAINANCE); @@ -730,15 +896,36 @@ static const char *socket_sub_state_to_string(Unit *u) { static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { Socket *s = SOCKET(u); + int cfd = -1; assert(s); log_debug("Incoming traffic on %s", u->meta.id); - if (events != EPOLLIN) + if (events != EPOLLIN) { + log_error("Got invalid poll event on socket."); socket_enter_stop_pre(s, false); + return; + } + + if (w->data.socket_accept) { + for (;;) { + + if ((cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK)) < 0) { + + if (errno == EINTR) + continue; + + log_error("Failed to accept socket: %m"); + socket_enter_stop_pre(s, false); + return; + } + + break; + } + } - socket_enter_running(s); + socket_enter_running(s, cfd); } static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { diff --git a/socket.h b/socket.h index 6f2a7e811b..5105adfb84 100644 --- a/socket.h +++ b/socket.h @@ -102,6 +102,9 @@ struct Socket { mode_t directory_mode; mode_t socket_mode; + bool accept; + unsigned n_accepted; + bool failure; Watch timer_watch; }; diff --git a/unit-name.c b/unit-name.c index 219997b681..eb2f704e2f 100644 --- a/unit-name.c +++ b/unit-name.c @@ -29,7 +29,7 @@ "0123456789" \ "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "-_.\\" + ":-_.\\" UnitType unit_name_to_type(const char *n) { UnitType t; diff --git a/unit.c b/unit.c index 7f147537e3..872abf3e13 100644 --- a/unit.c +++ b/unit.c @@ -1339,13 +1339,22 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { static char *default_cgroup_path(Unit *u) { char *p; + int r; assert(u); - if (asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id) < 0) - return NULL; + if (u->meta.instance) { + char *t; - return p; + if (!(t = unit_name_template(u->meta.id))) + return NULL; + + r = asprintf(&p, "%s/%s/%s", u->meta.manager->cgroup_hierarchy, t, u->meta.instance); + free(t); + } else + r = asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id); + + return r < 0 ? NULL : p; } int unit_add_cgroup_from_text(Unit *u, const char *name) { -- cgit v1.2.3-54-g00ecf