]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsclocks: add support for RTC
authorThomas Weißschuh <thomas@t-8ch.de>
Tue, 11 Jul 2023 19:01:05 +0000 (21:01 +0200)
committerThomas Weißschuh <thomas@t-8ch.de>
Wed, 2 Aug 2023 11:51:16 +0000 (13:51 +0200)
Signed-off-by: Thomas Weißschuh <thomas@t-8ch.de>
bash-completion/lsclocks
misc-utils/lsclocks.1.adoc
misc-utils/lsclocks.c
tests/expected/misc/lsclocks-rtc [new file with mode: 0644]
tests/ts/misc/lsclocks

index 30cf9c545a2dd6c6202d9f05d593d396225a29d1..7158701ea7ce7bf56e114cea433662d3c52d5999 100644 (file)
@@ -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"
index 32de275f60b7f4f3af0b1e126bd9ff79893b25c4..bb0e1db8bc789dc50cb147ad6cbc13518efd98e9 100644 (file)
@@ -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.
index 1e81a428153918297945246f196f85897aeb2630..a8531510a5440b4e46bb3ef1080d36f21c44108f 100644 (file)
 #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 (file)
index 0000000..85615b1
--- /dev/null
@@ -0,0 +1 @@
+rtc     /dev/rtc0              /dev/rtc0
index f1e403c5bdd835041305cd7ea67e97dc02760448..71fefaa52bf650db7f778e715235e44f203e54f8 100755 (executable)
@@ -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"