hostname: add new hostnamectl tool as text client for hostnamed

This commit is contained in:
Lennart Poettering 2012-10-17 21:23:30 +02:00
parent 0ce8860a15
commit dbc4fbae58
9 changed files with 809 additions and 8 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/hostnamectl
/timedatectl
/test-date
/install-tree

View File

@ -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 += \

View File

@ -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>

228
man/hostnamectl.xml Normal file
View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

525
src/hostname/hostnamectl.c Normal file
View File

@ -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;
}