chiark / gitweb /
rework socket handling
authorLennart Poettering <lennart@poettering.net>
Sat, 23 Jan 2010 02:35:54 +0000 (03:35 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 23 Jan 2010 02:35:54 +0000 (03:35 +0100)
load-fragment.c
manager.c
name.c
socket-util.c
socket-util.h
socket.c
socket.h
test1/postfix.socket
test1/syslog.socket

index a2c3636..e85c1d4 100644 (file)
@@ -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 }
         };
 
index 701e645..27b740e 100644 (file)
--- 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 5dba010..33d7f34 100644 (file)
--- 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",
index 407c961..525c334 100644 (file)
@@ -7,12 +7,13 @@
 #include <stdlib.h>
 #include <arpa/inet.h>
 #include <stdio.h>
+#include <net/if.h>
 
 #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;
 }
index 711f774..bea3a89 100644 (file)
@@ -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
index 9f87184..9b69487 100644 (file)
--- 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,
index fcf09dd..db5cd21 100644 (file)
--- 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;
index 8f9c0e8..81c2b07 100644 (file)
@@ -2,5 +2,5 @@
 Description=Postfix SMTP Socket
 
 [Socket]
-Listen=25
-Type=stream
+ListenStream=25
+ListenFIFO=/dev/test
index b599762..73a5662 100644 (file)
@@ -2,5 +2,5 @@
 Description=Syslog Socket
 
 [Socket]
-Listen=/dev/log
-Type=dgram
+ListenDatagram=/dev/log
+ListenStream=eth0:4711