]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hostnamed: add SetTags() Varlink method
authorLennart Poettering <lennart@amutable.com>
Fri, 29 May 2026 16:16:21 +0000 (18:16 +0200)
committerLennart Poettering <lennart@amutable.com>
Mon, 1 Jun 2026 08:56:16 +0000 (10:56 +0200)
Add a SetTags() method to the io.systemd.Hostname Varlink interface that
combines what the D-Bus SetTags() and AddAndRemoveTags() methods do into
a single call: it takes three tag lists, 'set', 'add' and 'remove'. If
'set' is supplied the tag list is first reset to it (an explicit empty
array clears it), otherwise the current tags are used as basis; then the
'add' tags are added and the 'remove' tags are dropped.

The set arithmetic is shared with the D-Bus AddAndRemoveTags() handler
via machine_tags_add_remove().

Being reachable over Varlink (and hence socket-activatable without the
system bus) this is usable very early at boot, before dbus-daemon is up.

src/hostname/hostnamed.c
src/shared/varlink-io.systemd.Hostname.c

index bb30b4c90d2f16e43ba07f983d9c7bfef2e6092f..8849843e13e64c58b9abc63aad39854e14b6b9e2 100644 (file)
@@ -2229,6 +2229,87 @@ static int vl_method_describe(sd_varlink *link, sd_json_variant *parameters, sd_
         return sd_varlink_reply(link, v);
 }
 
+typedef struct SetTagsParameters {
+        char **set;
+        char **add;
+        char **remove;
+} SetTagsParameters;
+
+static void set_tags_parameters_done(SetTagsParameters *p) {
+        assert(p);
+
+        strv_free(p->set);
+        strv_free(p->add);
+        strv_free(p->remove);
+}
+
+static int vl_method_set_tags(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "set",    SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(SetTagsParameters, set),    SD_JSON_NULLABLE },
+                { "add",    SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(SetTagsParameters, add),    SD_JSON_NULLABLE },
+                { "remove", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(SetTagsParameters, remove), SD_JSON_NULLABLE },
+                VARLINK_DISPATCH_POLKIT_FIELD,
+                {}
+        };
+
+        Context *c = ASSERT_PTR(userdata);
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        _cleanup_(set_tags_parameters_done) SetTagsParameters p = {};
+        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        /* Both an absent 'set' field and an explicit null map to a NULL strv after dispatching, but only the
+         * former should keep the current tags — an explicit (possibly empty) 'set' resets the list. Hence
+         * check for the field's presence separately. */
+        bool reset = sd_json_variant_by_key(parameters, "set");
+
+        if (reset && !machine_tag_list_is_valid(p.set))
+                return sd_varlink_error_invalid_parameter_name(link, "set");
+        if (!machine_tag_list_is_valid(p.add))
+                return sd_varlink_error_invalid_parameter_name(link, "add");
+        if (!machine_tag_list_is_valid(p.remove))
+                return sd_varlink_error_invalid_parameter_name(link, "remove");
+
+        context_read_machine_info(c);
+
+        _cleanup_strv_free_ char **current = NULL;
+        r = machine_tags_from_string(c->data[PROP_TAGS], /* graceful= */ true, &current);
+        if (r < 0)
+                return r;
+
+        /* Use either the explicitly specified tag list or the current one as the basis, then apply the
+         * additions and removals on top. */
+        _cleanup_strv_free_ char **tags = NULL;
+        r = machine_tags_add_remove(reset ? p.set : current, p.add, p.remove, &tags);
+        if (r == -E2BIG)
+                return sd_varlink_error_invalid_parameter_name(link, "add");
+        if (r < 0)
+                return r;
+
+        if (strv_equal(tags, current))
+                return sd_varlink_reply(link, NULL);
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        c->bus,
+                        "org.freedesktop.hostname1.set-machine-info",
+                        /* details= */ NULL,
+                        &c->polkit_registry);
+        if (r <= 0)
+                return r;
+
+        r = context_store_tags(c, tags);
+        if (r < 0)
+                return r;
+
+        return sd_varlink_reply(link, NULL);
+}
+
 static int connect_varlink(Context *c) {
         int r;
 
@@ -2253,6 +2334,7 @@ static int connect_varlink(Context *c) {
         r = sd_varlink_server_bind_method_many(
                         c->varlink_server,
                         "io.systemd.Hostname.Describe",      vl_method_describe,
+                        "io.systemd.Hostname.SetTags",       vl_method_set_tags,
                         "io.systemd.service.Ping",           varlink_method_ping,
                         "io.systemd.service.SetLogLevel",    varlink_method_set_log_level,
                         "io.systemd.service.GetEnvironment", varlink_method_get_environment);
index e06587039b5100d840b10c2138b146f6c73a01d4..e7e03e127ec89b2e5cc00a5a94f982cf5fcb89b6 100644 (file)
@@ -44,7 +44,19 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_DEFINE_OUTPUT(ProductUUID, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
                 SD_VARLINK_DEFINE_OUTPUT(VSockCID, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
 
+static SD_VARLINK_DEFINE_METHOD(
+                SetTags,
+                SD_VARLINK_FIELD_COMMENT("If specified, the machine tag list is first reset to exactly these tags, before the 'add' and 'remove' fields are applied. If null, the current tag list is used as basis instead."),
+                SD_VARLINK_DEFINE_INPUT(set, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
+                SD_VARLINK_FIELD_COMMENT("Machine tags to add to the list."),
+                SD_VARLINK_DEFINE_INPUT(add, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
+                SD_VARLINK_FIELD_COMMENT("Machine tags to remove from the list."),
+                SD_VARLINK_DEFINE_INPUT(remove, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
 SD_VARLINK_DEFINE_INTERFACE(
                 io_systemd_Hostname,
                 "io.systemd.Hostname",
-                &vl_method_Describe);
+                &vl_method_Describe,
+                SD_VARLINK_SYMBOL_COMMENT("Edits the machine tag list: optionally resets it to 'set', then adds 'add' and removes 'remove'."),
+                &vl_method_SetTags);