timedatectl.c revision e1636421f46db6d06fbd028ef20a3113fa3e11f8
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen/***
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen This file is part of systemd.
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen Copyright 2012 Lennart Poettering
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen Copyright 2013 Kay Sievers
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen systemd is free software; you can redistribute it and/or modify it
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen under the terms of the GNU Lesser General Public License as published by
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen (at your option) any later version.
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen systemd is distributed in the hope that it will be useful, but
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen Lesser General Public License for more details.
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen You should have received a copy of the GNU Lesser General Public License
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen***/
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include <stdlib.h>
cf0fbc49e67b55f8d346fc94de28c90113505297Thomas Hindoe Paaboel Andersen#include <stdbool.h>
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include <unistd.h>
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include <getopt.h>
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include <locale.h>
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include <string.h>
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include <sys/timex.h>
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "sd-bus.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "bus-util.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "bus-error.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "util.h"
920b52e4909d9dc812817fd8b82f83ca23a11c91Thomas Hindoe Paaboel Andersen#include "spawn-polkit-agent.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "build.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "strv.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "pager.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen#include "time-dst.h"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
79008bddf679a5e0900369950eb346c9fa687107Lennart Poetteringstatic bool arg_no_pager = false;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic bool arg_ask_password = true;
f0213e3796b4dd66e546e2de4d677db319f9171bTom Gundersenstatic BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic char *arg_host = NULL;
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poetteringstatic bool arg_adjust_system_clock = false;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic void pager_open_if_enabled(void) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (arg_no_pager)
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen pager_open(false);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen}
91b5f997316ddc77d26f9a7a5e24c335484586bdTom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic void polkit_agent_open_if_enabled(void) {
ed9e361a8a798f9fee353b5c7e0e23308e0d329fTom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen /* Open the polkit agent as a child process if necessary */
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering if (!arg_ask_password)
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen polkit_agent_open();
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen}
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
86655331bc28887def7998d321b14ef8fccbeaf9Tom Gundersentypedef struct StatusInfo {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen usec_t time;
91b5f997316ddc77d26f9a7a5e24c335484586bdTom Gundersen const char *timezone;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
8012cd391932d58b44332df106d426a360faf0a6Tom Gundersen usec_t rtc_time;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen bool rtc_local;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen bool ntp_enabled;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen bool ntp_capable;
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen bool ntp_synced;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen} StatusInfo;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic const char *jump_str(int delta_minutes, char *s, size_t size) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (delta_minutes == 60)
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return "one hour forward";
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (delta_minutes == -60)
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen return "one hour backwards";
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (delta_minutes < 0) {
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering snprintf(s, size, "%i minutes backwards", -delta_minutes);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return s;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen }
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (delta_minutes > 0) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen snprintf(s, size, "%i minutes forward", delta_minutes);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return s;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen }
8012cd391932d58b44332df106d426a360faf0a6Tom Gundersen return "";
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen}
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic void print_status_info(StatusInfo *i) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char a[FORMAT_TIMESTAMP_MAX];
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen char b[FORMAT_TIMESTAMP_MAX];
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char s[32];
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen struct tm tm;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen time_t sec;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char *zc, *zn;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen time_t t, tc, tn;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen int dn;
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen bool is_dstc, is_dstn;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen int r;
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert(i);
45af44d47da6933b260c734ad9ff721f63f80a4dTom Gundersen
200a0868fcdf7b95f3d8d1fda3aa2aef48d84fddTom Gundersen /* Enforce the values of /etc/localtime */
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (getenv("TZ")) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen fprintf(stderr, "Warning: ignoring the TZ variable, reading the system's timezone setting only.\n\n");
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen unsetenv("TZ");
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen }
8012cd391932d58b44332df106d426a360faf0a6Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen sec = (time_t) (i->time / USEC_PER_SEC);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen zero(tm);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" Local time: %s\n", a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen zero(tm);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" Universal time: %s\n", a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (i->rtc_time > 0) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen time_t rtc_sec;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen rtc_sec = (time_t)(i->rtc_time / USEC_PER_SEC);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen zero(tm);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(a);
79008bddf679a5e0900369950eb346c9fa687107Lennart Poettering printf(" RTC time: %s\n", a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen } else
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" RTC time: n/a\n");
f0213e3796b4dd66e546e2de4d677db319f9171bTom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen zero(tm);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert_se(strftime(a, sizeof(a), "%Z, %z", localtime_r(&sec, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" Timezone: %s (%s)\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " NTP enabled: %s\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen "NTP synchronized: %s\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " RTC in local TZ: %s\n",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen strna(i->timezone), a,
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
6666907869fb3bc7fe6a6025540db5b887c7a78bTom Gundersen yes_no(i->ntp_synced),
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen yes_no(i->rtc_local));
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen r = time_get_dst(sec, "/etc/localtime",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen &tc, &zc, &is_dstc,
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen &tn, &dn, &zn, &is_dstn);
ed9e361a8a798f9fee353b5c7e0e23308e0d329fTom Gundersen if (r < 0)
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" DST active: n/a\n");
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen else {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" DST active: %s\n", yes_no(is_dstc));
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen t = tc - 1;
ed9e361a8a798f9fee353b5c7e0e23308e0d329fTom Gundersen zero(tm);
86655331bc28887def7998d321b14ef8fccbeaf9Tom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen zero(tm);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(b);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" Last DST change: DST %s at\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " %s\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " %s\n",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen is_dstc ? "began" : "ended", a, b);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen t = tn - 1;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen zero(tm);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(a);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen zero(tm);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm)) > 0);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen char_array_0(b);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen printf(" Next DST change: DST %s (the clock jumps %s) at\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " %s\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " %s\n",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen is_dstn ? "begins" : "ends", jump_str(dn, s, sizeof(s)), a, b);
be19c5b5e0c0f78b8429b126936fa15856550a23David Herrmann
be19c5b5e0c0f78b8429b126936fa15856550a23David Herrmann free(zc);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen free(zn);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen }
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (i->rtc_local)
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen fputs("\n" ANSI_HIGHLIGHT_ON
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen "Warning: The RTC is configured to maintain time in the local timezone. This\n"
be19c5b5e0c0f78b8429b126936fa15856550a23David Herrmann " mode is not fully supported and will create various problems with time\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " zone changes and daylight saving adjustments. If at all possible use\n"
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen " RTC in UTC, by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen}
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic int get_timedate_property_bool(sd_bus *bus, const char *name, bool *target) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b45e4eb679ad0c9a77c4fe6e404c8842d4097fdbTom Gundersen int r, b;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert(name);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen r = sd_bus_get_property_trivial(
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen bus,
dbe81cbd2a93088236a2e4e41eeb33378940f7b9Martin Pitt "org.freedesktop.timedate1",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen "/org/freedesktop/timedate1",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen "org.freedesktop.timedate1",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen name,
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen &error,
e0ee46f29028e291eb67f435aff1b6202d75d9d6Lennart Poettering 'b', &b);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (r < 0) {
0bc70f1d9c5453ba614ec0ed041dc30b9cd52071Tom Gundersen log_error("Failed to get property: %s %s", name, bus_error_message(&error, -r));
0bc70f1d9c5453ba614ec0ed041dc30b9cd52071Tom Gundersen return r;
0bc70f1d9c5453ba614ec0ed041dc30b9cd52071Tom Gundersen }
0bc70f1d9c5453ba614ec0ed041dc30b9cd52071Tom Gundersen
0bc70f1d9c5453ba614ec0ed041dc30b9cd52071Tom Gundersen *target = b;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return 0;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen}
dbe81cbd2a93088236a2e4e41eeb33378940f7b9Martin Pitt
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic int get_timedate_property_usec(sd_bus *bus, const char *name, usec_t *target) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
dbe81cbd2a93088236a2e4e41eeb33378940f7b9Martin Pitt int r;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen assert(name);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen r = sd_bus_get_property_trivial(
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen bus,
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen "org.freedesktop.timedate1",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen "/org/freedesktop/timedate1",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen "org.freedesktop.timedate1",
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen name,
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen &error,
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen 't', target);
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen if (r < 0) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen log_error("Failed to get property: %s %s", name, bus_error_message(&error, -r));
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return r;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen }
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen return 0;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen}
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersenstatic int show_status(sd_bus *bus, char **args, unsigned n) {
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b22d8a00f48f3c5fc4510b4acd3e1a43e731e592Tom Gundersen 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;
}