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