timedatectl.c revision 7f35b7bc4a241e9aa3b1512fd345cbf5b2e5a782
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/***
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen This file is part of systemd.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright 2012 Lennart Poettering
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright 2013 Kay Sievers
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is free software; you can redistribute it and/or modify it
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen under the terms of the GNU Lesser General Public License as published by
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen (at your option) any later version.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is distributed in the hope that it will be useful, but
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Lesser General Public License for more details.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen You should have received a copy of the GNU Lesser General Public License
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen***/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include <stdlib.h>
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include <stdbool.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <unistd.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <getopt.h>
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include <locale.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <string.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <sys/timex.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "sd-bus.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "bus-util.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "bus-error.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "util.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "spawn-polkit-agent.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "build.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "strv.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "pager.h"
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include "time-dst.h"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersenstatic bool arg_adjust_system_clock = false;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic bool arg_no_pager = false;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic bool arg_ask_password = true;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic char *arg_host = NULL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic void pager_open_if_enabled(void) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (arg_no_pager)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen pager_open(false);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic void polkit_agent_open_if_enabled(void) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen /* Open the polkit agent as a child process if necessary */
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (!arg_ask_password)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen polkit_agent_open();
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersentypedef struct StatusInfo {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen usec_t time;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen const char *timezone;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen usec_t rtc_time;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen bool rtc_local;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen bool ntp_enabled;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen bool ntp_capable;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen bool ntp_synced;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen} StatusInfo;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic const char *jump_str(int delta_minutes, char *s, size_t size) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (delta_minutes == 60)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return "one hour forward";
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (delta_minutes == -60)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return "one hour backwards";
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (delta_minutes < 0) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen snprintf(s, size, "%i minutes backwards", -delta_minutes);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return s;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen }
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (delta_minutes > 0) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen snprintf(s, size, "%i minutes forward", delta_minutes);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return s;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen }
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return "";
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic void print_status_info(StatusInfo *i) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen char a[FORMAT_TIMESTAMP_MAX];
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen char b[FORMAT_TIMESTAMP_MAX];
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen char s[32];
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen struct tm tm;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen time_t sec;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen char *zc, *zn;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen time_t t, tc, tn;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen int dn;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen bool is_dstc, is_dstn;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen int r;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert(i);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen /* Enforce the values of /etc/localtime */
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (getenv("TZ")) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen fprintf(stderr, "Warning: ignoring the TZ variable, reading the system's timezone setting only.\n\n");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen unsetenv("TZ");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen }
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen sec = (time_t) (i->time / USEC_PER_SEC);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" Local time: %s\n", a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" Universal time: %s\n", a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (i->rtc_time > 0) {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen time_t rtc_sec;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen rtc_sec = (time_t)(i->rtc_time / USEC_PER_SEC);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" RTC time: %s\n", a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen } else
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" RTC time: n/a\n");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(a, sizeof(a), "%Z, %z", localtime_r(&sec, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" Timezone: %s (%s)\n"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen " NTP enabled: %s\n"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen "NTP synchronized: %s\n"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen " RTC in local TZ: %s\n",
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen strna(i->timezone), a,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen yes_no(i->ntp_synced),
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen yes_no(i->rtc_local));
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen r = time_get_dst(sec, "/etc/localtime",
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen &tc, &zc, &is_dstc,
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen &tn, &dn, &zn, &is_dstn);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (r < 0)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" DST active: n/a\n");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen else {
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" DST active: %s\n", yes_no(is_dstc));
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen t = tc - 1;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(b);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" Last DST change: DST %s at\n"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen " %s\n"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen " %s\n",
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen is_dstc ? "began" : "ended", a, b);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen t = tn - 1;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(a);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen zero(tm);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm)) > 0);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen char_array_0(b);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen printf(" Next DST change: DST %s (the clock jumps %s) at\n"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen " %s\n"
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen " %s\n",
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen is_dstn ? "begins" : "ends", jump_str(dn, s, sizeof(s)), a, b);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen 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 get_timedate_property_bool(sd_bus *bus, const char *name, bool *target) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r, b;
assert(name);
r = sd_bus_get_property_trivial(
bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
name,
&error,
'b', &b);
if (r < 0) {
log_error("Failed to get property: %s %s", name, bus_error_message(&error, -r));
return r;
}
*target = b;
return 0;
}
static int get_timedate_property_usec(sd_bus *bus, const char *name, usec_t *target) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(name);
r = sd_bus_get_property_trivial(
bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
name,
&error,
't', target);
if (r < 0) {
log_error("Failed to get property: %s %s", name, bus_error_message(&error, -r));
return r;
}
return 0;
}
static int show_status(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
StatusInfo info = {};
assert(bus);
r = sd_bus_get_property(
bus,
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
"org.freedesktop.timedate1",
"Timezone",
&error,
&reply,
"s");
if (r < 0) {
log_error("Failed to get property: Timezone %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "s", &info.timezone);
if (r < 0)
return r;
r = get_timedate_property_bool(bus, "LocalRTC", &info.rtc_local);
if (r < 0)
return r;
r = get_timedate_property_bool(bus, "NTP", &info.ntp_enabled);
if (r < 0)
return r;
r = get_timedate_property_bool(bus, "CanNTP", &info.ntp_capable);
if (r < 0)
return r;
r = get_timedate_property_bool(bus, "NTPSynchronized", &info.ntp_synced);
if (r < 0)
return r;
r = get_timedate_property_usec(bus, "TimeUSec", &info.time);
if (r < 0)
return r;
r = get_timedate_property_usec(bus, "RTCTimeUSec", &info.rtc_time);
if (r < 0)
return r;
print_status_info(&info);
return 0;
}
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 },
{ NULL, 0, NULL, 0 }
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
switch (c) {
case 'h':
help();
return 0;
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:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
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[]) {
int r, ret = EXIT_FAILURE;
_cleanup_bus_unref_ sd_bus *bus = NULL;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r < 0)
goto finish;
else if (r == 0) {
ret = EXIT_SUCCESS;
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));
ret = EXIT_FAILURE;
goto finish;
}
r = timedatectl_main(bus, argc, argv);
ret = r < 0 ? EXIT_FAILURE : r;
finish:
pager_close();
return ret;
}