[systemd-commits] 7 commits - .gitignore Makefile.am TODO docs/Makefile docs/sysvinit docs/var-log man/hostname.xml man/hostnamectl.xml man/localtime.xml man/machine-info.xml man/systemd-hostnamed.service.xml man/systemd-timedated.service.xml man/timedatectl.xml rules/50-udev-default.rules src/hostname src/journal src/login src/shared src/timedate

Lennart Poettering lennart at kemper.freedesktop.org
Wed Oct 17 12:25:54 PDT 2012


 .gitignore                        |    2 
 Makefile.am                       |   61 +++
 TODO                              |    4 
 docs/Makefile                     |    1 
 docs/sysvinit/.gitignore          |    1 
 docs/sysvinit/Makefile            |    1 
 docs/sysvinit/README.in           |   27 +
 docs/var-log/.gitignore           |    1 
 docs/var-log/Makefile             |    1 
 docs/var-log/README.in            |   26 +
 man/hostname.xml                  |    9 
 man/hostnamectl.xml               |  228 +++++++++++++
 man/localtime.xml                 |   13 
 man/machine-info.xml              |    9 
 man/systemd-hostnamed.service.xml |    5 
 man/systemd-timedated.service.xml |    5 
 man/timedatectl.xml               |  249 ++++++++++++++
 rules/50-udev-default.rules       |    2 
 src/hostname/hostnamectl.c        |  525 ++++++++++++++++++++++++++++++
 src/journal/journal-vacuum.c      |    8 
 src/login/loginctl.c              |   59 +--
 src/shared/dbus-common.h          |    1 
 src/shared/macro.h                |    1 
 src/shared/strv.c                 |    8 
 src/shared/strv.h                 |    1 
 src/shared/util.c                 |    4 
 src/timedate/timedatectl.c        |  650 ++++++++++++++++++++++++++++++++++++++
 27 files changed, 1859 insertions(+), 43 deletions(-)

New commits:
commit 599659860c770058f2eb04d578c521c16e0b1853
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 17 21:24:36 2012 +0200

    timedatectl: properly initialize struct before decoding bus messages

diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
index 8c6e7c9..e94a847 100644
--- a/src/timedate/timedatectl.c
+++ b/src/timedate/timedatectl.c
@@ -69,11 +69,9 @@ typedef struct StatusInfo {
 } StatusInfo;
 
 static bool ntp_synced(void) {
-
         struct timex txc;
 
         zero(txc);
-
         if (adjtimex(&txc) < 0)
                 return false;
 
@@ -90,6 +88,8 @@ static void print_status_info(StatusInfo *i) {
         time_t sec;
         int r;
 
+        assert(i);
+
         n = now(CLOCK_REALTIME);
         sec = (time_t) (n / USEC_PER_SEC);
 
@@ -191,6 +191,7 @@ static int show_status(DBusConnection *bus, char **args, unsigned n) {
                 return -EIO;
         }
 
+        zero(info);
         dbus_message_iter_recurse(&iter, &sub);
 
         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {

commit 01539d6ef9689ffdc7c0743e12740a78bb938b97
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 17 21:24:14 2012 +0200

    hostnamed: allow UTF8 chars in pretty hostname again

diff --git a/src/shared/util.c b/src/shared/util.c
index 50c4c08..1c97a8a 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -5929,7 +5929,7 @@ bool string_is_safe(const char *p) {
         assert(p);
 
         for (t = p; *t; t++) {
-                if (*t < ' ')
+                if (*t > 0 && *t < ' ')
                         return false;
 
                 if (strchr("\\\"\'", *t))

commit 4fa25d62bd586b56fa6a30009f41ce6dbc5fdd54
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 17 21:23:52 2012 +0200

    journal: fix potential integer overflow

diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 22c9cfc..731f6c7 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -243,6 +243,7 @@ int journal_directory_vacuum(
 
                         have_seqnum = false;
                 } else
+                        /* We do not vacuum active files or unknown files! */
                         continue;
 
                 patch_realtime(directory, de->d_name, &st, &realtime);
@@ -291,7 +292,12 @@ int journal_directory_vacuum(
 
                 if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
                         log_debug("Deleted archived journal %s/%s.", directory, list[i].filename);
-                        sum -= list[i].usage;
+
+                        if ((uint64_t) list[i].usage > sum)
+                                sum -= list[i].usage;
+                        else
+                                sum = 0;
+
                 } else if (errno != ENOENT)
                         log_warning("Failed to delete %s/%s: %m", directory, list[i].filename);
         }

commit dbc4fbae58e39cb0d33738f0a4d1e74511ed1fb5
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 17 21:23:30 2012 +0200

    hostname: add new hostnamectl tool as text client for hostnamed

diff --git a/.gitignore b/.gitignore
index bc72be8..c41db45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/hostnamectl
 /timedatectl
 /test-date
 /install-tree
diff --git a/Makefile.am b/Makefile.am
index ad7a749..8e88c42 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 2ada32f..84a2961 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 0000000..955e79b
--- /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 at 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 c0e87bf..88c84a3 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 e27b600..b310d71 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 9fee2a0..555bb7a 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 8fb4e38..9065577 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 0000000..de2f724
--- /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;
+}

commit 0ce8860a15fb08ac358fb9c5347bd20c0bcdebcd
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 17 20:03:21 2012 +0200

    docs: install README files into /var/log and 7etc/rc.d/init.d
    
    On systemd systems seasoned admins might be surprised to see that the
    init scripts and log files are gone. To ease the transition let's place
    some README files there, that hopefully help clearing up the situation.

diff --git a/Makefile.am b/Makefile.am
index 06dd1b1..ad7a749 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -61,6 +61,8 @@ pkgconfiglibdir=$(libdir)/pkgconfig
 polkitpolicydir=$(datadir)/polkit-1/actions
 bashcompletiondir=$(sysconfdir)/bash_completion.d
 rpmmacrosdir=$(sysconfdir)/rpm
+sysvinitdir=$(SYSTEM_SYSVINIT_PATH)
+varlogdir=$(localstatedir)/log
 
 # Our own, non-special dirs
 pkgsysconfdir=$(sysconfdir)/systemd
@@ -3594,6 +3596,8 @@ SED_PROCESS = \
 		-e 's, at sushell\@,$(sushell),g' \
 		-e 's, at QUOTAON\@,$(QUOTAON),g' \
 		-e 's, at QUOTACHECK\@,$(QUOTACHECK),g' \
+		-e 's, at SYSTEM_SYSVINIT_PATH\@,$(sysvinitdir),g' \
+		-e 's, at VARLOGDIR\@,$(varlogdir),g' \
 		< $< > $@
 
 units/%: units/%.in Makefile
@@ -3704,6 +3708,26 @@ DBUS_PREPROCESS = $(CPP) -P $(CFLAGS) $(DBUS_CFLAGS) -imacros dbus/dbus-protocol
 CLEANFILES += \
 	$(dbusinterface_DATA)
 
+if HAVE_SYSV_COMPAT
+sysvinit_DATA = \
+	docs/sysvinit/README
+
+varlog_DATA = \
+	docs/var-log/README
+
+docs/sysvinit/README: docs/sysvinit/README.in
+	$(SED_PROCESS)
+
+docs/var-log/README: docs/var-log/README.in
+	$(SED_PROCESS)
+
+EXTRA_DIST += \
+	docs/sysvinit/README.in \
+	docs/var-log/README.in
+
+endif
+
+
 systemd-install-data-hook:
 	$(MKDIR_P) -m 0755 \
 		$(DESTDIR)$(tmpfilesdir) \
diff --git a/docs/Makefile b/docs/Makefile
new file mode 120000
index 0000000..bd10475
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1 @@
+../src/Makefile
\ No newline at end of file
diff --git a/docs/sysvinit/.gitignore b/docs/sysvinit/.gitignore
new file mode 100644
index 0000000..c3fea74
--- /dev/null
+++ b/docs/sysvinit/.gitignore
@@ -0,0 +1 @@
+/README
diff --git a/docs/sysvinit/Makefile b/docs/sysvinit/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/docs/sysvinit/Makefile
@@ -0,0 +1 @@
+../../src/Makefile
\ No newline at end of file
diff --git a/docs/sysvinit/README.in b/docs/sysvinit/README.in
new file mode 100644
index 0000000..996402d
--- /dev/null
+++ b/docs/sysvinit/README.in
@@ -0,0 +1,27 @@
+You are looking for the traditional init scripts in @SYSTEM_SYSVINIT_PATH@,
+and they are gone?
+
+Here's an explanation on what's going on:
+
+You are running a systemd-based OS where traditional init scripts have
+been replaced by native systemd services files. Service files provide
+very similar functionality to init scripts. To make use of service
+files simply invoke "systemctl", which will output a list of all
+currently running services (and other units). Use "systemctl
+list-unit-files" to get a listing of all known unit files, including
+stopped, disabled and masked ones. Use "systemctl start
+foobar.service" and "systemctl stop foobar.service" to start or stop a
+service, respectively. For further details, please refer to
+systemctl(1).
+
+Note that traditional init scripts continue to function on a systemd
+system. An init script @SYSTEM_SYSVINIT_PATH@/foobar is implicitly mapped
+into a service unit foobar.service during system initialization.
+
+Thank you!
+
+Further reading:
+        man:systemctl(1)
+        man:systemd(1)
+        http://0pointer.de/blog/projects/systemd-for-admins-3.html
+        http://www.freedesktop.org/wiki/Software/systemd/Incompatibilities
diff --git a/docs/var-log/.gitignore b/docs/var-log/.gitignore
new file mode 100644
index 0000000..c3fea74
--- /dev/null
+++ b/docs/var-log/.gitignore
@@ -0,0 +1 @@
+/README
diff --git a/docs/var-log/Makefile b/docs/var-log/Makefile
new file mode 120000
index 0000000..50be211
--- /dev/null
+++ b/docs/var-log/Makefile
@@ -0,0 +1 @@
+../../src/Makefile
\ No newline at end of file
diff --git a/docs/var-log/README.in b/docs/var-log/README.in
new file mode 100644
index 0000000..2e64fb1
--- /dev/null
+++ b/docs/var-log/README.in
@@ -0,0 +1,26 @@
+You are looking for the traditional text log files in @VARLOGDIR@, and
+they are gone?
+
+Here's an explanation on what's going on:
+
+You are running a systemd-based OS where traditional syslog has been
+replaced with the Journal. The journal stores the same (and more)
+information as classic syslog. To make use of the journal and access
+the collected log data simply invoke "journalctl", which will output
+the logs in the identical text-based format the syslog files in
+ at VARLOGDIR@ used to be. For further details, please refer to
+journalctl(1).
+
+Alternatively, consider installing one of the traditional syslog
+implementations available for your distribution, which will generate
+the classic log files for you. Syslog implementations such as
+syslog-ng or rsyslog may be installed side-by-side with the journal
+and will continue to function the way they always did.
+
+Thank you!
+
+Further reading:
+        man:journalctl(1)
+        man:systemd-journald.service(8)
+        man:journald.conf(5)
+        http://0pointer.de/blog/projects/the-journal.html

commit c846716a5a0c12eb9c7aa566da4666c50cdf2eba
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 17 03:15:16 2012 +0200

    loginctl: show pager also for status command

diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 0d9358d..97c6617 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -979,8 +979,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
 
         show_properties = !strstr(args[0], "status");
 
-        if (show_properties)
-                pager_open_if_enabled();
+        pager_open_if_enabled();
 
         if (show_properties && n <= 1) {
                 /* If not argument is specified inspect the manager

commit 6d0274f11547a0f11200bb82bf598a5a253e12cf
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 17 02:50:09 2012 +0200

    timedatectl: introduce new command line client for timedated
    
    Much like logind has a client in loginctl, and journald in journalctl
    introduce timedatectl, to change the system time (incl. RTC), timezones
    and related settings.

diff --git a/.gitignore b/.gitignore
index b9b333d..bc72be8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/timedatectl
 /test-date
 /install-tree
 /systemd-journal-gatewayd
diff --git a/Makefile.am b/Makefile.am
index 0a3ed18..06dd1b1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3194,6 +3194,24 @@ MANPAGES_ALIAS += \
 	man/systemd-timedated.8
 
 man/systemd-timedated.8: man/systemd-timedated.service.8
+
+timedatectl_SOURCES = \
+	src/timedate/timedatectl.c
+
+timedatectl_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+timedatectl_LDADD = \
+	libsystemd-shared.la \
+	libsystemd-dbus.la
+
+bin_PROGRAMS += \
+	timedatectl
+
+MANPAGES += \
+	man/timedatectl.1
+
 endif
 
 polkitpolicy_in_files += \
diff --git a/TODO b/TODO
index 218aad2..a5a8057 100644
--- a/TODO
+++ b/TODO
@@ -19,6 +19,10 @@ F18:
 
 Features:
 
+* timedated: export boolean that clarifies whether NTP is even available
+
+* timedated: refuse time changes when NTP is on
+
 * journald: don't make SystemMinFileSize= configurable
 
 * clean up date formatting and parsing so that all absolute/relative timestamps we format can also be parsed
diff --git a/man/systemd-timedated.service.xml b/man/systemd-timedated.service.xml
index 47e7622..ea2abc5 100644
--- a/man/systemd-timedated.service.xml
+++ b/man/systemd-timedated.service.xml
@@ -64,6 +64,10 @@
                 is automatically activated on request and terminates
                 itself when it is unused.</para>
 
+                <para>The tool
+                <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                is a command line client to this service.</para>
+
                 <para>See the <ulink
                 url="http://www.freedesktop.org/wiki/Software/systemd/timedated">
                 developer documentation</ulink> for information about
@@ -75,6 +79,7 @@
                 <title>See Also</title>
                 <para>
                         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>localtime</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>hwclock</refentrytitle><manvolnum>8</manvolnum></citerefentry>
                 </para>
diff --git a/man/timedatectl.xml b/man/timedatectl.xml
new file mode 100644
index 0000000..8fb4e38
--- /dev/null
+++ b/man/timedatectl.xml
@@ -0,0 +1,243 @@
+<?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="timedatectl">
+
+        <refentryinfo>
+                <title>timedatectl</title>
+                <productname>systemd</productname>
+
+                <authorgroup>
+                        <author>
+                                <contrib>Developer</contrib>
+                                <firstname>Lennart</firstname>
+                                <surname>Poettering</surname>
+                                <email>lennart at poettering.net</email>
+                        </author>
+                </authorgroup>
+        </refentryinfo>
+
+        <refmeta>
+                <refentrytitle>timedatectl</refentrytitle>
+                <manvolnum>1</manvolnum>
+        </refmeta>
+
+        <refnamediv>
+                <refname>timedatectl</refname>
+                <refpurpose>Control the system time and date</refpurpose>
+        </refnamediv>
+
+        <refsynopsisdiv>
+                <cmdsynopsis>
+                        <command>timedatectl <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="req">COMMAND</arg></command>
+                </cmdsynopsis>
+        </refsynopsisdiv>
+
+        <refsect1>
+                <title>Description</title>
+
+                <para><command>timedatectl</command> may be used to
+                query and change the system clock and its
+                settings.</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-pager</option></term>
+
+                                <listitem><para>Do not pipe output into a
+                                pager.</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>--fix-system</option></term>
+
+                                <listitem><para>If
+                                <command>set-local-rtc</command> is
+                                invoked and this option is passed the
+                                system clock is synchronized from the
+                                RTC again, taking the new setting into
+                                account. Otherwise the RTC is
+                                synchonized from the system
+                                clock.</para></listitem>
+                        </varlistentry>
+                </variablelist>
+
+                <para>The following commands are understood:</para>
+
+                <variablelist>
+                        <varlistentry>
+                                <term><command>status</command></term>
+
+                                <listitem><para>Show current settings
+                                of the system clock and
+                                RTC.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>set-time [TIME]</command></term>
+
+                                <listitem><para>Set the system clock
+                                to the specified time. This will also
+                                update the RTC time accordingly. The time
+                                may be specified in the format
+                                "2012-10-30
+                                18:17:16".</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>set-timezone [TIMEZONE]</command></term>
+
+                                <listitem><para>Set the system time
+                                zone to the specified value. Available
+                                time zones my be listed with
+                                <command>list-timezones</command>. If
+                                the RTC is configured to be in the
+                                local time this will also update the
+                                RTC time.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>list-timezones</command></term>
+
+                                <listitem><para>List available time
+                                zones, one per line. Entries from the
+                                list may selected as the system
+                                timezone with
+                                <command>set-timezone</command>.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>set-local-rtc [BOOL]</command></term>
+
+                                <listitem><para>Takes a boolean
+                                argument. If <literal>0</literal> the
+                                system is configured to maintain the
+                                RTC in universal time, if
+                                <literal>1</literal> it will maintain
+                                the RTC in local time instead. Note
+                                that maintaining the RTC in the local
+                                timezone is is not fully supported and
+                                will create various problems with time
+                                zone changes and daylight saving
+                                adjustments. If at all possible use
+                                RTC in UTC. Note that invoking this
+                                will also synchronize the RTC from the
+                                system clock, unless
+                                <option>--fix-system</option> is
+                                passed (see above). This command will
+                                change the 3rd line of
+                                <filename>/etc/adjtime</filename>, as
+                                documented in
+                                <citerefentry><refentrytitle>hwclock</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>set-ntp [BOOL]</command></term>
+
+                                <listitem><para>Takes a boolean
+                                argument. Controls whether NTP based
+                                network time synchronization is
+                                enabled (if
+                                available).</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>Environment</title>
+
+                <variablelist>
+                        <varlistentry>
+                                <term><varname>$SYSTEMD_PAGER</varname></term>
+                                <listitem><para>Pager to use when
+                                <option>--no-pager</option> is not given;
+                                overrides <varname>$PAGER</varname>.  Setting
+                                this to an empty string or the value
+                                <literal>cat</literal> is equivalent to passing
+                                <option>--no-pager</option>.</para></listitem>
+                        </varlistentry>
+                </variablelist>
+        </refsect1>
+
+        <refsect1>
+                <title>See Also</title>
+                <para>
+                        <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>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>systemd-timedated.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+                </para>
+        </refsect1>
+
+</refentry>
diff --git a/rules/50-udev-default.rules b/rules/50-udev-default.rules
index 6e3ab94..20ef5fd 100644
--- a/rules/50-udev-default.rules
+++ b/rules/50-udev-default.rules
@@ -83,6 +83,6 @@ KERNEL=="rfkill", MODE="0644"
 
 KERNEL=="fuse", ACTION=="add", MODE="0666", OPTIONS+="static_node=fuse"
 
-SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc"
+SUBSYSTEM=="rtc", ATTR{hctosys}=="1", MODE="0644", SYMLINK+="rtc"
 
 SUBSYSTEM=="firmware", ACTION=="add", IMPORT{builtin}="firmware"
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 7ef9dde..0d9358d 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -862,7 +862,7 @@ static int print_property(const char *name, DBusMessageIter *iter) {
 }
 
 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
-        DBusMessage *reply = NULL;
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
         const char *interface = "";
         int r;
         DBusMessageIter iter, sub, sub2, sub3;
@@ -877,7 +877,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
         zero(user_info);
         zero(seat_info);
 
-        r = bus_method_call_with_reply (
+        r = bus_method_call_with_reply(
                         bus,
                         "org.freedesktop.login1",
                         path,
@@ -887,7 +887,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
                         NULL,
                         DBUS_TYPE_STRING, &interface,
                         DBUS_TYPE_INVALID);
-        if (r)
+        if (r < 0)
                 goto finish;
 
         if (!dbus_message_iter_init(reply, &iter) ||
@@ -941,7 +941,6 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
 
                 if (r < 0) {
                         log_error("Failed to parse reply.");
-                        r = -EIO;
                         goto finish;
                 }
 
@@ -957,14 +956,11 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
                         print_seat_status_info(&seat_info);
         }
 
-        strv_free(seat_info.sessions);
-        strv_free(user_info.sessions);
-
         r = 0;
 
 finish:
-        if (reply)
-                dbus_message_unref(reply);
+        strv_free(seat_info.sessions);
+        strv_free(user_info.sessions);
 
         return r;
 }
@@ -1339,16 +1335,16 @@ static int help(void) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
                "Send control commands to or query the login manager.\n\n"
-               "  -h --help           Show this help\n"
-               "     --version        Show package version\n"
-               "  -p --property=NAME  Show only properties by this name\n"
-               "  -a --all            Show all properties, including empty ones\n"
-               "     --kill-who=WHO   Who to send signal to\n"
-               "  -s --signal=SIGNAL  Which signal to send\n"
-               "  -H --host=[USER@]HOST\n"
-               "                      Show information for remote host\n"
-               "  -P --privileged     Acquire privileges before execution\n"
-               "     --no-pager       Do not pipe output into a pager\n\n"
+               "  -h --help              Show this help\n"
+               "     --version           Show package version\n"
+               "  -p --property=NAME     Show only properties by this name\n"
+               "  -a --all               Show all properties, including empty ones\n"
+               "     --kill-who=WHO      Who to send signal to\n"
+               "  -s --signal=SIGNAL     Which signal to send\n"
+               "     --no-ask-password   Don't prompt for password\n"
+               "  -H --host=[USER@]HOST  Show information for remote host\n"
+               "  -P --privileged        Acquire privileges before execution\n"
+               "     --no-pager          Do not pipe output into a pager\n\n"
                "Commands:\n"
                "  list-sessions                   List sessions\n"
                "  session-status [ID...]          Show session status\n"
@@ -1387,17 +1383,17 @@ static int parse_argv(int argc, char *argv[]) {
         };
 
         static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "version",   no_argument,       NULL, ARG_VERSION   },
-                { "property",  required_argument, NULL, 'p'           },
-                { "all",       no_argument,       NULL, 'a'           },
-                { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
-                { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
-                { "signal",    required_argument, NULL, 's'           },
-                { "host",      required_argument, NULL, 'H'           },
-                { "privileged",no_argument,       NULL, 'P'           },
-                { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
-                { NULL,        0,                 NULL, 0             }
+                { "help",            no_argument,       NULL, 'h'                 },
+                { "version",         no_argument,       NULL, ARG_VERSION         },
+                { "property",        required_argument, NULL, 'p'                 },
+                { "all",             no_argument,       NULL, 'a'                 },
+                { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
+                { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
+                { "signal",          required_argument, NULL, 's'                 },
+                { "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;
diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h
index 394ab4c..005a715 100644
--- a/src/shared/dbus-common.h
+++ b/src/shared/dbus-common.h
@@ -22,6 +22,7 @@
 ***/
 
 #include <dbus/dbus.h>
+#include <inttypes.h>
 
 #ifndef DBUS_ERROR_UNKNOWN_OBJECT
 #define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject"
diff --git a/src/shared/macro.h b/src/shared/macro.h
index dbdf5b3..e930fda 100644
--- a/src/shared/macro.h
+++ b/src/shared/macro.h
@@ -192,6 +192,7 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
 #define _cleanup_close_ __attribute__((cleanup(closep)))
 #define _cleanup_closedir_ __attribute__((cleanup(closedirp)))
 #define _cleanup_umask_ __attribute__((cleanup(umaskp)))
+#define _cleanup_strv_free_ __attribute__((cleanup(strv_freep)))
 
 #define VA_FORMAT_ADVANCE(format, ap)                                   \
 do {                                                                    \
diff --git a/src/shared/strv.c b/src/shared/strv.c
index c8d8563..822b2dc 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -64,6 +64,14 @@ void strv_free(char **l) {
         free(l);
 }
 
+void strv_freep(char ***l) {
+        if (!l)
+                return;
+
+        strv_free(*l);
+        *l = NULL;
+}
+
 char **strv_copy(char **l) {
         char **r, **k;
 
diff --git a/src/shared/strv.h b/src/shared/strv.h
index ae4e31f..81e3335 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -30,6 +30,7 @@ char *strv_find(char **l, const char *name);
 char *strv_find_prefix(char **l, const char *name);
 
 void strv_free(char **l);
+void strv_freep(char ***l);
 char **strv_copy(char **l) _malloc_;
 unsigned strv_length(char **l);
 
diff --git a/src/shared/util.c b/src/shared/util.c
index 1e1eb2a..50c4c08 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -1959,7 +1959,7 @@ char *format_timestamp(char *buf, size_t l, usec_t t) {
 
         sec = (time_t) (t / USEC_PER_SEC);
 
-        if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
+        if (strftime(buf, l, "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
                 return NULL;
 
         return buf;
diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c
new file mode 100644
index 0000000..8c6e7c9
--- /dev/null
+++ b/src/timedate/timedatectl.c
@@ -0,0 +1,649 @@
+/*-*- 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 "pager.h"
+
+static bool arg_fix_system = false;
+static bool arg_no_pager = false;
+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 void pager_open_if_enabled(void) {
+
+        if (arg_no_pager)
+                return;
+
+        pager_open();
+}
+
+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 *timezone;
+        bool local_rtc;
+        bool ntp;
+} StatusInfo;
+
+static bool ntp_synced(void) {
+
+        struct timex txc;
+
+        zero(txc);
+
+        if (adjtimex(&txc) < 0)
+                return false;
+
+        if (txc.status & STA_UNSYNC)
+                return false;
+
+        return true;
+}
+
+static void print_status_info(StatusInfo *i) {
+        usec_t n;
+        char b[FORMAT_TIMESTAMP_MAX];
+        struct tm tm;
+        time_t sec;
+        int r;
+
+        n = now(CLOCK_REALTIME);
+        sec = (time_t) (n / USEC_PER_SEC);
+
+        zero(tm);
+        assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
+        char_array_0(b);
+        printf("      Local time: %s\n", b);
+
+        zero(tm);
+        assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
+        char_array_0(b);
+        printf("  Universal time: %s\n", b);
+
+        zero(tm);
+        r = hwclock_get_time(&tm);
+        if (r >= 0) {
+                /* Calculcate the week-day */
+                mktime(&tm);
+
+                assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S", &tm) > 0);
+                char_array_0(b);
+                printf("        RTC time: %s\n", b);
+        }
+
+        printf("        Timezone: %s\n"
+               "     NTP enabled: %s\n"
+               "NTP synchronized: %s\n"
+               " RTC in local TZ: %s\n",
+               strna(i->timezone),
+               yes_no(i->ntp),
+               yes_no(ntp_synced()),
+               yes_no(i->local_rtc));
+
+        if (i->local_rtc)
+                fputs("\n" ANSI_HIGHLIGHT_ON
+                      "Warning: The RTC is configured to maintain time in the local time zone. This\n"
+                      "         mode is not fully supported and will create various problems with time\n"
+                      "         zone changes and daylight saving adjustments. If at all possible use\n"
+                      "         RTC in UTC, by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout);
+}
+
+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, "Timezone"))
+                                i->timezone = s;
+                }
+                break;
+        }
+
+        case DBUS_TYPE_BOOLEAN: {
+                dbus_bool_t b;
+
+                dbus_message_iter_get_basic(iter, &b);
+                if (streq(name, "LocalRTC"))
+                        i->local_rtc = b;
+                else if (streq(name, "NTP"))
+                        i->ntp = b;
+        }
+        }
+
+        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.timedate1",
+                        "/org/freedesktop/timedate1",
+                        "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;
+        }
+
+        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 int set_time(DBusConnection *bus, char **args, unsigned n) {
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+        dbus_bool_t relative = false, interactive = true;
+        usec_t t;
+        dbus_int64_t u;
+        int r;
+
+        assert(args);
+        assert(n == 2);
+
+        polkit_agent_open_if_enabled();
+
+        r = parse_timestamp(args[1], &t);
+        if (r < 0) {
+                log_error("Failed to parse time specification: %s", args[1]);
+                return r;
+        }
+
+        u = (dbus_uint64_t) t;
+
+        return bus_method_call_with_reply(
+                        bus,
+                        "org.freedesktop.timedate1",
+                        "/org/freedesktop/timedate1",
+                        "org.freedesktop.timedate1",
+                        "SetTime",
+                        &reply,
+                        NULL,
+                        DBUS_TYPE_INT64, &u,
+                        DBUS_TYPE_BOOLEAN, &relative,
+                        DBUS_TYPE_BOOLEAN, &interactive,
+                        DBUS_TYPE_INVALID);
+}
+
+static int set_timezone(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.timedate1",
+                        "/org/freedesktop/timedate1",
+                        "org.freedesktop.timedate1",
+                        "SetTimezone",
+                        &reply,
+                        NULL,
+                        DBUS_TYPE_STRING, &args[1],
+                        DBUS_TYPE_BOOLEAN, &interactive,
+                        DBUS_TYPE_INVALID);
+}
+
+static int set_local_rtc(DBusConnection *bus, char **args, unsigned n) {
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+        dbus_bool_t interactive = true, b, q;
+        int r;
+
+        assert(args);
+        assert(n == 2);
+
+        polkit_agent_open_if_enabled();
+
+        r = parse_boolean(args[1]);
+        if (r < 0) {
+                log_error("Failed to parse local RTC setting: %s", args[1]);
+                return r;
+        }
+
+        b = r;
+        q = arg_fix_system;
+
+        return bus_method_call_with_reply(
+                        bus,
+                        "org.freedesktop.timedate1",
+                        "/org/freedesktop/timedate1",
+                        "org.freedesktop.timedate1",
+                        "SetLocalRTC",
+                        &reply,
+                        NULL,
+                        DBUS_TYPE_BOOLEAN, &b,
+                        DBUS_TYPE_BOOLEAN, &q,
+                        DBUS_TYPE_BOOLEAN, &interactive,
+                        DBUS_TYPE_INVALID);
+}
+
+static int set_ntp(DBusConnection *bus, char **args, unsigned n) {
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+        dbus_bool_t interactive = true, b;
+        int r;
+
+        assert(args);
+        assert(n == 2);
+
+        polkit_agent_open_if_enabled();
+
+        r = parse_boolean(args[1]);
+        if (r < 0) {
+                log_error("Failed to parse NTP setting: %s", args[1]);
+                return r;
+        }
+
+        b = r;
+
+        return bus_method_call_with_reply(
+                        bus,
+                        "org.freedesktop.timedate1",
+                        "/org/freedesktop/timedate1",
+                        "org.freedesktop.timedate1",
+                        "SetNTP",
+                        &reply,
+                        NULL,
+                        DBUS_TYPE_BOOLEAN, &b,
+                        DBUS_TYPE_BOOLEAN, &interactive,
+                        DBUS_TYPE_INVALID);
+}
+
+static int zone_compare(const void *_a, const void *_b) {
+        const char **a = (const char**) _a, **b = (const char**) _b;
+
+        return strcmp(*a, *b);
+}
+
+static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_strv_free_ char **zones = NULL;
+        size_t n_zones;
+        char **i;
+
+        assert(args);
+        assert(n == 1);
+
+        f = fopen("/usr/share/zoneinfo/zone.tab", "re");
+        if (!f) {
+                log_error("Failed to open timezone database: %m");
+                return -errno;
+        }
+
+        for (;;) {
+                char l[LINE_MAX], *p, **z, *w;
+                size_t k;
+
+                if (!fgets(l, sizeof(l), f)) {
+                        if (feof(f))
+                                break;
+
+                        log_error("Failed to read timezone database: %m");
+                        return -errno;
+                }
+
+                p = strstrip(l);
+
+                if (isempty(p) || *p == '#')
+                        continue;
+
+
+                /* Skip over country code */
+                p += strcspn(p, WHITESPACE);
+                p += strspn(p, WHITESPACE);
+
+                /* Skip over coordinates */
+                p += strcspn(p, WHITESPACE);
+                p += strspn(p, WHITESPACE);
+
+                /* Found timezone name */
+                k = strcspn(p, WHITESPACE);
+                if (k <= 0)
+                        continue;
+
+                w = strndup(p, k);
+                if (!w)
+                        return log_oom();
+
+                z = realloc(zones, sizeof(char*) * (n_zones + 2));
+                if (!z) {
+                        free(w);
+                        return log_oom();
+                }
+
+                zones = z;
+                zones[n_zones++] = w;
+        }
+
+        if (zones)
+                zones[n_zones] = 0;
+
+        qsort(zones, n_zones, sizeof(char*), zone_compare);
+
+        pager_open_if_enabled();
+
+        STRV_FOREACH(i, zones)
+                puts(*i);
+
+        return 0;
+}
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+               "Query or control system time and date settings.\n\n"
+               "  -h --help              Show this help\n"
+               "     --version           Show package version\n"
+               "     --fix-system        Adjust system clock when changing local RTC mode\n"
+               "     --no-pager          Do not pipe output into a pager\n"
+               "     --no-ask-password   Do not prompt for password\n"
+               "  -H --host=[USER@]HOST  Operate on remote host\n\n"
+               "Commands:\n"
+               "  status                          Show current time settings\n"
+               "  set-time [TIME]                 Set system time\n"
+               "  set-timezone [ZONE]             Set system timezone\n"
+               "  list-timezones                  Show known timezones\n"
+               "  set-local-rtc [BOOL]            Control whether RTC is in local time\n"
+               "  set-ntp [BOOL]                  Control whether NTP is enabled\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_NO_PAGER,
+                ARG_FIX_SYSTEM,
+                ARG_NO_ASK_PASSWORD
+        };
+
+        static const struct option options[] = {
+                { "help",            no_argument,       NULL, 'h'                 },
+                { "version",         no_argument,       NULL, ARG_VERSION         },
+                { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
+                { "host",            required_argument, NULL, 'H'                 },
+                { "privileged",      no_argument,       NULL, 'P'                 },
+                { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
+                { "fix-system",      no_argument,       NULL, ARG_FIX_SYSTEM      },
+                { 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_FIX_SYSTEM:
+                        arg_fix_system = true;
+                        break;
+
+                case ARG_NO_PAGER:
+                        arg_no_pager = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        return 1;
+}
+
+static int timedatectl_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-time",              EQUAL,  2, set_time         },
+                { "set-timezone",          EQUAL,  2, set_timezone     },
+                { "list-timezones",        EQUAL,  1, list_timezones   },
+                { "set-local-rtc",         EQUAL,  2, set_local_rtc    },
+                { "set-ntp",               EQUAL,  2, set_ntp,         },
+        };
+
+        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 = timedatectl_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();
+
+        pager_close();
+
+        return retval;
+}



More information about the systemd-commits mailing list