diff options
author | Lennart Poettering <lennart@poettering.net> | 2012-10-17 21:23:30 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2012-10-17 21:25:42 +0200 |
commit | dbc4fbae58e39cb0d33738f0a4d1e74511ed1fb5 (patch) | |
tree | eeb212d8a62a696a80b498ae91c2951387f5712c | |
parent | 0ce8860a15fb08ac358fb9c5347bd20c0bcdebcd (diff) |
hostname: add new hostnamectl tool as text client for hostnamed
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 19 | ||||
-rw-r--r-- | man/hostname.xml | 9 | ||||
-rw-r--r-- | man/hostnamectl.xml | 228 | ||||
-rw-r--r-- | man/localtime.xml | 13 | ||||
-rw-r--r-- | man/machine-info.xml | 9 | ||||
-rw-r--r-- | man/systemd-hostnamed.service.xml | 5 | ||||
-rw-r--r-- | man/timedatectl.xml | 8 | ||||
-rw-r--r-- | src/hostname/hostnamectl.c | 525 |
9 files changed, 809 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore index bc72be8210..c41db45be2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/hostnamectl /timedatectl /test-date /install-tree diff --git a/Makefile.am b/Makefile.am index ad7a749cac..8e88c42c8f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3062,6 +3062,25 @@ MANPAGES_ALIAS += \ man/systemd-hostnamed.8 man/systemd-hostnamed.8: man/systemd-hostnamed.service.8 + +hostnamectl_SOURCES = \ + src/hostname/hostnamectl.c + +hostnamectl_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +hostnamectl_LDADD = \ + libsystemd-shared.la \ + libsystemd-dbus.la \ + libsystemd-id128-internal.la + +bin_PROGRAMS += \ + hostnamectl + +MANPAGES += \ + man/hostnamectl.1 + endif polkitpolicy_in_files += \ diff --git a/man/hostname.xml b/man/hostname.xml index 2ada32ff71..84a2961664 100644 --- a/man/hostname.xml +++ b/man/hostname.xml @@ -70,6 +70,11 @@ <para>Depending on the operating system other configuration files might be checked for configuration of the host name as well, however only as fallback.</para> + + <para>You may use + <citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> + to change the value of this file from the command + line.</para> </refsect1> <refsect1> @@ -88,7 +93,9 @@ <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>7</manvolnum></citerefentry>, <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>, - <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> + <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml new file mode 100644 index 0000000000..955e79bfd7 --- /dev/null +++ b/man/hostnamectl.xml @@ -0,0 +1,228 @@ +<?xml version='1.0'?> <!--*-nxml-*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="hostnamectl"> + + <refentryinfo> + <title>hostnamectl</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>hostnamectl</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>hostnamectl</refname> + <refpurpose>Control the system hostname</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>hostnamectl <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="req">COMMAND</arg></command> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><command>hostnamectl</command> may be used to + query and change the system hostname and related + settings.</para> + + <para>This tool distuingishes three different host + names: the high-level "pretty" hostname which might + include all all kinds of special characters + (e.g. "Lennart's Laptop"), the static hostname which + is used to initialize the kernel hostname at boot + (e.g. "lennarts-laptop"), and the transient hostname + which might be assigned temporarily due to network + configuration and might revert back to the static + hostname if network connectivity is lost and is only + temporarily written to the kernel hostname + (e.g. "dhcp-47-11").</para> + + <para>Note that the pretty hostname has little + restrictions on the characters used, while the static + and transient hostnames are limited to the usually + accepted characters of internet domain names.</para> + + <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 + <filename>/etc/machine-info</filename>, see + <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> + </refsect1> + + <refsect1> + <title>Options</title> + + <para>The following options are understood:</para> + + <variablelist> + <varlistentry> + <term><option>--help</option></term> + <term><option>-h</option></term> + + <listitem><para>Prints a short help + text and exits.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--version</option></term> + + <listitem><para>Prints a short version + string and exits.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--no-ask-password</option></term> + + <listitem><para>Don't query the user + for authentication for privileged + operations.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>-H</option></term> + <term><option>--host</option></term> + + <listitem><para>Execute operation + remotely. Specify a hostname, or + username and hostname separated by @, + to connect to. This will use SSH to + talk to a remote + system.</para></listitem> + </varlistentry> + + <varlistentry> + <term><option>--static</option></term> + <term><option>--transient</option></term> + <term><option>--pretty</option></term> + + <listitem><para>If + <command>set-hostname</command> is + invoked and one or more of these + options are passed only the selected + hostnames is + updated.</para></listitem> + </varlistentry> + </variablelist> + + <para>The following commands are understood:</para> + + <variablelist> + <varlistentry> + <term><command>status</command></term> + + <listitem><para>Show current system + hostname and related + information.</para></listitem> + </varlistentry> + + <varlistentry> + <term><command>set-hostname [NAME]</command></term> + + <listitem><para>Set the system + hostname. By default this will alter + the pretty, the static, and the + transient hostname alike, however if + one or more of + <option>--static</option>, + <option>--transient</option>, + <option>--pretty</option> are used + only the selected hostnames are + changed. If the pretty hostname is + being set, and static or transient are + being set as well the specified host + name will be simplified in regards to + the character set used before the + latter are updated. This is done by + replacing spaces by "-" and removing + special characters. This ensures that + the pretty and the static hostname + are always closely related while still + following the validity rules of the + specific name. This simplification of + the hostname string is not done if + only the transient and/or static host + names are set, and the pretty host + name is left untouched. Pass the empty + string "" as hostname to reset the + selected hostnames to their default + (usually + "localhost").</para></listitem> + </varlistentry> + + <varlistentry> + <term><command>set-icon-name [NAME]</command></term> + + <listitem><para>Set the system icon + name. The icon name is used by some + graphical applications to visualize + this host. The icon name should follow + the <ulink + url="http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html">Icon + Naming Specification</ulink>. Pass an + empty string to this operatoin to + reset the icon name to the default + value which is determined from the + system form factor and possibly other + parameters.</para></listitem> + </varlistentry> + + </variablelist> + </refsect1> + + <refsect1> + <title>Exit status</title> + + <para>On success 0 is returned, a non-zero failure + code otherwise.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/localtime.xml b/man/localtime.xml index c0e87bf78f..88c84a3682 100644 --- a/man/localtime.xml +++ b/man/localtime.xml @@ -82,14 +82,21 @@ <para>The time zone may be overridden for individual programs by using the TZ environment variable. See <citerefentry><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> + + <para>You may use + <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> + to change the settings of this file from the command + line.</para> </refsect1> <refsect1> <title>See Also</title> <para> - <citerefentry><refentrytitle>tzset</refentrytitle><manvolnum>3</manvolnum></citerefentry> - <citerefentry><refentrytitle>localtime</refentrytitle><manvolnum>3</manvolnum></citerefentry> - <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>tzset</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>localtime</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemd-timedated.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/machine-info.xml b/man/machine-info.xml index e27b600211..b310d71334 100644 --- a/man/machine-info.xml +++ b/man/machine-info.xml @@ -74,6 +74,11 @@ <para>Depending on the operating system other configuration files might be checked for machine information as well, however only as fallback.</para> + + <para>You may use + <citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> + to change the settings of this file from the command + line.</para> </refsect1> <refsect1> @@ -140,7 +145,9 @@ ICON_NAME=computer-laptop</programlisting> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry>, - <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> + <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/systemd-hostnamed.service.xml b/man/systemd-hostnamed.service.xml index 9fee2a096d..555bb7a2df 100644 --- a/man/systemd-hostnamed.service.xml +++ b/man/systemd-hostnamed.service.xml @@ -74,8 +74,9 @@ <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry>, - <citerefentry><refentrytitle>sethostname</refentrytitle><manvolnum>2</manvolnum></citerefentry>, - <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> + <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>, + <citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sethostname</refentrytitle><manvolnum>2</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/timedatectl.xml b/man/timedatectl.xml index 8fb4e38f37..9065577692 100644 --- a/man/timedatectl.xml +++ b/man/timedatectl.xml @@ -154,7 +154,12 @@ <command>list-timezones</command>. If the RTC is configured to be in the local time this will also update the - RTC time.</para></listitem> + RTC time. This call will alter the + <filename>/etc/localtime</filename> + symlink. See + <citerefentry><refentrytitle>localtime</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for more + information.</para></listitem> </varlistentry> <varlistentry> @@ -235,6 +240,7 @@ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>hwclock</refentrytitle><manvolnum>8</manvolnum></citerefentry>, <citerefentry><refentrytitle>date</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>localtime</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd-timedated.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> </para> diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c new file mode 100644 index 0000000000..de2f7242ea --- /dev/null +++ b/src/hostname/hostnamectl.c @@ -0,0 +1,525 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <sys/timex.h> + +#include "dbus-common.h" +#include "util.h" +#include "spawn-polkit-agent.h" +#include "build.h" +#include "hwclock.h" +#include "strv.h" +#include "sd-id128.h" +#include "virt.h" + +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static bool arg_ask_password = true; +static const char *arg_host = NULL; +static bool arg_set_transient = false; +static bool arg_set_pretty = false; +static bool arg_set_static = false; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + const char *hostname; + const char *static_hostname; + const char *pretty_hostname; + const char *icon_name; +} StatusInfo; + +static void print_status_info(StatusInfo *i) { + sd_id128_t mid, bid; + int r; + const char *id; + + assert(i); + + printf(" Static hostname: %s\n", + strna(i->static_hostname)); + + if (!streq_ptr(i->hostname, i->static_hostname)) + printf("Transient hostname: %s\n", + strna(i->hostname)); + + printf(" Pretty hostname: %s\n" + " Icon name: %s\n", + strna(i->pretty_hostname), + strna(i->icon_name)); + + r = sd_id128_get_machine(&mid); + if (r >= 0) + printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid)); + + r = sd_id128_get_boot(&bid); + if (r >= 0) + printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid)); + + if (detect_virtualization(&id) >= 0) + printf(" Virtualization: %s\n", id); +} + +static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) { + assert(name); + assert(iter); + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + + dbus_message_iter_get_basic(iter, &s); + if (!isempty(s)) { + if (streq(name, "Hostname")) + i->hostname = s; + if (streq(name, "StaticHostname")) + i->static_hostname = s; + if (streq(name, "PrettyHostname")) + i->pretty_hostname = s; + if (streq(name, "IconName")) + i->icon_name = s; + } + break; + } + } + + return 0; +} + +static int show_status(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = ""; + int r; + DBusMessageIter iter, sub, sub2, sub3; + StatusInfo info; + + assert(args); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + zero(info); + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *name; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { + log_error("Failed to parse reply."); + return -EIO; + } + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + r = status_property(name, &sub3, &info); + if (r < 0) { + log_error("Failed to parse reply."); + return r; + } + + dbus_message_iter_next(&sub); + } + + print_status_info(&info); + return 0; +} + +static char* hostname_simplify(char *s) { + char *p, *d; + + for (p = s, d = s; *p; p++) { + if ((*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || + *p == '-' || *p == '_') + *(d++) = *p; + else if (*p >= 'A' && *p <= 'Z') + *(d++) = *p - 'A' + 'a'; + else if (*p == ' ') + *(d++) = '-'; + } + + *d = 0; + + strshorten(s, HOST_NAME_MAX); + return s; +} + +static int set_hostname(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true; + _cleanup_free_ char *h = NULL; + const char *hostname = args[1]; + int r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + if (arg_set_pretty) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetPrettyHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + h = strdup(hostname); + if (!h) + return log_oom(); + + hostname = hostname_simplify(h); + } + + if (arg_set_static) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetStaticHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + + if (r < 0) + return r; + } + + if (arg_set_transient) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + + if (r < 0) + return r; + } + + return 0; +} + +static int set_icon_name(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", + "SetIconName", + &reply, + NULL, + DBUS_TYPE_STRING, &args[1], + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Query or set system hostname.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-ask-password Do not prompt for password\n" + " --transient Only set transient hostname\n" + " --static Only set static hostname\n" + " --pretty Only set pretty hostname\n" + " -H --host=[USER@]HOST Operate on remote host\n\n" + "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", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_ASK_PASSWORD, + ARG_SET_TRANSIENT, + ARG_SET_STATIC, + ARG_SET_PRETTY + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "transient", no_argument, NULL, ARG_SET_TRANSIENT }, + { "static", no_argument, NULL, ARG_SET_STATIC }, + { "pretty", no_argument, NULL, ARG_SET_PRETTY }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + + case ARG_SET_TRANSIENT: + arg_set_transient = true; + break; + + case ARG_SET_PRETTY: + arg_set_pretty = true; + break; + + case ARG_SET_STATIC: + arg_set_static = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (!arg_set_transient && !arg_set_pretty && !arg_set_static) + arg_set_transient = arg_set_pretty = arg_set_static = true; + + return 1; +} + +static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-hostname", LESS, 2, set_hostname }, + { "set-icon-name", EQUAL, 2, set_icon_name }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char *argv[]) { + int r, retval = EXIT_FAILURE; + DBusConnection *bus = NULL; + DBusError error; + + dbus_error_init(&error); + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + goto finish; + else if (r == 0) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = hostnamectl_main(bus, argc, argv, &error); + retval = r < 0 ? EXIT_FAILURE : r; + +finish: + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + return retval; +} |