From d622b222a9acbda3a4c842beeb593e7075c32bec Mon Sep 17 00:00:00 2001 From: Thomas Kupper Date: Sat, 14 Feb 2026 12:24:32 +0100 Subject: [PATCH] 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 - - --- client.c | 124 ++++++++++++++++++++++-------------- doc/chronyc.adoc | 4 ++ test/simulation/110-chronyc | 45 +++++++++++++ 3 files changed, 126 insertions(+), 47 deletions(-) diff --git a/client.c b/client.c index a3c2ab3..71db567 100644 --- a/client.c +++ b/client.c @@ -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; diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc index b7f30c6..ee19185 100644 --- a/doc/chronyc.adoc +++ b/doc/chronyc.adoc @@ -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. diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc index 6488d97..fa9196e 100755 --- a/test/simulation/110-chronyc +++ b/test/simulation/110-chronyc @@ -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" \