timedatectl.c revision eb9da376d76b48585b3b63b4f91903b54f7abd36
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering/***
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering This file is part of systemd.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering Copyright 2012 Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering Copyright 2013 Kay Sievers
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering systemd is free software; you can redistribute it and/or modify it
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering under the terms of the GNU Lesser General Public License as published by
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering (at your option) any later version.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering systemd is distributed in the hope that it will be useful, but
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering Lesser General Public License for more details.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering You should have received a copy of the GNU Lesser General Public License
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering***/
11c3a36649e5e5e77db499c92f3cdcbd619efd3aThomas Hindoe Paaboel Andersen
11c3a36649e5e5e77db499c92f3cdcbd619efd3aThomas Hindoe Paaboel Andersen#include <stdlib.h>
11c3a36649e5e5e77db499c92f3cdcbd619efd3aThomas Hindoe Paaboel Andersen#include <stdbool.h>
11c3a36649e5e5e77db499c92f3cdcbd619efd3aThomas Hindoe Paaboel Andersen#include <unistd.h>
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <getopt.h>
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include <locale.h>
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include <string.h>
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include <sys/timex.h>
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "sd-bus.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "bus-util.h"
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin#include "bus-error.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "util.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "spawn-polkit-agent.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "build.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "strv.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "pager.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "time-dst.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic bool arg_no_pager = false;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic bool arg_ask_password = true;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic char *arg_host = NULL;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic bool arg_adjust_system_clock = false;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic void pager_open_if_enabled(void) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (arg_no_pager)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering pager_open(false);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering}
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic void polkit_agent_open_if_enabled(void) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering /* Open the polkit agent as a child process if necessary */
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!arg_ask_password)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering polkit_agent_open();
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering}
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringtypedef struct StatusInfo {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering usec_t time;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char *timezone;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering usec_t rtc_time;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering bool rtc_local;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering bool ntp_enabled;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering bool ntp_capable;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering bool ntp_synced;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering} StatusInfo;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic const char *jump_str(int delta_minutes, char *s, size_t size) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (delta_minutes == 60)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return "one hour forward";
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (delta_minutes == -60)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return "one hour backwards";
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (delta_minutes < 0) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering snprintf(s, size, "%i minutes backwards", -delta_minutes);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return s;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering }
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (delta_minutes > 0) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering snprintf(s, size, "%i minutes forward", delta_minutes);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return s;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering }
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return "";
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering}
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringstatic void print_status_info(const StatusInfo *i) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char a[FORMAT_TIMESTAMP_MAX];
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char b[FORMAT_TIMESTAMP_MAX];
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char s[32];
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering struct tm tm;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering time_t sec;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char *zc, *zn;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering time_t t, tc, tn;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering int dn;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering bool is_dstc, is_dstn;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering int r;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert(i);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering /* Enforce the values of /etc/localtime */
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (getenv("TZ")) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering fprintf(stderr, "Warning: ignoring the TZ variable, reading the system's timezone setting only.\n\n");
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering unsetenv("TZ");
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering }
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering sec = (time_t) (i->time / USEC_PER_SEC);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering zero(tm);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char_array_0(a);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering printf(" Local time: %s\n", a);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering zero(tm);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char_array_0(a);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering printf(" Universal time: %s\n", a);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (i->rtc_time > 0) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering time_t rtc_sec;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering rtc_sec = (time_t)(i->rtc_time / USEC_PER_SEC);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering zero(tm);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)) > 0);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char_array_0(a);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering printf(" RTC time: %s\n", a);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering } else
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering printf(" RTC time: n/a\n");
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering zero(tm);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert_se(strftime(a, sizeof(a), "%Z, %z", localtime_r(&sec, &tm)) > 0);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char_array_0(a);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering printf(" Timezone: %s (%s)\n"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering " NTP enabled: %s\n"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering "NTP synchronized: %s\n"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering " RTC in local TZ: %s\n",
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering strna(i->timezone), a,
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering yes_no(i->ntp_synced),
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering yes_no(i->rtc_local));
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering r = time_get_dst(sec, "/etc/localtime",
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering &tc, &zc, &is_dstc,
934ae16baf543af03f3f521277d14524ca772d17Lennart Poettering &tn, &dn, &zn, &is_dstn);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (r < 0)
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin printf(" DST active: n/a\n");
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin else {
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin printf(" DST active: %s\n", yes_no(is_dstc));
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin t = tc - 1;
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin zero(tm);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin char_array_0(a);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin zero(tm);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm)) > 0);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin char_array_0(b);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin printf(" Last DST change: DST %s at\n"
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin " %s\n"
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin " %s\n",
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin is_dstc ? "began" : "ended", a, b);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin t = tn - 1;
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin zero(tm);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin char_array_0(a);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin zero(tm);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm)) > 0);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin char_array_0(b);
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin printf(" Next DST change: DST %s (the clock jumps %s) at\n"
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin " %s\n"
c573dcfeb1abd390cd4c4288056df97752ef18f0Evgeny Vereshchagin " %s\n",
is_dstn ? "begins" : "ends", jump_str(dn, s, sizeof(s)), a, b);
free(zc);
free(zn);
}
if (i->rtc_local)
fputs("\n" ANSI_HIGHLIGHT_ON
"Warning: The RTC is configured to maintain time in the local timezone. 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 show_status(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
StatusInfo info = {};
static const struct bus_properties_map map[] = {
{ "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
{ "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
{ "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) },
{ "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
{ "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
{ "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
{ "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
{}
};
int r;
assert(bus);
r = bus_map_all_properties(bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
map,
&info);
if (r < 0)
goto fail;
print_status_info(&info);
fail:
free(info.timezone);
return r;
}
static int set_time(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
bool relative = false, interactive = arg_ask_password;
usec_t t;
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;
}
r = sd_bus_call_method(bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
"SetTime",
&error,
NULL,
"xbb", (int64_t)t, relative, interactive);
if (r < 0)
log_error("Failed to set time: %s", bus_error_message(&error, -r));
return r;
}
static int set_timezone(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(args);
assert(n == 2);
polkit_agent_open_if_enabled();
r = sd_bus_call_method(bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
"SetTimezone",
&error,
NULL,
"sb", args[1], arg_ask_password);
if (r < 0)
log_error("Failed to set timezone: %s", bus_error_message(&error, -r));
return r;
}
static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r, b;
assert(args);
assert(n == 2);
polkit_agent_open_if_enabled();
b = parse_boolean(args[1]);
if (b < 0) {
log_error("Failed to parse local RTC setting: %s", args[1]);
return b;
}
r = sd_bus_call_method(bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
"SetLocalRTC",
&error,
NULL,
"bbb", b, arg_adjust_system_clock, arg_ask_password);
if (r < 0)
log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
return r;
}
static int set_ntp(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int b, r;
assert(args);
assert(n == 2);
polkit_agent_open_if_enabled();
b = parse_boolean(args[1]);
if (b < 0) {
log_error("Failed to parse NTP setting: %s", args[1]);
return b;
}
r = sd_bus_call_method(bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
"SetNTP",
&error,
NULL,
"bb", b, arg_ask_password);
if (r < 0)
log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
return r;
}
static int list_timezones(sd_bus *bus, char **args, unsigned n) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **zones = NULL;
size_t n_zones = 0;
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] = NULL;
pager_open_if_enabled();
strv_sort(zones);
strv_print(zones);
return 0;
}
static int help(void) {
printf("%s [OPTIONS...] COMMAND ...\n\n"
"Query or change system time and date settings.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --adjust-system-clock\n"
" 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"
" -M --machine=CONTAINER Operate on local container\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_ADJUST_SYSTEM_CLOCK,
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' },
{ "machine", required_argument, NULL, 'M' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
switch (c) {
case 'h':
return help();
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case 'H':
arg_transport = BUS_TRANSPORT_REMOTE;
arg_host = optarg;
break;
case 'M':
arg_transport = BUS_TRANSPORT_CONTAINER;
arg_host = optarg;
break;
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
case ARG_ADJUST_SYSTEM_CLOCK:
arg_adjust_system_clock = true;
break;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
}
return 1;
}
static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
static const struct {
const char* verb;
const enum {
MORE,
LESS,
EQUAL
} argc_cmp;
const int argc;
int (* const dispatch)(sd_bus *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);
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.");
}
return verbs[i].dispatch(bus, argv + optind, left);
}
int main(int argc, char *argv[]) {
_cleanup_bus_unref_ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
r = bus_open_transport(arg_transport, arg_host, false, &bus);
if (r < 0) {
log_error("Failed to create bus connection: %s", strerror(-r));
goto finish;
}
r = timedatectl_main(bus, argc, argv);
finish:
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}