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) {
77 if (adjtimex(&txc) < 0)
80 if (txc.status & STA_UNSYNC)
86 static void print_status_info(StatusInfo *i) {
88 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.");
194 dbus_message_iter_recurse(&iter, &sub);
196 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
199 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
200 log_error("Failed to parse reply.");
204 dbus_message_iter_recurse(&sub, &sub2);
206 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
207 log_error("Failed to parse reply.");
211 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
212 log_error("Failed to parse reply.");
216 dbus_message_iter_recurse(&sub2, &sub3);
218 r = status_property(name, &sub3, &info);
220 log_error("Failed to parse reply.");
224 dbus_message_iter_next(&sub);
227 print_status_info(&info);
231 static int set_time(DBusConnection *bus, char **args, unsigned n) {
232 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
233 dbus_bool_t relative = false, interactive = true;
241 polkit_agent_open_if_enabled();
243 r = parse_timestamp(args[1], &t);
245 log_error("Failed to parse time specification: %s", args[1]);
249 u = (dbus_uint64_t) t;
251 return bus_method_call_with_reply(
253 "org.freedesktop.timedate1",
254 "/org/freedesktop/timedate1",
255 "org.freedesktop.timedate1",
260 DBUS_TYPE_BOOLEAN, &relative,
261 DBUS_TYPE_BOOLEAN, &interactive,
265 static int set_timezone(DBusConnection *bus, char **args, unsigned n) {
266 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
267 dbus_bool_t interactive = true;
272 polkit_agent_open_if_enabled();
274 return bus_method_call_with_reply(
276 "org.freedesktop.timedate1",
277 "/org/freedesktop/timedate1",
278 "org.freedesktop.timedate1",
282 DBUS_TYPE_STRING, &args[1],
283 DBUS_TYPE_BOOLEAN, &interactive,
287 static int set_local_rtc(DBusConnection *bus, char **args, unsigned n) {
288 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
289 dbus_bool_t interactive = true, b, q;
295 polkit_agent_open_if_enabled();
297 r = parse_boolean(args[1]);
299 log_error("Failed to parse local RTC setting: %s", args[1]);
306 return bus_method_call_with_reply(
308 "org.freedesktop.timedate1",
309 "/org/freedesktop/timedate1",
310 "org.freedesktop.timedate1",
314 DBUS_TYPE_BOOLEAN, &b,
315 DBUS_TYPE_BOOLEAN, &q,
316 DBUS_TYPE_BOOLEAN, &interactive,
320 static int set_ntp(DBusConnection *bus, char **args, unsigned n) {
321 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
322 dbus_bool_t interactive = true, b;
328 polkit_agent_open_if_enabled();
330 r = parse_boolean(args[1]);
332 log_error("Failed to parse NTP setting: %s", args[1]);
338 return bus_method_call_with_reply(
340 "org.freedesktop.timedate1",
341 "/org/freedesktop/timedate1",
342 "org.freedesktop.timedate1",
346 DBUS_TYPE_BOOLEAN, &b,
347 DBUS_TYPE_BOOLEAN, &interactive,
351 static int zone_compare(const void *_a, const void *_b) {
352 const char **a = (const char**) _a, **b = (const char**) _b;
354 return strcmp(*a, *b);
357 static int list_timezones(DBusConnection *bus, char **args, unsigned n) {
358 _cleanup_fclose_ FILE *f = NULL;
359 _cleanup_strv_free_ char **zones = NULL;
366 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
368 log_error("Failed to open timezone database: %m");
373 char l[LINE_MAX], *p, **z, *w;
376 if (!fgets(l, sizeof(l), f)) {
380 log_error("Failed to read timezone database: %m");
386 if (isempty(p) || *p == '#')
390 /* Skip over country code */
391 p += strcspn(p, WHITESPACE);
392 p += strspn(p, WHITESPACE);
394 /* Skip over coordinates */
395 p += strcspn(p, WHITESPACE);
396 p += strspn(p, WHITESPACE);
398 /* Found timezone name */
399 k = strcspn(p, WHITESPACE);
407 z = realloc(zones, sizeof(char*) * (n_zones + 2));
414 zones[n_zones++] = w;
420 qsort(zones, n_zones, sizeof(char*), zone_compare);
422 pager_open_if_enabled();
424 STRV_FOREACH(i, zones)
430 static int help(void) {
432 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
433 "Query or control system time and date settings.\n\n"
434 " -h --help Show this help\n"
435 " --version Show package version\n"
436 " --fix-system Adjust system clock when changing local RTC mode\n"
437 " --no-pager Do not pipe output into a pager\n"
438 " --no-ask-password Do not prompt for password\n"
439 " -H --host=[USER@]HOST Operate on remote host\n\n"
441 " status Show current time settings\n"
442 " set-time [TIME] Set system time\n"
443 " set-timezone [ZONE] Set system timezone\n"
444 " list-timezones Show known timezones\n"
445 " set-local-rtc [BOOL] Control whether RTC is in local time\n"
446 " set-ntp [BOOL] Control whether NTP is enabled\n",
447 program_invocation_short_name);
452 static int parse_argv(int argc, char *argv[]) {
461 static const struct option options[] = {
462 { "help", no_argument, NULL, 'h' },
463 { "version", no_argument, NULL, ARG_VERSION },
464 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
465 { "host", required_argument, NULL, 'H' },
466 { "privileged", no_argument, NULL, 'P' },
467 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
468 { "fix-system", no_argument, NULL, ARG_FIX_SYSTEM },
477 while ((c = getopt_long(argc, argv, "+hp:as:H:P", options, NULL)) >= 0) {
486 puts(PACKAGE_STRING);
488 puts(SYSTEMD_FEATURES);
492 arg_transport = TRANSPORT_POLKIT;
496 arg_transport = TRANSPORT_SSH;
501 arg_fix_system = true;
512 log_error("Unknown option code %c", c);
520 static int timedatectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
522 static const struct {
530 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
532 { "status", LESS, 1, show_status },
533 { "set-time", EQUAL, 2, set_time },
534 { "set-timezone", EQUAL, 2, set_timezone },
535 { "list-timezones", EQUAL, 1, list_timezones },
536 { "set-local-rtc", EQUAL, 2, set_local_rtc },
537 { "set-ntp", EQUAL, 2, set_ntp, },
547 left = argc - optind;
550 /* Special rule: no arguments means "status" */
553 if (streq(argv[optind], "help")) {
558 for (i = 0; i < ELEMENTSOF(verbs); i++)
559 if (streq(argv[optind], verbs[i].verb))
562 if (i >= ELEMENTSOF(verbs)) {
563 log_error("Unknown operation %s", argv[optind]);
568 switch (verbs[i].argc_cmp) {
571 if (left != verbs[i].argc) {
572 log_error("Invalid number of arguments.");
579 if (left < verbs[i].argc) {
580 log_error("Too few arguments.");
587 if (left > verbs[i].argc) {
588 log_error("Too many arguments.");
595 assert_not_reached("Unknown comparison operator.");
599 log_error("Failed to get D-Bus connection: %s", error->message);
603 return verbs[i].dispatch(bus, argv + optind, left);
606 int main(int argc, char *argv[]) {
607 int r, retval = EXIT_FAILURE;
608 DBusConnection *bus = NULL;
611 dbus_error_init(&error);
613 log_parse_environment();
616 r = parse_argv(argc, argv);
620 retval = EXIT_SUCCESS;
624 if (arg_transport == TRANSPORT_NORMAL)
625 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
626 else if (arg_transport == TRANSPORT_POLKIT)
627 bus_connect_system_polkit(&bus, &error);
628 else if (arg_transport == TRANSPORT_SSH)
629 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
631 assert_not_reached("Uh, invalid transport...");
633 r = timedatectl_main(bus, argc, argv, &error);
634 retval = r < 0 ? EXIT_FAILURE : r;
638 dbus_connection_flush(bus);
639 dbus_connection_close(bus);
640 dbus_connection_unref(bus);
643 dbus_error_free(&error);