diff --git a/presets/90-systemd.preset b/presets/90-systemd.preset index 9ecebf0e08..bce96b7a6d 100644 --- a/presets/90-systemd.preset +++ b/presets/90-systemd.preset @@ -42,6 +42,7 @@ enable systemd-resolved.service enable systemd-sysext.service enable systemd-sysupdate-notify-bootctl.socket enable systemd-sysupdate-notify-pcrlock.socket +enable systemd-sysupdate-notify-sysext.socket enable systemd-timesyncd.service enable systemd-tpm2-clear.service enable systemd-userdbd.socket diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 0f4102d850..4cc9883628 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -68,6 +68,7 @@ #include "strv.h" #include "time-util.h" #include "unit-name.h" +#include "varlink-io.systemd.SysUpdate.Notify.h" #include "varlink-io.systemd.sysext.h" #include "varlink-util.h" #include "verbs.h" @@ -2898,6 +2899,42 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v return sd_varlink_reply(link, NULL); } +static int refresh_class(ImageClass image_class) { + _cleanup_strv_free_ char **hierarchies = NULL; + int r; + + r = parse_env_extension_hierarchies(&hierarchies, image_class_info[image_class].name_env); + if (r < 0) + return log_error_errno(r, "Failed to determine %s hierarchies: %m", image_class_info[image_class].short_identifier); + + return refresh(image_class, hierarchies, arg_force, arg_no_reload, arg_always_refresh, arg_noexec); +} + +static int vl_method_on_completed_update(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + int r; + + assert(link); + + /* Only honour update notifications if they come from root */ + r = varlink_check_privileged_peer(link); + if (r < 0) + return r; + + /* Triggered by systemd-sysupdate after an update completed. We deliberately ignore all parameters + * (we don't even dispatch them). This method is reached through the sysext socket, but a single + * notification refreshes both image classes, so freshly downloaded sysexts and confexts are both + * picked up, equivalent to "systemd-sysext refresh" plus "systemd-confext refresh". We attempt both + * classes and only fail afterwards, so a problem with one does not prevent refreshing the other. */ + + r = 0; + RET_GATHER(r, refresh_class(IMAGE_SYSEXT)); + RET_GATHER(r, refresh_class(IMAGE_CONFEXT)); + if (r < 0) + return r; + + return sd_varlink_reply(link, NULL); +} + VERB_NOARG(verb_list, "list", "List installed extensions"); static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { _cleanup_hashmap_free_ Hashmap *images = NULL; @@ -3184,16 +3221,20 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to allocate Varlink server: %m"); - r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_sysext); + r = sd_varlink_server_add_interface_many( + varlink_server, + &vl_interface_io_systemd_sysext, + &vl_interface_io_systemd_SysUpdate_Notify); if (r < 0) - return log_error_errno(r, "Failed to add Varlink interface: %m"); + return log_error_errno(r, "Failed to add Varlink interfaces: %m"); r = sd_varlink_server_bind_method_many( varlink_server, "io.systemd.sysext.Merge", vl_method_merge, "io.systemd.sysext.Unmerge", vl_method_unmerge, "io.systemd.sysext.Refresh", vl_method_refresh, - "io.systemd.sysext.List", vl_method_list); + "io.systemd.sysext.List", vl_method_list, + "io.systemd.SysUpdate.Notify.OnCompletedUpdate", vl_method_on_completed_update); if (r < 0) return log_error_errno(r, "Failed to bind Varlink methods: %m"); diff --git a/units/meson.build b/units/meson.build index b126f8f1bc..83dae605fa 100644 --- a/units/meson.build +++ b/units/meson.build @@ -823,6 +823,14 @@ units = [ 'conditions' : ['ENABLE_SYSEXT'], 'symlinks' : ['sockets.target.wants/'], }, + { + 'file' : 'systemd-sysupdate-notify-sysext@.service', + 'conditions' : ['ENABLE_SYSEXT'], + }, + { + 'file' : 'systemd-sysupdate-notify-sysext.socket', + 'conditions' : ['ENABLE_SYSEXT'], + }, { 'file' : 'systemd-sysext@.service', 'conditions' : ['ENABLE_SYSEXT'], diff --git a/units/systemd-sysupdate-notify-sysext.socket b/units/systemd-sysupdate-notify-sysext.socket new file mode 100644 index 0000000000..55d29a28dd --- /dev/null +++ b/units/systemd-sysupdate-notify-sysext.socket @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=System Extension and Configuration Refresh on Completed System Update +Documentation=man:systemd-sysext(8) +DefaultDependencies=no +After=local-fs.target +Before=sockets.target +ConditionCapability=CAP_SYS_ADMIN + +[Socket] +ListenStream=/run/systemd/sysupdate/notify/io.systemd.sysext +FileDescriptorName=varlink +SocketMode=0644 +Accept=yes +MaxConnectionsPerSource=16 +XAttrEntryPoint=user.varlink=entrypoint +XAttrListen=user.varlink=listen +XAttrAccept=user.varlink=server + +[Install] +WantedBy=sockets.target diff --git a/units/systemd-sysupdate-notify-sysext@.service b/units/systemd-sysupdate-notify-sysext@.service new file mode 100644 index 0000000000..c0efe542dd --- /dev/null +++ b/units/systemd-sysupdate-notify-sysext@.service @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=System Extension and Configuration Refresh on Completed System Update +Documentation=man:systemd-sysext(8) +DefaultDependencies=no +Conflicts=shutdown.target initrd-switch-root.target +After=local-fs.target +Before=shutdown.target initrd-switch-root.target + +[Service] +ExecStart=-systemd-sysext