core: reliably check if varlink socket has been deserialized

Follow-up for 6906c028e8

The mentioned commit uses access() to check if varlink socket
already exists in the filesystem, but that isn't sufficient.

> Varlink sockets are not serialized until v252, so upgrading from
> v251 or older means we will not listen anymore on the varlink sockets.
>
> See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1074789
> for more details as this was found when updating from Debian Bullseye to a new version.

After this commit, the set up of varlink_server is effectively
split into two steps. manager_varlink_init_system(), which is
called after deserialization, would no longer skip listening
even if Manager.varlink_server is in place, but actually
check if we're listening on desired sockets.
Then, manager_deserialize() can be switched back to using
manager_setup_varlink_server().

Alternative to #33817

Co-authored-by: Luca Boccassi <bluca@debian.org>
This commit is contained in:
Mike Yuan
2024-07-23 19:38:57 +02:00
co-authored by Luca Boccassi
parent 7973f33e81
commit d4e5c66ed4
3 changed files with 31 additions and 23 deletions
+28 -22
View File
@@ -7,6 +7,7 @@
#include "mkdir-label.h"
#include "strv.h"
#include "user-util.h"
#include "varlink-internal.h"
#include "varlink-io.systemd.UserDatabase.h"
#include "varlink-io.systemd.ManagedOOM.h"
@@ -500,12 +501,17 @@ static void vl_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata
m->managed_oom_varlink = sd_varlink_unref(link);
}
static int manager_setup_varlink_server(Manager *m, sd_varlink_server **ret) {
int manager_setup_varlink_server(Manager *m) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
int r;
assert(m);
assert(ret);
if (m->varlink_server)
return 0;
if (!MANAGER_IS_SYSTEM(m))
return -EINVAL;
r = sd_varlink_server_new(&s, SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA);
if (r < 0)
@@ -533,51 +539,51 @@ static int manager_setup_varlink_server(Manager *m, sd_varlink_server **ret) {
if (r < 0)
return log_debug_errno(r, "Failed to register varlink disconnect handler: %m");
*ret = TAKE_PTR(s);
return 0;
r = sd_varlink_server_attach_event(s, m->event, EVENT_PRIORITY_IPC);
if (r < 0)
return log_debug_errno(r, "Failed to attach varlink connection to event loop: %m");
m->varlink_server = TAKE_PTR(s);
return 1;
}
static int manager_varlink_init_system(Manager *m) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
int r;
assert(m);
if (m->varlink_server)
return 1;
if (!MANAGER_IS_SYSTEM(m))
return 0;
r = manager_setup_varlink_server(m, &s);
r = manager_setup_varlink_server(m);
if (r < 0)
return log_error_errno(r, "Failed to set up varlink server: %m");
bool fresh = r > 0;
if (!MANAGER_IS_TEST_RUN(m)) {
(void) mkdir_p_label("/run/systemd/userdb", 0755);
FOREACH_STRING(address, "/run/systemd/userdb/io.systemd.DynamicUser", VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM) {
if (MANAGER_IS_RELOADING(m)) {
/* If manager is reloading, we skip listening on existing addresses, since
* the fd should be acquired later through deserialization. */
if (access(address, F_OK) >= 0)
if (!fresh) {
/* We might have got sockets through deserialization. Do not bind to them twice. */
bool found = false;
LIST_FOREACH(sockets, ss, m->varlink_server->sockets)
if (path_equal(ss->address, address)) {
found = true;
break;
}
if (found)
continue;
if (errno != ENOENT)
return log_error_errno(errno,
"Failed to check if varlink socket '%s' exists: %m", address);
}
r = sd_varlink_server_listen_address(s, address, 0666);
r = sd_varlink_server_listen_address(m->varlink_server, address, 0666);
if (r < 0)
return log_error_errno(r, "Failed to bind to varlink socket '%s': %m", address);
}
}
r = sd_varlink_server_attach_event(s, m->event, EVENT_PRIORITY_IPC);
if (r < 0)
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
m->varlink_server = TAKE_PTR(s);
return 1;
}
+2
View File
@@ -3,6 +3,8 @@
#include "manager.h"
int manager_setup_varlink_server(Manager *m);
int manager_varlink_init(Manager *m);
void manager_varlink_done(Manager *m);
+1 -1
View File
@@ -508,7 +508,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
return r;
} else if ((val = startswith(l, "varlink-server-socket-address="))) {
if (!m->varlink_server && MANAGER_IS_SYSTEM(m)) {
r = manager_varlink_init(m);
r = manager_setup_varlink_server(m);
if (r < 0) {
log_warning_errno(r, "Failed to setup varlink server, ignoring: %m");
continue;