chiark / gitweb /
hostnamed: make chassis type configurable via /etc/machine-info
authorLennart Poettering <lennart@poettering.net>
Mon, 24 Dec 2012 18:03:59 +0000 (19:03 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 24 Dec 2012 18:03:59 +0000 (19:03 +0100)
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.

man/hostnamectl.xml
man/machine-info.xml
src/hostname/hostnamectl.c
src/hostname/hostnamed.c

index c36f522..a29d2f5 100644 (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>
                                 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>
 
index b310d71..1c3a21c 100644 (file)
                                 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>
         <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>
index 265c7ec..e38be89 100644 (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;
index cd3ef49..92b150b 100644 (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" \
         "   <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;
+
+        v = detect_virtualization(NULL);
+
+        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";
 
-        if (detect_virtualization(NULL) > 0)
-                return "computer-vm";
+        case 4:
+        case 5:
+        case 7:
+                return "server";
 
-#if defined(__i386__) || defined(__x86_64__)
+        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))