2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2012-10-17 02:50:09 +02:00
|
|
|
|
|
|
|
#include <getopt.h>
|
2012-11-12 20:16:07 +01:00
|
|
|
#include <locale.h>
|
2018-05-03 11:07:43 +02:00
|
|
|
#include <math.h>
|
2015-09-23 03:01:06 +02:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2013-10-30 21:13:46 +01:00
|
|
|
#include "sd-bus.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
|
2013-10-30 21:13:46 +01:00
|
|
|
#include "bus-error.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "bus-util.h"
|
2020-01-09 07:41:21 +01:00
|
|
|
#include "format-table.h"
|
2018-05-03 11:07:43 +02:00
|
|
|
#include "in-addr-util.h"
|
2018-11-20 09:18:21 +01:00
|
|
|
#include "main-func.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "pager.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "parse-util.h"
|
2018-11-20 15:42:57 +01:00
|
|
|
#include "pretty-print.h"
|
2012-10-17 02:50:09 +02:00
|
|
|
#include "spawn-polkit-agent.h"
|
2018-05-03 11:07:43 +02:00
|
|
|
#include "sparse-endian.h"
|
|
|
|
#include "string-table.h"
|
2012-10-17 02:50:09 +02:00
|
|
|
#include "strv.h"
|
2015-04-10 23:15:59 +02:00
|
|
|
#include "terminal-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "util.h"
|
2018-03-14 07:09:28 +01:00
|
|
|
#include "verbs.h"
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2018-11-11 12:56:29 +01:00
|
|
|
static PagerFlags arg_pager_flags = 0;
|
2012-10-17 02:50:09 +02:00
|
|
|
static bool arg_ask_password = true;
|
2013-10-31 03:12:37 +01:00
|
|
|
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
|
2013-06-09 22:54:39 +02:00
|
|
|
static char *arg_host = NULL;
|
2013-10-31 03:12:37 +01:00
|
|
|
static bool arg_adjust_system_clock = false;
|
2018-05-03 11:07:43 +02:00
|
|
|
static bool arg_monitor = false;
|
|
|
|
static char **arg_property = NULL;
|
|
|
|
static bool arg_value = false;
|
|
|
|
static bool arg_all = false;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
|
|
|
typedef struct StatusInfo {
|
2013-10-31 02:26:07 +01:00
|
|
|
usec_t time;
|
2018-03-19 15:46:29 +01:00
|
|
|
const char *timezone;
|
2013-10-31 02:26:07 +01:00
|
|
|
|
|
|
|
usec_t rtc_time;
|
2018-03-19 15:46:29 +01:00
|
|
|
bool rtc_local;
|
2013-10-31 02:26:07 +01:00
|
|
|
|
2018-03-19 15:46:29 +01:00
|
|
|
bool ntp_capable;
|
2018-04-17 06:55:07 +02:00
|
|
|
bool ntp_active;
|
2018-03-19 15:46:29 +01:00
|
|
|
bool ntp_synced;
|
2012-10-17 02:50:09 +02:00
|
|
|
} StatusInfo;
|
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
static int print_status_info(const StatusInfo *i) {
|
|
|
|
_cleanup_(table_unrefp) Table *table = NULL;
|
2019-11-12 17:52:35 +01:00
|
|
|
const char *old_tz = NULL, *tz, *tz_colon;
|
2018-04-17 06:55:07 +02:00
|
|
|
bool have_time = false;
|
2017-10-18 16:15:09 +02:00
|
|
|
char a[LINE_MAX];
|
2020-01-09 07:41:21 +01:00
|
|
|
TableCell *cell;
|
2012-10-17 02:50:09 +02:00
|
|
|
struct tm tm;
|
|
|
|
time_t sec;
|
2017-10-18 16:15:09 +02:00
|
|
|
size_t n;
|
2018-04-17 06:55:07 +02:00
|
|
|
int r;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2012-10-17 21:24:36 +02:00
|
|
|
assert(i);
|
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
table = table_new("key", "value");
|
|
|
|
if (!table)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
table_set_header(table, false);
|
|
|
|
|
|
|
|
assert_se(cell = table_get_cell(table, 0, 0));
|
|
|
|
(void) table_set_ellipsize_percent(table, cell, 100);
|
|
|
|
(void) table_set_align_percent(table, cell, 100);
|
|
|
|
|
|
|
|
assert_se(cell = table_get_cell(table, 0, 1));
|
|
|
|
(void) table_set_ellipsize_percent(table, cell, 100);
|
|
|
|
|
2015-04-02 12:15:53 +02:00
|
|
|
/* Save the old $TZ */
|
|
|
|
tz = getenv("TZ");
|
|
|
|
if (tz)
|
|
|
|
old_tz = strdupa(tz);
|
2012-11-11 16:55:25 +01:00
|
|
|
|
2015-04-02 12:15:53 +02:00
|
|
|
/* Set the new $TZ */
|
2019-11-12 17:52:35 +01:00
|
|
|
tz_colon = strjoina(":", isempty(i->timezone) ? "UTC" : i->timezone);
|
|
|
|
if (setenv("TZ", tz_colon, true) < 0)
|
2015-04-02 12:15:53 +02:00
|
|
|
log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
|
|
|
|
else
|
|
|
|
tzset();
|
2015-03-23 12:44:57 +01:00
|
|
|
|
2013-12-12 19:00:03 +01:00
|
|
|
if (i->time != 0) {
|
|
|
|
sec = (time_t) (i->time / USEC_PER_SEC);
|
|
|
|
have_time = true;
|
2015-04-02 12:15:53 +02:00
|
|
|
} else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
|
2013-12-12 19:00:03 +01:00
|
|
|
sec = time(NULL);
|
|
|
|
have_time = true;
|
|
|
|
} else
|
2015-04-02 12:15:53 +02:00
|
|
|
log_warning("Could not get time from timedated and not operating locally, ignoring.");
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
if (have_time)
|
2017-10-18 16:15:09 +02:00
|
|
|
n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
|
2015-01-27 14:00:11 +01:00
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_many(table,
|
|
|
|
TABLE_STRING, "Local time:",
|
|
|
|
TABLE_STRING, have_time && n > 0 ? a : "n/a");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
if (have_time)
|
2017-10-18 16:15:09 +02:00
|
|
|
n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_many(table,
|
|
|
|
TABLE_STRING, "Universal time:",
|
|
|
|
TABLE_STRING, have_time && n > 0 ? a : "n/a");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2013-10-31 02:26:07 +01:00
|
|
|
if (i->rtc_time > 0) {
|
|
|
|
time_t rtc_sec;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2015-04-02 12:15:53 +02:00
|
|
|
rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
|
2017-10-18 16:15:09 +02:00
|
|
|
n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
|
2020-01-09 07:41:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
r = table_add_many(table,
|
|
|
|
TABLE_STRING, "RTC time:",
|
|
|
|
TABLE_STRING, i->rtc_time > 0 && n > 0 ? a : "n/a");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2015-01-27 14:00:11 +01:00
|
|
|
if (have_time)
|
2017-10-18 16:15:09 +02:00
|
|
|
n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
|
2014-04-14 19:16:56 +02:00
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, "Time zone:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->timezone), have_time && n > 0 ? a : "n/a");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
|
2015-04-02 12:15:53 +02:00
|
|
|
/* Restore the $TZ */
|
|
|
|
if (old_tz)
|
|
|
|
r = setenv("TZ", old_tz, true);
|
|
|
|
else
|
|
|
|
r = unsetenv("TZ");
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
|
|
|
|
else
|
|
|
|
tzset();
|
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_many(table,
|
|
|
|
TABLE_STRING, "System clock synchronized:",
|
|
|
|
TABLE_BOOLEAN, i->ntp_synced,
|
|
|
|
TABLE_STRING, "NTP service:",
|
|
|
|
TABLE_STRING, i->ntp_capable ? (i->ntp_active ? "active" : "inactive") : "n/a",
|
|
|
|
TABLE_STRING, "RTC in local TZ:",
|
|
|
|
TABLE_BOOLEAN, i->rtc_local);
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_print(table, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to show table: %m");
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2013-10-31 02:26:07 +01:00
|
|
|
if (i->rtc_local)
|
2016-05-30 18:22:16 +02:00
|
|
|
printf("\n%s"
|
|
|
|
"Warning: The system is configured to read the RTC time in the local time zone.\n"
|
2018-02-08 10:34:52 +01:00
|
|
|
" This mode cannot be fully supported. It will create various problems\n"
|
2016-05-30 18:22:16 +02:00
|
|
|
" 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());
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
return 0;
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
static int show_status(int argc, char **argv, void *userdata) {
|
2018-03-19 15:46:29 +01:00
|
|
|
StatusInfo info = {};
|
2013-11-05 02:57:49 +01:00
|
|
|
static const struct bus_properties_map map[] = {
|
2018-04-17 06:55:07 +02:00
|
|
|
{ "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
|
|
|
|
{ "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
|
|
|
|
{ "NTP", "b", NULL, offsetof(StatusInfo, ntp_active) },
|
2013-11-05 02:57:49 +01:00
|
|
|
{ "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
|
2018-04-17 06:55:07 +02:00
|
|
|
{ "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
|
|
|
|
{ "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
|
|
|
|
{ "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
|
2013-11-02 00:10:12 +01:00
|
|
|
{}
|
|
|
|
};
|
2017-02-08 17:59:58 +01:00
|
|
|
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2018-03-19 15:46:29 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
2018-03-14 07:09:28 +01:00
|
|
|
sd_bus *bus = userdata;
|
2013-11-02 00:10:12 +01:00
|
|
|
int r;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2013-10-30 21:13:46 +01:00
|
|
|
assert(bus);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2013-11-02 00:10:12 +01:00
|
|
|
r = bus_map_all_properties(bus,
|
|
|
|
"org.freedesktop.timedate1",
|
|
|
|
"/org/freedesktop/timedate1",
|
2013-11-05 02:57:49 +01:00
|
|
|
map,
|
2018-03-28 13:37:27 +02:00
|
|
|
BUS_MAP_BOOLEAN_AS_BOOL,
|
2017-02-08 17:59:58 +01:00
|
|
|
&error,
|
2018-03-19 15:46:29 +01:00
|
|
|
&m,
|
2013-11-05 02:57:49 +01:00
|
|
|
&info);
|
2015-06-14 15:08:52 +02:00
|
|
|
if (r < 0)
|
2017-02-08 17:59:58 +01:00
|
|
|
return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
return print_status_info(&info);
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
2018-06-10 10:17:34 +02:00
|
|
|
static int show_properties(int argc, char **argv, void *userdata) {
|
|
|
|
sd_bus *bus = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
|
|
|
r = bus_print_all_properties(bus,
|
|
|
|
"org.freedesktop.timedate1",
|
|
|
|
"/org/freedesktop/timedate1",
|
|
|
|
NULL,
|
|
|
|
arg_property,
|
|
|
|
arg_value,
|
|
|
|
arg_all,
|
|
|
|
NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
static int set_time(int argc, char **argv, void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-10-30 21:13:46 +01:00
|
|
|
bool relative = false, interactive = arg_ask_password;
|
2018-03-14 07:09:28 +01:00
|
|
|
sd_bus *bus = userdata;
|
2012-10-17 02:50:09 +02:00
|
|
|
usec_t t;
|
|
|
|
int r;
|
|
|
|
|
2017-10-30 09:57:53 +01:00
|
|
|
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
r = parse_timestamp(argv[1], &t);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to parse time specification '%s': %m", argv[1]);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_call_method(
|
|
|
|
bus,
|
|
|
|
bus_timedate,
|
|
|
|
"SetTime",
|
|
|
|
&error,
|
|
|
|
NULL,
|
|
|
|
"xbb", (int64_t) t, relative, interactive);
|
2013-10-30 21:13:46 +01:00
|
|
|
if (r < 0)
|
2018-08-07 03:14:30 +02:00
|
|
|
return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r));
|
2013-10-30 21:13:46 +01:00
|
|
|
|
2018-08-07 03:14:30 +02:00
|
|
|
return 0;
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
static int set_timezone(int argc, char **argv, void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2018-03-14 07:09:28 +01:00
|
|
|
sd_bus *bus = userdata;
|
2013-10-30 21:13:46 +01:00
|
|
|
int r;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2017-10-30 09:57:53 +01:00
|
|
|
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_call_method(bus, bus_timedate, "SetTimezone", &error, NULL, "sb", argv[1], arg_ask_password);
|
2013-10-30 21:13:46 +01:00
|
|
|
if (r < 0)
|
2018-08-07 03:14:30 +02:00
|
|
|
return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r));
|
2013-10-30 21:13:46 +01:00
|
|
|
|
2018-08-07 03:14:30 +02:00
|
|
|
return 0;
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
static int set_local_rtc(int argc, char **argv, void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2018-03-14 07:09:28 +01:00
|
|
|
sd_bus *bus = userdata;
|
2013-10-31 03:02:49 +01:00
|
|
|
int r, b;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2017-10-30 09:57:53 +01:00
|
|
|
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
b = parse_boolean(argv[1]);
|
|
|
|
if (b < 0)
|
|
|
|
return log_error_errno(b, "Failed to parse local RTC setting '%s': %m", argv[1]);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_call_method(
|
|
|
|
bus,
|
|
|
|
bus_timedate,
|
|
|
|
"SetLocalRTC",
|
|
|
|
&error,
|
|
|
|
NULL,
|
|
|
|
"bbb", b, arg_adjust_system_clock, arg_ask_password);
|
2013-10-30 21:13:46 +01:00
|
|
|
if (r < 0)
|
2018-08-07 03:14:30 +02:00
|
|
|
return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r));
|
2013-10-30 21:13:46 +01:00
|
|
|
|
2018-08-07 03:14:30 +02:00
|
|
|
return 0;
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
static int set_ntp(int argc, char **argv, void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2018-03-14 07:09:28 +01:00
|
|
|
sd_bus *bus = userdata;
|
2013-10-31 03:02:49 +01:00
|
|
|
int b, r;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2017-10-30 09:57:53 +01:00
|
|
|
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
b = parse_boolean(argv[1]);
|
|
|
|
if (b < 0)
|
|
|
|
return log_error_errno(b, "Failed to parse NTP setting '%s': %m", argv[1]);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_call_method(bus, bus_timedate, "SetNTP", &error, NULL, "bb", b, arg_ask_password);
|
2013-10-30 21:13:46 +01:00
|
|
|
if (r < 0)
|
2018-08-07 03:14:30 +02:00
|
|
|
return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r));
|
2013-10-30 21:13:46 +01:00
|
|
|
|
2018-08-07 03:14:30 +02:00
|
|
|
return 0;
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
static int list_timezones(int argc, char **argv, void *userdata) {
|
2018-12-12 20:49:04 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
sd_bus *bus = userdata;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
2014-07-07 11:49:48 +02:00
|
|
|
int r;
|
2018-12-12 20:49:04 +01:00
|
|
|
char** zones;
|
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_call_method(bus, bus_timedate, "ListTimezones", &error, &reply, NULL);
|
2018-12-12 20:49:04 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to request list of time zones: %s",
|
|
|
|
bus_error_message(&error, r));
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2018-12-12 20:49:04 +01:00
|
|
|
r = sd_bus_message_read_strv(reply, &zones);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2018-12-12 20:49:04 +01:00
|
|
|
return bus_log_parse_error(r);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2018-11-11 12:56:29 +01:00
|
|
|
(void) pager_open(arg_pager_flags);
|
2013-02-07 00:15:27 +01:00
|
|
|
strv_print(zones);
|
2012-10-17 02:50:09 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-03 11:07:43 +02:00
|
|
|
typedef struct NTPStatusInfo {
|
|
|
|
const char *server_name;
|
|
|
|
char *server_address;
|
|
|
|
usec_t poll_interval, poll_max, poll_min;
|
|
|
|
usec_t root_distance_max;
|
|
|
|
|
|
|
|
uint32_t leap, version, mode, stratum;
|
|
|
|
int32_t precision;
|
|
|
|
usec_t root_delay, root_dispersion;
|
|
|
|
union {
|
|
|
|
char str[5];
|
|
|
|
uint32_t val;
|
|
|
|
} reference;
|
|
|
|
usec_t origin, recv, trans, dest;
|
|
|
|
|
|
|
|
bool spike;
|
|
|
|
uint64_t packet_count;
|
|
|
|
usec_t jitter;
|
|
|
|
|
|
|
|
int64_t freq;
|
|
|
|
} NTPStatusInfo;
|
|
|
|
|
|
|
|
static void ntp_status_info_clear(NTPStatusInfo *p) {
|
|
|
|
p->server_address = mfree(p->server_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * const ntp_leap_table[4] = {
|
|
|
|
[0] = "normal",
|
|
|
|
[1] = "last minute of the day has 61 seconds",
|
|
|
|
[2] = "last minute of the day has 59 seconds",
|
|
|
|
[3] = "not synchronized",
|
|
|
|
};
|
|
|
|
|
2020-05-25 18:20:52 +02:00
|
|
|
DISABLE_WARNING_TYPE_LIMITS;
|
2018-05-03 11:07:43 +02:00
|
|
|
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ntp_leap, uint32_t);
|
2020-05-25 18:20:52 +02:00
|
|
|
REENABLE_WARNING;
|
2018-05-03 11:07:43 +02:00
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
static int print_ntp_status_info(NTPStatusInfo *i) {
|
|
|
|
char ts[FORMAT_TIMESPAN_MAX], jitter[FORMAT_TIMESPAN_MAX],
|
|
|
|
tmin[FORMAT_TIMESPAN_MAX], tmax[FORMAT_TIMESPAN_MAX];
|
2018-05-03 11:07:43 +02:00
|
|
|
usec_t delay, t14, t23, offset, root_distance;
|
2020-01-09 07:41:21 +01:00
|
|
|
_cleanup_(table_unrefp) Table *table = NULL;
|
2018-05-03 11:07:43 +02:00
|
|
|
bool offset_sign;
|
2020-01-09 07:41:21 +01:00
|
|
|
TableCell *cell;
|
|
|
|
int r;
|
2018-05-03 11:07:43 +02:00
|
|
|
|
|
|
|
assert(i);
|
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
table = table_new("key", "value");
|
|
|
|
if (!table)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
table_set_header(table, false);
|
|
|
|
|
|
|
|
assert_se(cell = table_get_cell(table, 0, 0));
|
|
|
|
(void) table_set_ellipsize_percent(table, cell, 100);
|
|
|
|
(void) table_set_align_percent(table, cell, 100);
|
|
|
|
|
|
|
|
assert_se(cell = table_get_cell(table, 0, 1));
|
|
|
|
(void) table_set_ellipsize_percent(table, cell, 100);
|
|
|
|
|
2018-05-03 11:07:43 +02:00
|
|
|
/*
|
|
|
|
* "Timestamp Name ID When Generated
|
|
|
|
* ------------------------------------------------------------
|
|
|
|
* Originate Timestamp T1 time request sent by client
|
|
|
|
* Receive Timestamp T2 time request received by server
|
|
|
|
* Transmit Timestamp T3 time reply sent by server
|
|
|
|
* Destination Timestamp T4 time reply received by client
|
|
|
|
*
|
|
|
|
* The round-trip delay, d, and system clock offset, t, are defined as:
|
|
|
|
* d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2"
|
|
|
|
*/
|
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, "Server:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell_stringf(table, NULL, "%s (%s)", i->server_address, i->server_name);
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, "Poll interval:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell_stringf(table, NULL, "%s (min: %s; max %s)",
|
|
|
|
format_timespan(ts, sizeof(ts), i->poll_interval, 0),
|
|
|
|
format_timespan(tmin, sizeof(tmin), i->poll_min, 0),
|
|
|
|
format_timespan(tmax, sizeof(tmax), i->poll_max, 0));
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2018-05-03 11:07:43 +02:00
|
|
|
|
|
|
|
if (i->packet_count == 0) {
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_many(table,
|
|
|
|
TABLE_STRING, "Packet count:",
|
|
|
|
TABLE_STRING, "0");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_print(table, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to show table: %m");
|
|
|
|
|
|
|
|
return 0;
|
2018-05-03 11:07:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i->dest < i->origin || i->trans < i->recv || i->dest - i->origin < i->trans - i->recv) {
|
|
|
|
log_error("Invalid NTP response");
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_print(table, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to show table: %m");
|
|
|
|
|
|
|
|
return 0;
|
2018-05-03 11:07:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
delay = (i->dest - i->origin) - (i->trans - i->recv);
|
|
|
|
|
|
|
|
t14 = i->origin + i->dest;
|
|
|
|
t23 = i->recv + i->trans;
|
|
|
|
offset_sign = t14 < t23;
|
|
|
|
offset = (offset_sign ? t23 - t14 : t14 - t23) / 2;
|
|
|
|
|
|
|
|
root_distance = i->root_delay / 2 + i->root_dispersion;
|
|
|
|
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_many(table,
|
|
|
|
TABLE_STRING, "Leap:",
|
|
|
|
TABLE_STRING, ntp_leap_to_string(i->leap),
|
|
|
|
TABLE_STRING, "Version:",
|
|
|
|
TABLE_UINT32, i->version,
|
|
|
|
TABLE_STRING, "Stratum:",
|
|
|
|
TABLE_UINT32, i->stratum,
|
|
|
|
TABLE_STRING, "Reference:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
2018-05-03 11:07:43 +02:00
|
|
|
if (i->stratum <= 1)
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, i->reference.str);
|
2018-05-03 11:07:43 +02:00
|
|
|
else
|
2020-01-09 07:41:21 +01:00
|
|
|
r = table_add_cell_stringf(table, NULL, "%" PRIX32, be32toh(i->reference.val));
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, "Precision:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell_stringf(table, NULL, "%s (%" PRIi32 ")",
|
|
|
|
format_timespan(ts, sizeof(ts), DIV_ROUND_UP((nsec_t) (exp2(i->precision) * NSEC_PER_SEC), NSEC_PER_USEC), 0),
|
|
|
|
i->precision);
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, "Root distance:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell_stringf(table, NULL, "%s (max: %s)",
|
|
|
|
format_timespan(ts, sizeof(ts), root_distance, 0),
|
|
|
|
format_timespan(tmax, sizeof(tmax), i->root_distance_max, 0));
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, "Offset:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell_stringf(table, NULL, "%s%s",
|
|
|
|
offset_sign ? "+" : "-",
|
|
|
|
format_timespan(ts, sizeof(ts), offset, 0));
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_many(table,
|
|
|
|
TABLE_STRING, "Delay:",
|
|
|
|
TABLE_STRING, format_timespan(ts, sizeof(ts), delay, 0),
|
|
|
|
TABLE_STRING, "Jitter:",
|
|
|
|
TABLE_STRING, format_timespan(jitter, sizeof(jitter), i->jitter, 0),
|
|
|
|
TABLE_STRING, "Packet count:",
|
|
|
|
TABLE_UINT64, i->packet_count);
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
if (!i->spike) {
|
|
|
|
r = table_add_cell(table, NULL, TABLE_STRING, "Frequency:");
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
|
|
|
|
r = table_add_cell_stringf(table, NULL, "%+.3fppm", (double) i->freq / 0x10000);
|
|
|
|
if (r < 0)
|
2020-01-10 10:23:24 +01:00
|
|
|
return table_log_add_error(r);
|
2020-01-09 07:41:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
r = table_print(table, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
log_error_errno(r, "Failed to show table: %m");
|
|
|
|
|
|
|
|
return 0;
|
2018-05-03 11:07:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int map_server_address(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
|
|
|
|
char **p = (char **) userdata;
|
|
|
|
const void *d;
|
|
|
|
int family, r;
|
|
|
|
size_t sz;
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(m, 'r', "iay");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(m, "i", &family);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_array(m, 'y', &d, &sz);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (sz == 0 && family == AF_UNSPEC) {
|
|
|
|
*p = mfree(*p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (!IN_SET(family, AF_INET, AF_INET6))
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Unknown address family %i", family);
|
2018-05-03 11:07:43 +02:00
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (sz != FAMILY_ADDRESS_SIZE(family))
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Invalid address size");
|
2018-05-03 11:07:43 +02:00
|
|
|
|
|
|
|
r = in_addr_to_string(family, d, p);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int map_ntp_message(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
|
|
|
|
NTPStatusInfo *p = userdata;
|
|
|
|
const void *d;
|
|
|
|
size_t sz;
|
|
|
|
int32_t b;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(m, 'r', "uuuuittayttttbtt");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(m, "uuuuitt",
|
|
|
|
&p->leap, &p->version, &p->mode, &p->stratum, &p->precision,
|
|
|
|
&p->root_delay, &p->root_dispersion);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_read_array(m, 'y', &d, &sz);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(m, "ttttbtt",
|
|
|
|
&p->origin, &p->recv, &p->trans, &p->dest,
|
|
|
|
&b, &p->packet_count, &p->jitter);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (sz != 4)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memcpy(p->reference.str, d, sz);
|
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
p->spike = b;
|
2018-05-03 11:07:43 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int show_timesync_status_once(sd_bus *bus) {
|
|
|
|
static const struct bus_properties_map map_timesync[] = {
|
|
|
|
{ "ServerName", "s", NULL, offsetof(NTPStatusInfo, server_name) },
|
|
|
|
{ "ServerAddress", "(iay)", map_server_address, offsetof(NTPStatusInfo, server_address) },
|
|
|
|
{ "PollIntervalUSec", "t", NULL, offsetof(NTPStatusInfo, poll_interval) },
|
|
|
|
{ "PollIntervalMinUSec", "t", NULL, offsetof(NTPStatusInfo, poll_min) },
|
|
|
|
{ "PollIntervalMaxUSec", "t", NULL, offsetof(NTPStatusInfo, poll_max) },
|
|
|
|
{ "RootDistanceMaxUSec", "t", NULL, offsetof(NTPStatusInfo, root_distance_max) },
|
|
|
|
{ "NTPMessage", "(uuuuittayttttbtt)", map_ntp_message, 0 },
|
|
|
|
{ "Frequency", "x", NULL, offsetof(NTPStatusInfo, freq) },
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
_cleanup_(ntp_status_info_clear) NTPStatusInfo info = {};
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
|
|
|
r = bus_map_all_properties(bus,
|
|
|
|
"org.freedesktop.timesync1",
|
|
|
|
"/org/freedesktop/timesync1",
|
|
|
|
map_timesync,
|
|
|
|
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));
|
|
|
|
|
|
|
|
if (arg_monitor && !terminal_is_dumb())
|
|
|
|
fputs(ANSI_HOME_CLEAR, stdout);
|
|
|
|
|
|
|
|
print_ntp_status_info(&info);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
|
|
|
|
const char *name;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
r = sd_bus_message_read(m, "s", &name);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to read interface name: %m");
|
|
|
|
|
|
|
|
if (!streq_ptr(name, "org.freedesktop.timesync1.Manager"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return show_timesync_status_once(sd_bus_message_get_bus(m));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int show_timesync_status(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
|
|
|
sd_bus *bus = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
|
|
|
r = show_timesync_status_once(bus);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!arg_monitor)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
r = sd_event_default(&event);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get event loop: %m");
|
|
|
|
|
|
|
|
r = sd_bus_match_signal(bus,
|
|
|
|
NULL,
|
|
|
|
"org.freedesktop.timesync1",
|
|
|
|
"/org/freedesktop/timesync1",
|
|
|
|
"org.freedesktop.DBus.Properties",
|
|
|
|
"PropertiesChanged",
|
|
|
|
on_properties_changed, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
|
|
|
|
|
|
|
|
r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to attach bus to event loop: %m");
|
|
|
|
|
|
|
|
r = sd_event_loop(event);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to run event loop: %m");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-11 10:18:14 +02:00
|
|
|
static int print_timesync_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
|
2018-05-03 11:07:43 +02:00
|
|
|
char type;
|
|
|
|
const char *contents;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
r = sd_bus_message_peek_type(m, &type, &contents);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
|
|
case SD_BUS_TYPE_STRUCT:
|
|
|
|
if (streq(name, "NTPMessage")) {
|
|
|
|
_cleanup_(ntp_status_info_clear) NTPStatusInfo i = {};
|
|
|
|
char ts[FORMAT_TIMESPAN_MAX], stamp[FORMAT_TIMESTAMP_MAX];
|
|
|
|
|
|
|
|
r = map_ntp_message(NULL, NULL, m, NULL, &i);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (i.packet_count == 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
fputs(name, stdout);
|
|
|
|
fputc('=', stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("{ Leap=%u, Version=%u, Mode=%u, Stratum=%u, Precision=%i,",
|
|
|
|
i.leap, i.version, i.mode, i.stratum, i.precision);
|
|
|
|
printf(" RootDelay=%s,",
|
|
|
|
format_timespan(ts, sizeof(ts), i.root_delay, 0));
|
|
|
|
printf(" RootDispersion=%s,",
|
|
|
|
format_timespan(ts, sizeof(ts), i.root_dispersion, 0));
|
|
|
|
|
|
|
|
if (i.stratum == 1)
|
|
|
|
printf(" Reference=%s,", i.reference.str);
|
|
|
|
else
|
|
|
|
printf(" Reference=%" PRIX32 ",", be32toh(i.reference.val));
|
|
|
|
|
|
|
|
printf(" OriginateTimestamp=%s,",
|
|
|
|
format_timestamp(stamp, sizeof(stamp), i.origin));
|
|
|
|
printf(" ReceiveTimestamp=%s,",
|
|
|
|
format_timestamp(stamp, sizeof(stamp), i.recv));
|
|
|
|
printf(" TransmitTimestamp=%s,",
|
|
|
|
format_timestamp(stamp, sizeof(stamp), i.trans));
|
|
|
|
printf(" DestinationTimestamp=%s,",
|
|
|
|
format_timestamp(stamp, sizeof(stamp), i.dest));
|
|
|
|
printf(" Ignored=%s PacketCount=%" PRIu64 ",",
|
|
|
|
yes_no(i.spike), i.packet_count);
|
|
|
|
printf(" Jitter=%s }\n",
|
|
|
|
format_timespan(ts, sizeof(ts), i.jitter, 0));
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
} else if (streq(name, "ServerAddress")) {
|
|
|
|
_cleanup_free_ char *str = NULL;
|
|
|
|
|
|
|
|
r = map_server_address(NULL, NULL, m, NULL, &str);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (arg_all || !isempty(str))
|
2019-03-02 15:35:26 +01:00
|
|
|
bus_print_property_value(name, expected_value, value, str);
|
2018-05-03 11:07:43 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int show_timesync(int argc, char **argv, void *userdata) {
|
|
|
|
sd_bus *bus = userdata;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
|
|
|
r = bus_print_all_properties(bus,
|
|
|
|
"org.freedesktop.timesync1",
|
|
|
|
"/org/freedesktop/timesync1",
|
|
|
|
print_timesync_property,
|
|
|
|
arg_property,
|
|
|
|
arg_value,
|
|
|
|
arg_all,
|
|
|
|
NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-18 13:57:01 +01:00
|
|
|
static int parse_ifindex_bus(sd_bus *bus, const char *str) {
|
2019-05-28 05:07:47 +02:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
int32_t i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(str);
|
|
|
|
|
2019-12-18 13:54:13 +01:00
|
|
|
r = parse_ifindex(str);
|
2019-12-18 13:57:01 +01:00
|
|
|
if (r > 0)
|
|
|
|
return r;
|
|
|
|
assert(r < 0);
|
2019-05-28 05:07:47 +02:00
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_call_method(bus, bus_network_mgr, "GetLinkByName", &error, &reply, "s", str);
|
2019-05-28 05:07:47 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get ifindex of interfaces %s: %s", str, bus_error_message(&error, r));
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "io", &i, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
2019-12-18 13:57:01 +01:00
|
|
|
return i;
|
2019-05-28 05:07:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int verb_ntp_servers(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
|
|
|
|
sd_bus *bus = userdata;
|
|
|
|
int ifindex, r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
2019-12-18 13:57:01 +01:00
|
|
|
ifindex = parse_ifindex_bus(bus, argv[1]);
|
|
|
|
if (ifindex < 0)
|
|
|
|
return ifindex;
|
2019-05-28 05:07:47 +02:00
|
|
|
|
|
|
|
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_message_new_method_call(bus, &req, bus_network_mgr, "SetLinkNTP");
|
2019-05-28 05:07:47 +02:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_message_append(req, "i", ifindex);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_message_append_strv(req, argv + 2);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_create_error(r);
|
|
|
|
|
|
|
|
r = sd_bus_call(bus, req, 0, &error, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to set NTP servers: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int verb_revert(int argc, char **argv, void *userdata) {
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
sd_bus *bus = userdata;
|
|
|
|
int ifindex, r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
2019-12-18 13:57:01 +01:00
|
|
|
ifindex = parse_ifindex_bus(bus, argv[1]);
|
|
|
|
if (ifindex < 0)
|
|
|
|
return ifindex;
|
2019-05-28 05:07:47 +02:00
|
|
|
|
|
|
|
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
|
|
|
|
2020-05-03 09:03:16 +02:00
|
|
|
r = bus_call_method(bus, bus_network_mgr, "RevertLinkNTP", &error, NULL, "i", ifindex);
|
2019-05-28 05:07:47 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
static int help(void) {
|
2018-08-09 10:32:31 +02:00
|
|
|
_cleanup_free_ char *link = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = terminal_urlify_man("timedatectl", "1", &link);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
2019-11-15 18:38:44 +01:00
|
|
|
printf("%s [OPTIONS...] COMMAND ...\n"
|
|
|
|
"\n%sQuery or change system time and date settings.%s\n"
|
2019-10-08 17:58:44 +02:00
|
|
|
"\nCommands:\n"
|
2013-12-08 14:16:59 +01:00
|
|
|
" status Show current time settings\n"
|
2018-06-10 10:17:34 +02:00
|
|
|
" show Show properties of systemd-timedated\n"
|
2013-12-08 14:16:59 +01:00
|
|
|
" set-time TIME Set system time\n"
|
2014-03-18 05:27:05 +01:00
|
|
|
" set-timezone ZONE Set system time zone\n"
|
|
|
|
" list-timezones Show known time zones\n"
|
2013-12-08 14:16:59 +01:00
|
|
|
" set-local-rtc BOOL Control whether RTC is in local time\n"
|
2018-05-03 11:07:43 +02:00
|
|
|
" set-ntp BOOL Enable or disable network time synchronization\n"
|
2019-11-15 18:38:44 +01:00
|
|
|
"\nsystemd-timesyncd Commands:\n"
|
2018-05-03 11:07:43 +02:00
|
|
|
" timesync-status Show status of systemd-timesyncd\n"
|
|
|
|
" show-timesync Show properties of systemd-timesyncd\n"
|
2019-11-15 18:38:44 +01:00
|
|
|
"\nOptions:\n"
|
2019-10-08 17:58:44 +02:00
|
|
|
" -h --help Show this help message\n"
|
|
|
|
" --version Show package version\n"
|
|
|
|
" --no-pager Do not pipe output into a pager\n"
|
|
|
|
" --no-ask-password Do not prompt for password\n"
|
|
|
|
" -H --host=[USER@]HOST Operate on remote host\n"
|
|
|
|
" -M --machine=CONTAINER Operate on local container\n"
|
|
|
|
" --adjust-system-clock Adjust system clock when changing local RTC mode\n"
|
|
|
|
" --monitor Monitor status of systemd-timesyncd\n"
|
|
|
|
" -p --property=NAME Show only properties by this name\n"
|
|
|
|
" -a --all Show all properties, including empty ones\n"
|
|
|
|
" --value When showing properties, only print the value\n"
|
2018-08-09 10:32:31 +02:00
|
|
|
"\nSee the %s for details.\n"
|
|
|
|
, program_invocation_short_name
|
2019-11-15 18:38:44 +01:00
|
|
|
, ansi_highlight()
|
2019-10-08 18:19:59 +02:00
|
|
|
, ansi_normal()
|
2018-08-09 10:32:31 +02:00
|
|
|
, link
|
|
|
|
);
|
2018-03-14 07:09:28 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int verb_help(int argc, char **argv, void *userdata) {
|
|
|
|
return help();
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_argv(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ARG_VERSION = 0x100,
|
|
|
|
ARG_NO_PAGER,
|
2012-10-17 22:52:21 +02:00
|
|
|
ARG_ADJUST_SYSTEM_CLOCK,
|
2018-05-03 11:07:43 +02:00
|
|
|
ARG_NO_ASK_PASSWORD,
|
|
|
|
ARG_MONITOR,
|
|
|
|
ARG_VALUE,
|
2012-10-17 02:50:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option options[] = {
|
2012-10-17 22:52:21 +02:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{ "version", no_argument, NULL, ARG_VERSION },
|
|
|
|
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
|
|
|
{ "host", required_argument, NULL, 'H' },
|
2013-10-30 21:13:46 +01:00
|
|
|
{ "machine", required_argument, NULL, 'M' },
|
2012-10-17 22:52:21 +02:00
|
|
|
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
|
|
|
{ "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
|
2018-05-03 11:07:43 +02:00
|
|
|
{ "monitor", no_argument, NULL, ARG_MONITOR },
|
|
|
|
{ "property", required_argument, NULL, 'p' },
|
|
|
|
{ "all", no_argument, NULL, 'a' },
|
|
|
|
{ "value", no_argument, NULL, ARG_VALUE },
|
2013-11-06 18:28:39 +01:00
|
|
|
{}
|
2012-10-17 02:50:09 +02:00
|
|
|
};
|
|
|
|
|
2018-05-03 11:07:43 +02:00
|
|
|
int c, r;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
|
|
|
assert(argc >= 0);
|
|
|
|
assert(argv);
|
|
|
|
|
2018-05-03 11:07:43 +02:00
|
|
|
while ((c = getopt_long(argc, argv, "hH:M:p:a", options, NULL)) >= 0)
|
2012-10-17 02:50:09 +02:00
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
|
|
|
case 'h':
|
2018-03-14 07:09:28 +01:00
|
|
|
return help();
|
2012-10-17 02:50:09 +02:00
|
|
|
|
|
|
|
case ARG_VERSION:
|
2015-09-23 03:01:06 +02:00
|
|
|
return version();
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2013-10-30 21:13:46 +01:00
|
|
|
case 'H':
|
|
|
|
arg_transport = BUS_TRANSPORT_REMOTE;
|
|
|
|
arg_host = optarg;
|
2012-10-17 02:50:09 +02:00
|
|
|
break;
|
|
|
|
|
2013-10-30 21:13:46 +01:00
|
|
|
case 'M':
|
2014-12-23 23:38:13 +01:00
|
|
|
arg_transport = BUS_TRANSPORT_MACHINE;
|
2013-10-30 21:13:46 +01:00
|
|
|
arg_host = optarg;
|
2012-10-17 02:50:09 +02:00
|
|
|
break;
|
|
|
|
|
2013-05-17 15:38:12 +02:00
|
|
|
case ARG_NO_ASK_PASSWORD:
|
|
|
|
arg_ask_password = false;
|
|
|
|
break;
|
|
|
|
|
2012-10-17 22:52:21 +02:00
|
|
|
case ARG_ADJUST_SYSTEM_CLOCK:
|
|
|
|
arg_adjust_system_clock = true;
|
2012-10-17 02:50:09 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_NO_PAGER:
|
2018-11-11 12:56:29 +01:00
|
|
|
arg_pager_flags |= PAGER_DISABLE;
|
2012-10-17 02:50:09 +02:00
|
|
|
break;
|
|
|
|
|
2018-05-03 11:07:43 +02:00
|
|
|
case ARG_MONITOR:
|
|
|
|
arg_monitor = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p': {
|
|
|
|
r = strv_extend(&arg_property, optarg);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
/* If the user asked for a particular
|
|
|
|
* property, show it to him, even if it is
|
|
|
|
* empty. */
|
|
|
|
arg_all = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'a':
|
|
|
|
arg_all = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_VALUE:
|
|
|
|
arg_value = true;
|
|
|
|
break;
|
|
|
|
|
2012-10-17 02:50:09 +02:00
|
|
|
case '?':
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
default:
|
2013-11-06 18:28:39 +01:00
|
|
|
assert_not_reached("Unhandled option");
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-10-30 21:13:46 +01:00
|
|
|
static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
|
2018-03-14 07:09:28 +01:00
|
|
|
static const Verb verbs[] = {
|
2018-05-03 11:07:43 +02:00
|
|
|
{ "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
|
2018-06-10 10:17:34 +02:00
|
|
|
{ "show", VERB_ANY, 1, 0, show_properties },
|
2018-05-03 11:07:43 +02:00
|
|
|
{ "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 },
|
|
|
|
{ "timesync-status", VERB_ANY, 1, 0, show_timesync_status },
|
|
|
|
{ "show-timesync", VERB_ANY, 1, 0, show_timesync },
|
2019-05-28 05:07:47 +02:00
|
|
|
{ "ntp-servers", 3, VERB_ANY, 0, verb_ntp_servers },
|
|
|
|
{ "revert", 2, 2, 0, verb_revert },
|
2018-05-03 11:07:43 +02:00
|
|
|
{ "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
|
2018-03-14 07:09:28 +01:00
|
|
|
{}
|
2012-10-17 02:50:09 +02:00
|
|
|
};
|
|
|
|
|
2018-03-14 07:09:28 +01:00
|
|
|
return dispatch_verb(argc, argv, verbs, bus);
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
|
|
|
|
2018-11-20 09:18:21 +01:00
|
|
|
static int run(int argc, char *argv[]) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-11-06 17:32:51 +01:00
|
|
|
int r;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2012-11-12 20:16:07 +01:00
|
|
|
setlocale(LC_ALL, "");
|
2019-04-26 12:28:25 +02:00
|
|
|
log_show_color(true);
|
2012-10-17 02:50:09 +02:00
|
|
|
log_parse_environment();
|
|
|
|
log_open();
|
|
|
|
|
|
|
|
r = parse_argv(argc, argv);
|
2013-11-06 17:32:51 +01:00
|
|
|
if (r <= 0)
|
2018-11-20 09:18:21 +01:00
|
|
|
return r;
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2015-09-24 13:30:10 +02:00
|
|
|
r = bus_connect_transport(arg_transport, arg_host, false, &bus);
|
2018-11-20 09:18:21 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2012-10-17 02:50:09 +02:00
|
|
|
|
2018-11-20 09:18:21 +01:00
|
|
|
return timedatectl_main(bus, argc, argv);
|
2012-10-17 02:50:09 +02:00
|
|
|
}
|
2018-11-20 09:18:21 +01:00
|
|
|
|
|
|
|
DEFINE_MAIN_FUNCTION(run);
|