1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
7 Copyright 2013 Kay Sievers
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include <sys/timex.h>
33 #include "bus-error.h"
35 #include "spawn-polkit-agent.h"
41 static bool arg_no_pager = false;
42 static bool arg_ask_password = true;
43 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
44 static char *arg_host = NULL;
45 static bool arg_adjust_system_clock = false;
47 static void pager_open_if_enabled(void) {
55 static void polkit_agent_open_if_enabled(void) {
57 /* Open the polkit agent as a child process if necessary */
58 if (!arg_ask_password)
61 if (arg_transport != BUS_TRANSPORT_LOCAL)
67 typedef struct StatusInfo {
79 static const char *jump_str(int delta_minutes, char *s, size_t size) {
80 if (delta_minutes == 60)
81 return "one hour forward";
82 if (delta_minutes == -60)
83 return "one hour backwards";
84 if (delta_minutes < 0) {
85 snprintf(s, size, "%i minutes backwards", -delta_minutes);
88 if (delta_minutes > 0) {
89 snprintf(s, size, "%i minutes forward", delta_minutes);
95 static void print_status_info(const StatusInfo *i) {
96 char a[FORMAT_TIMESTAMP_MAX];
97 char b[FORMAT_TIMESTAMP_MAX];
104 bool is_dstc, is_dstn;
109 /* Enforce the values of /etc/localtime */
111 fprintf(stderr, "Warning: ignoring the TZ variable, reading the system's timezone setting only.\n\n");
115 sec = (time_t) (i->time / USEC_PER_SEC);
118 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
120 printf(" Local time: %s\n", a);
123 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
125 printf(" Universal time: %s\n", a);
127 if (i->rtc_time > 0) {
130 rtc_sec = (time_t)(i->rtc_time / USEC_PER_SEC);
132 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)) > 0);
134 printf(" RTC time: %s\n", a);
136 printf(" RTC time: n/a\n");
139 assert_se(strftime(a, sizeof(a), "%Z, %z", localtime_r(&sec, &tm)) > 0);
141 printf(" Timezone: %s (%s)\n"
143 "NTP synchronized: %s\n"
144 " RTC in local TZ: %s\n",
145 strna(i->timezone), a,
146 i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
147 yes_no(i->ntp_synced),
148 yes_no(i->rtc_local));
150 r = time_get_dst(sec, "/etc/localtime",
152 &tn, &dn, &zn, &is_dstn);
154 printf(" DST active: n/a\n");
156 printf(" DST active: %s\n", yes_no(is_dstc));
160 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
164 assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm)) > 0);
166 printf(" Last DST change: DST %s at\n"
169 is_dstc ? "began" : "ended", a, b);
173 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
177 assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm)) > 0);
179 printf(" Next DST change: DST %s (the clock jumps %s) at\n"
182 is_dstn ? "begins" : "ends", jump_str(dn, s, sizeof(s)), a, b);
189 fputs("\n" ANSI_HIGHLIGHT_ON
190 "Warning: The RTC is configured to maintain time in the local timezone. This\n"
191 " mode is not fully supported and will create various problems with time\n"
192 " zone changes and daylight saving adjustments. If at all possible use\n"
193 " RTC in UTC, by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout);
196 static int show_status(sd_bus *bus, char **args, unsigned n) {
197 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
198 StatusInfo info = {};
199 static const struct bus_properties_map map[] = {
200 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
201 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
202 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) },
203 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
204 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
205 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
206 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
213 r = bus_map_all_properties(bus,
214 "org.freedesktop.timedate1",
215 "/org/freedesktop/timedate1",
221 print_status_info(&info);
228 static int set_time(sd_bus *bus, char **args, unsigned n) {
229 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
230 bool relative = false, interactive = arg_ask_password;
237 polkit_agent_open_if_enabled();
239 r = parse_timestamp(args[1], &t);
241 log_error("Failed to parse time specification: %s", args[1]);
245 r = sd_bus_call_method(bus,
246 "org.freedesktop.timedate1",
247 "/org/freedesktop/timedate1",
248 "org.freedesktop.timedate1",
252 "xbb", (int64_t)t, relative, interactive);
254 log_error("Failed to set time: %s", bus_error_message(&error, -r));
259 static int set_timezone(sd_bus *bus, char **args, unsigned n) {
260 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
266 polkit_agent_open_if_enabled();
268 r = sd_bus_call_method(bus,
269 "org.freedesktop.timedate1",
270 "/org/freedesktop/timedate1",
271 "org.freedesktop.timedate1",
275 "sb", args[1], arg_ask_password);
277 log_error("Failed to set timezone: %s", bus_error_message(&error, -r));
282 static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
283 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
289 polkit_agent_open_if_enabled();
291 b = parse_boolean(args[1]);
293 log_error("Failed to parse local RTC setting: %s", args[1]);
297 r = sd_bus_call_method(bus,
298 "org.freedesktop.timedate1",
299 "/org/freedesktop/timedate1",
300 "org.freedesktop.timedate1",
304 "bbb", b, arg_adjust_system_clock, arg_ask_password);
306 log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
311 static int set_ntp(sd_bus *bus, char **args, unsigned n) {
312 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
318 polkit_agent_open_if_enabled();
320 b = parse_boolean(args[1]);
322 log_error("Failed to parse NTP setting: %s", args[1]);
326 r = sd_bus_call_method(bus,
327 "org.freedesktop.timedate1",
328 "/org/freedesktop/timedate1",
329 "org.freedesktop.timedate1",
333 "bb", b, arg_ask_password);
335 log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
340 static int list_timezones(sd_bus *bus, char **args, unsigned n) {
341 _cleanup_fclose_ FILE *f = NULL;
342 _cleanup_strv_free_ char **zones = NULL;
348 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
350 log_error("Failed to open timezone database: %m");
355 char l[LINE_MAX], *p, **z, *w;
358 if (!fgets(l, sizeof(l), f)) {
362 log_error("Failed to read timezone database: %m");
368 if (isempty(p) || *p == '#')
372 /* Skip over country code */
373 p += strcspn(p, WHITESPACE);
374 p += strspn(p, WHITESPACE);
376 /* Skip over coordinates */
377 p += strcspn(p, WHITESPACE);
378 p += strspn(p, WHITESPACE);
380 /* Found timezone name */
381 k = strcspn(p, WHITESPACE);
389 z = realloc(zones, sizeof(char*) * (n_zones + 2));
396 zones[n_zones++] = w;
400 zones[n_zones] = NULL;
402 pager_open_if_enabled();
410 static int help(void) {
412 printf("%s [OPTIONS...] COMMAND ...\n\n"
413 "Query or change system time and date settings.\n\n"
414 " -h --help Show this help\n"
415 " --version Show package version\n"
416 " --adjust-system-clock\n"
417 " Adjust system clock when changing local RTC mode\n"
418 " --no-pager Do not pipe output into a pager\n"
419 " --no-ask-password Do not prompt for password\n"
420 " -H --host=[USER@]HOST Operate on remote host\n"
421 " -M --machine=CONTAINER Operate on local container\n\n"
423 " status Show current time settings\n"
424 " set-time TIME Set system time\n"
425 " set-timezone ZONE Set system timezone\n"
426 " list-timezones Show known timezones\n"
427 " set-local-rtc BOOL Control whether RTC is in local time\n"
428 " set-ntp BOOL Control whether NTP is enabled\n",
429 program_invocation_short_name);
434 static int parse_argv(int argc, char *argv[]) {
439 ARG_ADJUST_SYSTEM_CLOCK,
443 static const struct option options[] = {
444 { "help", no_argument, NULL, 'h' },
445 { "version", no_argument, NULL, ARG_VERSION },
446 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
447 { "host", required_argument, NULL, 'H' },
448 { "machine", required_argument, NULL, 'M' },
449 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
450 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
459 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
467 puts(PACKAGE_STRING);
468 puts(SYSTEMD_FEATURES);
472 arg_transport = BUS_TRANSPORT_REMOTE;
477 arg_transport = BUS_TRANSPORT_CONTAINER;
481 case ARG_NO_ASK_PASSWORD:
482 arg_ask_password = false;
485 case ARG_ADJUST_SYSTEM_CLOCK:
486 arg_adjust_system_clock = true;
497 assert_not_reached("Unhandled option");
504 static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
506 static const struct {
514 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
516 { "status", LESS, 1, show_status },
517 { "set-time", EQUAL, 2, set_time },
518 { "set-timezone", EQUAL, 2, set_timezone },
519 { "list-timezones", EQUAL, 1, list_timezones },
520 { "set-local-rtc", EQUAL, 2, set_local_rtc },
521 { "set-ntp", EQUAL, 2, set_ntp, },
530 left = argc - optind;
533 /* Special rule: no arguments means "status" */
536 if (streq(argv[optind], "help")) {
541 for (i = 0; i < ELEMENTSOF(verbs); i++)
542 if (streq(argv[optind], verbs[i].verb))
545 if (i >= ELEMENTSOF(verbs)) {
546 log_error("Unknown operation %s", argv[optind]);
551 switch (verbs[i].argc_cmp) {
554 if (left != verbs[i].argc) {
555 log_error("Invalid number of arguments.");
562 if (left < verbs[i].argc) {
563 log_error("Too few arguments.");
570 if (left > verbs[i].argc) {
571 log_error("Too many arguments.");
578 assert_not_reached("Unknown comparison operator.");
581 return verbs[i].dispatch(bus, argv + optind, left);
584 int main(int argc, char *argv[]) {
585 _cleanup_bus_unref_ sd_bus *bus = NULL;
588 setlocale(LC_ALL, "");
589 log_parse_environment();
592 r = parse_argv(argc, argv);
596 r = bus_open_transport(arg_transport, arg_host, false, &bus);
598 log_error("Failed to create bus connection: %s", strerror(-r));
602 r = timedatectl_main(bus, argc, argv);
607 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;