#include "strv.h"
#include "terminal-util.h"
#include "util.h"
+#include "verbs.h"
static bool arg_no_pager = false;
static bool arg_ask_password = true;
typedef struct StatusInfo {
usec_t time;
- char *timezone;
+ const char *timezone;
usec_t rtc_time;
- int rtc_local;
+ bool rtc_local;
- int ntp_enabled;
- int ntp_capable;
- int ntp_synced;
+ bool ntp_enabled;
+ bool ntp_capable;
+ bool ntp_synced;
} StatusInfo;
-static void status_info_clear(StatusInfo *info) {
- if (info) {
- free(info->timezone);
- zero(*info);
- }
-}
-
static void print_status_info(const StatusInfo *i) {
char a[LINE_MAX];
struct tm tm;
if (i->rtc_local)
printf("\n%s"
"Warning: The system is configured to read the RTC time in the local time zone.\n"
- " This mode can not be fully supported. It will create various problems\n"
+ " This mode cannot be fully supported. It will create various problems\n"
" with time zone changes and daylight saving time adjustments. The RTC\n"
" time is never updated, it relies on external facilities to maintain it.\n"
" If at all possible, use RTC in UTC by calling\n"
" 'timedatectl set-local-rtc 0'.%s\n", ansi_highlight(), ansi_normal());
}
-static int show_status(sd_bus *bus, char **args, unsigned n) {
- _cleanup_(status_info_clear) StatusInfo info = {};
+static int show_status(int argc, char **argv, void *userdata) {
+ StatusInfo info = {};
static const struct bus_properties_map map[] = {
{ "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
{ "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ sd_bus *bus = userdata;
int r;
assert(bus);
"org.freedesktop.timedate1",
"/org/freedesktop/timedate1",
map,
+ BUS_MAP_BOOLEAN_AS_BOOL,
&error,
+ &m,
&info);
if (r < 0)
return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
return r;
}
-static int set_time(sd_bus *bus, char **args, unsigned n) {
+static int set_time(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool relative = false, interactive = arg_ask_password;
+ sd_bus *bus = userdata;
usec_t t;
int r;
- assert(args);
- assert(n == 2);
-
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
- r = parse_timestamp(args[1], &t);
- if (r < 0) {
- log_error("Failed to parse time specification: %s", args[1]);
- return r;
- }
+ r = parse_timestamp(argv[1], &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse time specification '%s': %m", argv[1]);
r = sd_bus_call_method(bus,
"org.freedesktop.timedate1",
"SetTime",
&error,
NULL,
- "xbb", (int64_t)t, relative, interactive);
+ "xbb", (int64_t) t, relative, interactive);
if (r < 0)
- log_error("Failed to set time: %s", bus_error_message(&error, -r));
+ log_error("Failed to set time: %s", bus_error_message(&error, r));
return r;
}
-static int set_timezone(sd_bus *bus, char **args, unsigned n) {
+static int set_timezone(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
int r;
- assert(args);
- assert(n == 2);
-
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_bus_call_method(bus,
"SetTimezone",
&error,
NULL,
- "sb", args[1], arg_ask_password);
+ "sb", argv[1], arg_ask_password);
if (r < 0)
- log_error("Failed to set time zone: %s", bus_error_message(&error, -r));
+ log_error("Failed to set time zone: %s", bus_error_message(&error, r));
return r;
}
-static int set_local_rtc(sd_bus *bus, char **args, unsigned n) {
+static int set_local_rtc(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
int r, b;
- assert(args);
- assert(n == 2);
-
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
- b = parse_boolean(args[1]);
- if (b < 0) {
- log_error("Failed to parse local RTC setting: %s", args[1]);
- return b;
- }
+ b = parse_boolean(argv[1]);
+ if (b < 0)
+ return log_error_errno(b, "Failed to parse local RTC setting '%s': %m", argv[1]);
r = sd_bus_call_method(bus,
"org.freedesktop.timedate1",
NULL,
"bbb", b, arg_adjust_system_clock, arg_ask_password);
if (r < 0)
- log_error("Failed to set local RTC: %s", bus_error_message(&error, -r));
+ log_error("Failed to set local RTC: %s", bus_error_message(&error, r));
return r;
}
-static int set_ntp(sd_bus *bus, char **args, unsigned n) {
+static int set_ntp(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
int b, r;
- assert(args);
- assert(n == 2);
-
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
- b = parse_boolean(args[1]);
- if (b < 0) {
- log_error("Failed to parse NTP setting: %s", args[1]);
- return b;
- }
+ b = parse_boolean(argv[1]);
+ if (b < 0)
+ return log_error_errno(b, "Failed to parse NTP setting '%s': %m", argv[1]);
r = sd_bus_call_method(bus,
"org.freedesktop.timedate1",
NULL,
"bb", b, arg_ask_password);
if (r < 0)
- log_error("Failed to set ntp: %s", bus_error_message(&error, -r));
+ log_error("Failed to set ntp: %s", bus_error_message(&error, r));
return r;
}
-static int list_timezones(sd_bus *bus, char **args, unsigned n) {
+static int list_timezones(int argc, char **argv, void *userdata) {
_cleanup_strv_free_ char **zones = NULL;
int r;
- assert(args);
- assert(n == 1);
-
r = get_timezones(&zones);
if (r < 0)
return log_error_errno(r, "Failed to read list of time zones: %m");
- pager_open(arg_no_pager, false);
+ (void) pager_open(arg_no_pager, false);
strv_print(zones);
return 0;
}
-static void help(void) {
+static int help(void) {
printf("%s [OPTIONS...] COMMAND ...\n\n"
"Query or change system time and date settings.\n\n"
" -h --help Show this help message\n"
" set-local-rtc BOOL Control whether RTC is in local time\n"
" set-ntp BOOL Enable or disable network time synchronization\n",
program_invocation_short_name);
+
+ return 0;
+}
+
+static int verb_help(int argc, char **argv, void *userdata) {
+ return help();
}
static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
- static const struct {
- const char* verb;
- const enum {
- MORE,
- LESS,
- EQUAL
- } argc_cmp;
- const int argc;
- int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
- } verbs[] = {
- { "status", LESS, 1, show_status },
- { "set-time", EQUAL, 2, set_time },
- { "set-timezone", EQUAL, 2, set_timezone },
- { "list-timezones", EQUAL, 1, list_timezones },
- { "set-local-rtc", EQUAL, 2, set_local_rtc },
- { "set-ntp", EQUAL, 2, set_ntp, },
+ static const Verb verbs[] = {
+ { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
+ { "set-time", 2, 2, 0, set_time },
+ { "set-timezone", 2, 2, 0, set_timezone },
+ { "list-timezones", VERB_ANY, 1, 0, list_timezones },
+ { "set-local-rtc", 2, 2, 0, set_local_rtc },
+ { "set-ntp", 2, 2, 0, set_ntp },
+ { "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
+ {}
};
- int left;
- unsigned i;
-
- assert(argc >= 0);
- assert(argv);
-
- left = argc - optind;
-
- if (left <= 0)
- /* Special rule: no arguments means "status" */
- i = 0;
- else {
- if (streq(argv[optind], "help")) {
- help();
- return 0;
- }
-
- for (i = 0; i < ELEMENTSOF(verbs); i++)
- if (streq(argv[optind], verbs[i].verb))
- break;
-
- if (i >= ELEMENTSOF(verbs)) {
- log_error("Unknown operation %s", argv[optind]);
- return -EINVAL;
- }
- }
-
- switch (verbs[i].argc_cmp) {
-
- case EQUAL:
- if (left != verbs[i].argc) {
- log_error("Invalid number of arguments.");
- return -EINVAL;
- }
-
- break;
-
- case MORE:
- if (left < verbs[i].argc) {
- log_error("Too few arguments.");
- return -EINVAL;
- }
-
- break;
-
- case LESS:
- if (left > verbs[i].argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- break;
-
- default:
- assert_not_reached("Unknown comparison operator.");
- }
-
- return verbs[i].dispatch(bus, argv + optind, left);
+ return dispatch_verb(argc, argv, verbs, bus);
}
int main(int argc, char *argv[]) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
r = timedatectl_main(bus, argc, argv);
finish:
+ /* make sure we terminate the bus connection first, and then close the
+ * pager, see issue #3543 for the details. */
+ sd_bus_flush_close_unref(bus);
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;