summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/main.c38
-rw-r--r--src/core/manager.c38
-rw-r--r--src/core/manager.h1
-rw-r--r--src/core/socket.c29
-rw-r--r--src/core/unit.h3
-rw-r--r--src/shared/socket-util.c49
-rw-r--r--src/shared/socket-util.h2
7 files changed, 142 insertions, 18 deletions
diff --git a/src/core/main.c b/src/core/main.c
index 2fcd63d3e2..dfb53a8438 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -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);
diff --git a/src/core/manager.c b/src/core/manager.c
index 1ddd8bae62..ac11ce1806 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -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;
diff --git a/src/core/manager.h b/src/core/manager.h
index e9de4969b4..cc4edf8f1e 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -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);
diff --git a/src/core/socket.c b/src/core/socket.c
index 18b81761c5..324ec1e34b 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -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,
diff --git a/src/core/unit.h b/src/core/unit.h
index b40f034de1..790d3d758e 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -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);
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
index 42ea545917..56ec99f442 100644
--- a/src/shared/socket-util.c
+++ b/src/shared/socket-util.c
@@ -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",
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index 04cfb83f5a..771765d323 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -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);