aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bash-completion/lsclocks6
-rw-r--r--misc-utils/lsclocks.1.adoc7
-rw-r--r--misc-utils/lsclocks.c162
-rw-r--r--tests/expected/misc/lsclocks-rtc1
-rwxr-xr-xtests/ts/misc/lsclocks16
5 files changed, 160 insertions, 32 deletions
diff --git a/bash-completion/lsclocks b/bash-completion/lsclocks
index 30cf9c545a..7158701ea7 100644
--- a/bash-completion/lsclocks
+++ b/bash-completion/lsclocks
@@ -31,6 +31,11 @@ _lsclocks_module()
COMPREPLY=( $(compgen -o filenames -W "$clocks" -- "$cur") )
return 0
;;
+ '-x'|'--rtc')
+ clocks="$(echo /dev/rtc*)"
+ COMPREPLY=( $(compgen -o filenames -W "$clocks" -- "$cur") )
+ return 0
+ ;;
'-c'|'--cpu-clock')
_pids
return 0
@@ -48,6 +53,7 @@ _lsclocks_module()
--raw
--time
--dynamic-clock
+ --rtc
--cpu-clock
--help
--version"
diff --git a/misc-utils/lsclocks.1.adoc b/misc-utils/lsclocks.1.adoc
index 32de275f60..bb0e1db8bc 100644
--- a/misc-utils/lsclocks.1.adoc
+++ b/misc-utils/lsclocks.1.adoc
@@ -49,6 +49,13 @@ Do not try to discover dynamic clocks.
Also display specified dynamic clock.
Can be specified multiple times.
+*--no-discover-rtc*
+Do not try to discover RTCs.
+
+*-x*, *--rtc* _path_
+Also display specified RTC.
+Can be specified multiple times.
+
*-c*, *--cpu-clock* _pid_
Also display CPU clock of specified process.
Can be specified multiple times.
diff --git a/misc-utils/lsclocks.c b/misc-utils/lsclocks.c
index 1e81a42815..a8531510a5 100644
--- a/misc-utils/lsclocks.c
+++ b/misc-utils/lsclocks.c
@@ -24,9 +24,12 @@
#include <inttypes.h>
#include <getopt.h>
#include <glob.h>
+#include <sys/ioctl.h>
#include <libsmartcols.h>
+#include <linux/rtc.h>
+
#include "c.h"
#include "nls.h"
#include "strutils.h"
@@ -94,6 +97,7 @@ enum CLOCK_TYPE {
CT_SYS,
CT_PTP,
CT_CPU,
+ CT_RTC,
};
static const char *clock_type_name(enum CLOCK_TYPE type)
@@ -105,6 +109,8 @@ static const char *clock_type_name(enum CLOCK_TYPE type)
return "ptp";
case CT_CPU:
return "cpu";
+ case CT_RTC:
+ return "rtc";
}
errx(EXIT_FAILURE, _("Unknown clock type %d"), type);
}
@@ -115,6 +121,7 @@ struct clockinfo {
const char * const id_name;
const char * const name;
const char * const ns_offset_name;
+ bool no_id;
};
static const struct clockinfo clocks[] = {
@@ -291,9 +298,9 @@ static int64_t get_namespace_offset(const char *name)
}
static void add_clock_line(struct libscols_table *tb, const int *columns,
- size_t ncolumns, const struct clockinfo *clockinfo)
+ size_t ncolumns, const struct clockinfo *clockinfo,
+ const struct timespec *now, const struct timespec *resolution)
{
- struct timespec resolution, now;
char buf[FORMAT_TIMESTAMP_MAX];
struct libscols_line *ln;
size_t i;
@@ -303,22 +310,13 @@ static void add_clock_line(struct libscols_table *tb, const int *columns,
if (!ln)
errx(EXIT_FAILURE, _("failed to allocate output line"));
- /* outside the loop to guarantee consistency between COL_TIME and COL_ISO_TIME */
- rc = clock_gettime(clockinfo->id, &now);
- if (rc)
- now.tv_nsec = -1;
-
- rc = clock_getres(clockinfo->id, &resolution);
- if (rc)
- resolution.tv_nsec = -1;
-
for (i = 0; i < ncolumns; i++) {
switch (columns[i]) {
case COL_TYPE:
scols_line_set_data(ln, i, clock_type_name(clockinfo->type));
break;
case COL_ID:
- if (CLOCKID_IS_DYNAMIC(clockinfo->id))
+ if (!clockinfo->no_id)
scols_line_asprintf(ln, i, "%ju", (uintmax_t) clockinfo->id);
break;
case COL_CLOCK:
@@ -328,16 +326,16 @@ static void add_clock_line(struct libscols_table *tb, const int *columns,
scols_line_set_data(ln, i, clockinfo->name);
break;
case COL_TIME:
- if (now.tv_nsec == -1)
+ if (now->tv_nsec == -1)
break;
- scols_line_format_timespec(ln, i, &now);
+ scols_line_format_timespec(ln, i, now);
break;
case COL_ISO_TIME:
- if (now.tv_nsec == -1)
+ if (now->tv_nsec == -1)
break;
- rc = strtimespec_iso(&now,
+ rc = strtimespec_iso(now,
ISO_GMTIME | ISO_DATE | ISO_TIME | ISO_T | ISO_DOTNSEC | ISO_TIMEZONE,
buf, sizeof(buf));
if (rc)
@@ -345,23 +343,23 @@ static void add_clock_line(struct libscols_table *tb, const int *columns,
scols_line_set_data(ln, i, buf);
break;
case COL_RESOL:
- if (resolution.tv_nsec == -1)
+ if (resolution->tv_nsec == -1)
break;
- rc = strtimespec_relative(&resolution, buf, sizeof(buf));
+ rc = strtimespec_relative(resolution, buf, sizeof(buf));
if (rc)
errx(EXIT_FAILURE, _("failed to format relative time"));
scols_line_set_data(ln, i, buf);
break;
case COL_RESOL_RAW:
- if (resolution.tv_nsec == -1)
+ if (resolution->tv_nsec == -1)
break;
- scols_line_format_timespec(ln, i, &resolution);
+ scols_line_format_timespec(ln, i, resolution);
break;
case COL_REL_TIME:
- if (now.tv_nsec == -1)
+ if (now->tv_nsec == -1)
break;
- rc = strtimespec_relative(&now, buf, sizeof(buf));
+ rc = strtimespec_relative(now, buf, sizeof(buf));
if (rc)
errx(EXIT_FAILURE, _("failed to format relative time"));
scols_line_set_data(ln, i, buf);
@@ -375,6 +373,23 @@ static void add_clock_line(struct libscols_table *tb, const int *columns,
}
}
+static void add_posix_clock_line(struct libscols_table *tb, const int *columns,
+ size_t ncolumns, const struct clockinfo *clockinfo)
+{
+ struct timespec resolution, now;
+ int rc;
+
+ rc = clock_gettime(clockinfo->id, &now);
+ if (rc)
+ now.tv_nsec = -1;
+
+ rc = clock_getres(clockinfo->id, &resolution);
+ if (rc)
+ resolution.tv_nsec = -1;
+
+ add_clock_line(tb, columns, ncolumns, clockinfo, &now, &resolution);
+}
+
struct path_clock {
struct list_head head;
const char * path;
@@ -394,11 +409,11 @@ static void add_dynamic_clock_from_path(struct libscols_table *tb,
struct clockinfo clockinfo = {
.type = CT_PTP,
- .id = FD_TO_CLOCKID(fd),
+ .no_id = true,
.id_name = path,
.name = path,
};
- add_clock_line(tb, columns, ncolumns, &clockinfo);
+ add_posix_clock_line(tb, columns, ncolumns, &clockinfo);
close(fd);
}
@@ -422,6 +437,70 @@ static void add_dynamic_clocks_from_discovery(struct libscols_table *tb,
globfree(&state);
}
+static void add_rtc_clock_from_path(struct libscols_table *tb,
+ const int *columns, size_t ncolumns,
+ const char *path, bool explicit)
+{
+ int fd, rc;
+ struct rtc_time rtc_time;
+ struct tm tm = { 0 };
+ struct timespec now = { 0 }, resolution = { .tv_nsec = -1 };
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ if (explicit)
+ err(EXIT_FAILURE, _("Could not open %s"), path);
+ else
+ return;
+ }
+
+ rc = ioctl(fd, RTC_RD_TIME, &rtc_time);
+ if (rc)
+ err(EXIT_FAILURE,
+ _("ioctl(RTC_RD_NAME) to %s to read the time failed"), path);
+
+ tm.tm_sec = rtc_time.tm_sec;
+ tm.tm_min = rtc_time.tm_min;
+ tm.tm_hour = rtc_time.tm_hour;
+ tm.tm_mday = rtc_time.tm_mday;
+ tm.tm_mon = rtc_time.tm_mon;
+ tm.tm_year = rtc_time.tm_year;
+ tm.tm_wday = rtc_time.tm_wday;
+ tm.tm_yday = rtc_time.tm_yday;
+
+ now.tv_sec = mktime(&tm);
+
+ struct clockinfo clockinfo = {
+ .type = CT_RTC,
+ .no_id = true,
+ .id_name = path,
+ .name = path,
+ };
+ add_clock_line(tb, columns, ncolumns, &clockinfo, &now, &resolution);
+
+ close(fd);
+}
+
+static void add_rtc_clocks_from_discovery(struct libscols_table *tb,
+ const int *columns, size_t ncolumns)
+{
+ int rc;
+ size_t i;
+ glob_t state;
+
+ rc = glob("/dev/rtc*", 0, NULL, &state);
+ if (rc == GLOB_NOMATCH)
+ return;
+ if (rc)
+ errx(EXIT_FAILURE, _("Could not glob: %d"), rc);
+
+ for (i = 0; i < state.gl_pathc; i++)
+ add_rtc_clock_from_path(tb, columns, ncolumns,
+ state.gl_pathv[i], false);
+
+ globfree(&state);
+}
+
struct cpu_clock {
struct list_head head;
pid_t pid;
@@ -443,9 +522,9 @@ static void add_cpu_clock(struct libscols_table *tb,
struct clockinfo clockinfo = {
.type = CT_CPU,
.name = name,
- .id = clockid,
+ .no_id = true,
};
- add_clock_line(tb, columns, ncolumns, &clockinfo);
+ add_posix_clock_line(tb, columns, ncolumns, &clockinfo);
}
@@ -458,7 +537,8 @@ int main(int argc, char **argv)
struct libscols_table *tb;
struct libscols_column *col;
- bool noheadings = false, raw = false, json = false, disc_dynamic = true;
+ bool noheadings = false, raw = false, json = false,
+ disc_dynamic = true, disc_rtc = true;
const char *outarg = NULL;
int columns[ARRAY_SIZE(infos) * 2];
size_t ncolumns = 0;
@@ -466,13 +546,14 @@ int main(int argc, char **argv)
struct path_clock *path_clock;
struct cpu_clock *cpu_clock;
struct list_head *current_path_clock, *current_cpu_clock;
- struct list_head dynamic_clocks, cpu_clocks;
+ struct list_head dynamic_clocks, cpu_clocks, rtc_clocks;
struct timespec now;
enum {
OPT_OUTPUT_ALL = CHAR_MAX + 1,
OPT_NO_DISC_DYN,
+ OPT_NO_DISC_RTC,
};
static const struct option longopts[] = {
{ "noheadings", no_argument, NULL, 'n' },
@@ -486,6 +567,8 @@ int main(int argc, char **argv)
{ "no-discover-dynamic", no_argument, NULL, OPT_NO_DISC_DYN },
{ "dynamic-clock", required_argument, NULL, 'd' },
{ "cpu-clock", required_argument, NULL, 'c' },
+ { "no-discover-rtc", no_argument, NULL, OPT_NO_DISC_RTC },
+ { "rtc", required_argument, NULL, 'x' },
{ 0 }
};
@@ -496,8 +579,9 @@ int main(int argc, char **argv)
INIT_LIST_HEAD(&dynamic_clocks);
INIT_LIST_HEAD(&cpu_clocks);
+ INIT_LIST_HEAD(&rtc_clocks);
- while ((c = getopt_long(argc, argv, "no:Jrt:d:c:Vh", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "no:Jrt:d:c:x:Vh", longopts, NULL)) != -1) {
switch (c) {
case 'n':
noheadings = true;
@@ -533,6 +617,14 @@ int main(int argc, char **argv)
"%jd", (intmax_t) cpu_clock->pid);
list_add(&cpu_clock->head, &cpu_clocks);
break;
+ case 'x':
+ path_clock = xmalloc(sizeof(*path_clock));
+ path_clock->path = optarg;
+ list_add(&path_clock->head, &rtc_clocks);
+ break;
+ case OPT_NO_DISC_RTC:
+ disc_rtc = false;
+ break;
case 'V':
print_version(EXIT_SUCCESS);
case 'h':
@@ -584,7 +676,7 @@ int main(int argc, char **argv)
}
for (i = 0; i < ARRAY_SIZE(clocks); i++)
- add_clock_line(tb, columns, ncolumns, &clocks[i]);
+ add_posix_clock_line(tb, columns, ncolumns, &clocks[i]);
if (disc_dynamic)
add_dynamic_clocks_from_discovery(tb, columns, ncolumns);
@@ -596,6 +688,16 @@ int main(int argc, char **argv)
list_free(&dynamic_clocks, struct path_clock, head, free);
+ if (disc_rtc)
+ add_rtc_clocks_from_discovery(tb, columns, ncolumns);
+
+ list_for_each(current_path_clock, &rtc_clocks) {
+ path_clock = list_entry(current_path_clock, struct path_clock, head);
+ add_rtc_clock_from_path(tb, columns, ncolumns, path_clock->path, true);
+ }
+
+ list_free(&rtc_clocks, struct path_clock, head, free);
+
list_for_each(current_cpu_clock, &cpu_clocks) {
cpu_clock = list_entry(current_cpu_clock, struct cpu_clock, head);
add_cpu_clock(tb, columns, ncolumns, cpu_clock->pid, cpu_clock->name);
diff --git a/tests/expected/misc/lsclocks-rtc b/tests/expected/misc/lsclocks-rtc
new file mode 100644
index 0000000000..85615b147f
--- /dev/null
+++ b/tests/expected/misc/lsclocks-rtc
@@ -0,0 +1 @@
+rtc /dev/rtc0 /dev/rtc0
diff --git a/tests/ts/misc/lsclocks b/tests/ts/misc/lsclocks
index f1e403c5bd..71fefaa52b 100755
--- a/tests/ts/misc/lsclocks
+++ b/tests/ts/misc/lsclocks
@@ -27,9 +27,11 @@ mask_timestamps() {
sed 's/[0-9]\+\.[0-9]\+/X.X/g'
}
+NO_DISCOVER="--no-discover-dynamic --no-discover-rtc"
+
ts_init_subtest basic
-"$TS_CMD_LSCLOCKS" --no-discover-dynamic -o TYPE,ID,CLOCK,NAME > "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+"$TS_CMD_LSCLOCKS" $NO_DISCOVER -o TYPE,ID,CLOCK,NAME > "$TS_OUTPUT" 2>> "$TS_ERRLOG"
ts_finalize_subtest
@@ -42,13 +44,23 @@ ts_finalize_subtest
ts_init_subtest dynamic
if [ -c /dev/ptp0 ] && [ -r /dev/ptp0 ]; then
- "$TS_CMD_LSCLOCKS" --no-discover-dynamic --dynamic-clock /dev/ptp0 --output TYPE,ID,CLOCK,NAME \
+ "$TS_CMD_LSCLOCKS" $NO_DISCOVER --dynamic-clock /dev/ptp0 --output TYPE,ID,CLOCK,NAME \
| tail -1 > "$TS_OUTPUT" 2>> "$TS_ERRLOG"
ts_finalize_subtest
else
ts_skip_subtest "/dev/ptp0 not usable"
fi
+ts_init_subtest rtc
+
+if [ -c /dev/rtc0 ] && [ -r /dev/rtc0 ]; then
+ "$TS_CMD_LSCLOCKS" $NO_DISCOVER --rtc /dev/rtc0 --output TYPE,ID,CLOCK,NAME \
+ | tail -1 > "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+ ts_finalize_subtest
+else
+ ts_skip_subtest "/dev/rtc0 not usable"
+fi
+
ts_init_subtest cpu
"$TS_CMD_LSCLOCKS" --cpu 1 --output TYPE,ID,NAME | tail -1 > "$TS_OUTPUT" 2>> "$TS_ERRLOG"