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)
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",
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 */
}
/* 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);
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)
log_debug("Serializing state to %s", path);
free(path);
- if (!(f = fdopen(fd, "w+")))
+ f = fdopen(fd, "w+");
+ if (!f)
return -errno;
*_f = f;
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;
}
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;
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);
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);
.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,
/* 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);
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",
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);