]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hostnamed: add simple Varlink API, too
authorLennart Poettering <lennart@poettering.net>
Mon, 8 Jan 2024 14:13:07 +0000 (15:13 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 9 Jan 2024 09:46:25 +0000 (10:46 +0100)
src/hostname/hostnamed.c
src/shared/meson.build
src/shared/varlink-io.systemd.Hostname.c [new file with mode: 0644]
src/shared/varlink-io.systemd.Hostname.h [new file with mode: 0644]
test/units/testsuite-71.sh
test/units/testsuite-74.varlinkctl.sh
units/meson.build
units/systemd-hostnamed.socket [new file with mode: 0644]

index de0ee98aff7159288e10af6279871a96bea4ec64..f24c126d5d647246a389a142dc6e9eecdfff96f1 100644 (file)
@@ -38,6 +38,7 @@
 #include "string-table.h"
 #include "strv.h"
 #include "user-util.h"
+#include "varlink-io.systemd.Hostname.h"
 #include "virt.h"
 
 #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
@@ -77,6 +78,7 @@ typedef struct Context {
 
         sd_event *event;
         sd_bus *bus;
+        VarlinkServer *varlink_server;
         Hashmap *polkit_registry;
 } Context;
 
@@ -98,6 +100,7 @@ static void context_destroy(Context *c) {
         hashmap_free(c->polkit_registry);
         sd_event_unref(c->event);
         sd_bus_flush_close_unref(c->bus);
+        varlink_server_unref(c->varlink_server);
 }
 
 static void context_read_etc_hostname(Context *c) {
@@ -1347,34 +1350,19 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
         return sd_bus_send(NULL, reply, NULL);
 }
 
-static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
-        _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
+static int build_describe_response(Context *c, bool privileged, JsonVariant **ret) {
+        _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL,
                 *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
                 *firmware_vendor = NULL;
         usec_t firmware_date = USEC_INFINITY, eol = USEC_INFINITY;
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         sd_id128_t machine_id, boot_id, product_uuid = SD_ID128_NULL;
         unsigned local_cid = VMADDR_CID_ANY;
-        Context *c = ASSERT_PTR(userdata);
-        bool privileged;
         struct utsname u;
         int r;
 
-        assert(m);
-
-        r = bus_verify_polkit_async(
-                        m,
-                        "org.freedesktop.hostname1.get-description",
-                        /* details= */ NULL,
-                        &c->polkit_registry,
-                        error);
-        if (r == 0)
-                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
-        /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
-         * the product ID which we'll check explicitly. */
-        privileged = r > 0;
+        assert(c);
+        assert(ret);
 
         context_read_etc_hostname(c);
         context_read_machine_info(c);
@@ -1457,10 +1445,40 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                                        JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
                                        JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL),
                                        JSON_BUILD_PAIR_CONDITION(local_cid != VMADDR_CID_ANY, "VSockCID", JSON_BUILD_UNSIGNED(local_cid))));
-
         if (r < 0)
                 return log_error_errno(r, "Failed to build JSON data: %m");
 
+        *ret = TAKE_PTR(v);
+        return 0;
+}
+
+static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        Context *c = ASSERT_PTR(userdata);
+        _cleanup_free_ char *text = NULL;
+        bool privileged;
+        int r;
+
+        assert(m);
+
+        r = bus_verify_polkit_async(
+                        m,
+                        "org.freedesktop.hostname1.get-description",
+                        /* details= */ NULL,
+                        &c->polkit_registry,
+                        error);
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
+         * the product ID which we'll check explicitly. */
+        privileged = r > 0;
+
+        r = build_describe_response(c, privileged, &v);
+        if (r < 0)
+                return r;
+
         r = json_variant_format(v, 0, &text);
         if (r < 0)
                 return log_error_errno(r, "Failed to format JSON data: %m");
@@ -1564,7 +1582,6 @@ static const BusObjectImplementation manager_object = {
 };
 
 static int connect_bus(Context *c) {
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
         assert(c);
@@ -1594,6 +1611,87 @@ static int connect_bus(Context *c) {
         return 0;
 }
 
+static int vl_method_describe(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        static const JsonDispatch dispatch_table[] = {
+                VARLINK_DISPATCH_POLKIT_FIELD,
+                {}
+        };
+
+        Context *c = ASSERT_PTR(userdata);
+        bool privileged;
+        int r;
+
+        assert(link);
+        assert(parameters);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, /* userdata= */ NULL);
+        if (r != 0)
+                return r;
+
+        r = varlink_verify_polkit_async(
+                        link,
+                        c->bus,
+                        "org.freedesktop.hostname1.get-hardware-serial",
+                        /* details= */ NULL,
+                        /* good_user= */ UID_INVALID,
+                        &c->polkit_registry);
+        if (r == 0)
+                return 0; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
+         * the product ID which we'll check explicitly. */
+        privileged = r > 0;
+
+        if (json_variant_elements(parameters) > 0)
+                return varlink_error_invalid_parameter(link, parameters);
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        r = build_describe_response(c, privileged, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, v);
+}
+
+static int connect_varlink(Context *c) {
+        int r;
+
+        assert(c);
+        assert(c->event);
+        assert(!c->varlink_server);
+
+        r = varlink_server_new(&c->varlink_server, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+        varlink_server_set_userdata(c->varlink_server, c);
+
+        r = varlink_server_add_interface(c->varlink_server, &vl_interface_io_systemd_Hostname);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add Hostname interface to varlink server: %m");
+
+        r = varlink_server_bind_method_many(
+                        c->varlink_server,
+                        "io.systemd.Hostname.Describe", vl_method_describe);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind Varlink method calls: %m");
+
+        r = varlink_server_attach_event(c->varlink_server, c->event, SD_EVENT_PRIORITY_NORMAL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach Varlink server to event loop: %m");
+
+        r = varlink_server_listen_auto(c->varlink_server);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind to passed Varlink sockets: %m");
+        if (r == 0) {
+                r = varlink_server_listen_address(c->varlink_server, "/run/systemd/io.systemd.Hostname", 0666);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to bind to Varlink socket: %m");
+        }
+
+        return 0;
+}
+
 static int run(int argc, char *argv[]) {
         _cleanup_(context_destroy) Context context = {
                 .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */
@@ -1630,6 +1728,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
+        r = connect_varlink(&context);
+        if (r < 0)
+                return r;
+
         r = bus_event_loop_with_idle(
                         context.event,
                         context.bus,
index 1b95430f8843f703696c3a488b520515fc89765d..69a60b0f4569fb15c26fa84d7422ef2ddd9fc0fa 100644 (file)
@@ -174,6 +174,7 @@ shared_sources = files(
         'varlink-idl.c',
         'varlink-io.systemd.c',
         'varlink-io.systemd.Credentials.c',
+        'varlink-io.systemd.Hostname.c',
         'varlink-io.systemd.Journal.c',
         'varlink-io.systemd.ManagedOOM.c',
         'varlink-io.systemd.Network.c',
diff --git a/src/shared/varlink-io.systemd.Hostname.c b/src/shared/varlink-io.systemd.Hostname.c
new file mode 100644 (file)
index 0000000..b2c5e03
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.Credentials.h"
+
+static VARLINK_DEFINE_METHOD(
+                Describe,
+                VARLINK_DEFINE_OUTPUT(Hostname, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(StaticHostname, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(PrettyHostname, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(DefaultHostname, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HostnameSource, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(IconName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(Chassis, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(Deployment, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(Location, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(KernelName, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(KernelRelease, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(KernelVersion, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemPrettyName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemCPEName, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemHomeURL, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(OperatingSystemSupportEnd, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HardwareVendor, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HardwareModel, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(HardwareSerial, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(FirmwareVersion, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(FirmwareVendor, VARLINK_STRING, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(FirmwareDate, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT(MachineID, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(BootID, VARLINK_STRING, 0),
+                VARLINK_DEFINE_OUTPUT(ProductUUID, VARLINK_STRING, VARLINK_NULLABLE));
+
+VARLINK_DEFINE_INTERFACE(
+                io_systemd_Hostname,
+                "io.systemd.Hostname",
+                &vl_method_Describe);
diff --git a/src/shared/varlink-io.systemd.Hostname.h b/src/shared/varlink-io.systemd.Hostname.h
new file mode 100644 (file)
index 0000000..29bb20e
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Hostname;
index 92c0beb32b693f56d930ac07d6366c89862bc730..813a676c928d28218f3c88f729acebd943af5195 100755 (executable)
@@ -228,6 +228,14 @@ testcase_nss-myhostname() {
     (! getent hosts -s myhostname fd00:dead:beef:cafe::1)
 }
 
+test_varlink() {
+    A="$(mktemp -u)"
+    B="$(mktemp -u)"
+    varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}' --json=short > "$A"
+    hostnamectl --json=short > "$B"
+    cmp "$A" "$B"
+}
+
 run_testcases
 
 touch /testok
index d97de3f604354e05c29baa737a82936e828acd8f..7912360315e97187f266b19365d4e9231b46e452 100755 (executable)
@@ -113,3 +113,7 @@ done
 (! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord </dev/null)
 (! varlinkctl validate-idl "")
 (! varlinkctl validate-idl </dev/null)
+
+varlinkctl info /run/systemd/io.systemd.Hostname
+varlinkctl introspect /run/systemd/io.systemd.Hostname io.systemd.Hostname
+varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}'
index 40cc3d10d35ff9f6621b77be051de44b1a008445..efd2eac58356d3cf085e2a28bfa7c377a88d7309 100644 (file)
@@ -321,6 +321,11 @@ units = [
           'conditions' : ['ENABLE_HOSTNAMED'],
           'symlinks' : ['dbus-org.freedesktop.hostname1.service'],
         },
+        {
+          'file' : 'systemd-hostnamed.socket',
+          'conditions' : ['ENABLE_HOSTNAMED'],
+          'symlinks' : ['sockets.target.wants/'],
+        },
         {
           'file' : 'systemd-hwdb-update.service.in',
           'conditions' : ['ENABLE_HWDB'],
diff --git a/units/systemd-hostnamed.socket b/units/systemd-hostnamed.socket
new file mode 100644 (file)
index 0000000..8d0a06c
--- /dev/null
@@ -0,0 +1,19 @@
+#  SPDX-License-Identifier: LGPL-2.1-or-later
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Hostname Service Varlink Socket
+Documentation=man:systemd-hostnamed.service(8)
+Documentation=man:hostname(5)
+Documentation=man:machine-info(5)
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.Hostname
+FileDescriptorName=varlink
+SocketMode=0666