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