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

View File

@@ -134,6 +134,10 @@ This option is ignored and is provided only for compatibility.
*-a*:: *-a*::
This option is ignored and is provided only for compatibility. 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*:: *-v*, *--version*::
With this option *chronyc* displays its version number on the terminal and With this option *chronyc* displays its version number on the terminal and
exits. exits.

View File

@@ -103,6 +103,49 @@ check_chronyc_output "^#,.,SHM0.*
\.$" \ \.$" \
|| test_fail || 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="" chronyc_options=""
server_strata=0 server_strata=0
chronyc_start=0.5 chronyc_start=0.5
@@ -110,6 +153,8 @@ client_server_conf=""
client_conf="" client_conf=""
server_conf="server 192.168.123.1" server_conf="server 192.168.123.1"
limit=1 limit=1
dns=1
cmdmon_unix=0
for chronyc_conf in \ for chronyc_conf in \
"accheck 1.2.3.4" \ "accheck 1.2.3.4" \