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)
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(const 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", 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 show_status(sd_bus *bus, char **args, unsigned n) {
194 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
195 StatusInfo info = {};
196 static const struct bus_properties_map map[] = {
197 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
198 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
199 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) },
200 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
201 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
202 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
203 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
210 r = bus_map_all_properties(bus,
211 "org.freedesktop.timedate1",
212 "/org/freedesktop/timedate1",
218 print_status_info(&info);
225 static int set_time(sd_bus *bus, char **args, unsigned n) {
226 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
227 bool relative = false, interactive = arg_ask_password;
234 polkit_agent_open_if_enabled();
236 r = parse_timestamp(args[1], &t);
238 log_error("Failed to parse time specification: %s", args[1]);
242 r = sd_bus_call_method(bus,
243 "org.freedesktop.timedate1",
244 "/org/freedesktop/timedate1",
245 "org.freedesktop.timedate1",
249 "xbb", (int64_t)t, relative, interactive);
251 log_error("Failed to set time: %s", bus_error_message(&error, -r));
256 static int set_timezone(sd_bus *bus, char **args, unsigned n) {
257 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
263 polkit_agent_open_if_enabled();
265 r = sd_bus_call_method(bus,
266 "org.freedesktop.timedate1",
267 "/org/freedesktop/timedate1",
268 "org.freedesktop.timedate1",
272 "sb", args[1], arg_ask_password);
274 log_error("Failed to set timezone: %s", bus_error_message(&error, -r));
279 static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
280 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
286 polkit_agent_open_if_enabled();
288 b = parse_boolean(args[1]);
290 log_error("Failed to parse local RTC setting: %s", args[1]);
294 r = sd_bus_call_method(bus,
295 "org.freedesktop.timedate1",
296 "/org/freedesktop/timedate1",
297 "org.freedesktop.timedate1",
301 "bbb", b, arg_adjust_system_clock, arg_ask_password);
303 log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
308 static int set_ntp(sd_bus *bus, char **args, unsigned n) {
309 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
315 polkit_agent_open_if_enabled();
317 b = parse_boolean(args[1]);
319 log_error("Failed to parse NTP setting: %s", args[1]);
323 r = sd_bus_call_method(bus,
324 "org.freedesktop.timedate1",
325 "/org/freedesktop/timedate1",
326 "org.freedesktop.timedate1",
330 "bb", b, arg_ask_password);
332 log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
337 static int list_timezones(sd_bus *bus, char **args, unsigned n) {
338 _cleanup_fclose_ FILE *f = NULL;
339 _cleanup_strv_free_ char **zones = NULL;
345 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
347 log_error("Failed to open timezone database: %m");
352 char l[LINE_MAX], *p, **z, *w;
355 if (!fgets(l, sizeof(l), f)) {
359 log_error("Failed to read timezone database: %m");
365 if (isempty(p) || *p == '#')
369 /* Skip over country code */
370 p += strcspn(p, WHITESPACE);
371 p += strspn(p, WHITESPACE);
373 /* Skip over coordinates */
374 p += strcspn(p, WHITESPACE);
375 p += strspn(p, WHITESPACE);
377 /* Found timezone name */
378 k = strcspn(p, WHITESPACE);
386 z = realloc(zones, sizeof(char*) * (n_zones + 2));
393 zones[n_zones++] = w;
397 zones[n_zones] = NULL;
399 pager_open_if_enabled();
407 static int help(void) {
409 printf("%s [OPTIONS...] COMMAND ...\n\n"
410 "Query or change system time and date settings.\n\n"
411 " -h --help Show this help\n"
412 " --version Show package version\n"
413 " --adjust-system-clock\n"
414 " Adjust system clock when changing local RTC mode\n"
415 " --no-pager Do not pipe output into a pager\n"
416 " --no-ask-password Do not prompt for password\n"
417 " -H --host=[USER@]HOST Operate on remote host\n"
418 " -M --machine=CONTAINER Operate on local container\n\n"
420 " status Show current time settings\n"
421 " set-time TIME Set system time\n"
422 " set-timezone ZONE Set system timezone\n"
423 " list-timezones Show known timezones\n"
424 " set-local-rtc BOOL Control whether RTC is in local time\n"
425 " set-ntp BOOL Control whether NTP is enabled\n",
426 program_invocation_short_name);
431 static int parse_argv(int argc, char *argv[]) {
436 ARG_ADJUST_SYSTEM_CLOCK,
440 static const struct option options[] = {
441 { "help", no_argument, NULL, 'h' },
442 { "version", no_argument, NULL, ARG_VERSION },
443 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
444 { "host", required_argument, NULL, 'H' },
445 { "machine", required_argument, NULL, 'M' },
446 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
447 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
456 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
464 puts(PACKAGE_STRING);
465 puts(SYSTEMD_FEATURES);
469 arg_transport = BUS_TRANSPORT_REMOTE;
474 arg_transport = BUS_TRANSPORT_CONTAINER;
478 case ARG_NO_ASK_PASSWORD:
479 arg_ask_password = false;
482 case ARG_ADJUST_SYSTEM_CLOCK:
483 arg_adjust_system_clock = true;
494 assert_not_reached("Unhandled option");
501 static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
503 static const struct {
511 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
513 { "status", LESS, 1, show_status },
514 { "set-time", EQUAL, 2, set_time },
515 { "set-timezone", EQUAL, 2, set_timezone },
516 { "list-timezones", EQUAL, 1, list_timezones },
517 { "set-local-rtc", EQUAL, 2, set_local_rtc },
518 { "set-ntp", EQUAL, 2, set_ntp, },
527 left = argc - optind;
530 /* Special rule: no arguments means "status" */
533 if (streq(argv[optind], "help")) {
538 for (i = 0; i < ELEMENTSOF(verbs); i++)
539 if (streq(argv[optind], verbs[i].verb))
542 if (i >= ELEMENTSOF(verbs)) {
543 log_error("Unknown operation %s", argv[optind]);
548 switch (verbs[i].argc_cmp) {
551 if (left != verbs[i].argc) {
552 log_error("Invalid number of arguments.");
559 if (left < verbs[i].argc) {
560 log_error("Too few arguments.");
567 if (left > verbs[i].argc) {
568 log_error("Too many arguments.");
575 assert_not_reached("Unknown comparison operator.");
578 return verbs[i].dispatch(bus, argv + optind, left);
581 int main(int argc, char *argv[]) {
582 _cleanup_bus_unref_ sd_bus *bus = NULL;
585 setlocale(LC_ALL, "");
586 log_parse_environment();
589 r = parse_argv(argc, argv);
593 r = bus_open_transport(arg_transport, arg_host, false, &bus);
595 log_error("Failed to create bus connection: %s", strerror(-r));
599 r = timedatectl_main(bus, argc, argv);
604 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;