From 0f5852edfad06fe4e9f00aaddd3d93576269729e Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Tue, 23 Dec 2025 13:56:31 -0600 Subject: [PATCH 1/2] cgfsng: fix reboots when using dbus When using dbus on a systemd system, we ask systemd to create a "scope" for us to run in. We send a dbus message, and wait for the reply saying it is created. When we reboot, we were re-sending the request to create the scope. However, the scope still exists, because or single lxc-monitor (originally lxc-start) thread is still under the 'lxc.pivot' sub-directory of the scope. But, on reboot, our lxc_conf already has our scope recorded! So, just check whether that is set, and skip scope creation if so. With this patch, i can reboot ad nauseum with no apparent problems. We could probably move this check to the top of the function, but for now this fixes the bug. Signed-off-by: Serge Hallyn --- src/lxc/cgroups/cgfsng.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c index eea2b1f6d9..81994817c5 100644 --- a/src/lxc/cgroups/cgfsng.c +++ b/src/lxc/cgroups/cgfsng.c @@ -1521,6 +1521,9 @@ static int unpriv_systemd_create_scope(struct cgroup_ops *ops, struct lxc_conf * dbus_threads_initialized = true; } + if (conf->cgroup_meta.systemd_scope != NULL) + return log_error(true, "Already in a scope, must be a reboot."); + connection = open_systemd(); if (connection == NULL) return log_error(false, "Failed opening dbus connection"); From 6a5550ec2263d1b3c1f809e88fbf00d5fb73fad2 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 25 Dec 2025 21:50:53 -0600 Subject: [PATCH 2/2] Improve the dbus scope creation error handling If there is an actual dbus error, then return an error. If we have gotten a message that isn't what we are expecting, then keep waiting. Also put a time limit on our wiating for a reply. Until now, we were waiting indefinitely, causing the lxc monitor to hang. Signed-off-by: Serge Hallyn --- src/lxc/cgroups/cgfsng.c | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c index 81994817c5..ef92b943da 100644 --- a/src/lxc/cgroups/cgfsng.c +++ b/src/lxc/cgroups/cgfsng.c @@ -984,7 +984,13 @@ static void _dbus_message_free(DBusMessage **message) } } -static bool systemd_cgroup_scope_ready(DBusConnection *connection, const char *scope_name) +enum systemd_scope_ret { + SYSTEMD_SCOPE_RET_OK, // The scope was created (we got JobRemoved) + SYSTEMD_SCOPE_RET_IGNORE, // No helpful message + SYSTEMD_SCOPE_RET_ERROR, // Error decoding message +}; + +static enum systemd_scope_ret systemd_cgroup_scope_ready(DBusConnection *connection, const char *scope_name) { __attribute__((__cleanup__(_dbus_message_free))) DBusMessage* message = NULL; DBusMessageIter iter; @@ -993,38 +999,40 @@ static bool systemd_cgroup_scope_ready(DBusConnection *connection, const char *s dbus_connection_read_write(connection, 0); message = dbus_connection_pop_message(connection); if (!message) - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_IGNORE, "Dbus error..."); - if (!dbus_message_is_signal(message, INTERFACE, "JobRemoved")) - return false; + if (!dbus_message_is_signal(message, INTERFACE, "JobRemoved")) { + DEBUG("Got a message which is not JobRemoved"); + return SYSTEMD_SCOPE_RET_IGNORE; + } TRACE("got a JobRemoved signal."); // "uoss" -> &id, &path, &unit, &result) if (!dbus_message_iter_init(message, &iter)) // id - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_ERROR, "Dbus error..."); if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&iter)) - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_ERROR, "Dbus error..."); if (!dbus_message_iter_next(&iter)) // path - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_ERROR, "Dbus error..."); if (!dbus_message_iter_next(&iter)) // unit - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_ERROR, "Dbus error..."); if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter)) - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_ERROR, "Dbus error..."); dbus_message_iter_get_basic(&iter, &unit); if (strcmp(unit, scope_name) != 0) - return log_debug(false, "unit was '%s' not '%s'", unit, scope_name); + return log_debug(SYSTEMD_SCOPE_RET_IGNORE, "unit was '%s' not '%s'", unit, scope_name); if (!dbus_message_iter_next(&iter)) // result - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_ERROR, "Dbus error..."); if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter)) - return log_debug(false, "Dbus error..."); + return log_debug(SYSTEMD_SCOPE_RET_ERROR, "Dbus error..."); dbus_message_iter_get_basic(&iter, &result); if (strcmp(result, "done") != 0) - return log_debug(false, "JobRemoved signal received, but result was '%s' not done", result); + return log_debug(SYSTEMD_SCOPE_RET_IGNORE, "JobRemoved signal received, but result was '%s' not done", result); - return true; + return SYSTEMD_SCOPE_RET_OK; } struct dbus_iter { @@ -1124,6 +1132,8 @@ static bool start_scope(DBusConnection *connection, const char *scope_name) // ss scope_name, fail if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope_name)) return log_debug(false, "Dbus error..."); + // The final argument to dbus_message_iter_append_basic() is the mode, + // "replace" or "fail". If the directory exists, we want it to fail. if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail_name)) return log_debug(false, "Dbus error..."); @@ -1213,11 +1223,18 @@ static bool start_scope(DBusConnection *connection, const char *scope_name) // Wait on a signal telling us the async scope request is handled // TODO add a timeout + int count=0; while (true) { - if (systemd_cgroup_scope_ready(connection, scope_name)) + enum systemd_scope_ret sret; + sret = systemd_cgroup_scope_ready(connection, scope_name); + if (sret == SYSTEMD_SCOPE_RET_ERROR) + return log_debug(false, "error reading dbus message"); + if (sret == SYSTEMD_SCOPE_RET_OK) break; - nanosleep((const struct timespec[]){{0, 1000}}, NULL); - continue; + if (count > 50) + return log_debug(false, "timed out waiting for a response"); + nanosleep((const struct timespec[]){{0, 100000000L}}, NULL); + count++; } return true;