From 542563babda739f7aee468c3eaaab6315ffa4224 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 23 Jan 2010 03:35:54 +0100 Subject: [PATCH] rework socket handling --- load-fragment.c | 89 ++++++++++++++++++++++++++++++++------------ manager.c | 14 +++---- name.c | 1 + socket-util.c | 84 +++++++++++++++++++++++++---------------- socket-util.h | 21 ++++++----- socket.c | 71 +++++++++++++++++++++++++++-------- socket.h | 27 ++++++++++++-- test1/postfix.socket | 4 +- test1/syslog.socket | 4 +- 9 files changed, 219 insertions(+), 96 deletions(-) diff --git a/load-fragment.c b/load-fragment.c index a2c36368d..e85c1d4cc 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -132,21 +132,57 @@ static int config_parse_listen( void *userdata) { int r; + SocketPort *p; + Socket *s; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if ((r = address_parse(data, rvalue)) < 0) { - log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue); - return r; + s = (Socket*) data; + + if (!(p = new0(SocketPort, 1))) + return -ENOMEM; + + if (streq(lvalue, "ListenFIFO")) { + p->type = SOCKET_FIFO; + + if (!(p->path = strdup(rvalue))) { + free(p); + return -ENOMEM; + } + } else { + p->type = SOCKET_SOCKET; + + if ((r = socket_address_parse(&p->address, rvalue)) < 0) { + log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue); + free(p); + return r; + } + + if (streq(lvalue, "ListenStream")) + p->address.type = SOCK_STREAM; + else if (streq(lvalue, "ListenDatagram")) + p->address.type = SOCK_DGRAM; + else { + assert(streq(lvalue, "ListenSequentialPacket")); + p->address.type = SOCK_SEQPACKET; + } + + if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) { + free(p); + return -EPROTONOSUPPORT; + } } + p->fd = -1; + LIST_PREPEND(SocketPort, s->ports, p); + return 0; } -static int config_parse_type( +static int config_parse_bind( const char *filename, unsigned line, const char *section, @@ -155,22 +191,23 @@ static int config_parse_type( void *data, void *userdata) { - int *type = data; + int r; + Socket *s; assert(filename); assert(lvalue); assert(rvalue); assert(data); - if (streq(rvalue, "stream")) - *type = SOCK_STREAM; - else if (streq(rvalue, "dgram")) - *type = SOCK_DGRAM; - else { - log_error("[%s:%u] Failed to parse socket type value: %s", filename, line, rvalue); - return -EINVAL; + s = (Socket*) data; + + if ((r = parse_boolean(rvalue)) < 0) { + log_error("[%s:%u] Failed to parse bind IPv6 only value: %s", filename, line, rvalue); + return r; } + s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH; + return 0; } @@ -188,18 +225,22 @@ int name_load_fragment(Name *n) { }; const ConfigItem items[] = { - { "Names", config_parse_names, &n->meta.names, "Meta" }, - { "Description", config_parse_string, &n->meta.description, "Meta" }, - { "Requires", config_parse_deps, n->meta.dependencies+NAME_REQUIRES, "Meta" }, - { "SoftRequires", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUIRES, "Meta" }, - { "Wants", config_parse_deps, n->meta.dependencies+NAME_WANTS, "Meta" }, - { "Requisite", config_parse_deps, n->meta.dependencies+NAME_REQUISITE, "Meta" }, - { "SoftRequisite", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUISITE, "Meta" }, - { "Conflicts", config_parse_deps, n->meta.dependencies+NAME_CONFLICTS, "Meta" }, - { "Before", config_parse_deps, n->meta.dependencies+NAME_BEFORE, "Meta" }, - { "After", config_parse_deps, n->meta.dependencies+NAME_AFTER, "Meta" }, - { "Listen", config_parse_listen, &n->socket.address, "Socket" }, - { "Type", config_parse_type, &n->socket.address.type, "Socket" }, + { "Names", config_parse_names, &n->meta.names, "Meta" }, + { "Description", config_parse_string, &n->meta.description, "Meta" }, + { "Requires", config_parse_deps, n->meta.dependencies+NAME_REQUIRES, "Meta" }, + { "SoftRequires", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUIRES, "Meta" }, + { "Wants", config_parse_deps, n->meta.dependencies+NAME_WANTS, "Meta" }, + { "Requisite", config_parse_deps, n->meta.dependencies+NAME_REQUISITE, "Meta" }, + { "SoftRequisite", config_parse_deps, n->meta.dependencies+NAME_SOFT_REQUISITE, "Meta" }, + { "Conflicts", config_parse_deps, n->meta.dependencies+NAME_CONFLICTS, "Meta" }, + { "Before", config_parse_deps, n->meta.dependencies+NAME_BEFORE, "Meta" }, + { "After", config_parse_deps, n->meta.dependencies+NAME_AFTER, "Meta" }, + { "ListenStream", config_parse_listen, &n->socket, "Socket" }, + { "ListenDatagram", config_parse_listen, &n->socket, "Socket" }, + { "ListenSequentialPacket", config_parse_listen, &n->socket, "Socket" }, + { "ListenFIFO", config_parse_listen, &n->socket, "Socket" }, + { "BindIPv6Only", config_parse_bind, &n->socket, "Socket" }, + { "Backlog", config_parse_unsigned, &n->socket.backlog, "Socket" }, { NULL, NULL, NULL, NULL } }; diff --git a/manager.c b/manager.c index 701e64512..27b740e58 100644 --- a/manager.c +++ b/manager.c @@ -699,28 +699,28 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name * /* Finally, recursively add in all dependencies. */ if (type == JOB_START || type == JOB_RELOAD_OR_START) { SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state) - if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0 && r != -EBADR) goto fail; SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state) - if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR) goto fail; SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state) - if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0 && r != -EBADR) goto fail; SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state) - if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR) goto fail; SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state) - if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) < 0 && r != -EBADR) goto fail; SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state) - if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0 && r != -EBADR) goto fail; } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state) - if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) != -EBADR) + if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0 && r != -EBADR) goto fail; } diff --git a/name.c b/name.c index 5dba01055..33d7f347b 100644 --- a/name.c +++ b/name.c @@ -367,6 +367,7 @@ void name_dump(Name *n, FILE *f, const char *prefix) { [NAME_SOFT_REQUISITE] = "SoftRequisite", [NAME_REQUIRED_BY] = "RequiredBy", [NAME_SOFT_REQUIRED_BY] = "SoftRequiredBy", + [NAME_WANTED_BY] = "WantedBy", [NAME_CONFLICTS] = "Conflicts", [NAME_BEFORE] = "Before", [NAME_AFTER] = "After", diff --git a/socket-util.c b/socket-util.c index 407c9617b..525c33474 100644 --- a/socket-util.c +++ b/socket-util.c @@ -7,12 +7,13 @@ #include #include #include +#include #include "macro.h" #include "util.h" #include "socket-util.h" -int address_parse(Address *a, const char *s) { +int socket_address_parse(SocketAddress *a, const char *s) { int r; char *e, *n; unsigned u; @@ -21,6 +22,7 @@ int address_parse(Address *a, const char *s) { assert(s); memset(a, 0, sizeof(*a)); + a->type = SOCK_STREAM; if (*s == '[') { /* IPv6 in [x:.....:z]:p notation */ @@ -53,7 +55,6 @@ int address_parse(Address *a, const char *s) { a->sockaddr.in6.sin6_family = AF_INET6; a->sockaddr.in6.sin6_port = htons((uint16_t) u); a->size = sizeof(struct sockaddr_in6); - a->bind_ipv6_only = true; } else if (*s == '/') { /* AF_UNIX socket */ @@ -83,29 +84,46 @@ int address_parse(Address *a, const char *s) { } else { if ((e = strchr(s, ':'))) { + int r; + + if ((r = safe_atou(e+1, &u)) < 0) + return r; + + if (u <= 0 || u > 0xFFFF) + return -EINVAL; - /* IPv4 in w.x.y.z:p notation */ if (!(n = strndup(s, e-s))) return -ENOMEM; - errno = 0; - if (inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr) <= 0) { + /* IPv4 in w.x.y.z:p notation? */ + if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) { free(n); - return errno != 0 ? -errno : -EINVAL; + return -errno; } - free(n); + if (r > 0) { + /* Gotcha, it's a traditional IPv4 address */ + free(n); - e++; - if ((r = safe_atou(e, &u)) < 0) - return r; + a->sockaddr.in4.sin_family = AF_INET; + a->sockaddr.in4.sin_port = htons((uint16_t) u); + a->size = sizeof(struct sockaddr_in); + } else { + unsigned idx; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; + /* Uh, our last resort, an interface name */ + idx = if_nametoindex(n); + free(n); + + if (n == 0) + return -EINVAL; - a->sockaddr.in4.sin_family = AF_INET; - a->sockaddr.in4.sin_port = htons((uint16_t) u); - a->size = sizeof(struct sockaddr_in); + a->sockaddr.in6.sin6_family = AF_INET6; + a->sockaddr.in6.sin6_port = htons((uint16_t) u); + a->sockaddr.in6.sin6_scope_id = idx; + memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN); + a->size = sizeof(struct sockaddr_in6); + } } else { /* Just a port */ @@ -116,8 +134,8 @@ int address_parse(Address *a, const char *s) { return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; - memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN); a->sockaddr.in6.sin6_port = htons((uint16_t) u); + memcpy(&a->sockaddr.in6.sin6_addr, &in6addr_any, INET6_ADDRSTRLEN); a->size = sizeof(struct sockaddr_in6); } } @@ -125,10 +143,10 @@ int address_parse(Address *a, const char *s) { return 0; } -int address_verify(const Address *a) { +int socket_address_verify(const SocketAddress *a) { assert(a); - switch (address_family(a)) { + switch (socket_address_family(a)) { case AF_INET: if (a->size != sizeof(struct sockaddr_in)) return -EINVAL; @@ -176,15 +194,15 @@ int address_verify(const Address *a) { } } -int address_print(const Address *a, char **p) { +int socket_address_print(const SocketAddress *a, char **p) { int r; assert(a); assert(p); - if ((r = address_verify(a)) < 0) + if ((r = socket_address_verify(a)) < 0) return r; - switch (address_family(a)) { + switch (socket_address_family(a)) { case AF_INET: { char *ret; @@ -256,16 +274,25 @@ int address_print(const Address *a, char **p) { } } -int address_listen(const Address *a, int backlog) { +int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only) { int r, fd; assert(a); - if ((r = address_verify(a)) < 0) + if ((r = socket_address_verify(a)) < 0) return r; - if ((fd = socket(address_family(a), a->type, 0)) < 0) + if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) return -errno; + 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 (bind(fd, &a->sockaddr.sa, a->size) < 0) { close_nointr(fd); return -errno; @@ -277,14 +304,5 @@ int address_listen(const Address *a, int backlog) { return -errno; } - if (address_family(a) == AF_INET6) { - int flag = a->bind_ipv6_only; - - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) { - close_nointr(fd); - return -errno; - } - } - return 0; } diff --git a/socket-util.h b/socket-util.h index 711f7740a..bea3a8931 100644 --- a/socket-util.h +++ b/socket-util.h @@ -10,7 +10,7 @@ #include "macro.h" #include "util.h" -typedef struct Address { +typedef struct SocketAddress { union { struct sockaddr sa; struct sockaddr_in in4; @@ -25,16 +25,19 @@ typedef struct Address { /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ int type; +} SocketAddress; - /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ - bool bind_ipv6_only; -} Address; +typedef enum SocketAddressBindIPv6Only { + SOCKET_ADDRESS_DEFAULT, + SOCKET_ADDRESS_BOTH, + SOCKET_ADDRESS_IPV6_ONLY +} SocketAddressBindIPv6Only; -#define address_family(a) ((a)->sockaddr.sa.sa_family) +#define socket_address_family(a) ((a)->sockaddr.sa.sa_family) -int address_parse(Address *a, const char *s); -int address_print(const Address *a, char **p); -int address_verify(const Address *a); -int address_listen(const Address *a, int backlog); +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); #endif diff --git a/socket.c b/socket.c index 9f8718447..9b694872b 100644 --- a/socket.c +++ b/socket.c @@ -7,10 +7,24 @@ static int socket_load(Name *n) { Socket *s = SOCKET(n); exec_context_defaults(&s->exec_context); + s->backlog = SOMAXCONN; return name_load_fragment_and_dropin(n); } +static const char* listen_lookup(int type) { + + if (type == SOCK_STREAM) + return "ListenStream"; + else if (type == SOCK_DGRAM) + return "ListenDatagram"; + else if (type == SOCK_SEQPACKET) + return "ListenSequentialPacket"; + + assert_not_reached("Unkown socket type"); + return NULL; +} + static void socket_dump(Name *n, FILE *f, const char *prefix) { static const char* const state_table[_SOCKET_STATE_MAX] = { @@ -33,24 +47,35 @@ static void socket_dump(Name *n, FILE *f, const char *prefix) { SocketExecCommand c; Socket *s = SOCKET(n); - const char *t; - int r; - char *k; + SocketPort *p; assert(s); - if ((r = address_print(&n->socket.address, &k)) < 0) - t = strerror(-r); - else - t = k; - fprintf(f, "%sSocket State: %s\n" - "%sAddress: %s\n", + "%sBindIPv6Only: %s\n" + "%sBacklog: %u\n", prefix, state_table[s->state], - prefix, t); + prefix, yes_no(s->bind_ipv6_only), + prefix, s->backlog); + + LIST_FOREACH(p, s->ports) { - free(k); + if (p->type == SOCKET_SOCKET) { + const char *t; + int r; + char *k; + + if ((r = socket_address_print(&p->address, &k)) < 0) + t = strerror(-r); + else + t = k; + + fprintf(f, "%s%s: %s\n", prefix, listen_lookup(p->address.type), k); + free(k); + } else + fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); + } exec_context_dump(&s->exec_context, f, prefix); @@ -62,6 +87,14 @@ static void socket_dump(Name *n, FILE *f, const char *prefix) { } } +static int socket_start(Name *n) { + return 0; +} + +static int socket_stop(Name *n) { + return 0; +} + static NameActiveState socket_active_state(Name *n) { static const NameActiveState table[_SOCKET_STATE_MAX] = { @@ -79,14 +112,20 @@ static NameActiveState socket_active_state(Name *n) { } static void socket_free_hook(Name *n) { - unsigned i; SocketExecCommand c; Socket *s = SOCKET(n); + SocketPort *p; assert(s); - for (i = 0; i < s->n_fds; i++) - close_nointr(s->fds[i]); + while ((p = s->ports)) { + LIST_REMOVE(SocketPort, s->ports, p); + + if (p->fd >= 0) + close_nointr(p->fd); + free(p->path); + free(p); + } exec_context_free(&s->exec_context); @@ -103,8 +142,8 @@ const NameVTable socket_vtable = { .load = socket_load, .dump = socket_dump, - .start = NULL, - .stop = NULL, + .start = socket_start, + .stop = socket_stop, .reload = NULL, .active_state = socket_active_state, diff --git a/socket.h b/socket.h index fcf09dd0d..db5cd2143 100644 --- a/socket.h +++ b/socket.h @@ -6,6 +6,7 @@ typedef struct Socket Socket; #include "name.h" +#include "socket-util.h" typedef enum SocketState { SOCKET_DEAD, @@ -27,14 +28,34 @@ typedef enum SocketExecCommand { _SOCKET_EXEC_MAX } SocketExecCommand; +typedef enum SocketType { + SOCKET_SOCKET, + SOCKET_FIFO +} SocketType; + +typedef struct SocketPort SocketPort; + +struct SocketPort { + SocketType type; + + SocketAddress address; + char *path; + + int fd; + + LIST_FIELDS(SocketPort); +}; + struct Socket { Meta meta; SocketState state; - Address address; - int *fds; - unsigned n_fds; + LIST_HEAD(SocketPort, ports); + + /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ + bool bind_ipv6_only; + unsigned backlog; ExecCommand* exec_command[_SOCKET_EXEC_MAX]; ExecContext exec_context; diff --git a/test1/postfix.socket b/test1/postfix.socket index 8f9c0e8aa..81c2b07a3 100644 --- a/test1/postfix.socket +++ b/test1/postfix.socket @@ -2,5 +2,5 @@ Description=Postfix SMTP Socket [Socket] -Listen=25 -Type=stream +ListenStream=25 +ListenFIFO=/dev/test diff --git a/test1/syslog.socket b/test1/syslog.socket index b599762e7..73a566201 100644 --- a/test1/syslog.socket +++ b/test1/syslog.socket @@ -2,5 +2,5 @@ Description=Syslog Socket [Socket] -Listen=/dev/log -Type=dgram +ListenDatagram=/dev/log +ListenStream=eth0:4711 -- 2.30.2