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