]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hostnamed: add Varlink equivalents for the hostname/machine-info setters
authorLennart Poettering <lennart@amutable.com>
Fri, 29 May 2026 20:05:03 +0000 (22:05 +0200)
committerLennart Poettering <lennart@amutable.com>
Mon, 1 Jun 2026 08:56:16 +0000 (10:56 +0200)
Add SetHostname(), SetStaticHostname(), SetPrettyHostname(), SetIconName(),
SetChassis(), SetDeployment() and SetLocation() to the io.systemd.Hostname
Varlink interface, mirroring the existing D-Bus methods of the same names.

Each takes a single nullable string "newValue" (null clears/resets the
value; the method name itself makes clear what the value is) plus the usual
allowInteractiveAuthentication polkit input, and applies the same
validation, polkit actions and /etc/hostname resp. /etc/machine-info
persistence as its D-Bus counterpart. The five machine-info setters share
a common helper. The hostname validator is reused from the D-Bus path,
translating its INVALID_ARGS error into a Varlink invalid-parameter error.

Being reachable over the socket-activated Varlink socket, these work in
early boot before the system bus is up.

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

index 8849843e13e64c58b9abc63aad39854e14b6b9e2..42fc050f405bdac9b251de685104ac95a395dead 100644 (file)
@@ -1347,7 +1347,7 @@ static int property_get_vsock_cid(
         return sd_bus_message_append(reply, "u", (uint32_t) local_cid);
 }
 
-static int validate_and_substitute_hostname(const char *name, char **ret_substituted, sd_bus_error *error) {
+static int validate_and_substitute_hostname(const char *name, char **ret_substituted) {
         int r;
 
         assert(ret_substituted);
@@ -1366,12 +1366,24 @@ static int validate_and_substitute_hostname(const char *name, char **ret_substit
                 return log_error_errno(r, "Failed to substitute wildcards in hostname: %m");
 
         if (!hostname_is_valid(substituted, 0))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
+                return -EUCLEAN;
 
         *ret_substituted = TAKE_PTR(substituted);
         return 1;
 }
 
+static int bus_validate_and_substitute_hostname(const char *name, char **ret_substituted, sd_bus_error *error) {
+        int r;
+
+        assert(ret_substituted);
+
+        r = validate_and_substitute_hostname(name, ret_substituted);
+        if (r == -EUCLEAN)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
+
+        return r;
+}
+
 static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         Context *c = ASSERT_PTR(userdata);
         const char *name;
@@ -1389,7 +1401,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
          * we might want to adjust hostname source information even if the actual hostname is unchanged. */
 
         _cleanup_free_ char *substituted = NULL;
-        r = validate_and_substitute_hostname(name, &substituted, error);
+        r = bus_validate_and_substitute_hostname(name, &substituted, error);
         if (r < 0)
                 return r;
 
@@ -1440,7 +1452,7 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
                 return sd_bus_reply_method_return(m, NULL);
 
         _cleanup_free_ char *substituted = NULL;
-        r = validate_and_substitute_hostname(name, &substituted, error);
+        r = bus_validate_and_substitute_hostname(name, &substituted, error);
         if (r < 0)
                 return r;
 
@@ -2229,6 +2241,252 @@ static int vl_method_describe(sd_varlink *link, sd_json_variant *parameters, sd_
         return sd_varlink_reply(link, v);
 }
 
+static int vl_validate_and_substitute_hostname(sd_varlink *link, const char *name, char **ret_substituted) {
+        int r;
+
+        assert(link);
+        assert(ret_substituted);
+
+        r = validate_and_substitute_hostname(name, ret_substituted);
+        if (r == -EUCLEAN)
+                return sd_varlink_error_invalid_parameter_name(link, "newValue");
+
+        return r;
+}
+
+static int vl_method_set_hostname(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "newValue", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, 0, SD_JSON_NULLABLE },
+                VARLINK_DISPATCH_POLKIT_FIELD,
+                {}
+        };
+
+        Context *c = ASSERT_PTR(userdata);
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        _cleanup_free_ char *value = NULL;
+        r = sd_varlink_dispatch(link, parameters, dispatch_table, &value);
+        if (r != 0)
+                return r;
+
+        const char *name = empty_to_null(value);
+
+        /* We always go through with the procedure below without comparing to the current hostname, because
+         * we might want to adjust hostname source information even if the actual hostname is unchanged. */
+
+        _cleanup_free_ char *substituted = NULL;
+        r = vl_validate_and_substitute_hostname(link, name, &substituted);
+        if (r < 0)
+                return r;
+
+        name = substituted;
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        c->bus,
+                        "org.freedesktop.hostname1.set-hostname",
+                        /* details= */ NULL,
+                        &c->polkit_registry);
+        if (r <= 0)
+                return r;
+
+        context_read_etc_hostname(c);
+
+        r = context_update_kernel_hostname(c, name);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                (void) sd_bus_emit_properties_changed(c->bus,
+                                                      "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
+                                                      "Hostname", "HostnameSource", NULL);
+
+        return sd_varlink_reply(link, NULL);
+}
+
+static int vl_method_set_static_hostname(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "newValue", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, 0, SD_JSON_NULLABLE },
+                VARLINK_DISPATCH_POLKIT_FIELD,
+                {}
+        };
+
+        Context *c = ASSERT_PTR(userdata);
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        _cleanup_free_ char *value = NULL;
+        r = sd_varlink_dispatch(link, parameters, dispatch_table, &value);
+        if (r != 0)
+                return r;
+
+        const char *name = empty_to_null(value);
+
+        context_read_etc_hostname(c);
+
+        if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
+                return sd_varlink_reply(link, NULL);
+
+        _cleanup_free_ char *substituted = NULL;
+        r = vl_validate_and_substitute_hostname(link, name, &substituted);
+        if (r < 0)
+                return r;
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        c->bus,
+                        "org.freedesktop.hostname1.set-static-hostname",
+                        /* details= */ NULL,
+                        &c->polkit_registry);
+        if (r <= 0)
+                return r;
+
+        r = free_and_strdup_warn(&c->data[PROP_STATIC_HOSTNAME], name);
+        if (r < 0)
+                return r;
+
+        free_and_replace(c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS], substituted);
+
+        r = context_write_data_static_hostname(c);
+        if (r < 0)
+                return r;
+
+        r = context_update_kernel_hostname(c, /* transient_hostname= */ NULL);
+        if (r < 0)
+                return r;
+
+        (void) sd_bus_emit_properties_changed(c->bus,
+                                              "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
+                                              "StaticHostname", "Hostname", "HostnameSource", NULL);
+
+        return sd_varlink_reply(link, NULL);
+}
+
+static int vl_set_machine_info(sd_varlink *link, sd_json_variant *parameters, void *userdata, HostProperty prop) {
+        static const sd_json_dispatch_field dispatch_table[] = {
+                { "newValue", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, 0, SD_JSON_NULLABLE },
+                VARLINK_DISPATCH_POLKIT_FIELD,
+                {}
+        };
+
+        Context *c = ASSERT_PTR(userdata);
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        _cleanup_free_ char *value = NULL;
+        r = sd_varlink_dispatch(link, parameters, dispatch_table, &value);
+        if (r != 0)
+                return r;
+
+        const char *name = empty_to_null(value),
+                *polkit_action = "org.freedesktop.hostname1.set-machine-info",
+                *bus_property, *human_name;
+
+        switch (prop) {
+
+        case PROP_PRETTY_HOSTNAME:
+                if (name && string_has_cc(name, NULL))
+                        return sd_varlink_error_invalid_parameter_name(link, "newValue");
+                bus_property = "PrettyHostname";
+                human_name = "pretty hostname";
+                /* Since the pretty hostname should always be changed at the same time as the static one, use
+                 * the same policy action for both... */
+                polkit_action = "org.freedesktop.hostname1.set-static-hostname";
+                break;
+
+        case PROP_ICON_NAME:
+                /* The icon name might ultimately be used as file name, so better be safe than sorry. */
+                if (name && !filename_is_valid(name))
+                        return sd_varlink_error_invalid_parameter_name(link, "newValue");
+                bus_property = "IconName";
+                human_name = "icon name";
+                break;
+
+        case PROP_CHASSIS:
+                if (name && !valid_chassis(name))
+                        return sd_varlink_error_invalid_parameter_name(link, "newValue");
+                bus_property = "Chassis";
+                human_name = "chassis";
+                break;
+
+        case PROP_DEPLOYMENT:
+                if (name && !valid_deployment(name))
+                        return sd_varlink_error_invalid_parameter_name(link, "newValue");
+                bus_property = "Deployment";
+                human_name = "deployment";
+                break;
+
+        case PROP_LOCATION:
+                if (name && string_has_cc(name, NULL))
+                        return sd_varlink_error_invalid_parameter_name(link, "newValue");
+                bus_property = "Location";
+                human_name = "location";
+                break;
+
+        default:
+                assert_not_reached();
+        }
+
+        context_read_machine_info(c);
+
+        if (streq_ptr(name, c->data[prop]))
+                return sd_varlink_reply(link, NULL);
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        c->bus,
+                        polkit_action,
+                        /* details= */ NULL,
+                        &c->polkit_registry);
+        if (r <= 0)
+                return r;
+
+        r = free_and_strdup_warn(&c->data[prop], name);
+        if (r < 0)
+                return r;
+
+        r = context_write_data_machine_info(c);
+        if (r < 0)
+                return r;
+
+        log_info("Changed %s to '%s'", human_name, strna(c->data[prop]));
+
+        (void) sd_bus_emit_properties_changed(
+                        c->bus,
+                        "/org/freedesktop/hostname1",
+                        "org.freedesktop.hostname1",
+                        bus_property,
+                        NULL);
+
+        return sd_varlink_reply(link, NULL);
+}
+
+static int vl_method_set_pretty_hostname(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        return vl_set_machine_info(link, parameters, userdata, PROP_PRETTY_HOSTNAME);
+}
+
+static int vl_method_set_icon_name(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        return vl_set_machine_info(link, parameters, userdata, PROP_ICON_NAME);
+}
+
+static int vl_method_set_chassis(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        return vl_set_machine_info(link, parameters, userdata, PROP_CHASSIS);
+}
+
+static int vl_method_set_deployment(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        return vl_set_machine_info(link, parameters, userdata, PROP_DEPLOYMENT);
+}
+
+static int vl_method_set_location(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+        return vl_set_machine_info(link, parameters, userdata, PROP_LOCATION);
+}
+
 typedef struct SetTagsParameters {
         char **set;
         char **add;
@@ -2333,11 +2591,18 @@ 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);
+                        "io.systemd.Hostname.Describe",          vl_method_describe,
+                        "io.systemd.Hostname.SetHostname",       vl_method_set_hostname,
+                        "io.systemd.Hostname.SetStaticHostname", vl_method_set_static_hostname,
+                        "io.systemd.Hostname.SetPrettyHostname", vl_method_set_pretty_hostname,
+                        "io.systemd.Hostname.SetIconName",       vl_method_set_icon_name,
+                        "io.systemd.Hostname.SetChassis",        vl_method_set_chassis,
+                        "io.systemd.Hostname.SetDeployment",     vl_method_set_deployment,
+                        "io.systemd.Hostname.SetLocation",       vl_method_set_location,
+                        "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);
         if (r < 0)
                 return log_error_errno(r, "Failed to bind Varlink method calls: %m");
 
index e7e03e127ec89b2e5cc00a5a94f982cf5fcb89b6..ce02f5ecbecf2a35147a6c266b74f2fcd240fbd4 100644 (file)
@@ -44,6 +44,48 @@ 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(
+                SetHostname,
+                SD_VARLINK_FIELD_COMMENT("The transient (runtime) hostname to set. If null the transient hostname is reset, so that the static hostname is used again."),
+                SD_VARLINK_DEFINE_INPUT(newValue, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_METHOD(
+                SetStaticHostname,
+                SD_VARLINK_FIELD_COMMENT("The static hostname to set, stored in /etc/hostname. If null the static hostname is removed."),
+                SD_VARLINK_DEFINE_INPUT(newValue, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_METHOD(
+                SetPrettyHostname,
+                SD_VARLINK_FIELD_COMMENT("The pretty (free-form, UTF-8) hostname to set. If null the pretty hostname is removed."),
+                SD_VARLINK_DEFINE_INPUT(newValue, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_METHOD(
+                SetIconName,
+                SD_VARLINK_FIELD_COMMENT("The icon name to set. If null the icon name is removed."),
+                SD_VARLINK_DEFINE_INPUT(newValue, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_METHOD(
+                SetChassis,
+                SD_VARLINK_FIELD_COMMENT("The chassis type to set. If null the chassis type is removed."),
+                SD_VARLINK_DEFINE_INPUT(newValue, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_METHOD(
+                SetDeployment,
+                SD_VARLINK_FIELD_COMMENT("The deployment environment to set. If null the deployment is removed."),
+                SD_VARLINK_DEFINE_INPUT(newValue, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_METHOD(
+                SetLocation,
+                SD_VARLINK_FIELD_COMMENT("The physical location to set. If null the location is removed."),
+                SD_VARLINK_DEFINE_INPUT(newValue, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                VARLINK_DEFINE_POLKIT_INPUT);
+
 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."),
@@ -58,5 +100,19 @@ SD_VARLINK_DEFINE_INTERFACE(
                 io_systemd_Hostname,
                 "io.systemd.Hostname",
                 &vl_method_Describe,
+                SD_VARLINK_SYMBOL_COMMENT("Sets the transient (runtime) hostname."),
+                &vl_method_SetHostname,
+                SD_VARLINK_SYMBOL_COMMENT("Sets the static hostname, stored in /etc/hostname."),
+                &vl_method_SetStaticHostname,
+                SD_VARLINK_SYMBOL_COMMENT("Sets the pretty (free-form, UTF-8) hostname."),
+                &vl_method_SetPrettyHostname,
+                SD_VARLINK_SYMBOL_COMMENT("Sets the icon name representing this system."),
+                &vl_method_SetIconName,
+                SD_VARLINK_SYMBOL_COMMENT("Sets the chassis type of this system."),
+                &vl_method_SetChassis,
+                SD_VARLINK_SYMBOL_COMMENT("Sets the deployment environment of this system."),
+                &vl_method_SetDeployment,
+                SD_VARLINK_SYMBOL_COMMENT("Sets the physical location of this system."),
+                &vl_method_SetLocation,
                 SD_VARLINK_SYMBOL_COMMENT("Edits the machine tag list: optionally resets it to 'set', then adds 'add' and removes 'remove'."),
                 &vl_method_SetTags);