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 StatusInfo info = {};
198 static const struct bus_properties_map map[] = {
199 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
200 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
201 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) },
202 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
203 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
204 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
205 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
212 r = bus_map_all_properties(bus,
213 "org.freedesktop.timedate1",
214 "/org/freedesktop/timedate1",
220 print_status_info(&info);
227 static int set_time(sd_bus *bus, char **args, unsigned n) {
228 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
229 bool relative = false, interactive = arg_ask_password;
236 polkit_agent_open_if_enabled();
238 r = parse_timestamp(args[1], &t);
240 log_error("Failed to parse time specification: %s", args[1]);
244 r = sd_bus_call_method(bus,
245 "org.freedesktop.timedate1",
246 "/org/freedesktop/timedate1",
247 "org.freedesktop.timedate1",
251 "xbb", (int64_t)t, relative, interactive);
253 log_error("Failed to set time: %s", bus_error_message(&error, -r));
258 static int set_timezone(sd_bus *bus, char **args, unsigned n) {
259 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
265 polkit_agent_open_if_enabled();
267 r = sd_bus_call_method(bus,
268 "org.freedesktop.timedate1",
269 "/org/freedesktop/timedate1",
270 "org.freedesktop.timedate1",
274 "sb", args[1], arg_ask_password);
276 log_error("Failed to set timezone: %s", bus_error_message(&error, -r));
281 static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
282 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
288 polkit_agent_open_if_enabled();
290 b = parse_boolean(args[1]);
292 log_error("Failed to parse local RTC setting: %s", args[1]);
296 r = sd_bus_call_method(bus,
297 "org.freedesktop.timedate1",
298 "/org/freedesktop/timedate1",
299 "org.freedesktop.timedate1",
303 "bbb", b, arg_adjust_system_clock, arg_ask_password);
305 log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
310 static int set_ntp(sd_bus *bus, char **args, unsigned n) {
311 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
317 polkit_agent_open_if_enabled();
319 b = parse_boolean(args[1]);
321 log_error("Failed to parse NTP setting: %s", args[1]);
325 r = sd_bus_call_method(bus,
326 "org.freedesktop.timedate1",
327 "/org/freedesktop/timedate1",
328 "org.freedesktop.timedate1",
332 "bb", b, arg_ask_password);
334 log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
339 static int list_timezones(sd_bus *bus, char **args, unsigned n) {
340 _cleanup_fclose_ FILE *f = NULL;
341 _cleanup_strv_free_ char **zones = NULL;
347 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
349 log_error("Failed to open timezone database: %m");
354 char l[LINE_MAX], *p, **z, *w;
357 if (!fgets(l, sizeof(l), f)) {
361 log_error("Failed to read timezone database: %m");
367 if (isempty(p) || *p == '#')
371 /* Skip over country code */
372 p += strcspn(p, WHITESPACE);
373 p += strspn(p, WHITESPACE);
375 /* Skip over coordinates */
376 p += strcspn(p, WHITESPACE);
377 p += strspn(p, WHITESPACE);
379 /* Found timezone name */
380 k = strcspn(p, WHITESPACE);
388 z = realloc(zones, sizeof(char*) * (n_zones + 2));
395 zones[n_zones++] = w;
399 zones[n_zones] = NULL;
401 pager_open_if_enabled();
409 static int help(void) {
411 printf("%s [OPTIONS...] COMMAND ...\n\n"
412 "Query or change system time and date settings.\n\n"
413 " -h --help Show this help\n"
414 " --version Show package version\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"
419 " --adjust-system-clock\n"
420 " Adjust system clock when changing local RTC mode\n\n"
422 " status Show current time settings\n"
423 " set-time TIME Set system time\n"
424 " set-timezone ZONE Set system timezone\n"
425 " list-timezones Show known timezones\n"
426 " set-local-rtc BOOL Control whether RTC is in local time\n"
427 " set-ntp BOOL Control whether NTP is enabled\n",
428 program_invocation_short_name);
433 static int parse_argv(int argc, char *argv[]) {
438 ARG_ADJUST_SYSTEM_CLOCK,
442 static const struct option options[] = {
443 { "help", no_argument, NULL, 'h' },
444 { "version", no_argument, NULL, ARG_VERSION },
445 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
446 { "host", required_argument, NULL, 'H' },
447 { "machine", required_argument, NULL, 'M' },
448 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
449 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
458 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
466 puts(PACKAGE_STRING);
467 puts(SYSTEMD_FEATURES);
471 arg_transport = BUS_TRANSPORT_REMOTE;
476 arg_transport = BUS_TRANSPORT_CONTAINER;
480 case ARG_NO_ASK_PASSWORD:
481 arg_ask_password = false;
484 case ARG_ADJUST_SYSTEM_CLOCK:
485 arg_adjust_system_clock = true;
496 assert_not_reached("Unhandled option");
503 static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
505 static const struct {
513 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
515 { "status", LESS, 1, show_status },
516 { "set-time", EQUAL, 2, set_time },
517 { "set-timezone", EQUAL, 2, set_timezone },
518 { "list-timezones", EQUAL, 1, list_timezones },
519 { "set-local-rtc", EQUAL, 2, set_local_rtc },
520 { "set-ntp", EQUAL, 2, set_ntp, },
529 left = argc - optind;
532 /* Special rule: no arguments means "status" */
535 if (streq(argv[optind], "help")) {
540 for (i = 0; i < ELEMENTSOF(verbs); i++)
541 if (streq(argv[optind], verbs[i].verb))
544 if (i >= ELEMENTSOF(verbs)) {
545 log_error("Unknown operation %s", argv[optind]);
550 switch (verbs[i].argc_cmp) {
553 if (left != verbs[i].argc) {
554 log_error("Invalid number of arguments.");
561 if (left < verbs[i].argc) {
562 log_error("Too few arguments.");
569 if (left > verbs[i].argc) {
570 log_error("Too many arguments.");
577 assert_not_reached("Unknown comparison operator.");
580 return verbs[i].dispatch(bus, argv + optind, left);
583 int main(int argc, char *argv[]) {
584 _cleanup_bus_unref_ sd_bus *bus = NULL;
587 setlocale(LC_ALL, "");
588 log_parse_environment();
591 r = parse_argv(argc, argv);
595 r = bus_open_transport(arg_transport, arg_host, false, &bus);
597 log_error("Failed to create bus connection: %s", strerror(-r));
601 r = timedatectl_main(bus, argc, argv);
606 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;