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