hostnamed: make chassis type configurable via /etc/machine-info

For many usecases it is useful to store the chassis type somewhere, and
/etc/machine-info sounds like a good place. Ideally we could always
detect the chassis type from firmware, but frequently that's not
available and in many embedded devices probably entirely unrealistic.

This patch adds a configurable setting CHASSIS= to /etc/machine-info and
exposes this via hostnamectl/hostnamed. hostnamed will guess the chassis
type from DMI if nothing is set explicitly. I also added support for
detecting it from ACPI, which should be more useful as ACPI 5.0 actually
knows a "tablet" chassis type, which neither DMI nor previous ACPI
versions knew.

This also enables DMI-based and ACPI-based detection for non-x86 systems
as ACPI is apparently coming to ARM platforms soon.

I tried to minimize the vocabulary of chassis types understood and
added: desktop, laptop, server, tablet, handset. This is much less than
either APCI or DMI know. If we need more types later on we can easily
add them.
This commit is contained in:
Lennart Poettering 2012-12-24 19:03:59 +01:00
parent f9ea108e7c
commit 7871c8e932
4 changed files with 216 additions and 31 deletions

View File

@ -80,8 +80,8 @@
<para>The static host name is stored in
<filename>/etc/hostname</filename>, see
<citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for more information. The pretty host name and icon
name are stored in
for more information. The pretty host name, chassis
type and icon name are stored in
<filename>/etc/machine-info</filename>, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</refsect1>
@ -198,8 +198,34 @@
Naming Specification</ulink>. Pass an
empty string to this operation to
reset the icon name to the default
value which is determined from chassis
type (see below) and possibly other
parameters.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>set-chassis [TYPE]</command></term>
<listitem><para>Set the chassis
type. The chassis type is used by some
graphical applications to visualize
the host or alter user
interaction. Currently, the following
chassis types are defined:
<literal>desktop</literal>,
<literal>laptop</literal>,
<literal>server</literal>,
<literal>tablet</literal>,
<literal>handset</literal>, as well as
the special chassis types
<literal>vm</literal> and
<literal>container</literal> for
virtualized systems that lack an
immediate physical chassis. Pass an
empty string to this operation to
reset the chassis type to the default
value which is determined from the
system form factor and possibly other
firmware and possibly other
parameters.</para></listitem>
</varlistentry>

View File

@ -128,6 +128,34 @@
similar icon name.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>CHASSIS=</varname></term>
<listitem><para>The chassis
type. Currently, the following chassis
types are defined:
<literal>desktop</literal>,
<literal>laptop</literal>,
<literal>server</literal>,
<literal>tablet</literal>,
<literal>handset</literal>, as well as
the special chassis types
<literal>vm</literal> and
<literal>container</literal> for
virtualized systems that lack an
immediate physical chassis. Note that
many systems allow detection of the
chassis type automatically (based on
firmware information or
suchlike). This setting (if set) shall
take precedence over automatically
detected information and is useful to
override misdetected configuration or
to manually configure the chassis type
where automatic detection is not
available.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -135,8 +163,9 @@
<refsect1>
<title>Example</title>
<programlisting>PRETTY_HOSTNAME="Lennart's Computer"
ICON_NAME=computer-laptop</programlisting>
<programlisting>PRETTY_HOSTNAME="Lennart's Tablet"
ICON_NAME=computer-tablet
CHASSIS=tablet</programlisting>
</refsect1>
<refsect1>

View File

@ -63,6 +63,7 @@ typedef struct StatusInfo {
const char *static_hostname;
const char *pretty_hostname;
const char *icon_name;
const char *chassis;
} StatusInfo;
static void print_status_info(StatusInfo *i) {
@ -82,9 +83,11 @@ static void print_status_info(StatusInfo *i) {
strna(i->hostname));
printf(" Pretty hostname: %s\n"
" Icon name: %s\n",
" Icon name: %s\n"
" Chassis: %s\n",
strna(i->pretty_hostname),
strna(i->icon_name));
strna(i->icon_name),
strna(i->chassis));
r = sd_id128_get_machine(&mid);
if (r >= 0)
@ -133,6 +136,8 @@ static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *
i->pretty_hostname = s;
if (streq(name, "IconName"))
i->icon_name = s;
if (streq(name, "Chassis"))
i->chassis = s;
}
break;
}
@ -321,6 +326,28 @@ static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
DBUS_TYPE_INVALID);
}
static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
dbus_bool_t interactive = true;
assert(args);
assert(n == 2);
polkit_agent_open_if_enabled();
return bus_method_call_with_reply(
bus,
"org.freedesktop.hostname1",
"/org/freedesktop/hostname1",
"org.freedesktop.hostname1",
"SetChassis",
&reply,
NULL,
DBUS_TYPE_STRING, &args[1],
DBUS_TYPE_BOOLEAN, &interactive,
DBUS_TYPE_INVALID);
}
static int help(void) {
printf("%s [OPTIONS...] COMMAND ...\n\n"
@ -335,7 +362,8 @@ static int help(void) {
"Commands:\n"
" status Show current hostname settings\n"
" set-hostname NAME Set system hostname\n"
" set-icon-name NAME Set icon name for host\n",
" set-icon-name NAME Set icon name for host\n"
" set-chassis NAME Set chassis type for host\n",
program_invocation_short_name);
return 0;
@ -434,9 +462,10 @@ static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusErr
const int argc;
int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
} verbs[] = {
{ "status", LESS, 1, show_status },
{ "set-hostname", EQUAL, 2, set_hostname },
{ "set-icon-name", EQUAL, 2, set_icon_name },
{ "status", LESS, 1, show_status },
{ "set-hostname", EQUAL, 2, set_hostname },
{ "set-icon-name", EQUAL, 2, set_icon_name },
{ "set-chassis", EQUAL, 2, set_chassis },
};
int left;

View File

@ -39,6 +39,7 @@
" <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Chassis\" type=\"s\" access=\"read\"/>\n" \
" <method name=\"SetHostname\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
@ -55,6 +56,10 @@
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetChassis\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" </interface>\n"
#define INTROSPECTION \
@ -77,6 +82,7 @@ enum {
PROP_STATIC_HOSTNAME,
PROP_PRETTY_HOSTNAME,
PROP_ICON_NAME,
PROP_CHASSIS,
_PROP_MAX
};
@ -84,6 +90,7 @@ static char *data[_PROP_MAX] = {
NULL,
NULL,
NULL,
NULL,
NULL
};
@ -114,6 +121,7 @@ static int read_data(void) {
r = parse_env_file("/etc/machine-info", NEWLINE,
"PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
"ICON_NAME", &data[PROP_ICON_NAME],
"CHASSIS", &data[PROP_CHASSIS],
NULL);
if (r < 0 && r != -ENOENT)
return r;
@ -122,10 +130,10 @@ static int read_data(void) {
}
static bool check_nss(void) {
void *dl;
if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY);
if (dl) {
dlclose(dl);
return true;
}
@ -133,25 +141,77 @@ static bool check_nss(void) {
return false;
}
static const char* fallback_icon_name(void) {
static bool valid_chassis(const char *chassis) {
#if defined(__i386__) || defined(__x86_64__)
assert(chassis);
return nulstr_contains(
"vm\0"
"container\0"
"desktop\0"
"laptop\0"
"server\0"
"tablet\0"
"handset\0",
chassis);
}
static const char* fallback_chassis(void) {
int r;
char *type;
unsigned t;
#endif
Virtualization v;
if (detect_virtualization(NULL) > 0)
return "computer-vm";
v = detect_virtualization(NULL);
#if defined(__i386__) || defined(__x86_64__)
if (v == VIRTUALIZATION_VM)
return "vm";
if (v == VIRTUALIZATION_CONTAINER)
return "container";
r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
if (r < 0)
goto try_dmi;
r = safe_atou(type, &t);
free(type);
if (r < 0)
goto try_dmi;
/* We only list the really obvious cases here as the ACPI data
* is not really super reliable.
*
* See the ACPI 5.0 Spec Section 5.2.9.1 for details:
*
* http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
*/
switch(t) {
case 1:
case 3:
case 6:
return "desktop";
case 2:
return "laptop";
case 4:
case 5:
case 7:
return "server";
case 8:
return "tablet";
}
try_dmi:
r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
if (r < 0)
return NULL;
r = safe_atou(type, &t);
free(type);
if (r < 0)
return NULL;
@ -171,22 +231,38 @@ static const char* fallback_icon_name(void) {
case 0x4:
case 0x6:
case 0x7:
return "computer-desktop";
return "desktop";
case 0x8:
case 0x9:
case 0xA:
case 0xE:
return "computer-laptop";
return "laptop";
case 0xB:
return "handset";
case 0x11:
case 0x1C:
return "computer-server";
return "server";
}
#endif
return NULL;
}
static char* fallback_icon_name(void) {
const char *chassis;
if (!isempty(data[PROP_CHASSIS]))
return strappend("computer-", data[PROP_CHASSIS]);
chassis = fallback_chassis();
if (chassis)
return strappend("computer-", chassis);
return strdup("computer");
}
static int write_data_hostname(void) {
const char *hn;
@ -218,7 +294,8 @@ static int write_data_other(void) {
static const char * const name[_PROP_MAX] = {
[PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
[PROP_ICON_NAME] = "ICON_NAME"
[PROP_ICON_NAME] = "ICON_NAME",
[PROP_CHASSIS] = "CHASSIS"
};
char **l = NULL;
@ -268,23 +345,39 @@ static int write_data_other(void) {
static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
const char *name;
_cleanup_free_ char *n = NULL;
assert(i);
assert(property);
if (isempty(data[PROP_ICON_NAME]))
name = fallback_icon_name();
name = n = fallback_icon_name();
else
name = data[PROP_ICON_NAME];
return bus_property_append_string(i, property, (void*) name);
}
static int bus_hostname_append_chassis(DBusMessageIter *i, const char *property, void *userdata) {
const char *name;
assert(i);
assert(property);
if (isempty(data[PROP_CHASSIS]))
name = fallback_chassis();
else
name = data[PROP_CHASSIS];
return bus_property_append_string(i, property, (void*) name);
}
static const BusProperty bus_hostname_properties[] = {
{ "Hostname", bus_property_append_string, "s", sizeof(data[0])*PROP_HOSTNAME, true },
{ "StaticHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_STATIC_HOSTNAME, true },
{ "PrettyHostname", bus_property_append_string, "s", sizeof(data[0])*PROP_PRETTY_HOSTNAME, true },
{ "IconName", bus_hostname_append_icon_name, "s", sizeof(data[0])*PROP_ICON_NAME, true },
{ "Chassis", bus_hostname_append_chassis, "s", sizeof(data[0])*PROP_CHASSIS, true },
{ NULL, }
};
@ -414,7 +507,8 @@ static DBusHandlerResult hostname_message_handler(
}
} else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName") ||
dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetChassis")) {
const char *name;
dbus_bool_t interactive;
@ -431,7 +525,8 @@ static DBusHandlerResult hostname_message_handler(
if (isempty(name))
name = NULL;
k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME :
streq(dbus_message_get_member(message), "SetChassis") ? PROP_CHASSIS : PROP_ICON_NAME;
if (!streq_ptr(name, data[k])) {
@ -458,6 +553,8 @@ static DBusHandlerResult hostname_message_handler(
return bus_send_error_reply(connection, message, NULL, -EINVAL);
if (k == PROP_PRETTY_HOSTNAME && !string_is_safe(name))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
if (k == PROP_CHASSIS && !valid_chassis(name))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
h = strdup(name);
if (!h)
@ -473,12 +570,15 @@ static DBusHandlerResult hostname_message_handler(
return bus_send_error_reply(connection, message, NULL, r);
}
log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
log_info("Changed %s to '%s'",
k == PROP_PRETTY_HOSTNAME ? "pretty host name" :
k == PROP_CHASSIS ? "chassis" : "icon name", strempty(data[k]));
changed = bus_properties_changed_new(
"/org/freedesktop/hostname1",
"org.freedesktop.hostname1",
k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" :
k == PROP_CHASSIS ? "Chassis\0" : "IconName\0");
if (!changed)
goto oom;
}
@ -486,7 +586,8 @@ static DBusHandlerResult hostname_message_handler(
} else
return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
if (!(reply = dbus_message_new_method_return(message)))
reply = dbus_message_new_method_return(message);
if (!reply)
goto oom;
if (!dbus_connection_send(connection, reply, NULL))