1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <sys/timex.h>
29 #include "dbus-common.h"
31 #include "spawn-polkit-agent.h"
37 static bool arg_fix_system = false;
38 static bool arg_no_pager = false;
39 static enum transport {
43 } arg_transport = TRANSPORT_NORMAL;
44 static bool arg_ask_password = true;
45 static const 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 */
59 if (!arg_ask_password)
65 typedef struct StatusInfo {
71 static bool ntp_synced(void) {
75 if (adjtimex(&txc) < 0)
78 if (txc.status & STA_UNSYNC)
84 static void print_status_info(StatusInfo *i) {
86 char b[FORMAT_TIMESTAMP_MAX];
93 n = now(CLOCK_REALTIME);
94 sec = (time_t) (n / USEC_PER_SEC);
97 assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
99 printf(" Local time: %s\n", b);
102 assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
104 printf(" Universal time: %s\n", b);
107 r = hwclock_get_time(&tm);
109 /* Calculcate the week-day */
112 assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S", &tm) > 0);
114 printf(" RTC time: %s\n", b);
117 printf(" Timezone: %s\n"
119 "NTP synchronized: %s\n"
120 " RTC in local TZ: %s\n",
123 yes_no(ntp_synced()),
124 yes_no(i->local_rtc));
127 fputs("\n" ANSI_HIGHLIGHT_ON
128 "Warning: The RTC is configured to maintain time in the local time zone. This\n"
129 " mode is not fully supported and will create various problems with time\n"
130 " zone changes and daylight saving adjustments. If at all possible use\n"
131 " RTC in UTC, by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout);
134 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
138 switch (dbus_message_iter_get_arg_type(iter)) {
140 case DBUS_TYPE_STRING: {
143 dbus_message_iter_get_basic(iter, &s);
145 if (streq(name, "Timezone"))
151 case DBUS_TYPE_BOOLEAN: {
154 dbus_message_iter_get_basic(iter, &b);
155 if (streq(name, "LocalRTC"))
157 else if (streq(name, "NTP"))
165 static int show_status(DBusConnection *bus, char **args, unsigned n) {
166 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
167 const char *interface = "";
169 DBusMessageIter iter, sub, sub2, sub3;
174 r = bus_method_call_with_reply(
176 "org.freedesktop.timedate1",
177 "/org/freedesktop/timedate1",
178 "org.freedesktop.DBus.Properties",
182 DBUS_TYPE_STRING, &interface,
187 if (!dbus_message_iter_init(reply, &iter) ||
188 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
189 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
190 log_error("Failed to parse reply.");
195 dbus_message_iter_recurse(&iter, &sub);
197 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
200 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
201 log_error("Failed to parse reply.");
205 dbus_message_iter_recurse(&sub, &sub2);
207 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
208 log_error("Failed to parse reply.");
212 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
213 log_error("Failed to parse reply.");
217 dbus_message_iter_recurse(&sub2, &sub3);
219 r = status_property(name, &sub3, &info);
221 log_error("Failed to parse reply.");
225 dbus_message_iter_next(&sub);
228 print_status_info(&info);
232 static int set_time(DBusConnection *bus, char **args, unsigned n) {
233 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
234 dbus_bool_t relative = false, interactive = true;
242 polkit_agent_open_if_enabled();
244 r = parse_timestamp(args[1], &t);
246 log_error("Failed to parse time specification: %s", args[1]);
250 u = (dbus_uint64_t) t;
252 return bus_method_call_with_reply(
254 "org.freedesktop.timedate1",
255 "/org/freedesktop/timedate1",
256 "org.freedesktop.timedate1",
261 DBUS_TYPE_BOOLEAN, &relative,
262 DBUS_TYPE_BOOLEAN, &interactive,
266 static int set_timezone(DBusConnection *bus, char **args, unsigned n) {
267 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
268 dbus_bool_t interactive = true;
273 polkit_agent_open_if_enabled();
275 return bus_method_call_with_reply(
277 "org.freedesktop.timedate1",
278 "/org/freedesktop/timedate1",
279 "org.freedesktop.timedate1",
283 DBUS_TYPE_STRING, &args[1],
284 DBUS_TYPE_BOOLEAN, &interactive,
288 static int set_local_rtc(DBusConnection *bus, char **args, unsigned n) {
289 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
290 dbus_bool_t interactive = true, b, q;
296 polkit_agent_open_if_enabled();
298 r = parse_boolean(args[1]);
300 log_error("Failed to parse local RTC setting: %s", args[1]);
307 return bus_method_call_with_reply(
309 "org.freedesktop.timedate1",
310 "/org/freedesktop/timedate1",
311 "org.freedesktop.timedate1",
315 DBUS_TYPE_BOOLEAN, &b,
316 DBUS_TYPE_BOOLEAN, &q,
317 DBUS_TYPE_BOOLEAN, &interactive,
321 static int set_ntp(DBusConnection *bus, char **args, unsigned n) {
322 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
323 dbus_bool_t interactive = true, b;
329 polkit_agent_open_if_enabled();
331 r = parse_boolean(args[1]);
333 log_error("Failed to parse NTP setting: %s", args[1]);
339 return bus_method_call_with_reply(
341 "org.freedesktop.timedate1",
342 "/org/freedesktop/timedate1",
343 "org.freedesktop.timedate1",
347 DBUS_TYPE_BOOLEAN, &b,
348 DBUS_TYPE_BOOLEAN, &interactive,
352 static int zone_compare(const void *_a, const void *_b) {
353 const char **a = (const char**) _a, **b = (const char**) _b;
355 return strcmp(*a, *b);
358 static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
359 _cleanup_fclose_ FILE *f = NULL;
360 _cleanup_strv_free_ char **zones = NULL;
367 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
369 log_error("Failed to open timezone database: %m");
374 char l[LINE_MAX], *p, **z, *w;
377 if (!fgets(l, sizeof(l), f)) {
381 log_error("Failed to read timezone database: %m");
387 if (isempty(p) || *p == '#')
391 /* Skip over country code */
392 p += strcspn(p, WHITESPACE);
393 p += strspn(p, WHITESPACE);
395 /* Skip over coordinates */
396 p += strcspn(p, WHITESPACE);
397 p += strspn(p, WHITESPACE);
399 /* Found timezone name */
400 k = strcspn(p, WHITESPACE);
408 z = realloc(zones, sizeof(char*) * (n_zones + 2));
415 zones[n_zones++] = w;
421 qsort(zones, n_zones, sizeof(char*), zone_compare);
423 pager_open_if_enabled();
425 STRV_FOREACH(i, zones)
431 static int help(void) {
433 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
434 "Query or control system time and date settings.\n\n"
435 " -h --help Show this help\n"
436 " --version Show package version\n"
437 " --fix-system Adjust system clock when changing local RTC mode\n"
438 " --no-pager Do not pipe output into a pager\n"
439 " --no-ask-password Do not prompt for password\n"
440 " -H --host=[USER@]HOST Operate on remote host\n\n"
442 " status Show current time settings\n"
443 " set-time [TIME] Set system time\n"
444 " set-timezone [ZONE] Set system timezone\n"
445 " list-timezones Show known timezones\n"
446 " set-local-rtc [BOOL] Control whether RTC is in local time\n"
447 " set-ntp [BOOL] Control whether NTP is enabled\n",
448 program_invocation_short_name);
453 static int parse_argv(int argc, char *argv[]) {
462 static const struct option options[] = {
463 { "help", no_argument, NULL, 'h' },
464 { "version", no_argument, NULL, ARG_VERSION },
465 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
466 { "host", required_argument, NULL, 'H' },
467 { "privileged", no_argument, NULL, 'P' },
468 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
469 { "fix-system", no_argument, NULL, ARG_FIX_SYSTEM },
478 while ((c = getopt_long(argc, argv, "+hp:as:H:P", options, NULL)) >= 0) {
487 puts(PACKAGE_STRING);
489 puts(SYSTEMD_FEATURES);
493 arg_transport = TRANSPORT_POLKIT;
497 arg_transport = TRANSPORT_SSH;
502 arg_fix_system = true;
513 log_error("Unknown option code %c", c);
521 static int timedatectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
523 static const struct {
531 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
533 { "status", LESS, 1, show_status },
534 { "set-time", EQUAL, 2, set_time },
535 { "set-timezone", EQUAL, 2, set_timezone },
536 { "list-timezones", EQUAL, 1, list_timezones },
537 { "set-local-rtc", EQUAL, 2, set_local_rtc },
538 { "set-ntp", EQUAL, 2, set_ntp, },
548 left = argc - optind;
551 /* Special rule: no arguments means "status" */
554 if (streq(argv[optind], "help")) {
559 for (i = 0; i < ELEMENTSOF(verbs); i++)
560 if (streq(argv[optind], verbs[i].verb))
563 if (i >= ELEMENTSOF(verbs)) {
564 log_error("Unknown operation %s", argv[optind]);
569 switch (verbs[i].argc_cmp) {
572 if (left != verbs[i].argc) {
573 log_error("Invalid number of arguments.");
580 if (left < verbs[i].argc) {
581 log_error("Too few arguments.");
588 if (left > verbs[i].argc) {
589 log_error("Too many arguments.");
596 assert_not_reached("Unknown comparison operator.");
600 log_error("Failed to get D-Bus connection: %s", error->message);
604 return verbs[i].dispatch(bus, argv + optind, left);
607 int main(int argc, char *argv[]) {
608 int r, retval = EXIT_FAILURE;
609 DBusConnection *bus = NULL;
612 dbus_error_init(&error);
614 log_parse_environment();
617 r = parse_argv(argc, argv);
621 retval = EXIT_SUCCESS;
625 if (arg_transport == TRANSPORT_NORMAL)
626 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
627 else if (arg_transport == TRANSPORT_POLKIT)
628 bus_connect_system_polkit(&bus, &error);
629 else if (arg_transport == TRANSPORT_SSH)
630 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
632 assert_not_reached("Uh, invalid transport...");
634 r = timedatectl_main(bus, argc, argv, &error);
635 retval = r < 0 ? EXIT_FAILURE : r;
639 dbus_connection_flush(bus);
640 dbus_connection_close(bus);
641 dbus_connection_unref(bus);
644 dbus_error_free(&error);