]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedatectl.c
man: fix typos in sd_event_new
[thirdparty/systemd.git] / src / timedate / timedatectl.c
CommitLineData
6d0274f1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
2f6a5907 7 Copyright 2013 Kay Sievers
6d0274f1
LP
8
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.
13
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.
18
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/>.
21***/
22
23#include <stdlib.h>
24#include <stdbool.h>
25#include <unistd.h>
26#include <getopt.h>
a9cdc94f 27#include <locale.h>
6d0274f1
LP
28#include <string.h>
29#include <sys/timex.h>
30
a281d9c7
TA
31#include "sd-bus.h"
32#include "bus-util.h"
33#include "bus-error.h"
6d0274f1
LP
34#include "util.h"
35#include "spawn-polkit-agent.h"
36#include "build.h"
6d0274f1
LP
37#include "strv.h"
38#include "pager.h"
f18ca9dc 39#include "time-dst.h"
6d0274f1 40
6d0274f1 41static bool arg_no_pager = false;
6d0274f1 42static bool arg_ask_password = true;
e1636421 43static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 44static char *arg_host = NULL;
e1636421 45static bool arg_adjust_system_clock = false;
6d0274f1
LP
46
47static void pager_open_if_enabled(void) {
48
49 if (arg_no_pager)
50 return;
51
1b12a7b5 52 pager_open(false);
6d0274f1
LP
53}
54
55static void polkit_agent_open_if_enabled(void) {
56
57 /* Open the polkit agent as a child process if necessary */
6d0274f1
LP
58 if (!arg_ask_password)
59 return;
60
46e65dcc
LP
61 if (arg_transport != BUS_TRANSPORT_LOCAL)
62 return;
63
6d0274f1
LP
64 polkit_agent_open();
65}
66
67typedef struct StatusInfo {
2f6a5907 68 usec_t time;
ffc06c35 69 char *timezone;
2f6a5907
KS
70
71 usec_t rtc_time;
72 bool rtc_local;
73
74 bool ntp_enabled;
75 bool ntp_capable;
76 bool ntp_synced;
6d0274f1
LP
77} StatusInfo;
78
2311eb2f
KS
79static 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);
86 return s;
87 }
88 if (delta_minutes > 0) {
89 snprintf(s, size, "%i minutes forward", delta_minutes);
90 return s;
91 }
92 return "";
93}
94
ffc06c35 95static void print_status_info(const StatusInfo *i) {
f18ca9dc 96 char a[FORMAT_TIMESTAMP_MAX];
6d0274f1 97 char b[FORMAT_TIMESTAMP_MAX];
2311eb2f 98 char s[32];
6d0274f1
LP
99 struct tm tm;
100 time_t sec;
9ff09bcb 101 bool have_time = false;
480a61ae 102 _cleanup_free_ char *zc = NULL, *zn = NULL;
f18ca9dc 103 time_t t, tc, tn;
480a61ae
LP
104 int dn = 0;
105 bool is_dstc = false, is_dstn = false;
6d0274f1
LP
106 int r;
107
59965986
LP
108 assert(i);
109
2f6a5907 110 /* Enforce the values of /etc/localtime */
2311eb2f 111 if (getenv("TZ")) {
07a062a7 112 fprintf(stderr, "Warning: Ignoring the TZ variable. Reading the system's time zone setting only.\n\n");
2311eb2f
KS
113 unsetenv("TZ");
114 }
115
9ff09bcb
SL
116 if (i->time != 0) {
117 sec = (time_t) (i->time / USEC_PER_SEC);
118 have_time = true;
119 } else if (arg_transport == BUS_TRANSPORT_LOCAL) {
120 sec = time(NULL);
121 have_time = true;
122 } else
07a062a7 123 fprintf(stderr, "Warning: Could not get time from timedated and not operating locally.\n\n");
6d0274f1 124
9ff09bcb
SL
125 if (have_time) {
126 zero(tm);
127 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0);
128 char_array_0(a);
129 printf(" Local time: %s\n", a);
6d0274f1 130
9ff09bcb
SL
131 zero(tm);
132 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0);
133 char_array_0(a);
134 printf(" Universal time: %s\n", a);
135 } else {
136 printf(" Local time: %s\n", "n/a");
137 printf(" Universal time: %s\n", "n/a");
138 }
6d0274f1 139
2f6a5907
KS
140 if (i->rtc_time > 0) {
141 time_t rtc_sec;
6d0274f1 142
2f6a5907
KS
143 rtc_sec = (time_t)(i->rtc_time / USEC_PER_SEC);
144 zero(tm);
7f35b7bc 145 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)) > 0);
f18ca9dc
KS
146 char_array_0(a);
147 printf(" RTC time: %s\n", a);
2f6a5907 148 } else
9ff09bcb 149 printf(" RTC time: %s\n", "n/a");
6d0274f1 150
f18ca9dc 151 zero(tm);
bd5ce8e9 152 assert_se(strftime(a, sizeof(a), "%Z, %z", localtime_r(&sec, &tm)) > 0);
f18ca9dc 153 char_array_0(a);
07a062a7 154 printf(" Time zone: %s (%s)\n"
6d0274f1
LP
155 " NTP enabled: %s\n"
156 "NTP synchronized: %s\n"
157 " RTC in local TZ: %s\n",
2f6a5907
KS
158 strna(i->timezone), a,
159 i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
160 yes_no(i->ntp_synced),
161 yes_no(i->rtc_local));
6d0274f1 162
f18ca9dc
KS
163 r = time_get_dst(sec, "/etc/localtime",
164 &tc, &zc, &is_dstc,
2311eb2f 165 &tn, &dn, &zn, &is_dstn);
e2fd5e5b 166 if (r < 0)
9ff09bcb
SL
167 printf(" DST active: %s\n", "n/a");
168 else if (have_time) {
f18ca9dc
KS
169 printf(" DST active: %s\n", yes_no(is_dstc));
170
171 t = tc - 1;
172 zero(tm);
2af32104 173 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
f18ca9dc
KS
174 char_array_0(a);
175
176 zero(tm);
2af32104 177 assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tc, &tm)) > 0);
f18ca9dc 178 char_array_0(b);
3062c151 179 printf(" Last DST change: DST %s at\n"
f18ca9dc
KS
180 " %s\n"
181 " %s\n",
3062c151 182 is_dstc ? "began" : "ended", a, b);
f18ca9dc
KS
183
184 t = tn - 1;
185 zero(tm);
2af32104 186 assert_se(strftime(a, sizeof(a), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&t, &tm)) > 0);
f18ca9dc
KS
187 char_array_0(a);
188
189 zero(tm);
2af32104 190 assert_se(strftime(b, sizeof(b), "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&tn, &tm)) > 0);
f18ca9dc 191 char_array_0(b);
3062c151 192 printf(" Next DST change: DST %s (the clock jumps %s) at\n"
f18ca9dc
KS
193 " %s\n"
194 " %s\n",
3062c151 195 is_dstn ? "begins" : "ends", jump_str(dn, s, sizeof(s)), a, b);
9ff09bcb
SL
196 } else
197 printf(" DST active: %s\n", yes_no(is_dstc));
f18ca9dc 198
2f6a5907 199 if (i->rtc_local)
6d0274f1 200 fputs("\n" ANSI_HIGHLIGHT_ON
07a062a7 201 "Warning: The RTC is configured to maintain time in the local time zone. This\n"
6d0274f1 202 " mode is not fully supported and will create various problems with time\n"
07a062a7
JSJ
203 " zone changes and daylight saving time adjustments. If at all possible, use\n"
204 " RTC in UTC by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout);
6d0274f1
LP
205}
206
a281d9c7 207static int show_status(sd_bus *bus, char **args, unsigned n) {
b92bea5d 208 StatusInfo info = {};
9f6eb1cd
KS
209 static const struct bus_properties_map map[] = {
210 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
211 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
212 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) },
213 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
214 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
215 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
216 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
ffc06c35
KS
217 {}
218 };
219 int r;
6d0274f1 220
a281d9c7 221 assert(bus);
6d0274f1 222
ffc06c35
KS
223 r = bus_map_all_properties(bus,
224 "org.freedesktop.timedate1",
225 "/org/freedesktop/timedate1",
9f6eb1cd
KS
226 map,
227 &info);
adacb957
LP
228 if (r < 0) {
229 log_error("Failed to query server: %s", strerror(-r));
ffc06c35 230 goto fail;
adacb957 231 }
6d0274f1
LP
232
233 print_status_info(&info);
ffc06c35
KS
234
235fail:
236 free(info.timezone);
237 return r;
6d0274f1
LP
238}
239
a281d9c7
TA
240static int set_time(sd_bus *bus, char **args, unsigned n) {
241 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
242 bool relative = false, interactive = arg_ask_password;
6d0274f1 243 usec_t t;
6d0274f1
LP
244 int r;
245
246 assert(args);
247 assert(n == 2);
248
249 polkit_agent_open_if_enabled();
250
251 r = parse_timestamp(args[1], &t);
252 if (r < 0) {
253 log_error("Failed to parse time specification: %s", args[1]);
254 return r;
255 }
256
a281d9c7
TA
257 r = sd_bus_call_method(bus,
258 "org.freedesktop.timedate1",
259 "/org/freedesktop/timedate1",
260 "org.freedesktop.timedate1",
261 "SetTime",
262 &error,
263 NULL,
264 "xbb", (int64_t)t, relative, interactive);
265 if (r < 0)
266 log_error("Failed to set time: %s", bus_error_message(&error, -r));
267
268 return r;
6d0274f1
LP
269}
270
a281d9c7
TA
271static int set_timezone(sd_bus *bus, char **args, unsigned n) {
272 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
a281d9c7 273 int r;
6d0274f1
LP
274
275 assert(args);
276 assert(n == 2);
277
278 polkit_agent_open_if_enabled();
279
a281d9c7
TA
280 r = sd_bus_call_method(bus,
281 "org.freedesktop.timedate1",
282 "/org/freedesktop/timedate1",
283 "org.freedesktop.timedate1",
284 "SetTimezone",
285 &error,
286 NULL,
e5609878 287 "sb", args[1], arg_ask_password);
a281d9c7 288 if (r < 0)
07a062a7 289 log_error("Failed to set time zone: %s", bus_error_message(&error, -r));
a281d9c7
TA
290
291 return r;
6d0274f1
LP
292}
293
a281d9c7
TA
294static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
295 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e5609878 296 int r, b;
6d0274f1
LP
297
298 assert(args);
299 assert(n == 2);
300
301 polkit_agent_open_if_enabled();
302
e5609878
LP
303 b = parse_boolean(args[1]);
304 if (b < 0) {
6d0274f1 305 log_error("Failed to parse local RTC setting: %s", args[1]);
e5609878 306 return b;
6d0274f1
LP
307 }
308
a281d9c7
TA
309 r = sd_bus_call_method(bus,
310 "org.freedesktop.timedate1",
311 "/org/freedesktop/timedate1",
312 "org.freedesktop.timedate1",
313 "SetLocalRTC",
314 &error,
315 NULL,
e5609878 316 "bbb", b, arg_adjust_system_clock, arg_ask_password);
a281d9c7
TA
317 if (r < 0)
318 log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
319
320 return r;
6d0274f1
LP
321}
322
a281d9c7
TA
323static int set_ntp(sd_bus *bus, char **args, unsigned n) {
324 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e5609878 325 int b, r;
6d0274f1
LP
326
327 assert(args);
328 assert(n == 2);
329
330 polkit_agent_open_if_enabled();
331
e5609878
LP
332 b = parse_boolean(args[1]);
333 if (b < 0) {
6d0274f1 334 log_error("Failed to parse NTP setting: %s", args[1]);
e5609878 335 return b;
6d0274f1
LP
336 }
337
a281d9c7
TA
338 r = sd_bus_call_method(bus,
339 "org.freedesktop.timedate1",
340 "/org/freedesktop/timedate1",
341 "org.freedesktop.timedate1",
342 "SetNTP",
343 &error,
344 NULL,
e5609878 345 "bb", b, arg_ask_password);
a281d9c7
TA
346 if (r < 0)
347 log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
348
349 return r;
6d0274f1
LP
350}
351
a281d9c7 352static int list_timezones(sd_bus *bus, char **args, unsigned n) {
6d0274f1
LP
353 _cleanup_fclose_ FILE *f = NULL;
354 _cleanup_strv_free_ char **zones = NULL;
f75cb30b 355 size_t n_zones = 0;
6d0274f1
LP
356
357 assert(args);
358 assert(n == 1);
359
360 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
361 if (!f) {
07a062a7 362 log_error("Failed to open time zone database: %m");
6d0274f1
LP
363 return -errno;
364 }
365
366 for (;;) {
367 char l[LINE_MAX], *p, **z, *w;
368 size_t k;
369
370 if (!fgets(l, sizeof(l), f)) {
371 if (feof(f))
372 break;
373
07a062a7 374 log_error("Failed to read time zone database: %m");
6d0274f1
LP
375 return -errno;
376 }
377
378 p = strstrip(l);
379
380 if (isempty(p) || *p == '#')
381 continue;
382
6d0274f1
LP
383 /* Skip over country code */
384 p += strcspn(p, WHITESPACE);
385 p += strspn(p, WHITESPACE);
386
387 /* Skip over coordinates */
388 p += strcspn(p, WHITESPACE);
389 p += strspn(p, WHITESPACE);
390
391 /* Found timezone name */
392 k = strcspn(p, WHITESPACE);
393 if (k <= 0)
394 continue;
395
396 w = strndup(p, k);
397 if (!w)
398 return log_oom();
399
400 z = realloc(zones, sizeof(char*) * (n_zones + 2));
401 if (!z) {
402 free(w);
403 return log_oom();
404 }
405
406 zones = z;
407 zones[n_zones++] = w;
408 }
409
410 if (zones)
857a493d 411 zones[n_zones] = NULL;
6d0274f1
LP
412
413 pager_open_if_enabled();
414
857a493d 415 strv_sort(zones);
7c2d8094 416 strv_print(zones);
6d0274f1
LP
417
418 return 0;
419}
420
421static int help(void) {
422
7591abd4
LP
423 printf("%s [OPTIONS...] COMMAND ...\n\n"
424 "Query or change system time and date settings.\n\n"
07a062a7 425 " -h --help Show this help message\n"
4f8f66cb
ZJS
426 " --version Show package version\n"
427 " --no-pager Do not pipe output into a pager\n"
428 " --no-ask-password Do not prompt for password\n"
429 " -H --host=[USER@]HOST Operate on remote host\n"
430 " -M --machine=CONTAINER Operate on local container\n"
431 " --adjust-system-clock Adjust system clock when changing local RTC mode\n\n"
6d0274f1 432 "Commands:\n"
4f8f66cb
ZJS
433 " status Show current time settings\n"
434 " set-time TIME Set system time\n"
07a062a7
JSJ
435 " set-timezone ZONE Set system time zone\n"
436 " list-timezones Show known time zones\n"
4f8f66cb
ZJS
437 " set-local-rtc BOOL Control whether RTC is in local time\n"
438 " set-ntp BOOL Control whether NTP is enabled\n",
6d0274f1
LP
439 program_invocation_short_name);
440
441 return 0;
442}
443
444static int parse_argv(int argc, char *argv[]) {
445
446 enum {
447 ARG_VERSION = 0x100,
448 ARG_NO_PAGER,
c9783430 449 ARG_ADJUST_SYSTEM_CLOCK,
6d0274f1
LP
450 ARG_NO_ASK_PASSWORD
451 };
452
453 static const struct option options[] = {
c9783430
LP
454 { "help", no_argument, NULL, 'h' },
455 { "version", no_argument, NULL, ARG_VERSION },
456 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
457 { "host", required_argument, NULL, 'H' },
a281d9c7 458 { "machine", required_argument, NULL, 'M' },
c9783430
LP
459 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
460 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
eb9da376 461 {}
6d0274f1
LP
462 };
463
464 int c;
465
466 assert(argc >= 0);
467 assert(argv);
468
a281d9c7 469 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
6d0274f1
LP
470
471 switch (c) {
472
473 case 'h':
eb9da376 474 return help();
6d0274f1
LP
475
476 case ARG_VERSION:
477 puts(PACKAGE_STRING);
6d0274f1
LP
478 puts(SYSTEMD_FEATURES);
479 return 0;
480
a281d9c7
TA
481 case 'H':
482 arg_transport = BUS_TRANSPORT_REMOTE;
483 arg_host = optarg;
6d0274f1
LP
484 break;
485
a281d9c7
TA
486 case 'M':
487 arg_transport = BUS_TRANSPORT_CONTAINER;
488 arg_host = optarg;
6d0274f1
LP
489 break;
490
546158bc
JJ
491 case ARG_NO_ASK_PASSWORD:
492 arg_ask_password = false;
493 break;
494
c9783430
LP
495 case ARG_ADJUST_SYSTEM_CLOCK:
496 arg_adjust_system_clock = true;
6d0274f1
LP
497 break;
498
499 case ARG_NO_PAGER:
500 arg_no_pager = true;
501 break;
502
503 case '?':
504 return -EINVAL;
505
506 default:
eb9da376 507 assert_not_reached("Unhandled option");
6d0274f1
LP
508 }
509 }
510
511 return 1;
512}
513
a281d9c7 514static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
6d0274f1
LP
515
516 static const struct {
517 const char* verb;
518 const enum {
519 MORE,
520 LESS,
521 EQUAL
522 } argc_cmp;
523 const int argc;
a281d9c7 524 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
6d0274f1
LP
525 } verbs[] = {
526 { "status", LESS, 1, show_status },
527 { "set-time", EQUAL, 2, set_time },
528 { "set-timezone", EQUAL, 2, set_timezone },
529 { "list-timezones", EQUAL, 1, list_timezones },
530 { "set-local-rtc", EQUAL, 2, set_local_rtc },
531 { "set-ntp", EQUAL, 2, set_ntp, },
532 };
533
534 int left;
535 unsigned i;
536
537 assert(argc >= 0);
538 assert(argv);
6d0274f1
LP
539
540 left = argc - optind;
541
542 if (left <= 0)
543 /* Special rule: no arguments means "status" */
544 i = 0;
545 else {
546 if (streq(argv[optind], "help")) {
547 help();
548 return 0;
549 }
550
551 for (i = 0; i < ELEMENTSOF(verbs); i++)
552 if (streq(argv[optind], verbs[i].verb))
553 break;
554
555 if (i >= ELEMENTSOF(verbs)) {
556 log_error("Unknown operation %s", argv[optind]);
557 return -EINVAL;
558 }
559 }
560
561 switch (verbs[i].argc_cmp) {
562
563 case EQUAL:
564 if (left != verbs[i].argc) {
565 log_error("Invalid number of arguments.");
566 return -EINVAL;
567 }
568
569 break;
570
571 case MORE:
572 if (left < verbs[i].argc) {
573 log_error("Too few arguments.");
574 return -EINVAL;
575 }
576
577 break;
578
579 case LESS:
580 if (left > verbs[i].argc) {
581 log_error("Too many arguments.");
582 return -EINVAL;
583 }
584
585 break;
586
587 default:
588 assert_not_reached("Unknown comparison operator.");
589 }
590
6d0274f1
LP
591 return verbs[i].dispatch(bus, argv + optind, left);
592}
593
594int main(int argc, char *argv[]) {
a281d9c7 595 _cleanup_bus_unref_ sd_bus *bus = NULL;
84f6181c 596 int r;
6d0274f1 597
a9cdc94f 598 setlocale(LC_ALL, "");
6d0274f1
LP
599 log_parse_environment();
600 log_open();
601
602 r = parse_argv(argc, argv);
84f6181c 603 if (r <= 0)
6d0274f1 604 goto finish;
6d0274f1 605
a281d9c7
TA
606 r = bus_open_transport(arg_transport, arg_host, false, &bus);
607 if (r < 0) {
608 log_error("Failed to create bus connection: %s", strerror(-r));
a281d9c7 609 goto finish;
6d0274f1
LP
610 }
611
a281d9c7 612 r = timedatectl_main(bus, argc, argv);
6d0274f1 613
a281d9c7 614finish:
6d0274f1
LP
615 pager_close();
616
84f6181c 617 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
6d0274f1 618}