chronyc: add wider output option

Add option '-w' which set the output width from 80 to 94. This allows
for all table-outputting operations to display IPv4 and IPv6 addresses
aligned with the headers.

Extend test/simulation/110-chronyc to test for the five commands which
support wide mode.

To set wide display, call `chronyc` with option `-w`.

Example of a `clients` list before and after:

chronyc -n clients -p 5
Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
===============================================================================
2001:db8:1234:5678:90ab:cdef:1234:5678     952      0   7   -    75       0      0   -     -
192.168.1.1                     0      0   -   -     -       0      0   -     -

chronyc -n -w clients -p 5
Hostname                                    NTP   Drop Int IntL Last     Cmd   Drop Int  Last
=============================================================================================
2001:db8:1234:5678:90ab:cdef:1234:5678      952      0   7   -    75       0      0   -     -
192.168.1.1                                   0      0   -   -     -       0      0   -     -
This commit is contained in:
Thomas Kupper
2026-02-14 12:24:32 +01:00
committed by Miroslav Lichvar
parent 4ddc6b334d
commit d622b222a9
3 changed files with 126 additions and 47 deletions

124
client.c
View File

@@ -78,6 +78,8 @@ static int csv_mode = 0;
static int end_dot = 0;
static int wide = 0;
/* ================================================== */
/* Log a message. This is a minimalistic replacement of the logging.c
implementation to avoid linking with it and other modules. */
@@ -1768,7 +1770,7 @@ print_report(const char *format, ...)
{
char buf[256];
va_list ap;
int i, field, sign, width, prec, spec;
int i, field, sign, width, prec, spec, varwidth;
const char *string;
unsigned int uinteger;
uint64_t uinteger64;
@@ -1802,10 +1804,14 @@ print_report(const char *format, ...)
sign = 0;
width = 0;
prec = 5;
varwidth = 0;
if (*format == '+' || *format == '-') {
sign = 1;
format++;
} else if (*format == '*') {
varwidth = 1;
format++;
}
if (isdigit((unsigned char)*format)) {
@@ -1993,6 +1999,14 @@ print_report(const char *format, ...)
printf("%*o", width, uinteger);
break;
case 's': /* string */
if (varwidth) {
if (csv_mode) {
integer = va_arg(ap, int);
width = 0;
} else {
width = va_arg(ap, int);
}
}
string = va_arg(ap, const char *);
if (sign)
printf("%-*s", width, string);
@@ -2143,14 +2157,17 @@ process_cmd_sources(char *line)
printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n");
printf(" / .- Source state '*' = current best, '+' = combined, '-' = not combined,\n");
printf("| / 'x' = may be in error, '~' = too variable, '?' = unusable.\n");
printf("|| .- xxxx [ yyyy ] +/- zzzz\n");
printf("|| Reachability register (octal) -. | xxxx = adjusted offset,\n");
printf("|| Log2(Polling interval) --. | | yyyy = measured offset,\n");
printf("|| \\ | | zzzz = estimated error.\n");
printf("|| | | \\\n");
printf("|| %*s .- xxxx [ yyyy ] +/- zzzz\n", wide, "");
printf("|| %*s Reachability register (octal) -. | xxxx = adjusted offset,\n", wide, "");
printf("|| %*s Log2(Polling interval) --. | | yyyy = measured offset,\n", wide, "");
printf("|| %*s \\ | | zzzz = estimated error.\n", wide, "");
printf("|| %*s | | \\\n", wide, "");
}
print_header("MS Name/IP address Stratum Poll Reach LastRx Last sample ");
if (wide)
print_header("MS Name/IP address Stratum Poll Reach LastRx Last sample ");
else
print_header("MS Name/IP address Stratum Poll Reach LastRx Last sample ");
/* "MS NNNNNNNNNNNNNNNNNNNNNNNNNNN SS PP RRR RRRR SSSSSSS[SSSSSSS] +/- SSSSSS" */
@@ -2166,7 +2183,7 @@ process_cmd_sources(char *line)
continue;
ref = mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4;
format_name(name, sizeof (name), 25, ref, ref ? ip_addr.addr.in4 : 0, 1, &ip_addr);
format_name(name, sizeof (name), 25 + wide, ref, ref ? ip_addr.addr.in4 : 0, 1, &ip_addr);
switch (mode) {
case RPY_SD_MD_CLIENT:
@@ -2210,8 +2227,8 @@ process_cmd_sources(char *line)
break;
}
print_report("%c%c %-27s %2d %2d %3o %I %+S[%+S] +/- %S\n",
mode_ch, state_ch, name,
print_report("%c%c %*s %2d %2d %3o %I %+S[%+S] +/- %S\n",
mode_ch, state_ch, -27 - wide, name,
ntohs(reply.data.source_data.stratum),
(int16_t)ntohs(reply.data.source_data.poll),
ntohs(reply.data.source_data.reachability),
@@ -2246,18 +2263,21 @@ process_cmd_sourcestats(char *line)
n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) {
printf(" .- Number of sample points in measurement set.\n");
printf(" / .- Number of residual runs with same sign.\n");
printf(" | / .- Length of measurement set (time).\n");
printf(" | | / .- Est. clock freq error (ppm).\n");
printf(" | | | / .- Est. error in freq.\n");
printf(" | | | | / .- Est. offset.\n");
printf(" | | | | | | On the -.\n");
printf(" | | | | | | samples. \\\n");
printf(" | | | | | | |\n");
printf("%*s .- Number of sample points in measurement set.\n", wide, "");
printf("%*s / .- Number of residual runs with same sign.\n", wide, "");
printf("%*s | / .- Length of measurement set (time).\n", wide, "");
printf("%*s | | / .- Est. clock freq error (ppm).\n", wide, "");
printf("%*s | | | / .- Est. error in freq.\n", wide, "");
printf("%*s | | | | / .- Est. offset.\n", wide, "");
printf("%*s | | | | | | On the -.\n", wide, "");
printf("%*s | | | | | | samples. \\\n", wide, "");
printf("%*s | | | | | | |\n", wide, "");
}
print_header("Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev");
if (wide)
print_header("Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev");
else
print_header("Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev");
/* "NNNNNNNNNNNNNNNNNNNNNNNNN NP NR SSSS FFFFFFFFFF SSSSSSSSSS SSSSSSS SSSSSS" */
@@ -2271,11 +2291,11 @@ process_cmd_sourcestats(char *line)
if (!all && ip_addr.family == IPADDR_ID)
continue;
format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
format_name(name, sizeof (name), 25 + wide, ip_addr.family == IPADDR_UNSPEC,
ntohl(reply.data.sourcestats.ref_id), 1, &ip_addr);
print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
name,
print_report("%*s %3U %3U %I %+P %P %+S %S\n",
-25 - wide, name,
ntohl(reply.data.sourcestats.n_samples),
ntohl(reply.data.sourcestats.n_runs),
ntohl(reply.data.sourcestats.span_seconds),
@@ -2365,14 +2385,17 @@ process_cmd_authdata(char *line)
n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) {
printf( " .- Auth. mechanism (NTS, SK - symmetric key)\n");
printf( " | Key length -. Cookie length (bytes) -.\n");
printf( " | (bits) | Num. of cookies --. |\n");
printf( " | | Key est. attempts | |\n");
printf( " | | | | |\n");
printf( "%*s .- Auth. mechanism (NTS, SK - symmetric key)\n", wide, "");
printf( "%*s | Key length -. Cookie length (bytes) -.\n", wide, "");
printf( "%*s | (bits) | Num. of cookies --. |\n", wide, "");
printf( "%*s | | Key est. attempts | |\n", wide, "");
printf( "%*s | | | | |\n", wide, "");
}
print_header("Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen");
if (wide)
print_header("Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen");
else
print_header("Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen");
/* "NNNNNNNNNNNNNNNNNNNNNNNNNNN MMMM KKKKK AAAA LLLL LLLL AAAA NNNN CCCC LLLL" */
@@ -2395,7 +2418,7 @@ process_cmd_authdata(char *line)
if (!request_reply(&request, &reply, RPY_AUTH_DATA, 0))
return 0;
format_name(name, sizeof (name), 25, 0, 0, 1, &ip_addr);
format_name(name, sizeof (name), 25 + wide, 0, 0, 1, &ip_addr);
switch (ntohs(reply.data.auth_data.mode)) {
case RPY_AD_MD_NONE:
@@ -2412,8 +2435,8 @@ process_cmd_authdata(char *line)
break;
}
print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
name, mode_str,
print_report("%*s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
-27 - wide, name, mode_str,
ntohl(reply.data.auth_data.key_id),
ntohs(reply.data.auth_data.key_type),
ntohs(reply.data.auth_data.key_length),
@@ -2578,13 +2601,16 @@ process_cmd_selectdata(char *line)
printf( " / d/D - large distance, ~ - jittery, w/W - waits for others,\n");
printf( "| S - stale, O - orphan, T - not trusted, P - not preferred,\n");
printf( "| U - waits for update, x - falseticker, + - combined, * - best.\n");
printf( "| Effective options ---------. (N - noselect, P - prefer\n");
printf( "| Configured options ----. \\ T - trust, R - require)\n");
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
printf( "| | | | |\n");
printf( "|%*s Effective options ---------. (N - noselect, P - prefer\n", wide, "");
printf( "|%*s Configured options ----. \\ T - trust, R - require)\n", wide, "");
printf( "|%*s Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n", wide, "");
printf( "|%*s | | | |\n", wide, "");
}
print_header("S Name/IP Address Auth COpts EOpts Last Score Interval Leap");
if (wide)
print_header("S Name/IP Address Auth COpts EOpts Last Score Interval Leap");
else
print_header("S Name/IP Address Auth COpts EOpts Last Score Interval Leap");
/* "S NNNNNNNNNNNNNNNNNNNNNNNNN A OOOO- OOOO- LLLL SSSSS IIIIIII IIIIIII L" */
@@ -2598,15 +2624,15 @@ process_cmd_selectdata(char *line)
if (!all && ip_addr.family == IPADDR_ID)
continue;
format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
format_name(name, sizeof (name), 25 + wide, ip_addr.family == IPADDR_UNSPEC,
ntohl(reply.data.select_data.ref_id), 1, &ip_addr);
conf_options = ntohs(reply.data.select_data.conf_options);
eff_options = ntohs(reply.data.select_data.eff_options);
print_report("%c %-25s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S %1L\n",
print_report("%c %*s %c %c%c%c%c%c %c%c%c%c%c %I %5.1f %+S %+S %1L\n",
reply.data.select_data.state_char,
name,
-25 - wide, name,
reply.data.select_data.authentication ? 'Y' : 'N',
conf_options & RPY_SD_OPTION_NOSELECT ? 'N' : '-',
conf_options & RPY_SD_OPTION_PREFER ? 'P' : '-',
@@ -2774,7 +2800,7 @@ process_cmd_clients(char *line)
IPAddr ip;
uint32_t i, n_clients, next_index, n_indices, min_hits, reset;
RPY_ClientAccesses_Client *client;
char header[80], name[50], *opt, *arg;
char header[94], name[50], *opt, *arg;
int nke;
next_index = 0;
@@ -2800,8 +2826,8 @@ process_cmd_clients(char *line)
}
snprintf(header, sizeof (header),
"Hostname NTP Drop Int IntL Last %6s Drop Int Last",
nke ? "NTS-KE" : "Cmd");
"Hostname %*s NTP Drop Int IntL Last %6s Drop Int Last",
20 + wide, "", nke ? "NTS-KE" : "Cmd");
print_header(header);
while (1) {
@@ -2827,10 +2853,10 @@ process_cmd_clients(char *line)
if (ip.family == IPADDR_UNSPEC)
continue;
format_name(name, sizeof (name), 25, 0, 0, 0, &ip);
format_name(name, sizeof (name), 25 + wide, 0, 0, 0, &ip);
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
name,
print_report("%*s %6U %5U %C %C %I %6U %5U %C %I\n",
-25 - wide, name,
ntohl(client->ntp_hits),
ntohl(client->ntp_drops),
client->ntp_interval,
@@ -3589,6 +3615,7 @@ print_help(const char *progname)
" -h HOST\tSpecify server (%s)\n"
" -p PORT\tSpecify UDP port (%d)\n"
" -u USER\tSpecify user (%s)\n"
" -w\t\tWide output\n"
" -v, --version\tPrint version and exit\n"
" --help\tPrint usage and exit\n",
progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1",
@@ -3629,7 +3656,7 @@ main(int argc, char **argv)
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:u:v")) != -1) {
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:u:v:w")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -3671,6 +3698,9 @@ main(int argc, char **argv)
case 'v':
print_version();
return 0;
case 'w':
wide = 14;
break;
default:
print_help(progname);
return 1;

View File

@@ -134,6 +134,10 @@ This option is ignored and is provided only for compatibility.
*-a*::
This option is ignored and is provided only for compatibility.
*-w*::
This option sets the output width to 94 columns, from the standard 80, for the
commands *authdata*, *clients*, *selectdata*, *sources*, and *sourcestats*.
*-v*, *--version*::
With this option *chronyc* displays its version number on the terminal and
exits.

View File

@@ -103,6 +103,49 @@ check_chronyc_output "^#,.,SHM0.*
\.$" \
|| test_fail
client_conf=""
client_server_conf="
server node1.net1.clk
server ntp.notresolved.com
server 2001:db8:c001:cafe:1234:1234:1234:1234"
server_conf="server 192.168.123.1"
server_strata=1
cmdmon_unix=1
dns=0
chronyc_options="-w"
chronyc_conf="authdata -a
selectdata -a
sources -a
sourcestats -a
clients -k"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=======================================================================================
192\.168\.123\.1 - 0 0 0 - 0 0 0 0
ID#0000000002 - 0 0 0 - 0 0 0 0
(2001:db8:c001:cafe:1234:1234:1234:1234|ID#0000000003) [- ]+ 0 0 0 - 0 0 0 0
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=====================================================================================
\* 192\.168\.123\.1 N ----- ----- [0-9]+ 1\.0 [0-9 +-]+[un]s [0-9 +-]+[un]s N
M ID#0000000002 N ----- ----- 0 1\.0 \+0ns \+0ns \?
M (2001:db8:c001:cafe:1234:1234:1234:1234|ID#0000000003) [N ]+ ----- ----- 0 1\.0 \+0ns \+0ns \?
MS Name/IP address Stratum Poll Reach LastRx Last sample
=============================================================================================
\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
\^\? ID#0000000002 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns
\^\? (2001:db8:c001:cafe:1234:1234:1234:1234|ID#0000000003) [0 ]+ [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
============================================================================================
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
ID#0000000002 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms
(2001:db8:c001:cafe:1234:1234:1234:1234|ID#0000000003) [0 ]+ 0 0 \+0\.000 2000\.000 \+0ns 4000ms
Hostname NTP Drop Int IntL Last NTS-KE Drop Int Last
=============================================================================================$" || test_fail
chronyc_options=""
server_strata=0
chronyc_start=0.5
@@ -110,6 +153,8 @@ client_server_conf=""
client_conf=""
server_conf="server 192.168.123.1"
limit=1
dns=1
cmdmon_unix=0
for chronyc_conf in \
"accheck 1.2.3.4" \