From 775c7213288c9cf800036762d6dda8c4e33f35ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 29 May 2026 18:20:17 +0200 Subject: [PATCH] hostnamectl: allow incrementally adding/removing tags Teach the "tags" verb to accept arguments prefixed with '+' or '-': a '+'-prefixed argument adds all of its (colon-separated) tags to the configured list, a '-'-prefixed one removes them, mapping onto the new AddAndRemoveTags() D-Bus method. The prefix applies to the whole argument, so e.g. hostnamectl tags +foo:bar -quux:ooo adds foo and bar and removes quux and ooo. Plain (replace) and prefixed (add/remove) arguments may not be mixed in one invocation. --- man/hostnamectl.xml | 8 +++++ src/hostname/hostnamectl.c | 70 ++++++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml index 878837bee1..c120b938c9 100644 --- a/man/hostnamectl.xml +++ b/man/hostnamectl.xml @@ -177,6 +177,14 @@ resulting list is sorted before being stored. To remove all tags, invoke the command with a single empty string argument. + Alternatively, each argument may be prefixed with + or - + to incrementally update the configured tags rather than replacing them: an argument prefixed with + + adds all of its (colon-separated) tags to the list, one prefixed with + - removes them. For example, hostnamectl tags +foo:bar -quux:ooo + adds the tags foo and bar and removes quux + and ooo. The prefix applies to the whole argument, i.e. to every tag listed within + it. Prefixed and unprefixed arguments may not be combined in a single invocation. + Machine tags are short labels that may be used to classify and group machines for management purposes, for example to identify the role a machine plays in a deployment, the fleet or organizational unit it belongs to, or any other administrator-defined attribute. Each individual tag diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index d52580fbf5..a6ecad5178 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -772,25 +772,75 @@ static int verb_get_or_set_tags(int argc, char *argv[], uintptr_t _data, void *u return 0; } - _cleanup_strv_free_ char **l = NULL; + _cleanup_strv_free_ char **plain = NULL, **add = NULL, **remove = NULL; + bool prefixed = false, unprefixed = false; for (int i = 1; i < argc; i++) { - r = strv_split_and_extend(&l, argv[i], ":", /* filter_duplicates= */ true); - if (r < 0) + const char *e = argv[i]; + char ***dest; + + /* The first character of each argument selects the mode for all (colon-separated) tags in + * that argument: a leading '+' adds them, a leading '-' removes them, and otherwise the + * argument lists tags to set verbatim. Setting and adding/removing may not be mixed. */ + if (e[0] == '+') { + dest = &add; + e++; + prefixed = true; + } else if (e[0] == '-') { + dest = &remove; + e++; + prefixed = true; + } else { + dest = &plain; + unprefixed = true; + } + + if (isempty(e)) { + if (dest == &plain && !*dest) { + *dest = new0(char*, 1); + if (!*dest) + return log_oom(); + } + } else if (strv_split_and_extend(dest, e, ":", /* filter_duplicates= */ true) < 0) return log_oom(); } - strv_sort(l); + if (prefixed && unprefixed) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Plain tags may not be combined with '+'/'-' prefixed tags."); (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - r = bus_message_new_method_call(bus, &m, bus_hostname, "SetTags"); - if (r < 0) - return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, l); - if (r < 0) - return bus_log_create_error(r); + if (!prefixed) { + /* No prefixes used: replace the whole tag list with the given tags (possibly empty, which + * clears all tags). */ + strv_sort(plain); + + r = bus_message_new_method_call(bus, &m, bus_hostname, "SetTags"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, plain); + if (r < 0) + return bus_log_create_error(r); + } else { + /* Prefixes used: incrementally add and/or remove tags. */ + strv_sort(add); + strv_sort(remove); + + r = bus_message_new_method_call(bus, &m, bus_hostname, "AddAndRemoveTags"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, add); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, remove); + if (r < 0) + return bus_log_create_error(r); + } r = sd_bus_call(bus, m, /* usec= */ 0, &error, /* ret_reply= */ NULL); if (r < 0)