613b6e
From da81a108653e2ef19102698dbc0184bd18b084d9 Mon Sep 17 00:00:00 2001
613b6e
From: Mike Yuan <me@yhndnzj.com>
613b6e
Date: Thu, 10 Oct 2024 21:16:05 +0200
613b6e
Subject: [PATCH 1/4] core/manager: still send out STATUS=Ready for user
613b6e
 manager
613b6e
613b6e
This effectively reverts 37d15cd132f3a8a0bf42fb252c1604e804171ff2.
613b6e
613b6e
The offending commit wrongly assumed that the second READY=1
613b6e
notification is for system scope only, but it also serves the purpose
613b6e
of flushing out previous STATUS= containing user unit job status.
613b6e
---
613b6e
 src/core/manager.c | 14 +++++++-------
613b6e
 1 file changed, 7 insertions(+), 7 deletions(-)
613b6e
613b6e
diff --git a/src/core/manager.c b/src/core/manager.c
613b6e
index 2789f0e3d0c9c..456ad46135b72 100644
613b6e
--- a/src/core/manager.c
613b6e
+++ b/src/core/manager.c
613b6e
@@ -3885,7 +3885,7 @@ static void manager_notify_finished(Manager *m) {
613b6e
         log_taint_string(m);
613b6e
 }
613b6e
 
613b6e
-static void manager_send_ready_user_scope(Manager *m) {
613b6e
+static void manager_send_ready_on_basic_target(Manager *m) {
613b6e
         int r;
613b6e
 
613b6e
         assert(m);
613b6e
@@ -3904,18 +3904,18 @@ static void manager_send_ready_user_scope(Manager *m) {
613b6e
         m->status_ready = false;
613b6e
 }
613b6e
 
613b6e
-static void manager_send_ready_system_scope(Manager *m) {
613b6e
+static void manager_send_ready_on_idle(Manager *m) {
613b6e
         int r;
613b6e
 
613b6e
         assert(m);
613b6e
 
613b6e
-        if (!MANAGER_IS_SYSTEM(m))
613b6e
-                return;
613b6e
-
613b6e
         /* Skip the notification if nothing changed. */
613b6e
         if (m->ready_sent && m->status_ready)
613b6e
                 return;
613b6e
 
613b6e
+        /* Note that for user managers, we might have already sent READY=1 in manager_send_ready_user_scope().
613b6e
+         * But we still need to flush STATUS=. The second READY=1 will be treated as a noop so it doesn't
613b6e
+         * hurt to send it twice. */
613b6e
         r = sd_notify(/* unset_environment= */ false,
613b6e
                       "READY=1\n"
613b6e
                       "STATUS=Ready.");
613b6e
@@ -3940,7 +3940,7 @@ static void manager_check_basic_target(Manager *m) {
613b6e
                 return;
613b6e
 
613b6e
         /* For user managers, send out READY=1 as soon as we reach basic.target */
613b6e
-        manager_send_ready_user_scope(m);
613b6e
+        manager_send_ready_on_basic_target(m);
613b6e
 
613b6e
         /* Log the taint string as soon as we reach basic.target */
613b6e
         log_taint_string(m);
613b6e
@@ -3971,7 +3971,7 @@ void manager_check_finished(Manager *m) {
613b6e
         if (hashmap_buckets(m->jobs) > hashmap_size(m->units) / 10)
613b6e
                 m->jobs = hashmap_free(m->jobs);
613b6e
 
613b6e
-        manager_send_ready_system_scope(m);
613b6e
+        manager_send_ready_on_idle(m);
613b6e
 
613b6e
         /* Notify Type=idle units that we are done now */
613b6e
         manager_close_idle_pipe(m);
613b6e
613b6e
From 155098a702c4f6de6b1dca534661492625773fed Mon Sep 17 00:00:00 2001
613b6e
From: Mike Yuan <me@yhndnzj.com>
613b6e
Date: Thu, 10 Oct 2024 21:06:35 +0200
613b6e
Subject: [PATCH 2/4] core/manager-serialize: drop serialization for
613b6e
 Manager.ready_sent
613b6e
613b6e
This field indicates whether READY=1 has been sent to
613b6e
the service manager/supervisor. Whenever we reload/reexec/soft-reboot,
613b6e
manager_send_reloading() always resets it to false first,
613b6e
so that READY=1 is sent after reloading finishes. Hence
613b6e
we utterly get "false" at all times. Kill it.
613b6e
---
613b6e
 src/core/manager-serialize.c | 12 +-----------
613b6e
 1 file changed, 1 insertion(+), 11 deletions(-)
613b6e
613b6e
diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
613b6e
index 62dfce93a0a85..3f624619dfd19 100644
613b6e
--- a/src/core/manager-serialize.c
613b6e
+++ b/src/core/manager-serialize.c
613b6e
@@ -92,7 +92,6 @@ int manager_serialize(
613b6e
         (void) serialize_item_format(f, "current-job-id", "%" PRIu32, m->current_job_id);
613b6e
         (void) serialize_item_format(f, "n-installed-jobs", "%u", m->n_installed_jobs);
613b6e
         (void) serialize_item_format(f, "n-failed-jobs", "%u", m->n_failed_jobs);
613b6e
-        (void) serialize_bool(f, "ready-sent", m->ready_sent);
613b6e
         (void) serialize_bool(f, "taint-logged", m->taint_logged);
613b6e
         (void) serialize_bool(f, "service-watchdogs", m->service_watchdogs);
613b6e
 
613b6e
@@ -356,15 +355,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
613b6e
                         else
613b6e
                                 m->n_failed_jobs += n;
613b6e
 
613b6e
-                } else if ((val = startswith(l, "ready-sent="))) {
613b6e
-                        int b;
613b6e
-
613b6e
-                        b = parse_boolean(val);
613b6e
-                        if (b < 0)
613b6e
-                                log_notice("Failed to parse ready-sent flag '%s', ignoring.", val);
613b6e
-                        else
613b6e
-                                m->ready_sent = m->ready_sent || b;
613b6e
-
613b6e
                 } else if ((val = startswith(l, "taint-logged="))) {
613b6e
                         int b;
613b6e
 
613b6e
@@ -558,7 +548,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
613b6e
 
613b6e
                         if (q < _MANAGER_TIMESTAMP_MAX) /* found it */
613b6e
                                 (void) deserialize_dual_timestamp(val, m->timestamps + q);
613b6e
-                        else if (!STARTSWITH_SET(l, "kdbus-fd=", "honor-device-enumeration=")) /* ignore deprecated values */
613b6e
+                        else if (!STARTSWITH_SET(l, "kdbus-fd=", "honor-device-enumeration=", "ready-sent=")) /* ignore deprecated values */
613b6e
                                 log_notice("Unknown serialization item '%s', ignoring.", l);
613b6e
                 }
613b6e
         }
613b6e
613b6e
From a375e145190482e8a2f0971bffb332e31211622f Mon Sep 17 00:00:00 2001
613b6e
From: Mike Yuan <me@yhndnzj.com>
613b6e
Date: Thu, 10 Oct 2024 21:32:17 +0200
613b6e
Subject: [PATCH 3/4] units/{user,capsule}@.service: issue daemon-reexec when
613b6e
 notify-reloading
613b6e
613b6e
Closes #28367 (but not really in the exact form, see below)
613b6e
613b6e
We have the problem of restarting all user manager instances
613b6e
after upgrade. Current approaches involve systemctl kill
613b6e
with SIGRTMIN+25, which is async and feels rather ugly [1][2];
613b6e
or systemctl --machine=user@ --user, which requires entering
613b6e
each user session. Neither is particularly elegant.
613b6e
Instead, let's just signal daemon-reexec when user@.service
613b6e
is reloaded from system manager. Our long goal of dropping
613b6e
daemon-reload in favor of reexec (see TODO) is unlikely to happen
613b6e
due to user dbus restrictions, but here the synchronization
613b6e
is done via READY=1.
613b6e
613b6e
[1] https://gitlab.archlinux.org/archlinux/packaging/packages/systemd/-/blob/main/systemd.install?ref_type=heads#L37
613b6e
[2] https://salsa.debian.org/systemd-team/systemd/-/blob/debian/master/debian/systemd.postinst#L24
613b6e
613b6e
#28367 would not really work for us now I come to think about it,
613b6e
because all processes will be reparented to pid1 as soon as
613b6e
original user manager process exits. This alternative approach
613b6e
seems good enough for our use case.
613b6e
---
613b6e
 units/capsule@.service.in | 4 ++++
613b6e
 units/user@.service.in    | 4 ++++
613b6e
 2 files changed, 8 insertions(+)
613b6e
613b6e
diff --git a/units/capsule@.service.in b/units/capsule@.service.in
613b6e
index f2bb9e3a45a83..a64298786e490 100644
613b6e
--- a/units/capsule@.service.in
613b6e
+++ b/units/capsule@.service.in
613b6e
@@ -23,6 +23,10 @@ StateDirectory=capsules/%i
613b6e
 RuntimeDirectory=capsules/%i
613b6e
 LogExtraFields=CAPSULE=%i
613b6e
 Slice=capsule.slice
613b6e
+# Reexecute the manager on service reload, instead of reloading.
613b6e
+# This provides a synchronous method for restarting all user manager
613b6e
+# instances after upgrade.
613b6e
+ReloadSignal=RTMIN+25
613b6e
 KillMode=mixed
613b6e
 Delegate=pids memory cpu
613b6e
 DelegateSubgroup=init.scope
613b6e
diff --git a/units/user@.service.in b/units/user@.service.in
613b6e
index 5695465747217..381ab2a0db54e 100644
613b6e
--- a/units/user@.service.in
613b6e
+++ b/units/user@.service.in
613b6e
@@ -20,6 +20,10 @@ PAMName=systemd-user
613b6e
 Type=notify-reload
613b6e
 ExecStart={{LIBEXECDIR}}/systemd --user
613b6e
 Slice=user-%i.slice
613b6e
+# Reexecute the manager on service reload, instead of reloading.
613b6e
+# This provides a synchronous method for restarting all user manager
613b6e
+# instances after upgrade.
613b6e
+ReloadSignal=RTMIN+25
613b6e
 KillMode=mixed
613b6e
 Delegate=pids memory cpu
613b6e
 DelegateSubgroup=init.scope
613b6e
613b6e
From 2d0af8bc354f4a1429cebedfb387af72c88720a0 Mon Sep 17 00:00:00 2001
613b6e
From: Daan De Meyer <daan.j.demeyer@gmail.com>
613b6e
Date: Thu, 10 Oct 2024 22:37:39 +0200
613b6e
Subject: [PATCH 4/4] rpm/systemd-update-helper: Use systemctl reload to
613b6e
 reexec/reload user managers
613b6e
613b6e
Let's always use systemctl reload to reexec and reload user managers
613b6e
now that it always implies a reexec. This moves all the job management
613b6e
logic to pid 1 instead of bash and reduces the complexity of the logic
613b6e
as we remove systemd-run, pam and systemd-stdio-bridge from the equation.
613b6e
---
613b6e
 src/rpm/systemd-update-helper.in | 20 ++++----------------
613b6e
 1 file changed, 4 insertions(+), 16 deletions(-)
613b6e
613b6e
diff --git a/src/rpm/systemd-update-helper.in b/src/rpm/systemd-update-helper.in
613b6e
index c81e16c3d3ffb..8af914935261a 100755
613b6e
--- a/src/rpm/systemd-update-helper.in
613b6e
+++ b/src/rpm/systemd-update-helper.in
613b6e
@@ -107,25 +107,13 @@ case "$command" in
613b6e
 
613b6e
         [ -d /run/systemd/system ] || exit 0
613b6e
 
613b6e
-        users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
613b6e
-
613b6e
-        if [[ "$command" =~ reexec ]]; then
613b6e
-            for user in $users; do
613b6e
-                SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT_SEC}}s \
613b6e
-                        systemctl --user -M "$user@" daemon-reexec &
613b6e
-            done
613b6e
-            wait
613b6e
-        fi
613b6e
-
613b6e
-        if [[ "$command" =~ reload ]]; then
613b6e
-            for user in $users; do
613b6e
-                SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT_SEC}}s \
613b6e
-                        systemctl --user -M "$user@" daemon-reload &
613b6e
-            done
613b6e
-            wait
613b6e
+        if [[ "$command" =~ reexec|reload ]]; then
613b6e
+            SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT_SEC}}s systemctl reload "user@*.service"
613b6e
         fi
613b6e
 
613b6e
         if [[ "$command" =~ restart ]]; then
613b6e
+            users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
613b6e
+
613b6e
             for user in $users; do
613b6e
                 SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT_SEC}}s \
613b6e
                         systemctl --user -M "$user@" reload-or-restart --marked &