]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/timedate/timedatectl.c
5121dd153a4439dde8ae8ce1a96635e5de98624d
[thirdparty/systemd.git] / src / timedate / timedatectl.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2012 Lennart Poettering
5 Copyright 2013 Kay Sievers
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <getopt.h>
22 #include <locale.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25
26 #include "sd-bus.h"
27
28 #include "bus-error.h"
29 #include "bus-util.h"
30 #include "pager.h"
31 #include "parse-util.h"
32 #include "spawn-polkit-agent.h"
33 #include "strv.h"
34 #include "terminal-util.h"
35 #include "util.h"
36
37 static bool arg_no_pager = false;
38 static bool arg_ask_password = true;
39 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
40 static char *arg_host = NULL;
41 static bool arg_adjust_system_clock = false;
42
43 typedef struct StatusInfo {
44 usec_t time;
45 char *timezone;
46
47 usec_t rtc_time;
48 int rtc_local;
49
50 int ntp_enabled;
51 int ntp_capable;
52 int ntp_synced;
53 } StatusInfo;
54
55 static void status_info_clear(StatusInfo *info) {
56 if (info) {
57 free(info->timezone);
58 zero(*info);
59 }
60 }
61
62 static void print_status_info(const StatusInfo *i) {
63 char a[LINE_MAX];
64 struct tm tm;
65 time_t sec;
66 bool have_time = false;
67 const char *old_tz = NULL, *tz;
68 int r;
69 size_t n;
70
71 assert(i);
72
73 /* Save the old $TZ */
74 tz = getenv("TZ");
75 if (tz)
76 old_tz = strdupa(tz);
77
78 /* Set the new $TZ */
79 if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0)
80 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
81 else
82 tzset();
83
84 if (i->time != 0) {
85 sec = (time_t) (i->time / USEC_PER_SEC);
86 have_time = true;
87 } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
88 sec = time(NULL);
89 have_time = true;
90 } else
91 log_warning("Could not get time from timedated and not operating locally, ignoring.");
92
93 if (have_time) {
94 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
95 printf(" Local time: %s\n", n > 0 ? a : "n/a");
96
97 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
98 printf(" Universal time: %s\n", n > 0 ? a : "n/a");
99 } else {
100 printf(" Local time: %s\n", "n/a");
101 printf(" Universal time: %s\n", "n/a");
102 }
103
104 if (i->rtc_time > 0) {
105 time_t rtc_sec;
106
107 rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
108 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
109 printf(" RTC time: %s\n", n > 0 ? a : "n/a");
110 } else
111 printf(" RTC time: %s\n", "n/a");
112
113 if (have_time)
114 n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
115
116 /* Restore the $TZ */
117 if (old_tz)
118 r = setenv("TZ", old_tz, true);
119 else
120 r = unsetenv("TZ");
121 if (r < 0)
122 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
123 else
124 tzset();
125
126 printf(" Time zone: %s (%s)\n"
127 " System clock synchronized: %s\n"
128 "systemd-timesyncd.service active: %s\n"
129 " RTC in local TZ: %s\n",
130 strna(i->timezone), have_time && n > 0 ? a : "n/a",
131 i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a",
132 yes_no(i->ntp_synced),
133 yes_no(i->rtc_local));
134
135 if (i->rtc_local)
136 printf("\n%s"
137 "Warning: The system is configured to read the RTC time in the local time zone.\n"
138 " This mode can not be fully supported. It will create various problems\n"
139 " with time zone changes and daylight saving time adjustments. The RTC\n"
140 " time is never updated, it relies on external facilities to maintain it.\n"
141 " If at all possible, use RTC in UTC by calling\n"
142 " 'timedatectl set-local-rtc 0'.%s\n", ansi_highlight(), ansi_normal());
143 }
144
145 static int show_status(sd_bus *bus, char **args, unsigned n) {
146 _cleanup_(status_info_clear) StatusInfo info = {};
147 static const struct bus_properties_map map[] = {
148 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
149 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
150 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) },
151 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
152 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
153 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
154 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
155 {}
156 };
157
158 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
159 int r;
160
161 assert(bus);
162
163 r = bus_map_all_properties(bus,
164 "org.freedesktop.timedate1",
165 "/org/freedesktop/timedate1",
166 map,
167 &error,
168 &info);
169 if (r < 0)
170 return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
171
172 print_status_info(&info);
173
174 return r;
175 }
176
177 static int set_time(sd_bus *bus, char **args, unsigned n) {
178 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
179 bool relative = false, interactive = arg_ask_password;
180 usec_t t;
181 int r;
182
183 assert(args);
184 assert(n == 2);
185
186 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
187
188 r = parse_timestamp(args[1], &t);
189 if (r < 0) {
190 log_error("Failed to parse time specification: %s", args[1]);
191 return r;
192 }
193
194 r = sd_bus_call_method(bus,
195 "org.freedesktop.timedate1",
196 "/org/freedesktop/timedate1",
197 "org.freedesktop.timedate1",
198 "SetTime",
199 &error,
200 NULL,
201 "xbb", (int64_t)t, relative, interactive);
202 if (r < 0)
203 log_error("Failed to set time: %s", bus_error_message(&error, -r));
204
205 return r;
206 }
207
208 static int set_timezone(sd_bus *bus, char **args, unsigned n) {
209 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
210 int r;
211
212 assert(args);
213 assert(n == 2);
214
215 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
216
217 r = sd_bus_call_method(bus,
218 "org.freedesktop.timedate1",
219 "/org/freedesktop/timedate1",
220 "org.freedesktop.timedate1",
221 "SetTimezone",
222 &error,
223 NULL,
224 "sb", args[1], arg_ask_password);
225 if (r < 0)
226 log_error("Failed to set time zone: %s", bus_error_message(&error, -r));
227
228 return r;
229 }
230
231 static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
232 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
233 int r, b;
234
235 assert(args);
236 assert(n == 2);
237
238 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
239
240 b = parse_boolean(args[1]);
241 if (b < 0) {
242 log_error("Failed to parse local RTC setting: %s", args[1]);
243 return b;
244 }
245
246 r = sd_bus_call_method(bus,
247 "org.freedesktop.timedate1",
248 "/org/freedesktop/timedate1",
249 "org.freedesktop.timedate1",
250 "SetLocalRTC",
251 &error,
252 NULL,
253 "bbb", b, arg_adjust_system_clock, arg_ask_password);
254 if (r < 0)
255 log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
256
257 return r;
258 }
259
260 static int set_ntp(sd_bus *bus, char **args, unsigned n) {
261 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
262 int b, r;
263
264 assert(args);
265 assert(n == 2);
266
267 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
268
269 b = parse_boolean(args[1]);
270 if (b < 0) {
271 log_error("Failed to parse NTP setting: %s", args[1]);
272 return b;
273 }
274
275 r = sd_bus_call_method(bus,
276 "org.freedesktop.timedate1",
277 "/org/freedesktop/timedate1",
278 "org.freedesktop.timedate1",
279 "SetNTP",
280 &error,
281 NULL,
282 "bb", b, arg_ask_password);
283 if (r < 0)
284 log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
285
286 return r;
287 }
288
289 static int list_timezones(sd_bus *bus, char **args, unsigned n) {
290 _cleanup_strv_free_ char **zones = NULL;
291 int r;
292
293 assert(args);
294 assert(n == 1);
295
296 r = get_timezones(&zones);
297 if (r < 0)
298 return log_error_errno(r, "Failed to read list of time zones: %m");
299
300 pager_open(arg_no_pager, false);
301 strv_print(zones);
302
303 return 0;
304 }
305
306 static void help(void) {
307 printf("%s [OPTIONS...] COMMAND ...\n\n"
308 "Query or change system time and date settings.\n\n"
309 " -h --help Show this help message\n"
310 " --version Show package version\n"
311 " --no-pager Do not pipe output into a pager\n"
312 " --no-ask-password Do not prompt for password\n"
313 " -H --host=[USER@]HOST Operate on remote host\n"
314 " -M --machine=CONTAINER Operate on local container\n"
315 " --adjust-system-clock Adjust system clock when changing local RTC mode\n\n"
316 "Commands:\n"
317 " status Show current time settings\n"
318 " set-time TIME Set system time\n"
319 " set-timezone ZONE Set system time zone\n"
320 " list-timezones Show known time zones\n"
321 " set-local-rtc BOOL Control whether RTC is in local time\n"
322 " set-ntp BOOL Enable or disable network time synchronization\n",
323 program_invocation_short_name);
324 }
325
326 static int parse_argv(int argc, char *argv[]) {
327
328 enum {
329 ARG_VERSION = 0x100,
330 ARG_NO_PAGER,
331 ARG_ADJUST_SYSTEM_CLOCK,
332 ARG_NO_ASK_PASSWORD
333 };
334
335 static const struct option options[] = {
336 { "help", no_argument, NULL, 'h' },
337 { "version", no_argument, NULL, ARG_VERSION },
338 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
339 { "host", required_argument, NULL, 'H' },
340 { "machine", required_argument, NULL, 'M' },
341 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
342 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
343 {}
344 };
345
346 int c;
347
348 assert(argc >= 0);
349 assert(argv);
350
351 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
352
353 switch (c) {
354
355 case 'h':
356 help();
357 return 0;
358
359 case ARG_VERSION:
360 return version();
361
362 case 'H':
363 arg_transport = BUS_TRANSPORT_REMOTE;
364 arg_host = optarg;
365 break;
366
367 case 'M':
368 arg_transport = BUS_TRANSPORT_MACHINE;
369 arg_host = optarg;
370 break;
371
372 case ARG_NO_ASK_PASSWORD:
373 arg_ask_password = false;
374 break;
375
376 case ARG_ADJUST_SYSTEM_CLOCK:
377 arg_adjust_system_clock = true;
378 break;
379
380 case ARG_NO_PAGER:
381 arg_no_pager = true;
382 break;
383
384 case '?':
385 return -EINVAL;
386
387 default:
388 assert_not_reached("Unhandled option");
389 }
390
391 return 1;
392 }
393
394 static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
395
396 static const struct {
397 const char* verb;
398 const enum {
399 MORE,
400 LESS,
401 EQUAL
402 } argc_cmp;
403 const int argc;
404 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
405 } verbs[] = {
406 { "status", LESS, 1, show_status },
407 { "set-time", EQUAL, 2, set_time },
408 { "set-timezone", EQUAL, 2, set_timezone },
409 { "list-timezones", EQUAL, 1, list_timezones },
410 { "set-local-rtc", EQUAL, 2, set_local_rtc },
411 { "set-ntp", EQUAL, 2, set_ntp, },
412 };
413
414 int left;
415 unsigned i;
416
417 assert(argc >= 0);
418 assert(argv);
419
420 left = argc - optind;
421
422 if (left <= 0)
423 /* Special rule: no arguments means "status" */
424 i = 0;
425 else {
426 if (streq(argv[optind], "help")) {
427 help();
428 return 0;
429 }
430
431 for (i = 0; i < ELEMENTSOF(verbs); i++)
432 if (streq(argv[optind], verbs[i].verb))
433 break;
434
435 if (i >= ELEMENTSOF(verbs)) {
436 log_error("Unknown operation %s", argv[optind]);
437 return -EINVAL;
438 }
439 }
440
441 switch (verbs[i].argc_cmp) {
442
443 case EQUAL:
444 if (left != verbs[i].argc) {
445 log_error("Invalid number of arguments.");
446 return -EINVAL;
447 }
448
449 break;
450
451 case MORE:
452 if (left < verbs[i].argc) {
453 log_error("Too few arguments.");
454 return -EINVAL;
455 }
456
457 break;
458
459 case LESS:
460 if (left > verbs[i].argc) {
461 log_error("Too many arguments.");
462 return -EINVAL;
463 }
464
465 break;
466
467 default:
468 assert_not_reached("Unknown comparison operator.");
469 }
470
471 return verbs[i].dispatch(bus, argv + optind, left);
472 }
473
474 int main(int argc, char *argv[]) {
475 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
476 int r;
477
478 setlocale(LC_ALL, "");
479 log_parse_environment();
480 log_open();
481
482 r = parse_argv(argc, argv);
483 if (r <= 0)
484 goto finish;
485
486 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
487 if (r < 0) {
488 log_error_errno(r, "Failed to create bus connection: %m");
489 goto finish;
490 }
491
492 r = timedatectl_main(bus, argc, argv);
493
494 finish:
495 pager_close();
496
497 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
498 }