From b4be4ff8ae0c7ab54a60f77b95f967131e1fcc6b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Apr 2021 14:15:36 +0200 Subject: [PATCH 1/9] id128-util: use common implementation of helper to get/validate product ID --- src/hostname/hostnamed.c | 20 ++++++++------------ src/libsystemd/sd-id128/id128-util.c | 22 ++++++++++++++++++++++ src/libsystemd/sd-id128/id128-util.h | 2 ++ src/shared/machine-id-setup.c | 12 +++--------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 2ece8b2fbd..92af4d1913 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -932,27 +932,23 @@ static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error * static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Context *c = userdata; - bool has_uuid = false; int interactive, r; sd_id128_t uuid; assert(m); assert(c); - r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); - if (r == -ENOENT) - r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid); - if (r < 0) - log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, - "Failed to read product UUID, ignoring: %m"); - else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid)) - log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid)); - else - has_uuid = true; + r = id128_get_product(&uuid); + if (r < 0) { + if (r == -EADDRNOTAVAIL) + log_debug_errno(r, "DMI product UUID is all 0x00 or all 0xFF, ignoring."); + else + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read product UUID, ignoring: %m"); - if (!has_uuid) return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, "Failed to read product UUID from firmware."); + } r = sd_bus_message_read(m, "b", &interactive); if (r < 0) diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index a3f6da6381..2074771a41 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -210,3 +210,25 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id) { } DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); + +int id128_get_product(sd_id128_t *ret) { + sd_id128_t uuid; + int r; + + assert(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 = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); + if (r == -ENOENT) + r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid); + if (r < 0) + return r; + + if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid)) + return -EADDRNOTAVAIL; /* Recognizable error */ + + *ret = uuid; + return 0; +} diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h index 6b09bcd96a..053ef0a6a8 100644 --- a/src/libsystemd/sd-id128/id128-util.h +++ b/src/libsystemd/sd-id128/id128-util.h @@ -36,3 +36,5 @@ int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; sd_id128_t id128_make_v4_uuid(sd_id128_t id); + +int id128_get_product(sd_id128_t *ret); diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c index 6d15f9cd09..dd4c74b19c 100644 --- a/src/shared/machine-id-setup.c +++ b/src/shared/machine-id-setup.c @@ -62,16 +62,10 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) { } else if (detect_vm() == VIRTUALIZATION_KVM) { - /* If we are not running in a container, see if we are - * running in qemu/kvm and a machine ID was passed in - * via -uuid on the qemu/kvm command line */ + /* If we are not running in a container, see if we are running in qemu/kvm and a + * machine ID was passed in via -uuid on the qemu/kvm command line */ - if (id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, ret) >= 0) { - log_info("Initializing machine ID from KVM UUID."); - return 0; - } - /* on POWER, it's exported here instead */ - if (id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, ret) >= 0) { + if (id128_get_product(ret) >= 0) { log_info("Initializing machine ID from KVM UUID."); return 0; } From 66ee229879f998d6d1d02a1db23b906237d5258e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Apr 2021 14:32:49 +0200 Subject: [PATCH 2/9] hostnamed: retrieve product UUID after authentication, not before --- src/hostname/hostnamed.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 92af4d1913..27d9fecbc1 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -938,18 +938,6 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err assert(m); assert(c); - r = id128_get_product(&uuid); - if (r < 0) { - if (r == -EADDRNOTAVAIL) - log_debug_errno(r, "DMI product UUID is all 0x00 or all 0xFF, ignoring."); - else - log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, - "Failed to read product UUID, ignoring: %m"); - - return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, - "Failed to read product UUID from firmware."); - } - r = sd_bus_message_read(m, "b", &interactive); if (r < 0) return r; @@ -968,6 +956,18 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + r = id128_get_product(&uuid); + if (r < 0) { + if (r == -EADDRNOTAVAIL) + log_debug_errno(r, "DMI product UUID is all 0x00 or all 0xFF, ignoring."); + else + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read product UUID, ignoring: %m"); + + return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, + "Failed to read product UUID from firmware."); + } + r = sd_bus_message_new_method_return(m, &reply); if (r < 0) return r; From c52e295d68a56822d4e62356e1b10025a8c29306 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Apr 2021 14:33:19 +0200 Subject: [PATCH 3/9] hostnamed: use byte array when we need a byte array it's more corect this way, but shouldn#t change a thing binary-wise --- src/hostname/hostnamed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 27d9fecbc1..20b4f4e61f 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -972,7 +972,7 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err if (r < 0) return r; - r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid)); + r = sd_bus_message_append_array(reply, 'y', uuid.bytes, sizeof(uuid.bytes)); if (r < 0) return r; From 61d44b6bebc13b49922e0e1ec4027c48778536ed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Apr 2021 15:11:54 +0200 Subject: [PATCH 4/9] hostnamed: refactor vendor/model querying a bit, reuse function --- src/hostname/hostnamed.c | 69 +++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 20b4f4e61f..ca01962682 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -449,6 +449,41 @@ static int context_write_data_machine_info(Context *c) { return 0; } +static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + _cleanup_free_ char *b = NULL; + const char *s = NULL; + int r; + + r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id"); + if (r < 0) + return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m"); + + if (database_key) + (void) sd_device_get_property_value(device, database_key, &s); + if (!s && regular_key) + (void) sd_device_get_property_value(device, regular_key, &s); + + if (s) { + b = strdup(s); + if (!b) + return -ENOMEM; + } + + if (ret) + *ret = TAKE_PTR(b); + + return !!s; +} + +static int get_hardware_vendor(char **ret) { + return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret); +} + +static int get_hardware_model(char **ret) { + return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret); +} + static int property_get_hardware_vendor( sd_bus *bus, const char *path, @@ -457,20 +492,11 @@ static int property_get_hardware_vendor( sd_bus_message *reply, void *userdata, sd_bus_error *error) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *hardware_vendor = NULL; - int r; - r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id"); - if (r < 0) { - log_warning_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m"); - return sd_bus_message_append(reply, "s", NULL); - } + _cleanup_free_ char *vendor = NULL; - if (sd_device_get_property_value(device, "ID_VENDOR_FROM_DATABASE", &hardware_vendor) < 0) - (void) sd_device_get_property_value(device, "ID_VENDOR", &hardware_vendor); - - return sd_bus_message_append(reply, "s", hardware_vendor); + (void) get_hardware_vendor(&vendor); + return sd_bus_message_append(reply, "s", vendor); } static int property_get_hardware_model( @@ -481,20 +507,11 @@ static int property_get_hardware_model( sd_bus_message *reply, void *userdata, sd_bus_error *error) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *hardware_model = NULL; - int r; - r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id"); - if (r < 0) { - log_warning_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m"); - return sd_bus_message_append(reply, "s", NULL); - } + _cleanup_free_ char *model = NULL; - if (sd_device_get_property_value(device, "ID_MODEL_FROM_DATABASE", &hardware_model) < 0) - (void) sd_device_get_property_value(device, "ID_MODEL", &hardware_model); - - return sd_bus_message_append(reply, "s", hardware_model); + (void) get_hardware_model(&model); + return sd_bus_message_append(reply, "s", model); } static int property_get_hostname( @@ -548,7 +565,9 @@ static int property_get_default_hostname( void *userdata, sd_bus_error *error) { - _cleanup_free_ char *hn = get_default_hostname(); + _cleanup_free_ char *hn = NULL; + + hn = get_default_hostname(); if (!hn) return log_oom(); From f2a434a5b87fb4073357ec5e659c14e02ab17bc3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Apr 2021 15:28:53 +0200 Subject: [PATCH 5/9] hostnamed: split out how we determine the hostname a bit No changes in code, just some splitting out of code we want to use elsewhere soon. --- src/hostname/hostnamed.c | 59 ++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index ca01962682..d7e3e4e692 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -574,6 +574,37 @@ static int property_get_default_hostname( return sd_bus_message_append(reply, "s", hn); } +static void context_determine_hostname_source(Context *c) { + char hostname[HOST_NAME_MAX + 1] = {}; + _cleanup_free_ char *fallback = NULL; + int r; + + assert(c); + + if (c->hostname_source >= 0) + return; + + (void) get_hostname_filtered(hostname); + + if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME])) + c->hostname_source = HOSTNAME_STATIC; + else { + /* If the hostname was not set by us, try to figure out where it came from. If we set it to + * the default hostname, the file will tell us. We compare the string because it is possible + * that the hostname was set by an older version that had a different fallback, in the + * initramfs or before we reexecuted. */ + + r = read_one_line_file("/run/systemd/default-hostname", &fallback); + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read /run/systemd/default-hostname, ignoring: %m"); + + if (streq_ptr(fallback, hostname)) + c->hostname_source = HOSTNAME_DEFAULT; + else + c->hostname_source = HOSTNAME_TRANSIENT; + } +} + static int property_get_hostname_source( sd_bus *bus, const char *path, @@ -584,36 +615,10 @@ static int property_get_hostname_source( sd_bus_error *error) { Context *c = userdata; - int r; assert(c); context_read_etc_hostname(c); - - if (c->hostname_source < 0) { - char hostname[HOST_NAME_MAX + 1] = {}; - _cleanup_free_ char *fallback = NULL; - - (void) get_hostname_filtered(hostname); - - if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME])) - c->hostname_source = HOSTNAME_STATIC; - - else { - /* If the hostname was not set by us, try to figure out where it came from. If we set - * it to the default hostname, the file will tell us. We compare the string because - * it is possible that the hostname was set by an older version that had a different - * fallback, in the initramfs or before we reexecuted. */ - - r = read_one_line_file("/run/systemd/default-hostname", &fallback); - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /run/systemd/default-hostname, ignoring: %m"); - - if (streq_ptr(fallback, hostname)) - c->hostname_source = HOSTNAME_DEFAULT; - else - c->hostname_source = HOSTNAME_TRANSIENT; - } - } + context_determine_hostname_source(c); return sd_bus_message_append(reply, "s", hostname_source_to_string(c->hostname_source)); } From f8da67cdf2d229d7e90558dce08cce7425ebc52d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Apr 2021 15:33:14 +0200 Subject: [PATCH 6/9] hostnamed: drop unused enums --- src/hostname/hostnamed.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index d7e3e4e692..3d7f302435 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -40,6 +40,8 @@ #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:") +/* Properties we cache are indexed by an enum, to make invalidation easy and systematic (as we can iterate + * through them all, and they are uniformly strings). */ enum { /* Read from /etc/hostname */ PROP_STATIC_HOSTNAME, @@ -51,9 +53,6 @@ enum { PROP_DEPLOYMENT, PROP_LOCATION, - PROP_HARDWARE_VENDOR, - PROP_HARDWARE_MODEL, - /* Read from /etc/os-release (or /usr/lib/os-release) */ PROP_OS_PRETTY_NAME, PROP_OS_CPE_NAME, From 7ecead8f0838d776926f8e010ca001f31b7344ae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Apr 2021 11:29:54 +0200 Subject: [PATCH 7/9] hostnamed: add Describe() call to hostnamed, returning all props a JSON --- src/hostname/hostnamed.c | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 3d7f302435..bd535ddc4d 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -20,6 +20,7 @@ #include "hostname-setup.h" #include "hostname-util.h" #include "id128-util.h" +#include "json.h" #include "main-func.h" #include "missing_capability.h" #include "nscd-flush.h" @@ -1002,6 +1003,108 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err 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, *vendor = NULL, *model = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + sd_id128_t product_uuid = SD_ID128_NULL; + const char *chassis = NULL; + Context *c = userdata; + bool privileged; + struct utsname u; + int r; + + assert(m); + assert(c); + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.hostname1.get-product-uuid", + NULL, + false, + UID_INVALID, + &c->polkit_registry, + NULL); + 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; + + context_read_etc_hostname(c); + context_read_machine_info(c); + context_read_os_release(c); + context_determine_hostname_source(c); + + r = gethostname_strict(&hn); + if (r < 0) { + if (r != -ENXIO) + return log_error_errno(r, "Failed to read local host name: %m"); + + hn = get_default_hostname(); + if (!hn) + return log_oom(); + } + + dhn = get_default_hostname(); + if (!dhn) + return log_oom(); + + if (isempty(c->data[PROP_ICON_NAME])) + in = context_fallback_icon_name(c); + + if (isempty(c->data[PROP_CHASSIS])) + chassis = fallback_chassis(); + + assert_se(uname(&u) >= 0); + + (void) get_hardware_vendor(&vendor); + (void) get_hardware_model(&model); + + if (privileged) /* The product UUID is only available to privileged clients */ + id128_get_product(&product_uuid); + + r = json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)), + JSON_BUILD_PAIR("StaticHostname", JSON_BUILD_STRING(c->data[PROP_STATIC_HOSTNAME])), + JSON_BUILD_PAIR("PrettyHostname", JSON_BUILD_STRING(c->data[PROP_PRETTY_HOSTNAME])), + JSON_BUILD_PAIR("DefaultHostname", JSON_BUILD_STRING(dhn)), + JSON_BUILD_PAIR("HostnameSource", JSON_BUILD_STRING(hostname_source_to_string(c->hostname_source))), + JSON_BUILD_PAIR("IconName", JSON_BUILD_STRING(in ?: c->data[PROP_ICON_NAME])), + JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis ?: c->data[PROP_CHASSIS])), + JSON_BUILD_PAIR("Deployment", JSON_BUILD_STRING(c->data[PROP_DEPLOYMENT])), + JSON_BUILD_PAIR("Location", JSON_BUILD_STRING(c->data[PROP_LOCATION])), + JSON_BUILD_PAIR("KernelName", JSON_BUILD_STRING(u.sysname)), + JSON_BUILD_PAIR("KernelRelease", JSON_BUILD_STRING(u.release)), + JSON_BUILD_PAIR("KernelVersion", JSON_BUILD_STRING(u.version)), + JSON_BUILD_PAIR("OperatingSystemPrettyName", JSON_BUILD_STRING(c->data[PROP_OS_PRETTY_NAME])), + JSON_BUILD_PAIR("OperatingSystemCPEName", JSON_BUILD_STRING(c->data[PROP_OS_CPE_NAME])), + JSON_BUILD_PAIR("OperatingSystemHomeURL", JSON_BUILD_STRING(c->data[PROP_OS_HOME_URL])), + JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor)), + JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model)), + 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))); + + if (r < 0) + return log_error_errno(r, "Failed to build JSON data: %m"); + + r = json_variant_format(v, 0, &text); + if (r < 0) + return log_error_errno(r, "Failed to format JSON data: %m"); + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", text); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + static const sd_bus_vtable hostname_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -1078,6 +1181,11 @@ static const sd_bus_vtable hostname_vtable[] = { SD_BUS_PARAM(uuid), method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("Describe", + SD_BUS_NO_ARGS, + SD_BUS_RESULT("s", json), + method_describe, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; From ac8a4f6b78fe70680d3bc1704b1f85bbcbe7bb55 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Apr 2021 11:29:46 +0200 Subject: [PATCH 8/9] hostnamectl: add --json= switch for JSON output This wraps the new Describe() bus call of hostnamed. --- man/hostnamectl.xml | 1 + src/hostname/hostnamectl.c | 49 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml index 8be8972907..ed2dabef3b 100644 --- a/man/hostnamectl.xml +++ b/man/hostnamectl.xml @@ -195,6 +195,7 @@ + diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index e7cb015ef2..19c04869dd 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -17,7 +17,9 @@ #include "format-table.h" #include "hostname-setup.h" #include "hostname-util.h" +#include "json.h" #include "main-func.h" +#include "parse-argument.h" #include "pretty-print.h" #include "spawn-polkit-agent.h" #include "terminal-util.h" @@ -30,6 +32,7 @@ static char *arg_host = NULL; static bool arg_transient = false; static bool arg_pretty = false; static bool arg_static = false; +static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; typedef struct StatusInfo { const char *hostname; @@ -317,6 +320,37 @@ static int show_all_names(sd_bus *bus) { static int show_status(int argc, char **argv, void *userdata) { sd_bus *bus = userdata; + int r; + + if (arg_json_format_flags != JSON_FORMAT_OFF) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + const char *text = NULL; + + r = sd_bus_call_method( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "Describe", + &error, + &reply, + NULL); + if (r < 0) + return log_error_errno(r, "Could not get description: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "s", &text); + if (r < 0) + return bus_log_parse_error(r); + + r = json_parse(text, 0, &v, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to parse JSON: %m"); + + json_variant_dump(v, arg_json_format_flags, NULL, NULL); + return 0; + } if (arg_pretty || arg_static || arg_transient) { const char *attr; @@ -489,6 +523,8 @@ static int help(void) { " --transient Only set transient hostname\n" " --static Only set static hostname\n" " --pretty Only set pretty hostname\n" + " --json=pretty|short|off\n" + " Generate JSON output\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -509,7 +545,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_ASK_PASSWORD, ARG_TRANSIENT, ARG_STATIC, - ARG_PRETTY + ARG_PRETTY, + ARG_JSON, }; static const struct option options[] = { @@ -521,10 +558,11 @@ static int parse_argv(int argc, char *argv[]) { { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "json", required_argument, NULL, ARG_JSON }, {} }; - int c; + int c, r; assert(argc >= 0); assert(argv); @@ -565,6 +603,13 @@ static int parse_argv(int argc, char *argv[]) { arg_ask_password = false; break; + case ARG_JSON: + r = parse_json_argument(optarg, &arg_json_format_flags); + if (r <= 0) + return r; + + break; + case '?': return -EINVAL; From 97a72436506a08ee001eb8bbb1f28176ddb1f168 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Apr 2021 15:59:13 +0200 Subject: [PATCH 9/9] man: document new dbus method --- man/org.freedesktop.hostname1.xml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/man/org.freedesktop.hostname1.xml b/man/org.freedesktop.hostname1.xml index 3a5088eded..cc61caca71 100644 --- a/man/org.freedesktop.hostname1.xml +++ b/man/org.freedesktop.hostname1.xml @@ -58,6 +58,7 @@ node /org/freedesktop/hostname1 { in b interactive); GetProductUUID(in b interactive, out ay uuid); + Describe(out s UNNAMED); properties: readonly s Hostname = '...'; readonly s StaticHostname = '...'; @@ -118,6 +119,8 @@ node /org/freedesktop/hostname1 { + + @@ -232,6 +235,17 @@ node /org/freedesktop/hostname1 { machine-info3 for that. For more information on these files and syscalls see the respective man pages. + KernelName, KernelRelease, and + KernelVersion expose the kernel name (e.g. Linux), release + (e.g. 5.0.0-11), and version (i.e. the build number, e.g. #11) as + reported by uname2. + OperatingSystemPrettyName, OperatingSystemCPEName, and + HomeURL expose the PRETTY_NAME=, CPE_NAME= and + HOME_URL= fields from + os-release5. The + purpose of those properties is to allow remote clients to access this information over D-Bus. Local + clients can access the information directly. + Methods @@ -261,22 +275,12 @@ node /org/freedesktop/hostname1 { machine-info5 for the semantics of those settings. - GetProductUUID() returns the "product uuid" as exposed by the kernel based + GetProductUUID() returns the "product UUID" as exposed by the kernel based on DMI information in /sys/class/dmi/id/product_uuid. Reading the file directly requires root privileges, and this method allows access to unprivileged clients through the polkit framework. - KernelName, KernelRelease, and - KernelVersion expose the kernel name (e.g. Linux), release - (e.g. 5.0.0-11), and version (i.e. the build number, e.g. #11) as - reported by - uname2. - OperatingSystemPrettyName, OperatingSystemCPEName, and - HomeURL expose the PRETTY_NAME=, CPE_NAME= and - HOME_URL= fields from - os-release5. The - purpose of those properties is to allow remote clients to access this information over D-Bus. Local - clients can access the information directly. + Describe() returns a JSON representation of all properties in one.