3e5297
From a63d5d320f81c1cbae07897a401ed5cc5374e0bf Mon Sep 17 00:00:00 2001
3e5297
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
3e5297
Date: Wed, 7 Jul 2021 14:37:57 +0200
3e5297
Subject: [PATCH 4/5] rpm: restart user services at the end of the transaction
3e5297
3e5297
This closes an important gap: so far we would reexecute the system manager and
3e5297
restart system services that were configured to do so, but we wouldn't do the
3e5297
same for user managers or user services.
3e5297
3e5297
The scheme used for user managers is very similar to the system one, except
3e5297
that there can be multiple user managers running, so we query the system
3e5297
manager to get a list of them, and then tell each one to do the equivalent
3e5297
operations: daemon-reload, disable --now, set-property Markers=+needs-restart,
3e5297
reload-or-restart --marked.
3e5297
3e5297
The total time that can be spend on this is bounded: we execute the commands in
3e5297
parallel over user managers and units, and additionally set SYSTEMD_BUS_TIMEOUT
3e5297
to a lower value (15 s by default). User managers should not have too many
3e5297
units running, and they should be able to do all those operations very
3e5297
quickly (<< 1s). The final restart operation may take longer, but it's done
3e5297
asynchronously, so we only wait for the queuing to happen.
3e5297
3e5297
The advantage of doing this synchronously is that we can wait for each step to
3e5297
happen, and for example daemon-reloads can finish before we execute the service
3e5297
restarts, etc. We can also order various steps wrt. to the phases in the rpm
3e5297
transaction.
3e5297
3e5297
When this was initially proposed, we discussed a more relaxed scheme with bus
3e5297
property notifications. Such an approach would be more complex because a bunch
3e5297
of infrastructure would have to be added to system manager to propagate
3e5297
appropriate notifications to the user managers, and then the user managers
3e5297
would have to wait for them. Instead, now there is no new code in the managers,
3e5297
all new functionality is contained in src/rpm/. The ability to call 'systemctl
3e5297
--user user@' makes this approach very easy. Also, it would be very hard to
3e5297
order the user manager steps and the rpm transaction steps.
3e5297
3e5297
Note: 'systemctl --user disable' is only called for a user managers that are
3e5297
running. I don't see a nice way around this, and it shouldn't matter too much:
3e5297
we'll just leave a dangling symlink in the case where the user enabled the
3e5297
service manually.
3e5297
3e5297
A follow-up for https://bugzilla.redhat.com/show_bug.cgi?id=1792468 and
3e5297
fa97d2fcf64e0558054bee673f734f523373b146.
3e5297
3e5297
(cherry picked from commit 36d55958ccc75fa3c91bdd7354d74c910f2f6cc7)
3e5297
---
3e5297
 meson.build                      |  1 +
3e5297
 meson_options.txt                |  2 ++
3e5297
 src/rpm/macros.systemd.in        |  6 +++-
3e5297
 src/rpm/systemd-update-helper.in | 47 ++++++++++++++++++++++++++++++++
3e5297
 src/rpm/triggers.systemd.in      | 28 ++++++++++++++++++-
3e5297
 src/rpm/triggers.systemd.sh.in   | 13 ++++++++-
3e5297
 6 files changed, 94 insertions(+), 3 deletions(-)
3e5297
3e5297
diff --git a/meson.build b/meson.build
3e5297
index fb986e84f7..d898d9ccd0 100644
3e5297
--- a/meson.build
3e5297
+++ b/meson.build
3e5297
@@ -270,6 +270,7 @@ conf.set_quoted('TMPFILES_DIR',                               tmpfilesdir)
3e5297
 conf.set_quoted('UDEVLIBEXECDIR',                             udevlibexecdir)
3e5297
 conf.set_quoted('UDEV_HWDB_DIR',                              udevhwdbdir)
3e5297
 conf.set_quoted('UDEV_RULES_DIR',                             udevrulesdir)
3e5297
+conf.set_quoted('UPDATE_HELPER_USER_TIMEOUT',                 get_option('update-helper-user-timeout'))
3e5297
 conf.set_quoted('USER_CONFIG_UNIT_DIR',                       join_paths(pkgsysconfdir, 'user'))
3e5297
 conf.set_quoted('USER_DATA_UNIT_DIR',                         userunitdir)
3e5297
 conf.set_quoted('USER_ENV_GENERATOR_DIR',                     userenvgeneratordir)
3e5297
diff --git a/meson_options.txt b/meson_options.txt
3e5297
index 163c8df87d..9383c7da6a 100644
3e5297
--- a/meson_options.txt
3e5297
+++ b/meson_options.txt
3e5297
@@ -182,6 +182,8 @@ option('xinitrcdir', type : 'string', value : '',
3e5297
        description : 'directory for xinitrc files')
3e5297
 option('rpmmacrosdir', type : 'string', value : 'lib/rpm/macros.d',
3e5297
        description : 'directory for rpm macros ["no" disables]')
3e5297
+option('update-helper-user-timeout', type : 'string', value : '15s',
3e5297
+       description : 'how long to wait for user manager operations')
3e5297
 option('pamlibdir', type : 'string',
3e5297
        description : 'directory for PAM modules')
3e5297
 option('pamconfdir', type : 'string',
3e5297
diff --git a/src/rpm/macros.systemd.in b/src/rpm/macros.systemd.in
3e5297
index bbdf036da7..caa2e45595 100644
3e5297
--- a/src/rpm/macros.systemd.in
3e5297
+++ b/src/rpm/macros.systemd.in
3e5297
@@ -93,7 +93,11 @@ fi \
3e5297
 %{nil}
3e5297
 
3e5297
 %systemd_user_postun_with_restart() \
3e5297
-%{expand:%%{?__systemd_someargs_%#:%%__systemd_someargs_%# systemd_postun_with_restart}} \
3e5297
+%{expand:%%{?__systemd_someargs_%#:%%__systemd_someargs_%# systemd_user_postun_with_restart}} \
3e5297
+if [ $1 -ge 1 ] && [ -x "{{SYSTEMD_UPDATE_HELPER_PATH}}" ]; then \
3e5297
+    # Package upgrade, not uninstall \
3e5297
+    {{SYSTEMD_UPDATE_HELPER_PATH}} mark-restart-user-units %{?*} || : \
3e5297
+fi \
3e5297
 %{nil}
3e5297
 
3e5297
 %udev_hwdb_update() %{nil}
3e5297
diff --git a/src/rpm/systemd-update-helper.in b/src/rpm/systemd-update-helper.in
3e5297
index f3c75b75fa..f3466ab3c0 100755
3e5297
--- a/src/rpm/systemd-update-helper.in
3e5297
+++ b/src/rpm/systemd-update-helper.in
3e5297
@@ -26,6 +26,15 @@ case "$command" in
3e5297
 
3e5297
     remove-user-units)
3e5297
         systemctl --global disable "$@"
3e5297
+
3e5297
+        [ -d /run/systemd/system ] || exit 0
3e5297
+
3e5297
+        users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
3e5297
+        for user in $users; do
3e5297
+            SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \
3e5297
+                    systemctl --user -M "$user@" disable --now "$@" &
3e5297
+        done
3e5297
+        wait
3e5297
         ;;
3e5297
 
3e5297
     mark-restart-system-units)
3e5297
@@ -37,6 +46,17 @@ case "$command" in
3e5297
         wait
3e5297
         ;;
3e5297
 
3e5297
+    mark-restart-user-units)
3e5297
+        [ -d /run/systemd/system ] || exit 0
3e5297
+
3e5297
+        users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
3e5297
+        for user in $users; do
3e5297
+            SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \
3e5297
+                    systemctl --user -M "$user@" set-property "$unit" Markers=+needs-restart &
3e5297
+        done
3e5297
+        wait
3e5297
+        ;;
3e5297
+
3e5297
     system-reload-restart|system-reload|system-restart)
3e5297
         if [ -n "$*" ]; then
3e5297
             echo "Unexpected arguments for '$command': $*"
3e5297
@@ -54,6 +74,33 @@ case "$command" in
3e5297
         fi
3e5297
         ;;
3e5297
 
3e5297
+    user-reload-restart|user-reload|user-restart)
3e5297
+        if [ -n "$*" ]; then
3e5297
+            echo "Unexpected arguments for '$command': $*"
3e5297
+            exit 2
3e5297
+        fi
3e5297
+
3e5297
+        [ -d /run/systemd/system ] || exit 0
3e5297
+
3e5297
+        users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
3e5297
+
3e5297
+        if [[ "$command" =~ reload ]]; then
3e5297
+            for user in $users; do
3e5297
+                SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \
3e5297
+                        systemctl --user -M "$user@" daemon-reload &
3e5297
+            done
3e5297
+            wait
3e5297
+        fi
3e5297
+
3e5297
+        if [[ "$command" =~ restart ]]; then
3e5297
+            for user in $users; do
3e5297
+                SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \
3e5297
+                        systemctl --user -M "$user@" reload-or-restart --marked &
3e5297
+            done
3e5297
+            wait
3e5297
+        fi
3e5297
+        ;;
3e5297
+
3e5297
     *)
3e5297
         echo "Unknown verb '$command'"
3e5297
         exit 3
3e5297
diff --git a/src/rpm/triggers.systemd.in b/src/rpm/triggers.systemd.in
3e5297
index d29cc33dfd..8aeb2049c1 100644
3e5297
--- a/src/rpm/triggers.systemd.in
3e5297
+++ b/src/rpm/triggers.systemd.in
3e5297
@@ -20,6 +20,14 @@ elseif pid > 0 then
3e5297
     posix.wait(pid)
3e5297
 end
3e5297
 
3e5297
+%transfiletriggerin -P 900899 -p <lua> -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
3e5297
+pid = posix.fork()
3e5297
+if pid == 0 then
3e5297
+    assert(posix.exec("{{SYSTEMD_UPDATE_HELPER_PATH}}", "user-reload-restart"))
3e5297
+elseif pid > 0 then
3e5297
+    posix.wait(pid)
3e5297
+end
3e5297
+
3e5297
 %transfiletriggerpostun -P 1000100 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
3e5297
 -- On removal, we need to run daemon-reload after any units have been
3e5297
 -- removed.
3e5297
@@ -33,8 +41,17 @@ elseif pid > 0 then
3e5297
     posix.wait(pid)
3e5297
 end
3e5297
 
3e5297
+%transfiletriggerpostun -P 1000100 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
3e5297
+-- Execute daemon-reload in user managers.
3e5297
+pid = posix.fork()
3e5297
+if pid == 0 then
3e5297
+    assert(posix.exec("{{SYSTEMD_UPDATE_HELPER_PATH}}", "user-reload"))
3e5297
+elseif pid > 0 then
3e5297
+    posix.wait(pid)
3e5297
+end
3e5297
+
3e5297
 %transfiletriggerpostun -P 10000 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
3e5297
--- We restart remaining services that should be restarted here.
3e5297
+-- We restart remaining system services that should be restarted here.
3e5297
 pid = posix.fork()
3e5297
 if pid == 0 then
3e5297
     assert(posix.exec("{{SYSTEMD_UPDATE_HELPER_PATH}}", "system-restart"))
3e5297
@@ -42,6 +59,15 @@ elseif pid > 0 then
3e5297
     posix.wait(pid)
3e5297
 end
3e5297
 
3e5297
+%transfiletriggerpostun -P 9999 -p <lua> -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
3e5297
+-- We restart remaining user services that should be restarted here.
3e5297
+pid = posix.fork()
3e5297
+if pid == 0 then
3e5297
+    assert(posix.exec("{{SYSTEMD_UPDATE_HELPER_PATH}}", "user-restart"))
3e5297
+elseif pid > 0 then
3e5297
+    posix.wait(pid)
3e5297
+end
3e5297
+
3e5297
 %transfiletriggerin -P 100700 -p <lua> -- {{SYSUSERS_DIR}}
3e5297
 -- This script will process files installed in {{SYSUSERS_DIR}} to create
3e5297
 -- specified users automatically. The priority is set such that it
3e5297
diff --git a/src/rpm/triggers.systemd.sh.in b/src/rpm/triggers.systemd.sh.in
3e5297
index 83cd7617f8..694cd94e8d 100644
3e5297
--- a/src/rpm/triggers.systemd.sh.in
3e5297
+++ b/src/rpm/triggers.systemd.sh.in
3e5297
@@ -16,6 +16,9 @@
3e5297
 # so sometimes we will reload needlessly.
3e5297
 {{SYSTEMD_UPDATE_HELPER_PATH}} system-reload-restart || :
3e5297
 
3e5297
+%transfiletriggerin -P 900899 -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
3e5297
+{{SYSTEMD_UPDATE_HELPER_PATH}} user-reload-restart || :
3e5297
+
3e5297
 %transfiletriggerpostun -P 1000100 -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
3e5297
 # On removal, we need to run daemon-reload after any units have been
3e5297
 # removed.
3e5297
@@ -24,10 +27,18 @@
3e5297
 # executed.
3e5297
 {{SYSTEMD_UPDATE_HELPER_PATH}} system-reload || :
3e5297
 
3e5297
+%transfiletriggerpostun -P 1000099 -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
3e5297
+# Execute daemon-reload in user managers.
3e5297
+{{SYSTEMD_UPDATE_HELPER_PATH}} user-reload || :
3e5297
+
3e5297
 %transfiletriggerpostun -P 10000 -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
3e5297
-# We restart remaining services that should be restarted here.
3e5297
+# We restart remaining system services that should be restarted here.
3e5297
 {{SYSTEMD_UPDATE_HELPER_PATH}} system-restart || :
3e5297
 
3e5297
+%transfiletriggerpostun -P  9999 -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
3e5297
+# We restart remaining user services that should be restarted here.
3e5297
+{{SYSTEMD_UPDATE_HELPER_PATH}} user-restart || :
3e5297
+
3e5297
 %transfiletriggerin -P 1000700 -- {{SYSUSERS_DIR}}
3e5297
 # This script will process files installed in {{SYSUSERS_DIR}} to create
3e5297
 # specified users automatically. The priority is set such that it
3e5297
-- 
3e5297
2.31.1
3e5297