From 008798e90c8e05e02a2226c4d1804fd6d1353b1b Mon Sep 17 00:00:00 2001 From: Anita Zhang Date: Wed, 5 Oct 2022 00:13:32 -0700 Subject: [PATCH] core: serialize/deserialize varlink sockets for pid1 Fixes #20330 --- src/core/manager-serialize.c | 32 ++++++++++++++ src/shared/meson.build | 1 + src/shared/varlink-internal.h | 10 +++++ src/shared/varlink.c | 83 +++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 src/shared/varlink-internal.h diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c index 914bd92e36b..27cb0925aeb 100644 --- a/src/core/manager-serialize.c +++ b/src/core/manager-serialize.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "clean-ipc.h" +#include "core-varlink.h" #include "dbus.h" #include "fd-util.h" #include "fileio.h" @@ -13,6 +14,7 @@ #include "syslog-util.h" #include "unit-serialize.h" #include "user-util.h" +#include "varlink-internal.h" int manager_open_serialization(Manager *m, FILE **ret_f) { _cleanup_close_ int fd = -1; @@ -175,6 +177,10 @@ int manager_serialize( if (r < 0) return r; + r = varlink_server_serialize(m->varlink_server, f, fds); + if (r < 0) + return r; + (void) fputc('\n', f); HASHMAP_FOREACH_KEY(u, t, m->units) { @@ -290,6 +296,7 @@ static void manager_deserialize_gid_refs_one(Manager *m, const char *value) { } int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + bool deserialize_varlink_sockets = false; int r = 0; assert(m); @@ -516,7 +523,32 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { if (strv_extend(&m->deserialized_subscribed, val) < 0) return -ENOMEM; + } else if ((val = startswith(l, "varlink-server-socket-address="))) { + if (!m->varlink_server && MANAGER_IS_SYSTEM(m)) { + _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; + r = manager_setup_varlink_server(m, &s); + if (r < 0) { + log_warning_errno(r, "Failed to setup varlink server, ignoring: %m"); + continue; + } + + r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) { + log_warning_errno(r, "Failed to attach varlink connection to event loop, ignoring: %m"); + continue; + } + + m->varlink_server = TAKE_PTR(s); + deserialize_varlink_sockets = true; + } + + /* To void unnecessary deserialization (i.e. during reload vs. reexec) we only deserialize + * the FDs if we had to create a new m->varlink_server. The deserialize_varlink_sockets flag + * is initialized outside of the loop, is flipped after the VarlinkServer is setup, and + * remains set until all serialized contents are handled. */ + if (deserialize_varlink_sockets) + (void) varlink_server_deserialize_one(m->varlink_server, val, fds); } else { ManagerTimestamp q; diff --git a/src/shared/meson.build b/src/shared/meson.build index e805e9b898c..9e11e139344 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -326,6 +326,7 @@ shared_sources = files( 'utmp-wtmp.h', 'varlink.c', 'varlink.h', + 'varlink-internal.h', 'verb-log-control.c', 'verb-log-control.h', 'verbs.c', diff --git a/src/shared/varlink-internal.h b/src/shared/varlink-internal.h new file mode 100644 index 00000000000..715202a49e1 --- /dev/null +++ b/src/shared/varlink-internal.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "fdset.h" +#include "varlink.h" + +int varlink_server_serialize(VarlinkServer *s, FILE *f, FDSet *fds); +int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *fds); diff --git a/src/shared/varlink.c b/src/shared/varlink.c index 8b331be6672..4f7ac976891 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -12,6 +12,7 @@ #include "list.h" #include "process-util.h" #include "selinux-util.h" +#include "serialize.h" #include "set.h" #include "socket-util.h" #include "string-table.h" @@ -21,6 +22,7 @@ #include "umask-util.h" #include "user-util.h" #include "varlink.h" +#include "varlink-internal.h" #define VARLINK_DEFAULT_CONNECTIONS_MAX 4096U #define VARLINK_DEFAULT_CONNECTIONS_PER_UID_MAX 1024U @@ -2577,3 +2579,84 @@ int varlink_server_set_description(VarlinkServer *s, const char *description) { return free_and_strdup(&s->description, description); } + +int varlink_server_serialize(VarlinkServer *s, FILE *f, FDSet *fds) { + assert(f); + assert(fds); + + if (!s) + return 0; + + LIST_FOREACH(sockets, ss, s->sockets) { + int copy; + + assert(ss->address); + assert(ss->fd >= 0); + + fprintf(f, "varlink-server-socket-address=%s", ss->address); + + /* If we fail to serialize the fd, it will be considered an error during deserialization */ + copy = fdset_put_dup(fds, ss->fd); + if (copy < 0) + return copy; + + fprintf(f, " varlink-server-socket-fd=%i", copy); + + fputc('\n', f); + } + + return 0; +} + +int varlink_server_deserialize_one(VarlinkServer *s, const char *value, FDSet *fds) { + _cleanup_(varlink_server_socket_freep) VarlinkServerSocket *ss = NULL; + _cleanup_free_ char *address = NULL; + const char *v = ASSERT_PTR(value); + int r, fd = -1; + char *buf; + size_t n; + + assert(s); + assert(fds); + + n = strcspn(v, " "); + address = strndup(v, n); + if (!address) + return log_oom_debug(); + + if (v[n] != ' ') + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to deserialize VarlinkServerSocket: %s: %m", value); + v = startswith(v + n + 1, "varlink-server-socket-fd="); + if (!v) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to deserialize VarlinkServerSocket fd %s: %m", value); + + n = strcspn(v, " "); + buf = strndupa_safe(v, n); + + r = safe_atoi(buf, &fd); + if (r < 0) + return log_debug_errno(r, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf); + + if (!fdset_contains(fds, fd)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADF), + "VarlinkServerSocket varlink-server-socket-fd= has unknown fd %d: %m", fd); + + ss = new(VarlinkServerSocket, 1); + if (!ss) + return log_oom_debug(); + + *ss = (VarlinkServerSocket) { + .server = s, + .address = TAKE_PTR(address), + .fd = fdset_remove(fds, fd), + }; + + r = varlink_server_add_socket_event_source(s, ss, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return log_debug_errno(r, "Failed to add VarlinkServerSocket event source to the event loop: %m"); + + LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss)); + return 0; +}