diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index d629a07d0f..f01d771f9b 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -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 "-.:") @@ -75,6 +76,9 @@ typedef struct Context { struct stat etc_os_release_stat; struct stat etc_machine_info_stat; + sd_event *event; + sd_bus *bus; + VarlinkServer *varlink_server; Hashmap *polkit_registry; } Context; @@ -94,6 +98,9 @@ static void context_destroy(Context *c) { context_reset(c, UINT64_MAX); 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) { @@ -1343,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); @@ -1452,11 +1444,42 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro JSON_BUILD_PAIR_ID128("BootID", boot_id), 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)))); - + JSON_BUILD_PAIR_CONDITION(local_cid != VMADDR_CID_ANY, "VSockCID", JSON_BUILD_UNSIGNED(local_cid)), + JSON_BUILD_PAIR_CONDITION(local_cid == VMADDR_CID_ANY, "VSockCID", JSON_BUILD_NULL))); 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"); @@ -1559,35 +1582,114 @@ static const BusObjectImplementation manager_object = { .vtables = BUS_VTABLES(hostname_vtable), }; -static int connect_bus(Context *c, sd_event *event, sd_bus **ret) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; +static int connect_bus(Context *c) { int r; assert(c); - assert(event); - assert(ret); + assert(c->event); + assert(!c->bus); - r = sd_bus_default_system(&bus); + r = sd_bus_default_system(&c->bus); if (r < 0) return log_error_errno(r, "Failed to get system bus connection: %m"); - r = bus_add_implementation(bus, &manager_object, c); + r = bus_add_implementation(c->bus, &manager_object, c); if (r < 0) return r; - r = bus_log_control_api_register(bus); + r = bus_log_control_api_register(c->bus); if (r < 0) return r; - r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL); + r = sd_bus_request_name_async(c->bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to request name: %m"); - r = sd_bus_attach_event(bus, event, 0); + r = sd_bus_attach_event(c->bus, c->event, 0); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - *ret = TAKE_PTR(bus); + 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; } @@ -1595,8 +1697,6 @@ static int run(int argc, char *argv[]) { _cleanup_(context_destroy) Context context = { .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */ }; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; log_setup(); @@ -1615,27 +1715,31 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - - r = sd_event_default(&event); + r = sd_event_default(&context.event); if (r < 0) return log_error_errno(r, "Failed to allocate event loop: %m"); - (void) sd_event_set_watchdog(event, true); + (void) sd_event_set_watchdog(context.event, true); - r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + r = sd_event_set_signal_exit(context.event, true); if (r < 0) - return log_error_errno(r, "Failed to install SIGINT handler: %m"); + return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m"); - r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to install SIGTERM handler: %m"); - - r = connect_bus(&context, event, &bus); + r = connect_bus(&context); if (r < 0) return r; - r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL); + r = connect_varlink(&context); + if (r < 0) + return r; + + r = bus_event_loop_with_idle( + context.event, + context.bus, + "org.freedesktop.hostname1", + DEFAULT_EXIT_USEC, + /* check_idle= */ NULL, + /* userdata= */ NULL); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 69fc1bf07e..5808cea757 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -13,6 +13,7 @@ #include "stdio-util.h" #include "string-util.h" #include "sync-util.h" +#include "virt.h" int id128_from_string_nonzero(const char *s, sd_id128_t *ret) { sd_id128_t t; @@ -223,6 +224,13 @@ int id128_get_product(sd_id128_t *ret) { /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ + r = detect_container(); + if (r < 0) + return r; + if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but + * of the host */ + return -ENOENT; + r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid); if (r == -ENOENT) r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid); diff --git a/src/shared/meson.build b/src/shared/meson.build index 1b95430f88..69a60b0f45 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -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 index 0000000000..b2c5e038ff --- /dev/null +++ b/src/shared/varlink-io.systemd.Hostname.c @@ -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 index 0000000000..29bb20ef68 --- /dev/null +++ b/src/shared/varlink-io.systemd.Hostname.h @@ -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; diff --git a/test/units/testsuite-71.sh b/test/units/testsuite-71.sh index da765a9d97..813a676c92 100755 --- a/test/units/testsuite-71.sh +++ b/test/units/testsuite-71.sh @@ -61,6 +61,11 @@ get_chassis() ( echo "$CHASSIS" ) +stop_hostnamed() { + systemctl stop systemd-hostnamed.service + systemctl reset-failed systemd-hostnamed # reset trigger limit +} + testcase_chassis() { local i @@ -80,7 +85,7 @@ testcase_chassis() { assert_eq "$(get_chassis)" "$i" done - systemctl stop systemd-hostnamed.service + stop_hostnamed rm -f /etc/machine-info # fallback chassis type @@ -95,7 +100,7 @@ restore_sysfs_dmi() { umount /sys/class/dmi/id rm -rf /run/systemd/system/systemd-hostnamed.service.d systemctl daemon-reload - systemctl stop systemd-hostnamed + stop_hostnamed } testcase_firmware_date() { @@ -120,15 +125,15 @@ EOF echo '1' >/sys/class/dmi/id/uevent echo '09/08/2000' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed + stop_hostnamed assert_in '2000-09-08' "$(hostnamectl)" echo '2022' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed + stop_hostnamed assert_not_in 'Firmware Date' "$(hostnamectl)" echo 'garbage' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed + stop_hostnamed assert_not_in 'Firmware Date' "$(hostnamectl)" } @@ -223,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 diff --git a/test/units/testsuite-74.varlinkctl.sh b/test/units/testsuite-74.varlinkctl.sh index d97de3f604..7912360315 100755 --- a/test/units/testsuite-74.varlinkctl.sh +++ b/test/units/testsuite-74.varlinkctl.sh @@ -113,3 +113,7 @@ done (! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord