chiark / gitweb /
socket: support socket activation of containers
authorLennart Poettering <lennart@poettering.net>
Sat, 22 Dec 2012 18:30:07 +0000 (19:30 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 22 Dec 2012 21:17:58 +0000 (22:17 +0100)
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/socket.c
src/core/unit.h
src/shared/socket-util.c
src/shared/socket-util.h

index 2fcd63d..dfb53a8 100644 (file)
@@ -934,14 +934,18 @@ static int parse_argv(int argc, char *argv[]) {
                         int fd;
                         FILE *f;
 
-                        if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) {
+                        r = safe_atoi(optarg, &fd);
+                        if (r < 0 || fd < 0) {
                                 log_error("Failed to parse deserialize option %s.", optarg);
-                                return r;
+                                return r < 0 ? r : -EINVAL;
                         }
 
-                        if (!(f = fdopen(fd, "r"))) {
+                        fd_cloexec(fd, true);
+
+                        f = fdopen(fd, "r");
+                        if (!f) {
                                 log_error("Failed to open serialization fd: %m");
-                                return r;
+                                return -errno;
                         }
 
                         if (serialization)
@@ -1474,16 +1478,15 @@ int main(int argc, char *argv[]) {
         log_close();
 
         /* Remember open file descriptors for later deserialization */
-        if (serialization) {
-                r = fdset_new_fill(&fds);
-                if (r < 0) {
-                        log_error("Failed to allocate fd set: %s", strerror(-r));
-                        goto finish;
-                }
+        r = fdset_new_fill(&fds);
+        if (r < 0) {
+                log_error("Failed to allocate fd set: %s", strerror(-r));
+                goto finish;
+        } else
+                fdset_cloexec(fds, true);
 
+        if (serialization)
                 assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
-        } else
-                close_all_fds(NULL, 0);
 
         /* Set up PATH unless it is already set */
         setenv("PATH",
@@ -1518,6 +1521,12 @@ int main(int argc, char *argv[]) {
                 unsetenv("USER");
                 unsetenv("LOGNAME");
 
+                /* We suppress the socket activation env vars, as
+                 * we'll try to match *any* open fd to units if
+                 * possible. */
+                unsetenv("LISTEN_FDS");
+                unsetenv("LISTEN_PID");
+
                 /* All other variables are left as is, so that clients
                  * can still read them via /proc/1/environ */
         }
@@ -1653,10 +1662,7 @@ int main(int argc, char *argv[]) {
 
         /* This will close all file descriptors that were opened, but
          * not claimed by any unit. */
-        if (fds) {
-                fdset_free(fds);
-                fds = NULL;
-        }
+        fdset_free(fds);
 
         if (serialization) {
                 fclose(serialization);
index 1ddd8ba..ac11ce1 100644 (file)
@@ -706,6 +706,16 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
                         r = q;
         }
 
+        /* Any fds left? Find some unit which wants them. This is
+         * useful to allow container managers to pass some file
+         * descriptors to us pre-initialized. This enables
+         * socket-based activation of entire containers. */
+        if (fdset_size(fds) > 0) {
+                q = manager_distribute_fds(m, fds);
+                if (q < 0)
+                        r = q;
+        }
+
         /* Third, fire things up! */
         q = manager_coldplug(m);
         if (q < 0)
@@ -1807,7 +1817,8 @@ int manager_open_serialization(Manager *m, FILE **_f) {
         log_debug("Serializing state to %s", path);
         free(path);
 
-        if (!(f = fdopen(fd, "w+")))
+        f = fdopen(fd, "w+");
+        if (!f)
                 return -errno;
 
         *_f = f;
@@ -1965,7 +1976,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                 if ((r = manager_load_unit(m, strstrip(name), NULL, NULL, &u)) < 0)
                         goto finish;
 
-                if ((r = unit_deserialize(u, f, fds)) < 0)
+                r = unit_deserialize(u, f, fds);
+                if (r < 0)
                         goto finish;
         }
 
@@ -1981,6 +1993,28 @@ finish:
         return r;
 }
 
+int manager_distribute_fds(Manager *m, FDSet *fds) {
+        Unit *u;
+        Iterator i;
+        int r;
+
+        assert(m);
+
+        HASHMAP_FOREACH(u, m->units, i) {
+
+                if (fdset_size(fds) <= 0)
+                        break;
+
+                if (UNIT_VTABLE(u)->distribute_fds) {
+                        r = UNIT_VTABLE(u)->distribute_fds(u, fds);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
 int manager_reload(Manager *m) {
         int r, q;
         FILE *f;
index e9de496..cc4edf8 100644 (file)
@@ -272,6 +272,7 @@ int manager_open_serialization(Manager *m, FILE **_f);
 
 int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs);
 int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
+int manager_distribute_fds(Manager *m, FDSet *fds);
 
 int manager_reload(Manager *m);
 
index 18b8176..324ec1e 100644 (file)
@@ -1876,6 +1876,34 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
         return 0;
 }
 
+static int socket_distribute_fds(Unit *u, FDSet *fds) {
+        Socket *s = SOCKET(u);
+        SocketPort *p;
+
+        assert(u);
+
+        LIST_FOREACH(port, p, s->ports) {
+                Iterator i;
+                int fd;
+
+                if (p->type != SOCKET_SOCKET)
+                        continue;
+
+                if (p->fd >= 0)
+                        continue;
+
+                FDSET_FOREACH(fd, fds, i) {
+                        if (socket_address_matches_fd(&p->address, fd)) {
+                                p->fd = fdset_remove(fds, fd);
+                                s->deserialized_state = SOCKET_LISTENING;
+                                break;
+                        }
+                }
+        }
+
+        return 0;
+}
+
 static UnitActiveState socket_active_state(Unit *u) {
         assert(u);
 
@@ -2288,6 +2316,7 @@ const UnitVTable socket_vtable = {
 
         .serialize = socket_serialize,
         .deserialize_item = socket_deserialize_item,
+        .distribute_fds = socket_distribute_fds,
 
         .active_state = socket_active_state,
         .sub_state_to_string = socket_sub_state_to_string,
index b40f034..790d3d7 100644 (file)
@@ -310,6 +310,9 @@ struct UnitVTable {
         /* Restore one item from the serialization */
         int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds);
 
+        /* Try to match up fds with what we need for this unit */
+        int (*distribute_fds)(Unit *u, FDSet *fds);
+
         /* Boils down the more complex internal state of this unit to
          * a simpler one that the engine can understand */
         UnitActiveState (*active_state)(Unit *u);
index 42ea545..56ec99f 100644 (file)
@@ -515,6 +515,55 @@ bool socket_ipv6_is_supported(void) {
         return enabled;
 }
 
+bool socket_address_matches_fd(const SocketAddress *a, int fd) {
+        union sockaddr_union sa;
+        socklen_t salen = sizeof(sa), solen;
+        int protocol, type;
+
+        assert(a);
+        assert(fd >= 0);
+
+        if (getsockname(fd, &sa.sa, &salen) < 0)
+                return false;
+
+        if (sa.sa.sa_family != a->sockaddr.sa.sa_family)
+                return false;
+
+        solen = sizeof(type);
+        if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &solen) < 0)
+                return false;
+
+        if (type != a->type)
+                return false;
+
+        if (a->protocol != 0)  {
+                solen = sizeof(protocol);
+                if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &solen) < 0)
+                        return false;
+
+                if (protocol != a->protocol)
+                        return false;
+        }
+
+        switch (sa.sa.sa_family) {
+
+        case AF_INET:
+                return sa.in4.sin_port == a->sockaddr.in4.sin_port &&
+                        sa.in4.sin_addr.s_addr == a->sockaddr.in4.sin_addr.s_addr;
+
+        case AF_INET6:
+                return sa.in6.sin6_port == a->sockaddr.in6.sin6_port &&
+                        memcmp(&sa.in6.sin6_addr, &a->sockaddr.in6.sin6_addr, sizeof(struct in6_addr)) == 0;
+
+        case AF_UNIX:
+                return salen == a->size &&
+                        memcmp(sa.un.sun_path, a->sockaddr.un.sun_path, salen - offsetof(struct sockaddr_un, sun_path)) == 0;
+
+        }
+
+        return false;
+}
+
 static const char* const netlink_family_table[] = {
         [NETLINK_ROUTE] = "route",
         [NETLINK_FIREWALL] = "firewall",
index 04cfb83..771765d 100644 (file)
@@ -86,6 +86,8 @@ int socket_address_listen(
 bool socket_address_is(const SocketAddress *a, const char *s, int type);
 bool socket_address_is_netlink(const SocketAddress *a, const char *s);
 
+bool socket_address_matches_fd(const SocketAddress *a, int fd);
+
 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
 
 bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);