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.
This commit is contained in:
Lennart Poettering
2026-06-01 10:56:16 +02:00
parent 3eb956700f
commit 0f790ea87f
2 changed files with 95 additions and 1 deletions
+82
View 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);
+13 -1
View 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);