Merge branch 'clock' of https://github.com/t-8ch/util-linux
* 'clock' of https://github.com/t-8ch/util-linux: lsclocks: new util to interact with system clocks lib/timeutils: implement timespec formatting lib/timeutils: implement nanosecond formatting lib/timeutils: constify some arguments utmpdump: validate subsecond granularity
This commit is contained in:
commit
6462dbe2ef
16 changed files with 625 additions and 17 deletions
|
@ -347,5 +347,8 @@ endif
|
||||||
if BUILD_ENOSYS
|
if BUILD_ENOSYS
|
||||||
dist_bashcompletion_DATA += bash-completion/enosys
|
dist_bashcompletion_DATA += bash-completion/enosys
|
||||||
endif
|
endif
|
||||||
|
if BUILD_LSCLOCKS
|
||||||
|
dist_bashcompletion_DATA += bash-completion/lsclocks
|
||||||
|
endif
|
||||||
|
|
||||||
endif # BUILD_BASH_COMPLETION
|
endif # BUILD_BASH_COMPLETION
|
||||||
|
|
48
bash-completion/lsclocks
Normal file
48
bash-completion/lsclocks
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
_lsclocks_module()
|
||||||
|
{
|
||||||
|
local cur prev OPTS
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
'-J'|'--json')
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
'-n'|'--noheadings')
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
'-o'|'--output')
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
'-r'|'--raw')
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
'-t'|'--time')
|
||||||
|
clocks="$(command "$1" --noheadings --raw --output NAME)"
|
||||||
|
COMPREPLY=( $(compgen -W "$clocks" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
'-h'|'--help'|'-V'|'--version')
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
case $cur in
|
||||||
|
-*)
|
||||||
|
OPTS="--json
|
||||||
|
--noheadings
|
||||||
|
--output
|
||||||
|
--raw
|
||||||
|
--time
|
||||||
|
--help
|
||||||
|
--version"
|
||||||
|
COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
complete -F _lsclocks_module lsclocks
|
|
@ -1894,6 +1894,9 @@ AS_IF([test "x$build_enosys" = xyes], [
|
||||||
])
|
])
|
||||||
AM_CONDITIONAL([BUILD_ENOSYS], [test "x$build_enosys" = xyes])
|
AM_CONDITIONAL([BUILD_ENOSYS], [test "x$build_enosys" = xyes])
|
||||||
|
|
||||||
|
UL_BUILD_INIT([lsclocks], [yes])
|
||||||
|
AM_CONDITIONAL([BUILD_LSCLOCKS], [test "x$build_lsclocks" = xyes])
|
||||||
|
|
||||||
UL_BUILD_INIT([getopt], [yes])
|
UL_BUILD_INIT([getopt], [yes])
|
||||||
AM_CONDITIONAL([BUILD_GETOPT], [test "x$build_getopt" = xyes])
|
AM_CONDITIONAL([BUILD_GETOPT], [test "x$build_getopt" = xyes])
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,10 @@ enum {
|
||||||
ISO_TIMEZONE = (1 << 2),
|
ISO_TIMEZONE = (1 << 2),
|
||||||
ISO_DOTUSEC = (1 << 3),
|
ISO_DOTUSEC = (1 << 3),
|
||||||
ISO_COMMAUSEC = (1 << 4),
|
ISO_COMMAUSEC = (1 << 4),
|
||||||
ISO_T = (1 << 5),
|
ISO_DOTNSEC = (1 << 5),
|
||||||
ISO_GMTIME = (1 << 6),
|
ISO_COMMANSEC = (1 << 6),
|
||||||
|
ISO_T = (1 << 7),
|
||||||
|
ISO_GMTIME = (1 << 8),
|
||||||
ISO_TIMESTAMP = ISO_DATE | ISO_TIME | ISO_TIMEZONE,
|
ISO_TIMESTAMP = ISO_DATE | ISO_TIME | ISO_TIMEZONE,
|
||||||
ISO_TIMESTAMP_T = ISO_TIMESTAMP | ISO_T,
|
ISO_TIMESTAMP_T = ISO_TIMESTAMP | ISO_T,
|
||||||
ISO_TIMESTAMP_DOT = ISO_TIMESTAMP | ISO_DOTUSEC,
|
ISO_TIMESTAMP_DOT = ISO_TIMESTAMP | ISO_DOTUSEC,
|
||||||
|
@ -72,9 +74,10 @@ enum {
|
||||||
#define CTIME_BUFSIZ 26
|
#define CTIME_BUFSIZ 26
|
||||||
#define ISO_BUFSIZ 42
|
#define ISO_BUFSIZ 42
|
||||||
|
|
||||||
int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz);
|
int strtimeval_iso(const struct timeval *tv, int flags, char *buf, size_t bufsz);
|
||||||
int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz);
|
int strtm_iso(const struct tm *tm, int flags, char *buf, size_t bufsz);
|
||||||
int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz);
|
int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz);
|
||||||
|
int strtimespec_iso(const struct timespec *t, int flags, char *buf, size_t bufsz);
|
||||||
|
|
||||||
#define UL_SHORTTIME_THISYEAR_HHMM (1 << 1)
|
#define UL_SHORTTIME_THISYEAR_HHMM (1 << 1)
|
||||||
|
|
||||||
|
|
|
@ -448,8 +448,9 @@ int get_gmtoff(const struct tm *tp)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz)
|
static int format_iso_time(const struct tm *tm, uint32_t nsec, int flags, char *buf, size_t bufsz)
|
||||||
{
|
{
|
||||||
|
uint32_t usec = nsec / NSEC_PER_USEC;
|
||||||
char *p = buf;
|
char *p = buf;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
@ -479,15 +480,28 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf
|
||||||
p += len;
|
p += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & ISO_DOTUSEC) {
|
if (flags & ISO_DOTNSEC) {
|
||||||
len = snprintf(p, bufsz, ".%06"PRId64, (int64_t) usec);
|
len = snprintf(p, bufsz, ".%09"PRIu32, nsec);
|
||||||
|
if (len < 0 || (size_t) len > bufsz)
|
||||||
|
goto err;
|
||||||
|
bufsz -= len;
|
||||||
|
p += len;
|
||||||
|
|
||||||
|
} else if (flags & ISO_COMMANSEC) {
|
||||||
|
len = snprintf(p, bufsz, ",%09"PRIu32, nsec);
|
||||||
|
if (len < 0 || (size_t) len > bufsz)
|
||||||
|
goto err;
|
||||||
|
bufsz -= len;
|
||||||
|
p += len;
|
||||||
|
} else if (flags & ISO_DOTUSEC) {
|
||||||
|
len = snprintf(p, bufsz, ".%06"PRIu32, usec);
|
||||||
if (len < 0 || (size_t) len > bufsz)
|
if (len < 0 || (size_t) len > bufsz)
|
||||||
goto err;
|
goto err;
|
||||||
bufsz -= len;
|
bufsz -= len;
|
||||||
p += len;
|
p += len;
|
||||||
|
|
||||||
} else if (flags & ISO_COMMAUSEC) {
|
} else if (flags & ISO_COMMAUSEC) {
|
||||||
len = snprintf(p, bufsz, ",%06"PRId64, (int64_t) usec);
|
len = snprintf(p, bufsz, ",%06"PRIu32, usec);
|
||||||
if (len < 0 || (size_t) len > bufsz)
|
if (len < 0 || (size_t) len > bufsz)
|
||||||
goto err;
|
goto err;
|
||||||
bufsz -= len;
|
bufsz -= len;
|
||||||
|
@ -508,26 +522,37 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* timeval to ISO 8601 */
|
/* timespec to ISO 8601 */
|
||||||
int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz)
|
int strtimespec_iso(const struct timespec *ts, int flags, char *buf, size_t bufsz)
|
||||||
{
|
{
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
struct tm *rc;
|
struct tm *rc;
|
||||||
|
|
||||||
if (flags & ISO_GMTIME)
|
if (flags & ISO_GMTIME)
|
||||||
rc = gmtime_r(&tv->tv_sec, &tm);
|
rc = gmtime_r(&ts->tv_sec, &tm);
|
||||||
else
|
else
|
||||||
rc = localtime_r(&tv->tv_sec, &tm);
|
rc = localtime_r(&ts->tv_sec, &tm);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz);
|
return format_iso_time(&tm, ts->tv_nsec, flags, buf, bufsz);
|
||||||
|
|
||||||
warnx(_("time %"PRId64" is out of range."), (int64_t)(tv->tv_sec));
|
warnx(_("time %"PRId64" is out of range."), (int64_t)(ts->tv_sec));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* timeval to ISO 8601 */
|
||||||
|
int strtimeval_iso(const struct timeval *tv, int flags, char *buf, size_t bufsz)
|
||||||
|
{
|
||||||
|
struct timespec ts = {
|
||||||
|
.tv_sec = tv->tv_sec,
|
||||||
|
.tv_nsec = tv->tv_usec * NSEC_PER_USEC,
|
||||||
|
};
|
||||||
|
|
||||||
|
return strtimespec_iso(&ts, flags, buf, bufsz);
|
||||||
|
}
|
||||||
|
|
||||||
/* struct tm to ISO 8601 */
|
/* struct tm to ISO 8601 */
|
||||||
int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz)
|
int strtm_iso(const struct tm *tm, int flags, char *buf, size_t bufsz)
|
||||||
{
|
{
|
||||||
return format_iso_time(tm, 0, flags, buf, bufsz);
|
return format_iso_time(tm, 0, flags, buf, bufsz);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ static void print_utline(struct utmpx *ut, FILE *out)
|
||||||
addr_string = inet_ntop(AF_INET, &(ut->ut_addr_v6), buffer, sizeof(buffer));
|
addr_string = inet_ntop(AF_INET, &(ut->ut_addr_v6), buffer, sizeof(buffer));
|
||||||
|
|
||||||
tv.tv_sec = ut->ut_tv.tv_sec;
|
tv.tv_sec = ut->ut_tv.tv_sec;
|
||||||
tv.tv_usec = ut->ut_tv.tv_usec;
|
tv.tv_usec = ut->ut_tv.tv_usec < (int32_t) USEC_PER_SEC ? ut->ut_tv.tv_usec : 0;
|
||||||
|
|
||||||
if (strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA_GT, time_string,
|
if (strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA_GT, time_string,
|
||||||
sizeof(time_string)) != 0)
|
sizeof(time_string)) != 0)
|
||||||
|
|
13
meson.build
13
meson.build
|
@ -2908,6 +2908,19 @@ if cc.compiles(fs.read('include/audit-arch.h'), name : 'has AUDIT_ARCH_NATIVE')
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
exe = executable(
|
||||||
|
'lsclocks',
|
||||||
|
lsclocks_sources,
|
||||||
|
include_directories : includes,
|
||||||
|
link_with : [lib_common, lib_smartcols],
|
||||||
|
install_dir : usrbin_exec_dir,
|
||||||
|
install : true)
|
||||||
|
if not is_disabler(exe)
|
||||||
|
exes += exe
|
||||||
|
manadocs += ['misc-utils/lsclocks.1.adoc']
|
||||||
|
bashcompletions += ['lsclocks']
|
||||||
|
endif
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
opt = not get_option('build-schedutils').disabled()
|
opt = not get_option('build-schedutils').disabled()
|
||||||
|
|
|
@ -316,3 +316,11 @@ enosys_SOURCES = misc-utils/enosys.c
|
||||||
enosys_LDADD = $(LDADD) libcommon.la
|
enosys_LDADD = $(LDADD) libcommon.la
|
||||||
enosys_CFLAGS = $(AM_CFLAGS)
|
enosys_CFLAGS = $(AM_CFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if BUILD_LSCLOCKS
|
||||||
|
usrbin_exec_PROGRAMS += lsclocks
|
||||||
|
MANPAGES += misc-utils/lsclocks.1
|
||||||
|
lsclocks_SOURCES = misc-utils/lsclocks.c
|
||||||
|
lsclocks_LDADD = $(LDADD) libcommon.la libsmartcols.la
|
||||||
|
lsclocks_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
|
||||||
|
endif
|
||||||
|
|
81
misc-utils/lsclocks.1.adoc
Normal file
81
misc-utils/lsclocks.1.adoc
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
//po4a: entry man manual
|
||||||
|
= lsclocks(1)
|
||||||
|
:doctype: manpage
|
||||||
|
:man manual: User Commands
|
||||||
|
:man source: util-linux {release-version}
|
||||||
|
:page-layout: base
|
||||||
|
:command: lsclocks
|
||||||
|
|
||||||
|
== NAME
|
||||||
|
|
||||||
|
lsclocks - display system clocks
|
||||||
|
|
||||||
|
== SYNOPSIS
|
||||||
|
|
||||||
|
*lsclocks* [option]
|
||||||
|
|
||||||
|
== DESCRIPTION
|
||||||
|
|
||||||
|
*lsclocks* is a simple command to display system clocks.
|
||||||
|
|
||||||
|
It allows to display information like current time and resolutionof clocks like
|
||||||
|
CLOCK_MONOTONIC, CLOCK_REALTIME and CLOCK_BOOTTIME.
|
||||||
|
|
||||||
|
== OPTIONS
|
||||||
|
|
||||||
|
*-J*, *--json*::
|
||||||
|
Use JSON output format.
|
||||||
|
|
||||||
|
*-n*, *--noheadings*::
|
||||||
|
Don't print headings.
|
||||||
|
|
||||||
|
*-o*, *--output* _list_::
|
||||||
|
Specify which output columns to print. See the *OUTPUT COLUMNS*
|
||||||
|
section for details of available columns.
|
||||||
|
|
||||||
|
*-r*, *--raw*::
|
||||||
|
Use raw output format.
|
||||||
|
|
||||||
|
*-r*, *--time* _clock_
|
||||||
|
Show current time of one specific clocks.
|
||||||
|
|
||||||
|
include::man-common/help-version.adoc[]
|
||||||
|
|
||||||
|
== OUTPUT COLUMNS
|
||||||
|
|
||||||
|
Each column has a type. Types are surround by < and >.
|
||||||
|
|
||||||
|
ID <``number``>::
|
||||||
|
Numeric clock ID.
|
||||||
|
|
||||||
|
CLOCK <``string``>::
|
||||||
|
Name in the form *CLOCK_*
|
||||||
|
|
||||||
|
NAME <``string``>::
|
||||||
|
Shorter, easier to read name.
|
||||||
|
|
||||||
|
TIME <``number``>::
|
||||||
|
Current clock timestamp as returned by *clock_gettime()*.
|
||||||
|
|
||||||
|
ISO_TIME <``string``>::
|
||||||
|
ISO8601 formatted version of *TIME*.
|
||||||
|
|
||||||
|
RESOLUTION <``number``>::
|
||||||
|
Clock resolution as returned by *clock_getres()*.
|
||||||
|
|
||||||
|
|
||||||
|
== AUTHORS
|
||||||
|
|
||||||
|
mailto:thomas@t-8ch.de[Thomas Weißschuh]
|
||||||
|
|
||||||
|
== SEE ALSO
|
||||||
|
|
||||||
|
*clock_getres*(2) *clock_gettime*(2)
|
||||||
|
|
||||||
|
include::man-common/bugreports.adoc[]
|
||||||
|
|
||||||
|
include::man-common/footer.adoc[]
|
||||||
|
|
||||||
|
ifdef::translation[]
|
||||||
|
include::man-common/translation.adoc[]
|
||||||
|
endif::[]
|
366
misc-utils/lsclocks.c
Normal file
366
misc-utils/lsclocks.c
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
/*
|
||||||
|
* lsclocks(1) - display system clocks
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it would be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include <libsmartcols.h>
|
||||||
|
|
||||||
|
#include "c.h"
|
||||||
|
#include "nls.h"
|
||||||
|
#include "strutils.h"
|
||||||
|
#include "timeutils.h"
|
||||||
|
#include "closestream.h"
|
||||||
|
#include "xalloc.h"
|
||||||
|
|
||||||
|
#define CLOCKFD 3
|
||||||
|
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
|
||||||
|
|
||||||
|
#ifndef CLOCK_REALTIME
|
||||||
|
#define CLOCK_REALTIME 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_MONOTONIC
|
||||||
|
#define CLOCK_MONOTONIC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_MONOTONIC_RAW
|
||||||
|
#define CLOCK_MONOTONIC_RAW 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_REALTIME_COARSE
|
||||||
|
#define CLOCK_REALTIME_COARSE 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_MONOTONIC_COARSE
|
||||||
|
#define CLOCK_MONOTONIC_COARSE 6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_BOOTTIME
|
||||||
|
#define CLOCK_BOOTTIME 7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_REALTIME_ALARM
|
||||||
|
#define CLOCK_REALTIME_ALARM 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_BOOTTIME_ALARM
|
||||||
|
#define CLOCK_BOOTTIME_ALARM 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLOCK_TAI
|
||||||
|
#define CLOCK_TAI 11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct clockinfo {
|
||||||
|
clockid_t id;
|
||||||
|
const char * const id_name;
|
||||||
|
const char * const name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct clockinfo clocks[] = {
|
||||||
|
{ CLOCK_REALTIME, "CLOCK_REALTIME", "realtime" },
|
||||||
|
{ CLOCK_MONOTONIC, "CLOCK_MONOTONIC", "monotonic" },
|
||||||
|
{ CLOCK_MONOTONIC_RAW, "CLOCK_MONOTONIC_RAW", "monotonic-raw" },
|
||||||
|
{ CLOCK_REALTIME_COARSE, "CLOCK_REALTIME_COARSE", "realtime-coarse" },
|
||||||
|
{ CLOCK_MONOTONIC_COARSE, "CLOCK_MONOTONIC_COARSE", "monotonic-coarse" },
|
||||||
|
{ CLOCK_BOOTTIME, "CLOCK_BOOTTIME", "boottime" },
|
||||||
|
{ CLOCK_REALTIME_ALARM, "CLOCK_REALTIME_ALARM", "realtime-alarm" },
|
||||||
|
{ CLOCK_BOOTTIME_ALARM, "CLOCK_BOOTTIME_ALARM", "boottime-alarm" },
|
||||||
|
{ CLOCK_TAI, "CLOCK_TAI", "tai" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* column IDs */
|
||||||
|
enum {
|
||||||
|
COL_ID,
|
||||||
|
COL_CLOCK,
|
||||||
|
COL_NAME,
|
||||||
|
COL_TIME,
|
||||||
|
COL_ISO_TIME,
|
||||||
|
COL_RESOLUTION,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* column names */
|
||||||
|
struct colinfo {
|
||||||
|
const char * const name; /* header */
|
||||||
|
double whint; /* width hint (N < 1 is in percent of termwidth) */
|
||||||
|
int flags; /* SCOLS_FL_* */
|
||||||
|
int json_type; /* SCOLS_JSON_* */
|
||||||
|
const char * const help;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* columns descriptions */
|
||||||
|
static const struct colinfo infos[] = {
|
||||||
|
[COL_ID] = { "ID", 1, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("numeric id") },
|
||||||
|
[COL_CLOCK] = { "CLOCK", 1, 0, SCOLS_JSON_STRING, N_("symbolic name") },
|
||||||
|
[COL_NAME] = { "NAME", 1, 0, SCOLS_JSON_STRING, N_("readable name") },
|
||||||
|
[COL_TIME] = { "TIME", 1, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("numeric time") },
|
||||||
|
[COL_ISO_TIME] = { "ISO_TIME", 1, SCOLS_FL_RIGHT, SCOLS_JSON_STRING, N_("human readable ISO time") },
|
||||||
|
[COL_RESOLUTION] = { "RESOLUTION", 1, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER, N_("resolution") },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int column_name_to_id(const char *name, size_t namesz)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(infos); i++) {
|
||||||
|
const char *cn = infos[i].name;
|
||||||
|
|
||||||
|
if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
warnx(_("unknown column: %s"), name);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((__noreturn__)) usage(void)
|
||||||
|
{
|
||||||
|
FILE *out = stdout;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
fputs(USAGE_HEADER, out);
|
||||||
|
fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
|
||||||
|
|
||||||
|
fputs(USAGE_OPTIONS, out);
|
||||||
|
fputs(_(" -J, --json use JSON output format\n"), out);
|
||||||
|
fputs(_(" -n, --noheadings don't print headings\n"), out);
|
||||||
|
fputs(_(" -o, --output <list> output columns\n"), out);
|
||||||
|
fputs(_(" -r, --raw use raw output format\n"), out);
|
||||||
|
fputs(_(" -t, --time <clock> show current time of single clock\n"), out);
|
||||||
|
|
||||||
|
fputs(USAGE_SEPARATOR, out);
|
||||||
|
printf(USAGE_HELP_OPTIONS(25));
|
||||||
|
|
||||||
|
fprintf(out, USAGE_COLUMNS);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(infos); i++)
|
||||||
|
fprintf(out, " %16s %-10s%s\n", infos[i].name,
|
||||||
|
infos[i].json_type == SCOLS_JSON_STRING? "<string>":
|
||||||
|
infos[i].json_type == SCOLS_JSON_ARRAY_STRING? "<string>":
|
||||||
|
infos[i].json_type == SCOLS_JSON_ARRAY_NUMBER? "<string>":
|
||||||
|
infos[i].json_type == SCOLS_JSON_NUMBER? "<number>":
|
||||||
|
"<boolean>",
|
||||||
|
_(infos[i].help));
|
||||||
|
|
||||||
|
printf(USAGE_MAN_TAIL("lslocks(1)"));
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((__format__ (__printf__, 3, 4)))
|
||||||
|
static void scols_line_asprintf(struct libscols_line *ln, size_t n, const char *format, ...)
|
||||||
|
{
|
||||||
|
char *data;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
xvasprintf(&data, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
scols_line_refer_data(ln, n, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scols_line_format_timespec(struct libscols_line *ln, size_t n, const struct timespec *ts)
|
||||||
|
{
|
||||||
|
scols_line_asprintf(ln, n, "%ju.%09" PRId32, (uintmax_t) ts->tv_sec, (uint32_t) ts->tv_nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static clockid_t parse_clock(const char *name)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint32_t id = -1;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = ul_strtou32(name, &id, 10);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(clocks); i++) {
|
||||||
|
if (!strcmp(name, clocks[i].id_name)
|
||||||
|
|| !strcmp(name, clocks[i].name))
|
||||||
|
return clocks[i].id;
|
||||||
|
if (rc == 0 && (clockid_t) id == clocks[i].id)
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
errx(EXIT_FAILURE, _("Unknown clock: %s"), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
size_t i, j;
|
||||||
|
char c;
|
||||||
|
int rc;
|
||||||
|
const struct colinfo *colinfo;
|
||||||
|
const struct clockinfo *clockinfo;
|
||||||
|
|
||||||
|
struct libscols_table *tb;
|
||||||
|
struct libscols_line *ln;
|
||||||
|
struct libscols_column *col;
|
||||||
|
|
||||||
|
bool noheadings = false, raw = false, json = false;
|
||||||
|
const char *outarg = NULL;
|
||||||
|
int columns[ARRAY_SIZE(infos) * 2];
|
||||||
|
size_t ncolumns = 0;
|
||||||
|
clockid_t clock = -1;
|
||||||
|
|
||||||
|
struct timespec resolution, now;
|
||||||
|
char buf[FORMAT_TIMESTAMP_MAX];
|
||||||
|
|
||||||
|
static const struct option longopts[] = {
|
||||||
|
{ "noheadings", no_argument, NULL, 'n' },
|
||||||
|
{ "output", required_argument, NULL, 'o' },
|
||||||
|
{ "version", no_argument, NULL, 'V' },
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "json", no_argument, NULL, 'J' },
|
||||||
|
{ "raw", no_argument, NULL, 'r' },
|
||||||
|
{ "time", required_argument, NULL, 't' },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
||||||
|
textdomain(PACKAGE);
|
||||||
|
close_stdout_atexit();
|
||||||
|
|
||||||
|
while ((c = getopt_long(argc, argv, "no:Jrt:Vh", longopts, NULL)) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'n':
|
||||||
|
noheadings = true;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
outarg = optarg;
|
||||||
|
break;
|
||||||
|
case 'J':
|
||||||
|
json = true;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
raw = true;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
clock = parse_clock(optarg);
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
print_version(EXIT_SUCCESS);
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
default:
|
||||||
|
errtryhelp(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv[optind])
|
||||||
|
errtryhelp(EXIT_FAILURE);
|
||||||
|
|
||||||
|
if (clock != -1) {
|
||||||
|
rc = clock_gettime(clock, &now);
|
||||||
|
if (rc)
|
||||||
|
err(EXIT_FAILURE, _("failed to get time"));
|
||||||
|
printf("%ju.%09"PRId32"\n", (uintmax_t) now.tv_sec, (uint32_t) now.tv_nsec);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ncolumns) {
|
||||||
|
columns[ncolumns++] = COL_ID;
|
||||||
|
columns[ncolumns++] = COL_CLOCK;
|
||||||
|
columns[ncolumns++] = COL_NAME;
|
||||||
|
columns[ncolumns++] = COL_TIME;
|
||||||
|
columns[ncolumns++] = COL_ISO_TIME;
|
||||||
|
columns[ncolumns++] = COL_RESOLUTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
|
||||||
|
&ncolumns, column_name_to_id) < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
scols_init_debug(0);
|
||||||
|
|
||||||
|
tb = scols_new_table();
|
||||||
|
if (!tb)
|
||||||
|
errx(EXIT_FAILURE, _("failed to allocate output table"));
|
||||||
|
scols_table_set_name(tb, "clocks");
|
||||||
|
|
||||||
|
for (i = 0; i < ncolumns; i++) {
|
||||||
|
colinfo = &infos[columns[i]];
|
||||||
|
|
||||||
|
col = scols_table_new_column(tb, colinfo->name, colinfo->whint, colinfo->flags);
|
||||||
|
if (!col)
|
||||||
|
errx(EXIT_FAILURE, _("failed to allocate output column"));
|
||||||
|
|
||||||
|
scols_column_set_json_type(col, colinfo->json_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(clocks); i++) {
|
||||||
|
clockinfo = &clocks[i];
|
||||||
|
|
||||||
|
ln = scols_table_new_line(tb, NULL);
|
||||||
|
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;
|
||||||
|
|
||||||
|
for (j = 0; j < ncolumns; j++) {
|
||||||
|
switch (columns[j]) {
|
||||||
|
case COL_ID:
|
||||||
|
scols_line_asprintf(ln, j, "%ju", (uintmax_t) clockinfo->id);
|
||||||
|
break;
|
||||||
|
case COL_CLOCK:
|
||||||
|
scols_line_set_data(ln, j, clockinfo->id_name);
|
||||||
|
break;
|
||||||
|
case COL_NAME:
|
||||||
|
scols_line_set_data(ln, j, clockinfo->name);
|
||||||
|
break;
|
||||||
|
case COL_TIME:
|
||||||
|
if (now.tv_nsec == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
scols_line_format_timespec(ln, j, &now);
|
||||||
|
break;
|
||||||
|
case COL_ISO_TIME:
|
||||||
|
if (now.tv_nsec == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
rc = strtimespec_iso(&now,
|
||||||
|
ISO_GMTIME | ISO_DATE | ISO_TIME | ISO_T | ISO_DOTNSEC | ISO_TIMEZONE,
|
||||||
|
buf, sizeof(buf));
|
||||||
|
if (rc)
|
||||||
|
errx(EXIT_FAILURE, _("failed to format iso time"));
|
||||||
|
scols_line_set_data(ln, j, buf);
|
||||||
|
break;
|
||||||
|
case COL_RESOLUTION:
|
||||||
|
rc = clock_getres(clockinfo->id, &resolution);
|
||||||
|
if (!rc)
|
||||||
|
scols_line_format_timespec(ln, j, &resolution);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scols_table_enable_json(tb, json);
|
||||||
|
scols_table_enable_raw(tb, raw);
|
||||||
|
scols_table_enable_noheadings(tb, noheadings);
|
||||||
|
scols_print_table(tb);
|
||||||
|
scols_unref_table(tb);
|
||||||
|
}
|
|
@ -165,3 +165,7 @@ fadvise_sources = files(
|
||||||
waitpid_sources = files(
|
waitpid_sources = files(
|
||||||
'waitpid.c',
|
'waitpid.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lsclocks_sources = files(
|
||||||
|
'lsclocks.c',
|
||||||
|
)
|
||||||
|
|
|
@ -87,6 +87,7 @@ TS_CMD_LINE=${TS_CMD_LINE-"${ts_commandsdir}line"}
|
||||||
TS_CMD_LOOK=${TS_CMD_LOOK-"${ts_commandsdir}look"}
|
TS_CMD_LOOK=${TS_CMD_LOOK-"${ts_commandsdir}look"}
|
||||||
TS_CMD_LOSETUP=${TS_CMD_LOSETUP:-"${ts_commandsdir}losetup"}
|
TS_CMD_LOSETUP=${TS_CMD_LOSETUP:-"${ts_commandsdir}losetup"}
|
||||||
TS_CMD_LSBLK=${TS_CMD_LSBLK-"${ts_commandsdir}lsblk"}
|
TS_CMD_LSBLK=${TS_CMD_LSBLK-"${ts_commandsdir}lsblk"}
|
||||||
|
TS_CMD_LSCLOCKS=${TS_CMD_LSCPU-"${ts_commandsdir}lsclocks"}
|
||||||
TS_CMD_LSCPU=${TS_CMD_LSCPU-"${ts_commandsdir}lscpu"}
|
TS_CMD_LSCPU=${TS_CMD_LSCPU-"${ts_commandsdir}lscpu"}
|
||||||
TS_CMD_LSFD=${TS_CMD_LSFD-"${ts_commandsdir}lsfd"}
|
TS_CMD_LSFD=${TS_CMD_LSFD-"${ts_commandsdir}lsfd"}
|
||||||
TS_CMD_LSMEM=${TS_CMD_LSMEM-"${ts_commandsdir}lsmem"}
|
TS_CMD_LSMEM=${TS_CMD_LSMEM-"${ts_commandsdir}lsmem"}
|
||||||
|
|
10
tests/expected/misc/lsclocks-basic
Normal file
10
tests/expected/misc/lsclocks-basic
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
ID CLOCK NAME
|
||||||
|
0 CLOCK_REALTIME realtime
|
||||||
|
1 CLOCK_MONOTONIC monotonic
|
||||||
|
4 CLOCK_MONOTONIC_RAW monotonic-raw
|
||||||
|
5 CLOCK_REALTIME_COARSE realtime-coarse
|
||||||
|
6 CLOCK_MONOTONIC_COARSE monotonic-coarse
|
||||||
|
7 CLOCK_BOOTTIME boottime
|
||||||
|
8 CLOCK_REALTIME_ALARM realtime-alarm
|
||||||
|
9 CLOCK_BOOTTIME_ALARM boottime-alarm
|
||||||
|
11 CLOCK_TAI tai
|
1
tests/expected/misc/lsclocks-time
Normal file
1
tests/expected/misc/lsclocks-time
Normal file
|
@ -0,0 +1 @@
|
||||||
|
X.X
|
|
@ -1,4 +1,4 @@
|
||||||
last 9 is expected to disappear in conversion
|
last 9 is expected to disappear in conversion
|
||||||
[7] [00010] [ipv6] [IPv6 ] [root ] [dns-server ] [2001:503:ba3e::2:30] [2013-08-28T20:30:40,123456+00:00]
|
[7] [00010] [ipv6] [IPv6 ] [root ] [dns-server ] [2001:503:ba3e::2:30] [2013-08-28T20:30:40,123456+00:00]
|
||||||
[8] [00011] [ipv6] [IPv6 ] [root ] [dns-server ] [2001:503:ba3e::2:30] [2013-08-28T20:40:50,999999+00:00]
|
[8] [00011] [ipv6] [IPv6 ] [root ] [dns-server ] [2001:503:ba3e::2:30] [2013-08-28T20:40:50,999999+00:00]
|
||||||
[0] [00000] [ts/0] [nonvalid] [foo ] [zero ] [0.0.0.0 ] [2013-08-28T12:00:00,12345678+00:00]
|
[0] [00000] [ts/0] [nonvalid] [foo ] [zero ] [0.0.0.0 ] [2013-08-28T12:00:00,000000+00:00]
|
||||||
|
|
42
tests/ts/misc/lsclocks
Executable file
42
tests/ts/misc/lsclocks
Executable file
|
@ -0,0 +1,42 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
|
||||||
|
#
|
||||||
|
# This file is part of util-linux.
|
||||||
|
#
|
||||||
|
# This file is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This file is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
TS_TOPDIR="${0%/*}/../.."
|
||||||
|
TS_DESC="lsclocks"
|
||||||
|
|
||||||
|
. "$TS_TOPDIR"/functions.sh
|
||||||
|
ts_init "$*"
|
||||||
|
|
||||||
|
ts_check_test_command "$TS_CMD_LSCLOCKS"
|
||||||
|
ts_check_prog sed
|
||||||
|
|
||||||
|
mask_timestamps() {
|
||||||
|
sed 's/[0-9]\+\.[0-9]\+/X.X/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
ts_init_subtest basic
|
||||||
|
|
||||||
|
"$TS_CMD_LSCLOCKS" -o ID,CLOCK,NAME > "$TS_OUTPUT" 2>> "$TS_ERRLOG"
|
||||||
|
|
||||||
|
ts_finalize_subtest
|
||||||
|
|
||||||
|
ts_init_subtest time
|
||||||
|
|
||||||
|
"$TS_CMD_LSCLOCKS" --time monotonic | mask_timestamps > "$TS_OUTPUT" 2>> "$TS_ERRLOG"
|
||||||
|
|
||||||
|
ts_finalize_subtest
|
||||||
|
|
||||||
|
ts_finalize
|
Loading…
Add table
Add a link
Reference in a new issue