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