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_adjust_system_clock = false;
42 static bool arg_no_pager = false;
43 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
44 static bool arg_ask_password = true;
45 static char *arg_host = NULL;
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)
64 typedef struct StatusInfo {
76 static const char *jump_str(int delta_minutes, char *s, size_t size) {
77 if (delta_minutes == 60)
78 return "one hour forward";
79 if (delta_minutes == -60)
80 return "one hour backwards";
81 if (delta_minutes < 0) {
82 snprintf(s, size, "%i minutes backwards", -delta_minutes);
85 if (delta_minutes > 0) {
86 snprintf(s, size, "%i minutes forward", delta_minutes);
92 static void print_status_info(StatusInfo *i) {
93 char a[FORMAT_TIMESTAMP_MAX];
94 char b[FORMAT_TIMESTAMP_MAX];
101 bool is_dstc, is_dstn;
106 /* Enforce the values of /etc/localtime */
108 fprintf(stderr, "Warning: ignoring the TZ variable, reading the system's timezone setting only.\n\n");
112 sec = (time_t) (i->time / USEC_PER_SEC);
115 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
117 printf(" Local time: %s\n", a);
120 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
122 printf(" Universal time: %s\n", a);
124 if (i->rtc_time > 0) {
127 rtc_sec = (time_t)(i->rtc_time / USEC_PER_SEC);
129 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", gmtime_r(&rtc_sec, &tm)) > 0);
131 printf(" RTC time: %s\n", a);
133 printf(" RTC time: n/a\n");
136 assert_se(strftime(a, sizeof(a), "%Z, %z", localtime_r(&sec, &tm)) > 0);
138 printf(" Timezone: %s (%s)\n"
140 "NTP synchronized: %s\n"
141 " RTC in local TZ: %s\n",
142 strna(i->timezone), a,
143 i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
144 yes_no(i->ntp_synced),
145 yes_no(i->rtc_local));
147 r = time_get_dst(sec, "/etc/localtime",
149 &tn, &dn, &zn, &is_dstn);
151 printf(" DST active: n/a\n");
153 printf(" DST active: %s\n", yes_no(is_dstc));
157 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
161 assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm)) > 0);
163 printf(" Last DST change: DST %s at\n"
166 is_dstc ? "began" : "ended", a, b);
170 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
174 assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm)) > 0);
176 printf(" Next DST change: DST %s (the clock jumps %s) at\n"
179 is_dstn ? "begins" : "ends", jump_str(dn, s, sizeof(s)), a, b);
186 fputs("\n" ANSI_HIGHLIGHT_ON
187 "Warning: The RTC is configured to maintain time in the local timezone. This\n"
188 " mode is not fully supported and will create various problems with time\n"
189 " zone changes and daylight saving adjustments. If at all possible use\n"
190 " RTC in UTC, by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout);
193 static int get_timedate_property_bool(sd_bus *bus, const char *name, bool *target) {
194 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
195 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
200 r = sd_bus_get_property(bus,
201 "org.freedesktop.timedate1",
202 "/org/freedesktop/timedate1",
203 "org.freedesktop.timedate1",
209 log_error("Failed to get property: %s %s", name, bus_error_message(&error, -r));
213 r = sd_bus_message_read(reply, "b", target);
215 log_error("Failed to parse reply.");
222 static int get_timedate_property_usec(sd_bus *bus, const char *name, usec_t *target) {
223 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
224 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
229 r = sd_bus_get_property(bus,
230 "org.freedesktop.timedate1",
231 "/org/freedesktop/timedate1",
232 "org.freedesktop.timedate1",
238 log_error("Failed to get property: %s %s", name, bus_error_message(&error, -r));
242 r = sd_bus_message_read(reply, "t", target);
244 log_error("Failed to parse reply.");
251 static int show_status(sd_bus *bus, char **args, unsigned n) {
252 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
253 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
255 StatusInfo info = {};
259 r = sd_bus_get_property(bus,
260 "org.freedesktop.timedate1",
261 "/org/freedesktop/timedate1",
262 "org.freedesktop.timedate1",
268 log_error("Failed to get property: Timezone %s", bus_error_message(&error, -r));
272 r = sd_bus_message_read(reply, "s", &info.timezone);
276 r = get_timedate_property_bool(bus, "LocalRTC", &info.rtc_local);
280 r = get_timedate_property_bool(bus, "NTP", &info.ntp_enabled);
284 r = get_timedate_property_bool(bus, "CanNTP", &info.ntp_capable);
288 r = get_timedate_property_bool(bus, "NTPSynchronized", &info.ntp_synced);
292 r = get_timedate_property_usec(bus, "TimeUSec", &info.time);
296 r = get_timedate_property_usec(bus, "RTCTimeUSec", &info.rtc_time);
300 print_status_info(&info);
304 static int set_time(sd_bus *bus, char **args, unsigned n) {
305 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
306 bool relative = false, interactive = arg_ask_password;
313 polkit_agent_open_if_enabled();
315 r = parse_timestamp(args[1], &t);
317 log_error("Failed to parse time specification: %s", args[1]);
321 r = sd_bus_call_method(bus,
322 "org.freedesktop.timedate1",
323 "/org/freedesktop/timedate1",
324 "org.freedesktop.timedate1",
328 "xbb", (int64_t)t, relative, interactive);
330 log_error("Failed to set time: %s", bus_error_message(&error, -r));
335 static int set_timezone(sd_bus *bus, char **args, unsigned n) {
336 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
337 bool interactive = arg_ask_password;
343 polkit_agent_open_if_enabled();
345 r = sd_bus_call_method(bus,
346 "org.freedesktop.timedate1",
347 "/org/freedesktop/timedate1",
348 "org.freedesktop.timedate1",
352 "sb", args[1], interactive);
354 log_error("Failed to set timezone: %s", bus_error_message(&error, -r));
359 static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
360 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
361 bool interactive = arg_ask_password, b, q;
367 polkit_agent_open_if_enabled();
369 r = parse_boolean(args[1]);
371 log_error("Failed to parse local RTC setting: %s", args[1]);
376 q = arg_adjust_system_clock;
378 r = sd_bus_call_method(bus,
379 "org.freedesktop.timedate1",
380 "/org/freedesktop/timedate1",
381 "org.freedesktop.timedate1",
385 "bbb", b, q, interactive);
387 log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
392 static int set_ntp(sd_bus *bus, char **args, unsigned n) {
393 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
394 bool interactive = arg_ask_password, b;
400 polkit_agent_open_if_enabled();
402 r = parse_boolean(args[1]);
404 log_error("Failed to parse NTP setting: %s", args[1]);
410 r = sd_bus_call_method(bus,
411 "org.freedesktop.timedate1",
412 "/org/freedesktop/timedate1",
413 "org.freedesktop.timedate1",
417 "bb", b, interactive);
419 log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
424 static int list_timezones(sd_bus *bus, char **args, unsigned n) {
425 _cleanup_fclose_ FILE *f = NULL;
426 _cleanup_strv_free_ char **zones = NULL;
432 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
434 log_error("Failed to open timezone database: %m");
439 char l[LINE_MAX], *p, **z, *w;
442 if (!fgets(l, sizeof(l), f)) {
446 log_error("Failed to read timezone database: %m");
452 if (isempty(p) || *p == '#')
456 /* Skip over country code */
457 p += strcspn(p, WHITESPACE);
458 p += strspn(p, WHITESPACE);
460 /* Skip over coordinates */
461 p += strcspn(p, WHITESPACE);
462 p += strspn(p, WHITESPACE);
464 /* Found timezone name */
465 k = strcspn(p, WHITESPACE);
473 z = realloc(zones, sizeof(char*) * (n_zones + 2));
480 zones[n_zones++] = w;
484 zones[n_zones] = NULL;
486 pager_open_if_enabled();
494 static int help(void) {
496 printf("%s [OPTIONS...] COMMAND ...\n\n"
497 "Query or change system time and date settings.\n\n"
498 " -h --help Show this help\n"
499 " --version Show package version\n"
500 " --adjust-system-clock\n"
501 " Adjust system clock when changing local RTC mode\n"
502 " --no-pager Do not pipe output into a pager\n"
503 " --no-ask-password Do not prompt for password\n"
504 " -H --host=[USER@]HOST Operate on remote host\n"
505 " -M --machine=CONTAINER Operate on local container\n\n"
507 " status Show current time settings\n"
508 " set-time TIME Set system time\n"
509 " set-timezone ZONE Set system timezone\n"
510 " list-timezones Show known timezones\n"
511 " set-local-rtc BOOL Control whether RTC is in local time\n"
512 " set-ntp BOOL Control whether NTP is enabled\n",
513 program_invocation_short_name);
518 static int parse_argv(int argc, char *argv[]) {
523 ARG_ADJUST_SYSTEM_CLOCK,
527 static const struct option options[] = {
528 { "help", no_argument, NULL, 'h' },
529 { "version", no_argument, NULL, ARG_VERSION },
530 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
531 { "host", required_argument, NULL, 'H' },
532 { "machine", required_argument, NULL, 'M' },
533 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
534 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
543 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
552 puts(PACKAGE_STRING);
553 puts(SYSTEMD_FEATURES);
557 arg_transport = BUS_TRANSPORT_REMOTE;
562 arg_transport = BUS_TRANSPORT_CONTAINER;
566 case ARG_NO_ASK_PASSWORD:
567 arg_ask_password = false;
570 case ARG_ADJUST_SYSTEM_CLOCK:
571 arg_adjust_system_clock = true;
582 log_error("Unknown option code %c", c);
590 static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
592 static const struct {
600 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
602 { "status", LESS, 1, show_status },
603 { "set-time", EQUAL, 2, set_time },
604 { "set-timezone", EQUAL, 2, set_timezone },
605 { "list-timezones", EQUAL, 1, list_timezones },
606 { "set-local-rtc", EQUAL, 2, set_local_rtc },
607 { "set-ntp", EQUAL, 2, set_ntp, },
616 left = argc - optind;
619 /* Special rule: no arguments means "status" */
622 if (streq(argv[optind], "help")) {
627 for (i = 0; i < ELEMENTSOF(verbs); i++)
628 if (streq(argv[optind], verbs[i].verb))
631 if (i >= ELEMENTSOF(verbs)) {
632 log_error("Unknown operation %s", argv[optind]);
637 switch (verbs[i].argc_cmp) {
640 if (left != verbs[i].argc) {
641 log_error("Invalid number of arguments.");
648 if (left < verbs[i].argc) {
649 log_error("Too few arguments.");
656 if (left > verbs[i].argc) {
657 log_error("Too many arguments.");
664 assert_not_reached("Unknown comparison operator.");
667 return verbs[i].dispatch(bus, argv + optind, left);
670 int main(int argc, char *argv[]) {
671 int r, ret = EXIT_FAILURE;
672 _cleanup_bus_unref_ sd_bus *bus = NULL;
674 setlocale(LC_ALL, "");
675 log_parse_environment();
678 r = parse_argv(argc, argv);
686 r = bus_open_transport(arg_transport, arg_host, false, &bus);
688 log_error("Failed to create bus connection: %s", strerror(-r));
693 r = timedatectl_main(bus, argc, argv);
694 ret = r < 0 ? EXIT_FAILURE : r;