#include "varlink-io.systemd.Resolve.Monitor.h"
#include "varlink-io.systemd.Shutdown.h"
#include "varlink-io.systemd.StorageProvider.h"
+#include "varlink-io.systemd.SysUpdate.Notify.h"
#include "varlink-io.systemd.Udev.h"
#include "varlink-io.systemd.Unit.h"
#include "varlink-io.systemd.UserDatabase.h"
&vl_interface_io_systemd_Resolve_Monitor,
&vl_interface_io_systemd_Shutdown,
&vl_interface_io_systemd_StorageProvider,
+ &vl_interface_io_systemd_SysUpdate_Notify,
&vl_interface_io_systemd_Udev,
&vl_interface_io_systemd_Unit,
&vl_interface_io_systemd_UserDatabase,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.SysUpdate.Notify.h"
+
+static SD_VARLINK_DEFINE_STRUCT_TYPE(
+ Resource,
+ SD_VARLINK_DEFINE_FIELD(transfer, SD_VARLINK_STRING, 0),
+ SD_VARLINK_DEFINE_FIELD(path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
+
+static SD_VARLINK_DEFINE_METHOD(
+ OnCompletedUpdate,
+ SD_VARLINK_DEFINE_INPUT(component, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_DEFINE_INPUT(version, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_DEFINE_INPUT_BY_TYPE(resources, Resource, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
+
+SD_VARLINK_DEFINE_INTERFACE(
+ io_systemd_SysUpdate_Notify,
+ "io.systemd.SysUpdate.Notify",
+ &vl_type_Resource,
+ &vl_method_OnCompletedUpdate);
#include <unistd.h>
#include "sd-daemon.h"
+#include "sd-json.h"
+#include "sd-varlink.h"
#include "build.h"
#include "conf-files.h"
#include "constants.h"
#include "dissect-image.h"
+#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "sysupdate-transfer.h"
#include "sysupdate-update-set.h"
#include "sysupdate-util.h"
+#include "varlink-util.h"
#include "verbs.h"
static char *arg_definitions = NULL;
return 1;
}
+static int notify_subscribers_reply(
+ sd_varlink *link,
+ sd_json_variant *reply,
+ const char *error_id,
+ sd_varlink_reply_flags_t flags,
+ void *userdata) {
+
+ assert(link);
+
+ if (error_id)
+ log_warning("Notification subscriber '%s' returned error, ignoring: %s",
+ strna(sd_varlink_get_description(link)), error_id);
+
+ return 0;
+}
+
+static int context_notify_subscribers(Context *c, UpdateSet *us) {
+ int r;
+
+ assert(c);
+
+ /* 'us' is NULL when we are forced to notify even though no update was applied (via
+ * SYSTEMD_SYSUPDATE_FORCE_NOTIFY=1). In that case we send neither a version nor a resource list. */
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *resources = NULL;
+ if (us)
+ for (size_t i = 0; i < c->n_transfers; i++) {
+ Instance *inst = us->instances[i];
+ Transfer *t = c->transfers[i];
+
+ if (inst->resource == &t->target &&
+ !inst->is_pending)
+ continue;
+
+ /* Report where the resource was installed *to* (not the source it came from): the
+ * final on-disk path for filesystem targets, the partition device node for partition
+ * targets. */
+ const char *target_path =
+ RESOURCE_IS_FILESYSTEM(t->target.type) ? t->final_path :
+ t->target.type == RESOURCE_PARTITION ? t->partition_info.device :
+ NULL;
+
+ r = sd_json_variant_append_arraybo(
+ &resources,
+ SD_JSON_BUILD_PAIR_STRING("transfer", t->id),
+ SD_JSON_BUILD_PAIR_CONDITION(!!target_path, "path", SD_JSON_BUILD_STRING(target_path)));
+ if (r < 0)
+ return log_warning_errno(r, "Failed to build sysupdate notify resources list, skipping notification: %m");
+ }
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *params = NULL;
+ r = sd_json_buildo(
+ ¶ms,
+ SD_JSON_BUILD_PAIR_CONDITION(!!arg_component, "component", SD_JSON_BUILD_STRING(arg_component)),
+ SD_JSON_BUILD_PAIR_CONDITION(!!us, "version", SD_JSON_BUILD_STRING(us ? us->version : NULL)),
+ SD_JSON_BUILD_PAIR_CONDITION(!!resources, "resources", SD_JSON_BUILD_VARIANT(resources)));
+ if (r < 0)
+ return log_warning_errno(r, "Failed to build sysupdate notify parameters, skipping notification: %m");
+
+ ssize_t n = varlink_execute_directory(
+ VARLINK_DIR_SYSUPDATE_NOTIFY_HOOK,
+ "io.systemd.SysUpdate.Notify.OnCompletedUpdate",
+ params,
+ /* more= */ false,
+ /* timeout_usec= */ 5 * USEC_PER_MINUTE,
+ notify_subscribers_reply,
+ /* userdata= */ NULL);
+ if (n < 0)
+ log_debug_errno(n, "Failed to dispatch sysupdate notification to %s, ignoring: %m",
+ VARLINK_DIR_SYSUPDATE_NOTIFY_HOOK);
+ else if (n > 0)
+ log_debug("Dispatched sysupdate notification to %zi subscribers in %s.", n, VARLINK_DIR_SYSUPDATE_NOTIFY_HOOK);
+
+ return 0;
+}
+
static int context_install(
Context *c,
const char *version,
log_info("%s Successfully installed update '%s'.", glyph(GLYPH_SPARKLES), us->version);
+ if (!arg_root)
+ (void) context_notify_subscribers(c, us);
+
(void) sd_notifyf(/* unset_environment= */ false,
"STATUS=Installed '%s'.", us->version);
installed = r > 0;
}
+
+ /* context_install() returns > 0 (and emits a notification) only if it actually applied an update. If
+ * nothing was applied but SYSTEMD_SYSUPDATE_FORCE_NOTIFY=1 is set, still notify subscribers (without a
+ * resource list), so e.g. a kernel/policy refresh can be triggered unconditionally. */
+ if ((action_flags & UPDATE_ACTION_INSTALL) && !installed) {
+ int f = secure_getenv_bool("SYSTEMD_SYSUPDATE_FORCE_NOTIFY");
+ if (f < 0 && f != -ENXIO)
+ log_debug_errno(f, "Failed to parse $SYSTEMD_SYSUPDATE_FORCE_NOTIFY, ignoring: %m");
+ if (f > 0)
+ (void) context_notify_subscribers(context, /* us= */ NULL);
+ }
}
if (arg_cleanup > 0)