]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsclocks.c
Merge branch 'PR/libsmartcols-cell-data' of github.com:karelzak/util-linux-work
[thirdparty/util-linux.git] / misc-utils / lsclocks.c
CommitLineData
e9ddea79
TW
1/*
2 * lsclocks(1) - display system clocks
3 *
4 * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include <stdio.h>
22#include <stdbool.h>
23#include <time.h>
24#include <inttypes.h>
25#include <getopt.h>
6fcb27e7 26#include <glob.h>
edba74df 27#include <sys/ioctl.h>
e9ddea79
TW
28
29#include <libsmartcols.h>
30
edba74df
TW
31#include <linux/rtc.h>
32
e9ddea79
TW
33#include "c.h"
34#include "nls.h"
35#include "strutils.h"
36#include "timeutils.h"
37#include "closestream.h"
38#include "xalloc.h"
ed59648c
TW
39#include "pathnames.h"
40#include "all-io.h"
c4620c6a 41#include "list.h"
e9ddea79 42
e9ddea79
TW
43#ifndef CLOCK_REALTIME
44#define CLOCK_REALTIME 0
45#endif
46
47#ifndef CLOCK_MONOTONIC
48#define CLOCK_MONOTONIC 1
49#endif
50
51#ifndef CLOCK_MONOTONIC_RAW
52#define CLOCK_MONOTONIC_RAW 4
53#endif
54
55#ifndef CLOCK_REALTIME_COARSE
56#define CLOCK_REALTIME_COARSE 5
57#endif
58
59#ifndef CLOCK_MONOTONIC_COARSE
60#define CLOCK_MONOTONIC_COARSE 6
61#endif
62
63#ifndef CLOCK_BOOTTIME
64#define CLOCK_BOOTTIME 7
65#endif
66
67#ifndef CLOCK_REALTIME_ALARM
68#define CLOCK_REALTIME_ALARM 8
69#endif
70
71#ifndef CLOCK_BOOTTIME_ALARM
72#define CLOCK_BOOTTIME_ALARM 9
73#endif
74
75#ifndef CLOCK_TAI
76#define CLOCK_TAI 11
77#endif
78
ac9ee302
TW
79enum CLOCK_TYPE {
80 CT_SYS,
c4620c6a 81 CT_PTP,
dc3f05fc 82 CT_CPU,
edba74df 83 CT_RTC,
ac9ee302
TW
84};
85
86static const char *clock_type_name(enum CLOCK_TYPE type)
87{
88 switch (type) {
89 case CT_SYS:
90 return "sys";
c4620c6a
TW
91 case CT_PTP:
92 return "ptp";
dc3f05fc
TW
93 case CT_CPU:
94 return "cpu";
edba74df
TW
95 case CT_RTC:
96 return "rtc";
ac9ee302
TW
97 }
98 errx(EXIT_FAILURE, _("Unknown clock type %d"), type);
99}
100
e9ddea79 101struct clockinfo {
ac9ee302 102 enum CLOCK_TYPE type;
e9ddea79
TW
103 clockid_t id;
104 const char * const id_name;
105 const char * const name;
ed59648c 106 const char * const ns_offset_name;
edba74df 107 bool no_id;
e9ddea79
TW
108};
109
110static const struct clockinfo clocks[] = {
ac9ee302
TW
111 { CT_SYS, CLOCK_REALTIME, "CLOCK_REALTIME", "realtime" },
112 { CT_SYS, CLOCK_MONOTONIC, "CLOCK_MONOTONIC", "monotonic",
113 .ns_offset_name = "monotonic" },
114 { CT_SYS, CLOCK_MONOTONIC_RAW, "CLOCK_MONOTONIC_RAW", "monotonic-raw" },
115 { CT_SYS, CLOCK_REALTIME_COARSE, "CLOCK_REALTIME_COARSE", "realtime-coarse" },
116 { CT_SYS, CLOCK_MONOTONIC_COARSE, "CLOCK_MONOTONIC_COARSE", "monotonic-coarse" },
117 { CT_SYS, CLOCK_BOOTTIME, "CLOCK_BOOTTIME", "boottime",
118 .ns_offset_name = "boottime" },
119 { CT_SYS, CLOCK_REALTIME_ALARM, "CLOCK_REALTIME_ALARM", "realtime-alarm" },
120 { CT_SYS, CLOCK_BOOTTIME_ALARM, "CLOCK_BOOTTIME_ALARM", "boottime-alarm" },
121 { CT_SYS, CLOCK_TAI, "CLOCK_TAI", "tai" },
e9ddea79
TW
122};
123
124/* column IDs */
125enum {
ac9ee302 126 COL_TYPE,
e9ddea79
TW
127 COL_ID,
128 COL_CLOCK,
129 COL_NAME,
130 COL_TIME,
131 COL_ISO_TIME,
f9c9bb15 132 COL_RESOL,
36ebc72a 133 COL_RESOL_RAW,
0c7cba92 134 COL_REL_TIME,
ed59648c 135 COL_NS_OFFSET,
e9ddea79
TW
136};
137
138/* column names */
139struct colinfo {
140 const char * const name; /* header */
141 double whint; /* width hint (N < 1 is in percent of termwidth) */
142 int flags; /* SCOLS_FL_* */
143 int json_type; /* SCOLS_JSON_* */
144 const char * const help;
145};
146
147/* columns descriptions */
148static const struct colinfo infos[] = {
ac9ee302 149 [COL_TYPE] = { "TYPE", 1, 0, SCOLS_JSON_STRING, N_("type") },
e9ddea79
TW
150 [COL_ID] = { "ID", 1, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("numeric id") },
151 [COL_CLOCK] = { "CLOCK", 1, 0, SCOLS_JSON_STRING, N_("symbolic name") },
152 [COL_NAME] = { "NAME", 1, 0, SCOLS_JSON_STRING, N_("readable name") },
153 [COL_TIME] = { "TIME", 1, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("numeric time") },
154 [COL_ISO_TIME] = { "ISO_TIME", 1, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("human readable ISO time") },
f9c9bb15 155 [COL_RESOL] = { "RESOL", 1, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("human readable resolution") },
36ebc72a 156 [COL_RESOL_RAW] = { "RESOL_RAW", 1, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("resolution") },
0c7cba92 157 [COL_REL_TIME] = { "REL_TIME", 1, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("human readable relative time") },
ed59648c 158 [COL_NS_OFFSET] = { "NS_OFFSET", 1, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("namespace offset") },
e9ddea79
TW
159};
160
161static int column_name_to_id(const char *name, size_t namesz)
162{
163 size_t i;
164
165 for (i = 0; i < ARRAY_SIZE(infos); i++) {
166 const char *cn = infos[i].name;
167
168 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
169 return i;
170 }
171 warnx(_("unknown column: %s"), name);
172
173 return -1;
174}
175
176static void __attribute__((__noreturn__)) usage(void)
177{
178 FILE *out = stdout;
179 size_t i;
180
181 fputs(USAGE_HEADER, out);
182 fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
183
184 fputs(USAGE_OPTIONS, out);
c4620c6a
TW
185 fputs(_(" -J, --json use JSON output format\n"), out);
186 fputs(_(" -n, --noheadings don't print headings\n"), out);
187 fputs(_(" -o, --output <list> output columns\n"), out);
188 fputs(_(" --output-all output all columns\n"), out);
189 fputs(_(" -r, --raw use raw output format\n"), out);
190 fputs(_(" -t, --time <clock> show current time of single clock\n"), out);
6fcb27e7 191 fputs(_(" --no-discover-dynamic do not try to discover dynamic clocks\n"), out);
c4620c6a 192 fputs(_(" -d, --dynamic-clock <path> also display specified dynamic clock\n"), out);
dc3f05fc 193 fputs(_(" -c, --cpu-clock <pid> also display CPU clock of specified process\n"), out);
e9ddea79
TW
194
195 fputs(USAGE_SEPARATOR, out);
bad4c729 196 fprintf(out, USAGE_HELP_OPTIONS(29));
e9ddea79 197
38b8eac6 198 fputs(USAGE_COLUMNS, out);
e9ddea79
TW
199
200 for (i = 0; i < ARRAY_SIZE(infos); i++)
201 fprintf(out, " %16s %-10s%s\n", infos[i].name,
202 infos[i].json_type == SCOLS_JSON_STRING? "<string>":
203 infos[i].json_type == SCOLS_JSON_ARRAY_STRING? "<string>":
204 infos[i].json_type == SCOLS_JSON_ARRAY_NUMBER? "<string>":
205 infos[i].json_type == SCOLS_JSON_NUMBER? "<number>":
206 "<boolean>",
207 _(infos[i].help));
208
bad4c729 209 fprintf(out, USAGE_MAN_TAIL("lslocks(1)"));
e9ddea79
TW
210
211 exit(EXIT_SUCCESS);
212}
213
214__attribute__ ((__format__ (__printf__, 3, 4)))
215static void scols_line_asprintf(struct libscols_line *ln, size_t n, const char *format, ...)
216{
217 char *data;
218 va_list args;
219
220 va_start(args, format);
221 xvasprintf(&data, format, args);
222 va_end(args);
223
224 scols_line_refer_data(ln, n, data);
225}
226
227static void scols_line_format_timespec(struct libscols_line *ln, size_t n, const struct timespec *ts)
228{
229 scols_line_asprintf(ln, n, "%ju.%09" PRId32, (uintmax_t) ts->tv_sec, (uint32_t) ts->tv_nsec);
230}
231
232static clockid_t parse_clock(const char *name)
233{
234 size_t i;
235 uint32_t id = -1;
236 int rc;
237
238 rc = ul_strtou32(name, &id, 10);
239
240 for (i = 0; i < ARRAY_SIZE(clocks); i++) {
241 if (!strcmp(name, clocks[i].id_name)
242 || !strcmp(name, clocks[i].name))
243 return clocks[i].id;
244 if (rc == 0 && (clockid_t) id == clocks[i].id)
245 return id;
246 }
247
248 errx(EXIT_FAILURE, _("Unknown clock: %s"), name);
249}
250
ed59648c
TW
251static int64_t get_namespace_offset(const char *name)
252{
253 char *tokstr, *buf, *saveptr, *line, *space;
254 uint64_t ret;
255 int fd;
256
257 fd = open(_PATH_PROC_TIMENS_OFF, O_RDONLY);
258 if (fd == -1)
259 err(EXIT_FAILURE, _("Could not open %s"), _PATH_PROC_TIMENS_OFF);
260
261 read_all_alloc(fd, &buf);
262
263 for (tokstr = buf; ; tokstr = NULL) {
264 line = strtok_r(tokstr, "\n", &saveptr);
265 if (!line)
266 continue;
267 line = (char *) startswith(line, name);
268 if (!line || line[0] != ' ')
269 continue;
270
271 line = (char *) skip_blank(line);
272 space = strchr(line, ' ');
273 if (space)
274 *space = '\0';
275 ret = strtos64_or_err(line, _("Invalid offset"));
276 break;
277 }
278
279 free(buf);
280 return ret;
281}
282
c31251ef 283static void add_clock_line(struct libscols_table *tb, const int *columns,
edba74df
TW
284 size_t ncolumns, const struct clockinfo *clockinfo,
285 const struct timespec *now, const struct timespec *resolution)
c31251ef 286{
c31251ef
TW
287 char buf[FORMAT_TIMESTAMP_MAX];
288 struct libscols_line *ln;
289 size_t i;
290 int rc;
291
292 ln = scols_table_new_line(tb, NULL);
293 if (!ln)
294 errx(EXIT_FAILURE, _("failed to allocate output line"));
295
c31251ef
TW
296 for (i = 0; i < ncolumns; i++) {
297 switch (columns[i]) {
298 case COL_TYPE:
299 scols_line_set_data(ln, i, clock_type_name(clockinfo->type));
300 break;
301 case COL_ID:
edba74df 302 if (!clockinfo->no_id)
c4620c6a 303 scols_line_asprintf(ln, i, "%ju", (uintmax_t) clockinfo->id);
c31251ef
TW
304 break;
305 case COL_CLOCK:
306 scols_line_set_data(ln, i, clockinfo->id_name);
307 break;
308 case COL_NAME:
309 scols_line_set_data(ln, i, clockinfo->name);
310 break;
311 case COL_TIME:
edba74df 312 if (now->tv_nsec == -1)
c31251ef
TW
313 break;
314
edba74df 315 scols_line_format_timespec(ln, i, now);
c31251ef
TW
316 break;
317 case COL_ISO_TIME:
edba74df 318 if (now->tv_nsec == -1)
c31251ef
TW
319 break;
320
edba74df 321 rc = strtimespec_iso(now,
c31251ef
TW
322 ISO_GMTIME | ISO_DATE | ISO_TIME | ISO_T | ISO_DOTNSEC | ISO_TIMEZONE,
323 buf, sizeof(buf));
324 if (rc)
325 errx(EXIT_FAILURE, _("failed to format iso time"));
326 scols_line_set_data(ln, i, buf);
327 break;
328 case COL_RESOL:
edba74df 329 if (resolution->tv_nsec == -1)
c31251ef
TW
330 break;
331
edba74df 332 rc = strtimespec_relative(resolution, buf, sizeof(buf));
c31251ef
TW
333 if (rc)
334 errx(EXIT_FAILURE, _("failed to format relative time"));
335 scols_line_set_data(ln, i, buf);
336 break;
337 case COL_RESOL_RAW:
edba74df 338 if (resolution->tv_nsec == -1)
c31251ef 339 break;
edba74df 340 scols_line_format_timespec(ln, i, resolution);
c31251ef
TW
341 break;
342 case COL_REL_TIME:
edba74df 343 if (now->tv_nsec == -1)
c31251ef 344 break;
edba74df 345 rc = strtimespec_relative(now, buf, sizeof(buf));
c31251ef
TW
346 if (rc)
347 errx(EXIT_FAILURE, _("failed to format relative time"));
348 scols_line_set_data(ln, i, buf);
349 break;
350 case COL_NS_OFFSET:
351 if (clockinfo->ns_offset_name)
352 scols_line_asprintf(ln, i, "%"PRId64,
353 get_namespace_offset(clockinfo->ns_offset_name));
354 break;
355 }
356 }
357}
358
edba74df
TW
359static void add_posix_clock_line(struct libscols_table *tb, const int *columns,
360 size_t ncolumns, const struct clockinfo *clockinfo)
361{
362 struct timespec resolution, now;
363 int rc;
364
365 rc = clock_gettime(clockinfo->id, &now);
366 if (rc)
367 now.tv_nsec = -1;
368
369 rc = clock_getres(clockinfo->id, &resolution);
370 if (rc)
371 resolution.tv_nsec = -1;
372
373 add_clock_line(tb, columns, ncolumns, clockinfo, &now, &resolution);
374}
375
782c6564 376struct path_clock {
c4620c6a
TW
377 struct list_head head;
378 const char * path;
379};
380
381static void add_dynamic_clock_from_path(struct libscols_table *tb,
382 const int *columns, size_t ncolumns,
6fcb27e7 383 const char *path, bool explicit)
c4620c6a
TW
384{
385 int fd = open(path, O_RDONLY);
6fcb27e7
TW
386 if (fd == -1) {
387 if (explicit)
388 err(EXIT_FAILURE, _("Could not open %s"), path);
389 else
390 return;
391 }
c4620c6a
TW
392
393 struct clockinfo clockinfo = {
394 .type = CT_PTP,
edba74df 395 .no_id = true,
c4620c6a
TW
396 .id_name = path,
397 .name = path,
398 };
edba74df 399 add_posix_clock_line(tb, columns, ncolumns, &clockinfo);
c4620c6a
TW
400 close(fd);
401}
402
6fcb27e7
TW
403static void add_dynamic_clocks_from_discovery(struct libscols_table *tb,
404 const int *columns, size_t ncolumns)
405{
406 int rc;
407 size_t i;
408 glob_t state;
409
410 rc = glob("/dev/ptp*", 0, NULL, &state);
db5fffd5
TW
411 if (rc == GLOB_NOMATCH)
412 return;
413 else if (rc)
6fcb27e7
TW
414 errx(EXIT_FAILURE, _("Could not glob: %d"), rc);
415
416 for (i = 0; i < state.gl_pathc; i++)
417 add_dynamic_clock_from_path(tb, columns, ncolumns,
418 state.gl_pathv[i], false);
419
420 globfree(&state);
421}
422
edba74df
TW
423static void add_rtc_clock_from_path(struct libscols_table *tb,
424 const int *columns, size_t ncolumns,
425 const char *path, bool explicit)
426{
427 int fd, rc;
428 struct rtc_time rtc_time;
429 struct tm tm = { 0 };
430 struct timespec now = { 0 }, resolution = { .tv_nsec = -1 };
431
432 fd = open(path, O_RDONLY);
433 if (fd == -1) {
434 if (explicit)
435 err(EXIT_FAILURE, _("Could not open %s"), path);
436 else
437 return;
438 }
439
440 rc = ioctl(fd, RTC_RD_TIME, &rtc_time);
441 if (rc)
442 err(EXIT_FAILURE,
443 _("ioctl(RTC_RD_NAME) to %s to read the time failed"), path);
444
445 tm.tm_sec = rtc_time.tm_sec;
446 tm.tm_min = rtc_time.tm_min;
447 tm.tm_hour = rtc_time.tm_hour;
448 tm.tm_mday = rtc_time.tm_mday;
449 tm.tm_mon = rtc_time.tm_mon;
450 tm.tm_year = rtc_time.tm_year;
451 tm.tm_wday = rtc_time.tm_wday;
452 tm.tm_yday = rtc_time.tm_yday;
453
454 now.tv_sec = mktime(&tm);
455
456 struct clockinfo clockinfo = {
457 .type = CT_RTC,
458 .no_id = true,
459 .id_name = path,
460 .name = path,
461 };
462 add_clock_line(tb, columns, ncolumns, &clockinfo, &now, &resolution);
463
464 close(fd);
465}
466
467static void add_rtc_clocks_from_discovery(struct libscols_table *tb,
468 const int *columns, size_t ncolumns)
469{
470 int rc;
471 size_t i;
472 glob_t state;
473
474 rc = glob("/dev/rtc*", 0, NULL, &state);
475 if (rc == GLOB_NOMATCH)
476 return;
477 if (rc)
478 errx(EXIT_FAILURE, _("Could not glob: %d"), rc);
479
480 for (i = 0; i < state.gl_pathc; i++)
481 add_rtc_clock_from_path(tb, columns, ncolumns,
482 state.gl_pathv[i], false);
483
484 globfree(&state);
485}
486
dc3f05fc
TW
487struct cpu_clock {
488 struct list_head head;
489 pid_t pid;
490 char name[sizeof(stringify_value(SINT_MAX(pid_t)))];
491};
492
493static void add_cpu_clock(struct libscols_table *tb,
494 const int *columns, size_t ncolumns,
495 pid_t pid, const char *name)
496{
497 int rc;
498 clockid_t clockid;
499
500 rc = clock_getcpuclockid(pid, &clockid);
501 if (rc)
502 errx(EXIT_FAILURE, _("Could not get CPU clock of process %jd: %s"),
503 (intmax_t) pid, strerror(rc));
504
505 struct clockinfo clockinfo = {
506 .type = CT_CPU,
507 .name = name,
edba74df 508 .no_id = true,
dc3f05fc 509 };
edba74df 510 add_posix_clock_line(tb, columns, ncolumns, &clockinfo);
dc3f05fc
TW
511}
512
513
e9ddea79
TW
514int main(int argc, char **argv)
515{
c31251ef 516 size_t i;
9f59d21c 517 int c, rc;
e9ddea79 518 const struct colinfo *colinfo;
e9ddea79
TW
519
520 struct libscols_table *tb;
e9ddea79
TW
521 struct libscols_column *col;
522
edba74df
TW
523 bool noheadings = false, raw = false, json = false,
524 disc_dynamic = true, disc_rtc = true;
e9ddea79
TW
525 const char *outarg = NULL;
526 int columns[ARRAY_SIZE(infos) * 2];
527 size_t ncolumns = 0;
528 clockid_t clock = -1;
782c6564 529 struct path_clock *path_clock;
dc3f05fc 530 struct cpu_clock *cpu_clock;
782c6564 531 struct list_head *current_path_clock, *current_cpu_clock;
edba74df 532 struct list_head dynamic_clocks, cpu_clocks, rtc_clocks;
e9ddea79 533
c31251ef 534 struct timespec now;
e9ddea79 535
9f59d21c 536 enum {
6fcb27e7
TW
537 OPT_OUTPUT_ALL = CHAR_MAX + 1,
538 OPT_NO_DISC_DYN,
edba74df 539 OPT_NO_DISC_RTC,
9f59d21c 540 };
e9ddea79 541 static const struct option longopts[] = {
6fcb27e7
TW
542 { "noheadings", no_argument, NULL, 'n' },
543 { "output", required_argument, NULL, 'o' },
544 { "output-all", no_argument, NULL, OPT_OUTPUT_ALL },
545 { "version", no_argument, NULL, 'V' },
546 { "help", no_argument, NULL, 'h' },
547 { "json", no_argument, NULL, 'J' },
548 { "raw", no_argument, NULL, 'r' },
549 { "time", required_argument, NULL, 't' },
550 { "no-discover-dynamic", no_argument, NULL, OPT_NO_DISC_DYN },
551 { "dynamic-clock", required_argument, NULL, 'd' },
dc3f05fc 552 { "cpu-clock", required_argument, NULL, 'c' },
edba74df
TW
553 { "no-discover-rtc", no_argument, NULL, OPT_NO_DISC_RTC },
554 { "rtc", required_argument, NULL, 'x' },
e9ddea79
TW
555 { 0 }
556 };
557
558 setlocale(LC_ALL, "");
559 bindtextdomain(PACKAGE, LOCALEDIR);
560 textdomain(PACKAGE);
561 close_stdout_atexit();
562
c4620c6a 563 INIT_LIST_HEAD(&dynamic_clocks);
dc3f05fc 564 INIT_LIST_HEAD(&cpu_clocks);
edba74df 565 INIT_LIST_HEAD(&rtc_clocks);
c4620c6a 566
edba74df 567 while ((c = getopt_long(argc, argv, "no:Jrt:d:c:x:Vh", longopts, NULL)) != -1) {
e9ddea79
TW
568 switch (c) {
569 case 'n':
570 noheadings = true;
571 break;
572 case 'o':
573 outarg = optarg;
574 break;
9f59d21c
TW
575 case OPT_OUTPUT_ALL:
576 for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++)
577 columns[ncolumns] = ncolumns;
578 break;
e9ddea79
TW
579 case 'J':
580 json = true;
581 break;
582 case 'r':
583 raw = true;
584 break;
585 case 't':
586 clock = parse_clock(optarg);
587 break;
c4620c6a 588 case 'd':
782c6564
TW
589 path_clock = xmalloc(sizeof(*path_clock));
590 path_clock->path = optarg;
591 list_add(&path_clock->head, &dynamic_clocks);
c4620c6a 592 break;
6fcb27e7
TW
593 case OPT_NO_DISC_DYN:
594 disc_dynamic = false;
595 break;
dc3f05fc
TW
596 case 'c':
597 cpu_clock = xmalloc(sizeof(*cpu_clock));
598 cpu_clock->pid = strtopid_or_err(optarg, _("failed to parse pid"));
599 snprintf(cpu_clock->name, sizeof(cpu_clock->name),
600 "%jd", (intmax_t) cpu_clock->pid);
601 list_add(&cpu_clock->head, &cpu_clocks);
602 break;
edba74df
TW
603 case 'x':
604 path_clock = xmalloc(sizeof(*path_clock));
605 path_clock->path = optarg;
606 list_add(&path_clock->head, &rtc_clocks);
607 break;
608 case OPT_NO_DISC_RTC:
609 disc_rtc = false;
610 break;
e9ddea79
TW
611 case 'V':
612 print_version(EXIT_SUCCESS);
613 case 'h':
614 usage();
615 default:
616 errtryhelp(EXIT_FAILURE);
617 }
618 }
619
620 if (argv[optind])
621 errtryhelp(EXIT_FAILURE);
622
623 if (clock != -1) {
624 rc = clock_gettime(clock, &now);
625 if (rc)
626 err(EXIT_FAILURE, _("failed to get time"));
627 printf("%ju.%09"PRId32"\n", (uintmax_t) now.tv_sec, (uint32_t) now.tv_nsec);
628 return EXIT_SUCCESS;
629 }
630
631 if (!ncolumns) {
632 columns[ncolumns++] = COL_ID;
e9ddea79 633 columns[ncolumns++] = COL_NAME;
ac9ee302 634 columns[ncolumns++] = COL_TYPE;
e9ddea79 635 columns[ncolumns++] = COL_TIME;
f9c9bb15 636 columns[ncolumns++] = COL_RESOL;
2664e3a4 637 columns[ncolumns++] = COL_ISO_TIME;
e9ddea79
TW
638 }
639
640 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
641 &ncolumns, column_name_to_id) < 0)
642 return EXIT_FAILURE;
643
644 scols_init_debug(0);
645
646 tb = scols_new_table();
647 if (!tb)
648 errx(EXIT_FAILURE, _("failed to allocate output table"));
649 scols_table_set_name(tb, "clocks");
650
651 for (i = 0; i < ncolumns; i++) {
652 colinfo = &infos[columns[i]];
653
654 col = scols_table_new_column(tb, colinfo->name, colinfo->whint, colinfo->flags);
655 if (!col)
656 errx(EXIT_FAILURE, _("failed to allocate output column"));
657
658 scols_column_set_json_type(col, colinfo->json_type);
659 }
660
c31251ef 661 for (i = 0; i < ARRAY_SIZE(clocks); i++)
edba74df 662 add_posix_clock_line(tb, columns, ncolumns, &clocks[i]);
e9ddea79 663
6fcb27e7
TW
664 if (disc_dynamic)
665 add_dynamic_clocks_from_discovery(tb, columns, ncolumns);
666
782c6564
TW
667 list_for_each(current_path_clock, &dynamic_clocks) {
668 path_clock = list_entry(current_path_clock, struct path_clock, head);
669 add_dynamic_clock_from_path(tb, columns, ncolumns, path_clock->path, true);
c4620c6a
TW
670 }
671
782c6564 672 list_free(&dynamic_clocks, struct path_clock, head, free);
c4620c6a 673
edba74df
TW
674 if (disc_rtc)
675 add_rtc_clocks_from_discovery(tb, columns, ncolumns);
676
677 list_for_each(current_path_clock, &rtc_clocks) {
678 path_clock = list_entry(current_path_clock, struct path_clock, head);
679 add_rtc_clock_from_path(tb, columns, ncolumns, path_clock->path, true);
680 }
681
682 list_free(&rtc_clocks, struct path_clock, head, free);
683
dc3f05fc
TW
684 list_for_each(current_cpu_clock, &cpu_clocks) {
685 cpu_clock = list_entry(current_cpu_clock, struct cpu_clock, head);
686 add_cpu_clock(tb, columns, ncolumns, cpu_clock->pid, cpu_clock->name);
687 }
688
689 list_free(&cpu_clocks, struct cpu_clock, head, free);
690
e9ddea79
TW
691 scols_table_enable_json(tb, json);
692 scols_table_enable_raw(tb, raw);
693 scols_table_enable_noheadings(tb, noheadings);
694 scols_print_table(tb);
695 scols_unref_table(tb);
696}