/* 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 {
+typedef enum {
/* Read from /etc/hostname */
PROP_STATIC_HOSTNAME,
PROP_CHASSIS,
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_HOME_URL,
_PROP_MAX,
_PROP_INVALID = -EINVAL,
-};
+} HostProperty;
typedef struct Context {
char *data[_PROP_MAX];
(UINT64_C(1) << PROP_ICON_NAME) |
(UINT64_C(1) << PROP_CHASSIS) |
(UINT64_C(1) << PROP_DEPLOYMENT) |
- (UINT64_C(1) << PROP_LOCATION));
+ (UINT64_C(1) << PROP_LOCATION) |
+ (UINT64_C(1) << PROP_HARDWARE_VENDOR) |
+ (UINT64_C(1) << PROP_HARDWARE_MODEL));
r = parse_env_file(NULL, "/etc/machine-info",
"PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
"ICON_NAME", &c->data[PROP_ICON_NAME],
"CHASSIS", &c->data[PROP_CHASSIS],
"DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
- "LOCATION", &c->data[PROP_LOCATION]);
+ "LOCATION", &c->data[PROP_LOCATION],
+ "HARDWARE_VENDOR", &c->data[PROP_HARDWARE_VENDOR],
+ "HARDWARE_MODEL", &c->data[PROP_HARDWARE_MODEL]);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
c->etc_os_release_stat = current_stat;
}
+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 (!ret)
+ return !!s;
+
+ if (s) {
+ b = strdup(s);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ *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 get_hardware_firmware_data(const char *sysattr, char **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ _cleanup_free_ char *b = NULL;
+ const char *s = NULL;
+ int r;
+
+ assert(sysattr);
+
+ 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");
+
+ (void) sd_device_get_sysattr_value(device, sysattr, &s);
+ if (!isempty(s)) {
+ b = strdup(s);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ if (ret)
+ *ret = TAKE_PTR(b);
+
+ return !isempty(s);
+}
+
+static int get_hardware_serial(char **ret) {
+ int r;
+
+ r = get_hardware_firmware_data("product_serial", ret);
+ if (r <= 0)
+ return get_hardware_firmware_data("board_serial", ret);
+
+ return r;
+}
+
+static int get_firmware_version(char **ret) {
+ return get_hardware_firmware_data("bios_version", ret);
+}
+
static const char* valid_chassis(const char *chassis) {
assert(chassis);
static const char* fallback_chassis(void) {
const char *chassis;
- char *type;
+ _cleanup_free_ char *type = NULL;
+ Virtualization v;
unsigned t;
- int v, r;
+ int r;
v = detect_virtualization();
if (v < 0)
}
r = safe_atou(type, &t);
- free(type);
if (r < 0) {
- log_debug_errno(r, "Failed to parse DMI chassis type, ignoring: %m");
+ log_debug_errno(r, "Failed to parse DMI chassis type \"%s\", ignoring: %m", type);
goto try_acpi;
}
/* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
- additional guesswork on top of that.
-
- See the SMBIOS Specification 3.0 section 7.4.1 for details about the values listed here:
-
- https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
+ * additional guesswork on top of that.
+ *
+ * See the SMBIOS Specification 3.5.0 section 7.4.1 for details about the values listed here:
+ *
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.5.0.pdf
*/
switch (t) {
- case 0x3: /* Desktop */
- case 0x4: /* Low Profile Desktop */
- case 0x6: /* Mini Tower */
- case 0x7: /* Tower */
- case 0xD: /* All in one (i.e. PC built into monitor) */
+ case 0x03: /* Desktop */
+ case 0x04: /* Low Profile Desktop */
+ case 0x06: /* Mini Tower */
+ case 0x07: /* Tower */
+ case 0x0D: /* All in one (i.e. PC built into monitor) */
+ case 0x23: /* Mini PC */
+ case 0x24: /* Stick PC */
return "desktop";
case 0x8: /* Portable */
case 0x20: /* Detachable */
return "convertible";
+ case 0x21: /* IoT Gateway */
+ case 0x22: /* Embedded PC */
+ return "embedded";
+
default:
log_debug("Unhandled DMI chassis type 0x%02x, ignoring.", t);
}
try_acpi:
+ type = mfree(type);
r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
if (r < 0) {
log_debug_errno(r, "Failed read ACPI PM profile, ignoring: %m");
}
r = safe_atou(type, &t);
- free(type);
if (r < 0) {
- log_debug_errno(r, "Failed parse ACPI PM profile, ignoring: %m");
+ log_debug_errno(r, "Failed parse ACPI PM profile \"%s\", ignoring: %m", type);
goto try_devicetree;
}
* http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
*/
- switch(t) {
+ switch (t) {
case 1: /* Desktop */
case 3: /* Workstation */
}
try_devicetree:
- r = read_one_line_file("/sys/firmware/devicetree/base/chassis-type", &type);
+ type = mfree(type);
+ r = read_one_line_file("/proc/device-tree/chassis-type", &type);
if (r < 0) {
log_debug_errno(r, "Failed to read device-tree chassis type, ignoring: %m");
return NULL;
* https://github.com/devicetree-org/devicetree-specification/blob/master/source/chapter3-devicenodes.rst */
chassis = valid_chassis(type);
if (!chassis)
- log_debug("Invalid device-tree chassis type '%s', ignoring.", type);
-
- free(type);
-
+ log_debug("Invalid device-tree chassis type \"%s\", ignoring.", type);
return chassis;
}
-static char* context_fallback_icon_name(Context *c) {
- const char *chassis;
+static char* context_get_chassis(Context *c) {
+ const char *fallback;
+ char *dmi;
assert(c);
if (!isempty(c->data[PROP_CHASSIS]))
- return strjoin("computer-", c->data[PROP_CHASSIS]);
+ return strdup(c->data[PROP_CHASSIS]);
+
+ if (get_dmi_data("ID_CHASSIS", NULL, &dmi) > 0)
+ return dmi;
+
+ fallback = fallback_chassis();
+ if (fallback)
+ return strdup(fallback);
+
+ return NULL;
+}
+
+static char* context_fallback_icon_name(Context *c) {
+ _cleanup_free_ char *chassis = NULL;
+
+ assert(c);
- chassis = fallback_chassis();
+ chassis = context_get_chassis(c);
if (chassis)
return strjoin("computer-", chassis);
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;
+static int property_get_hardware_property(
+ sd_bus_message *reply,
+ Context *c,
+ HostProperty prop,
+ int (*getter)(char **)) {
- 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");
+ _cleanup_free_ char *from_dmi = NULL;
- 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);
+ assert(reply);
+ assert(c);
+ assert(IN_SET(prop, PROP_HARDWARE_VENDOR, PROP_HARDWARE_MODEL));
+ assert(getter);
- if (s) {
- b = strdup(s);
- if (!b)
- return -ENOMEM;
- }
+ context_read_machine_info(c);
- if (ret)
- *ret = TAKE_PTR(b);
+ if (isempty(c->data[prop]))
+ (void) getter(&from_dmi);
- return !!s;
+ return sd_bus_message_append(reply, "s", from_dmi ?: c->data[prop]);
}
-static int get_hardware_vendor(char **ret) {
- return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret);
-}
+static int property_get_hardware_vendor(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
-static int get_hardware_model(char **ret) {
- return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret);
+ return property_get_hardware_property(reply, userdata, PROP_HARDWARE_VENDOR, get_hardware_vendor);
}
-static int property_get_hardware_vendor(
+static int property_get_hardware_model(
sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
sd_bus_error *error) {
- _cleanup_free_ char *vendor = NULL;
-
- (void) get_hardware_vendor(&vendor);
- return sd_bus_message_append(reply, "s", vendor);
+ return property_get_hardware_property(reply, userdata, PROP_HARDWARE_MODEL, get_hardware_model);
}
-static int property_get_hardware_model(
+static int property_get_firmware_version(
sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
sd_bus_error *error) {
- _cleanup_free_ char *model = NULL;
+ _cleanup_free_ char *firmware_version = NULL;
+
+ (void) get_firmware_version(&firmware_version);
- (void) get_hardware_model(&model);
- return sd_bus_message_append(reply, "s", model);
+ return sd_bus_message_append(reply, "s", firmware_version);
}
static int property_get_hostname(
void *userdata,
sd_bus_error *error) {
- Context *c = userdata;
- assert(c);
+ Context *c = ASSERT_PTR(userdata);
context_read_etc_hostname(c);
void *userdata,
sd_bus_error *error) {
- Context *c = userdata;
- assert(c);
+ Context *c = ASSERT_PTR(userdata);
context_read_etc_hostname(c);
context_determine_hostname_source(c);
void *userdata,
sd_bus_error *error) {
+ _cleanup_free_ char *chassis = NULL;
Context *c = userdata;
- const char *name;
context_read_machine_info(c);
- if (isempty(c->data[PROP_CHASSIS]))
- name = fallback_chassis();
- else
- name = c->data[PROP_CHASSIS];
+ chassis = context_get_chassis(c);
- return sd_bus_message_append(reply, "s", name);
+ return sd_bus_message_append(reply, "s", chassis);
}
static int property_get_uname_field(
}
static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- Context *c = userdata;
+ Context *c = ASSERT_PTR(userdata);
const char *name;
int interactive, r;
assert(m);
- assert(c);
r = sd_bus_message_read(m, "sb", &name, &interactive);
if (r < 0)
}
static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- Context *c = userdata;
+ Context *c = ASSERT_PTR(userdata);
const char *name;
int interactive;
int r;
assert(m);
- assert(c);
r = sd_bus_message_read(m, "sb", &name, &interactive);
if (r < 0)
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;
+ Context *c = ASSERT_PTR(userdata);
int interactive, r;
sd_id128_t uuid;
assert(m);
- assert(c);
r = sd_bus_message_read(m, "b", &interactive);
if (r < 0)
return sd_bus_send(NULL, reply, NULL);
}
+static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ char *serial = NULL;
+ Context *c = ASSERT_PTR(userdata);
+ int r;
+
+ assert(m);
+
+ r = bus_verify_polkit_async(
+ m,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.hostname1.get-hardware-serial",
+ NULL,
+ false,
+ UID_INVALID,
+ &c->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = get_hardware_serial(&serial);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", serial);
+ if (r < 0)
+ return r;
+
+ 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_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
+ *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = 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;
+ Context *c = ASSERT_PTR(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",
+ "org.freedesktop.hostname1.get-description",
NULL,
false,
UID_INVALID,
if (isempty(c->data[PROP_ICON_NAME]))
in = context_fallback_icon_name(c);
- if (isempty(c->data[PROP_CHASSIS]))
- chassis = fallback_chassis();
+ chassis = context_get_chassis(c);
assert_se(uname(&u) >= 0);
- (void) get_hardware_vendor(&vendor);
- (void) get_hardware_model(&model);
+ if (isempty(c->data[PROP_HARDWARE_VENDOR]))
+ (void) get_hardware_vendor(&vendor);
+ if (isempty(c->data[PROP_HARDWARE_MODEL]))
+ (void) get_hardware_model(&model);
- if (privileged) /* The product UUID is only available to privileged clients */
- id128_get_product(&product_uuid);
+ if (privileged) {
+ /* The product UUID and hardware serial is only available to privileged clients */
+ (void) id128_get_product(&product_uuid);
+ (void) get_hardware_serial(&serial);
+ }
+ (void) get_firmware_version(&firmware_version);
r = json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
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("Chassis", JSON_BUILD_STRING(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("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("HardwareVendor", JSON_BUILD_STRING(vendor ?: c->data[PROP_HARDWARE_VENDOR])),
+ JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_HARDWARE_MODEL])),
+ JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
+ JSON_BUILD_PAIR("FirmwareVersion", JSON_BUILD_STRING(firmware_version)),
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)));
SD_BUS_PROPERTY("HomeURL", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HardwareVendor", "s", property_get_hardware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FirmwareVersion", "s", property_get_firmware_version, 0, SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_METHOD_WITH_NAMES("SetHostname",
- "sb",
- SD_BUS_PARAM(hostname)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_hostname,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
- "sb",
- SD_BUS_PARAM(hostname)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_static_hostname,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
- "sb",
- SD_BUS_PARAM(hostname)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_pretty_hostname,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetIconName",
- "sb",
- SD_BUS_PARAM(icon)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_icon_name,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetChassis",
- "sb",
- SD_BUS_PARAM(chassis)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_chassis,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetDeployment",
- "sb",
- SD_BUS_PARAM(deployment)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_deployment,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetLocation",
- "sb",
- SD_BUS_PARAM(location)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_location,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
- "b",
- SD_BUS_PARAM(interactive),
- "ay",
- SD_BUS_PARAM(uuid),
- method_get_product_uuid,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetHostname",
+ SD_BUS_ARGS("s", hostname, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_hostname,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetStaticHostname",
+ SD_BUS_ARGS("s", hostname, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_static_hostname,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetPrettyHostname",
+ SD_BUS_ARGS("s", hostname, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_pretty_hostname,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetIconName",
+ SD_BUS_ARGS("s", icon, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_icon_name,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetChassis",
+ SD_BUS_ARGS("s", chassis, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_chassis,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetDeployment",
+ SD_BUS_ARGS("s", deployment, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_deployment,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetLocation",
+ SD_BUS_ARGS("s", location, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_location,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetProductUUID",
+ SD_BUS_ARGS("b", interactive),
+ SD_BUS_RESULT("ay", uuid),
+ method_get_product_uuid,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetHardwareSerial",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("s", serial),
+ method_get_hardware_serial,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("Describe",
SD_BUS_NO_ARGS,
SD_BUS_RESULT("s", json),