hostname: add API for getting custom fields from machine-info
This commit is contained in:
@@ -60,6 +60,8 @@ node /org/freedesktop/hostname1 {
|
||||
out ay uuid);
|
||||
GetHardwareSerial(out s serial);
|
||||
Describe(out s json);
|
||||
GetMachineInfo(in s field,
|
||||
out s value);
|
||||
properties:
|
||||
readonly s Hostname = '...';
|
||||
readonly s StaticHostname = '...';
|
||||
@@ -146,6 +148,8 @@ node /org/freedesktop/hostname1 {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="Describe()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="GetMachineInfo()"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Hostname"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StaticHostname"/>
|
||||
@@ -377,6 +381,13 @@ node /org/freedesktop/hostname1 {
|
||||
access to unprivileged clients through the polkit framework.</para>
|
||||
|
||||
<para><function>Describe()</function> returns a JSON representation of all properties in one.</para>
|
||||
|
||||
<para><function>GetMachineInfo()</function> returns the value of the given field from
|
||||
<filename>/etc/machine-info</filename>. For well-known fields, this method reads values from the same
|
||||
internal cache used by the corresponding D-Bus property getters, but returns only the raw
|
||||
<filename>/etc/machine-info</filename> values (i.e. without property-level fallback logic such as
|
||||
DMI/chassis-based detection). Custom (non-standard) fields are read directly from the file.
|
||||
An error is returned if the field name is empty, invalid, or not set in the file.</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
@@ -494,6 +505,7 @@ node /org/freedesktop/hostname1 {
|
||||
<varname>OperatingSystemImageVersion</varname>, <varname>HardwareSKU</varname>, and
|
||||
<varname>HardwareVersion</varname> were added in version 258.</para>
|
||||
<para><varname>OperatingSystemFancyName</varname> was added in version 260.</para>
|
||||
<para><function>GetMachineInfo()</function> was added in version 261.</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -1652,6 +1652,65 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
|
||||
return sd_bus_reply_method_return(m, "s", serial);
|
||||
}
|
||||
|
||||
static int method_get_machine_info(sd_bus_message *m, void *userdata, sd_bus_error *error) {
|
||||
static const struct {
|
||||
const char *name;
|
||||
HostProperty prop;
|
||||
} field_table[] = {
|
||||
{ "PRETTY_HOSTNAME", PROP_PRETTY_HOSTNAME },
|
||||
{ "ICON_NAME", PROP_ICON_NAME },
|
||||
{ "CHASSIS", PROP_CHASSIS },
|
||||
{ "DEPLOYMENT", PROP_DEPLOYMENT },
|
||||
{ "LOCATION", PROP_LOCATION },
|
||||
{ "HARDWARE_VENDOR", PROP_HARDWARE_VENDOR },
|
||||
{ "HARDWARE_MODEL", PROP_HARDWARE_MODEL },
|
||||
{ "HARDWARE_SKU", PROP_HARDWARE_SKU },
|
||||
{ "HARDWARE_VERSION", PROP_HARDWARE_VERSION },
|
||||
};
|
||||
|
||||
Context *c = ASSERT_PTR(userdata);
|
||||
const char *field;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_read(m, "s", &field);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(field))
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Field name must not be empty.");
|
||||
|
||||
if (!env_name_is_valid(field))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid field name '%s'.", field);
|
||||
|
||||
FOREACH_ELEMENT(e, field_table)
|
||||
if (streq(field, e->name)) {
|
||||
/* For fields that are also exposed as D-Bus properties, use the same Context cache as the
|
||||
* property getters. Note that this returns the raw /etc/machine-info value only: property-level
|
||||
* fallback logic (e.g. DMI/chassis-based synthesis) is not applied here. For custom/unknown
|
||||
* fields, fall back to reading the file directly. */
|
||||
context_read_machine_info(c);
|
||||
|
||||
if (isempty(c->data[e->prop]))
|
||||
return sd_bus_error_setf(error, BUS_ERROR_FIELD_NOT_SET, "Field '%s' is not set or empty in /etc/machine-info.", field);
|
||||
|
||||
return sd_bus_reply_method_return(m, "s", c->data[e->prop]);
|
||||
}
|
||||
|
||||
_cleanup_free_ char *value = NULL;
|
||||
|
||||
r = parse_env_file(NULL, etc_machine_info(),
|
||||
field, &value);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read /etc/machine-info: %m");
|
||||
|
||||
if (isempty(value))
|
||||
return sd_bus_error_setf(error, BUS_ERROR_FIELD_NOT_SET, "Field '%s' is not set or empty in /etc/machine-info.", field);
|
||||
|
||||
return sd_bus_reply_method_return(m, "s", value);
|
||||
}
|
||||
|
||||
static int build_describe_response(Context *c, bool privileged, sd_json_variant **ret) {
|
||||
_cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL,
|
||||
*chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
|
||||
@@ -1883,6 +1942,11 @@ static const sd_bus_vtable hostname_vtable[] = {
|
||||
SD_BUS_RESULT("s", json),
|
||||
method_describe,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("GetMachineInfo",
|
||||
SD_BUS_ARGS("s", field),
|
||||
SD_BUS_RESULT("s", value),
|
||||
method_get_machine_info,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
@@ -111,6 +111,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
|
||||
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID, EOPNOTSUPP),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_HARDWARE_SERIAL, EOPNOTSUPP),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_FIELD_NOT_SET, ENODATA),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_FILE_IS_PROTECTED, EACCES),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_READ_ONLY_FILESYSTEM, EROFS),
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
|
||||
#define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID"
|
||||
#define BUS_ERROR_NO_HARDWARE_SERIAL "org.freedesktop.hostname1.NoHardwareSerial"
|
||||
#define BUS_ERROR_FIELD_NOT_SET "org.freedesktop.hostname1.FieldNotSet"
|
||||
#define BUS_ERROR_FILE_IS_PROTECTED "org.freedesktop.hostname1.FileIsProtected"
|
||||
#define BUS_ERROR_READ_ONLY_FILESYSTEM "org.freedesktop.hostname1.ReadOnlyFilesystem"
|
||||
|
||||
|
||||
@@ -320,6 +320,74 @@ EOF
|
||||
assert_in "CHASSIS=watch" "$(cat /run/alternate-path/mymachine-info)"
|
||||
}
|
||||
|
||||
testcase_get_machine_info() {
|
||||
if [[ -f /etc/machine-info ]]; then
|
||||
cp /etc/machine-info /tmp/machine-info.bak
|
||||
fi
|
||||
|
||||
trap restore_machine_info RETURN
|
||||
|
||||
# Test all standard cached fields
|
||||
cat >/etc/machine-info <<EOF
|
||||
PRETTY_HOSTNAME="Pretty Test"
|
||||
ICON_NAME=computer-laptop
|
||||
CHASSIS=laptop
|
||||
DEPLOYMENT=production
|
||||
LOCATION="Server Room 42"
|
||||
HARDWARE_VENDOR="Test Vendor"
|
||||
HARDWARE_MODEL="Test Model"
|
||||
HARDWARE_SKU="SKU-001"
|
||||
HARDWARE_VERSION="v1.0"
|
||||
TEST_CUSTOM_FIELD_1=still-here
|
||||
EOF
|
||||
|
||||
# Custom field should still work alongside standard fields
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s TEST_CUSTOM_FIELD_1 | cut -d'"' -f2)" "still-here"
|
||||
# Should fail for a field that doesn't exist
|
||||
assert_rc 1 busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s NONEXISTENT_FIELD
|
||||
# Should fail for an empty field name
|
||||
assert_rc 1 busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s ""
|
||||
# Should fail for an invalid field name
|
||||
assert_rc 1 busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s "invalid-name!"
|
||||
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s PRETTY_HOSTNAME | cut -d'"' -f2)" "Pretty Test"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s ICON_NAME | cut -d'"' -f2)" "computer-laptop"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s CHASSIS | cut -d'"' -f2)" "laptop"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s DEPLOYMENT | cut -d'"' -f2)" "production"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s LOCATION | cut -d'"' -f2)" "Server Room 42"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s HARDWARE_VENDOR | cut -d'"' -f2)" "Test Vendor"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s HARDWARE_MODEL | cut -d'"' -f2)" "Test Model"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s HARDWARE_SKU | cut -d'"' -f2)" "SKU-001"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s HARDWARE_VERSION | cut -d'"' -f2)" "v1.0"
|
||||
|
||||
# Verify cache consistency for all standard fields against D-Bus properties
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s PRETTY_HOSTNAME | cut -d'"' -f2)" \
|
||||
"$(busctl get-property org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 PrettyHostname | cut -d'"' -f2)"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s ICON_NAME | cut -d'"' -f2)" \
|
||||
"$(busctl get-property org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 IconName | cut -d'"' -f2)"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s CHASSIS | cut -d'"' -f2)" \
|
||||
"$(busctl get-property org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 Chassis | cut -d'"' -f2)"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s DEPLOYMENT | cut -d'"' -f2)" \
|
||||
"$(busctl get-property org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 Deployment | cut -d'"' -f2)"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s LOCATION | cut -d'"' -f2)" \
|
||||
"$(busctl get-property org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 Location | cut -d'"' -f2)"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s HARDWARE_VENDOR | cut -d'"' -f2)" \
|
||||
"$(busctl get-property org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 HardwareVendor | cut -d'"' -f2)"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s HARDWARE_MODEL | cut -d'"' -f2)" \
|
||||
"$(busctl get-property org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 HardwareModel | cut -d'"' -f2)"
|
||||
|
||||
# Test file modification is reflected (cache invalidation via stat)
|
||||
cat >/etc/machine-info <<EOF
|
||||
PRETTY_HOSTNAME="Updated Host"
|
||||
TEST_CUSTOM_FIELD_1=updated-value
|
||||
EOF
|
||||
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s PRETTY_HOSTNAME | cut -d'"' -f2)" "Updated Host"
|
||||
assert_eq "$(busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s TEST_CUSTOM_FIELD_1 | cut -d'"' -f2)" "updated-value"
|
||||
# Fields removed from the file should now fail
|
||||
assert_rc 1 busctl call org.freedesktop.hostname1 /org/freedesktop/hostname1 org.freedesktop.hostname1 GetMachineInfo s CHASSIS
|
||||
}
|
||||
|
||||
run_testcases
|
||||
|
||||
touch /testok
|
||||
|
||||
Reference in New Issue
Block a user