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