From: Lennart Poettering Date: Fri, 29 May 2026 16:16:21 +0000 (+0200) Subject: hostnamed: add SetTags() Varlink method X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0f790ea87fb27e9fda18c9457cfd253830d93532;p=thirdparty%2Fsystemd.git hostnamed: add SetTags() Varlink method 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. --- diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index bb30b4c90d2..8849843e13e 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -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, ¤t); + 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); diff --git a/src/shared/varlink-io.systemd.Hostname.c b/src/shared/varlink-io.systemd.Hostname.c index e06587039b5..e7e03e127ec 100644 --- a/src/shared/varlink-io.systemd.Hostname.c +++ b/src/shared/varlink-io.systemd.Hostname.c @@ -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);