1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-12 13:18:14 +03:00

socket: support socket activation of containers

This commit is contained in:
Lennart Poettering 2012-12-22 19:30:07 +01:00
parent 842f3b0fc9
commit 01e10de3c2
7 changed files with 143 additions and 19 deletions

View 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;
}
assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
r = fdset_new_fill(&fds);
if (r < 0) {
log_error("Failed to allocate fd set: %s", strerror(-r));
goto finish;
} else
close_all_fds(NULL, 0);
fdset_cloexec(fds, true);
if (serialization)
assert_se(fdset_remove(fds, fileno(serialization)) >= 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);

View 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;

View 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);

View 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,

View 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);

View 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",

View 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);