From: Rafael Sadowski Subject: relayctl: rework output and add -v flag To: tech@openbsd.org Date: Sat, 1 Aug 2026 11:54:43 +0200 I find the current output: 1. Pointless by default, the keyword "imsg" appears in the monitor mode. 2. Unreadable with long names. 3. Inconsistent. The diff attempts to improve this. "relayctl show sessions" is a different story. Even using "watch -s 0.2 'doas relayctl show sessions'" is of no use, as sessions are really short-lived. In my view, this would need to be a monitor. But that would involve more work. See the commit message below for impl details. Current: $ doas relayctl monitor sync: imsg type 38 len 0 peerid 4294967295 pid 1 timestamp: 1785577221, Sat Aug 1 11:40:21 2026 host_status: imsg type 37 len 32 peerid 4294967295 pid 1 timestamp: 1785577221, Sat Aug 1 11:40:21 2026 id: 3 state: down host_status: imsg type 37 len 32 peerid 4294967295 pid 1 timestamp: 1785577221, Sat Aug 1 11:40:21 2026 id: 1 state: up (Why someone should be interested in imsg?). $ doas relayctl show summary Id Type Name Avlblty Status 1 redirect db-redirect down 6 table db:5432 empty 8 host 10.0.23.9 0.00% down 9 host 10.0.23.10 0.00% down 2 redirect cache-redirect down 7 table cache2:11211 empty 10 host 10.0.23.11 0.00% down 3 redirect api-redirect down 8 table api:9000 empty 11 host 10.0.23.8 0.00% down 1 relay https-gateway-way-to-long-name-for-testing active 1 table ap:6443 active (1 hosts) 1 host 10.0.23.6 100.00% up 2 relay https-api-gateway active $ doas relayctl show hosts Id Type Name Avlblty Status 6 table db:5432 empty 8 host 10.0.23.9 0.00% down total: 0/3 checks, error: tcp connect timeout 9 host 10.0.23.10 0.00% down total: 0/3 checks, error: tcp connect timeout 7 table cache2:11211 empty 10 host 10.0.23.11 0.00% down total: 0/3 checks, error: tcp connect timeout 8 table api:9000 empty 11 host 10.0.23.8 0.00% down total: 0/3 checks, error: tcp connect timeout 1 table ap:6443 active (1 hosts) 1 host 10.0.23.6 100.00% up total: 3/3 checks 2 table api:8443 empty 2 host 10.0.23.8 0.00% down total: 0/3 checks, error: tcp connect timeout $ doas relayctl show relays Id Type Name Avlblty Status 1 relay https-gateway-way-to-long-name-for-testing active $ doas relayctl show hosts Id Type Name Avlblty Status 6 table db:5432 empty 8 host 10.0.23.9 0.00% down total: 0/10 checks, error: tcp connect timeout 9 host 10.0.23.10 0.00% down total: 0/10 checks, error: tcp connect timeout 7 table cache2:11211 empty 10 host 10.0.23.11 0.00% down total: 0/10 checks, error: tcp connect timeout 8 table api:9000 empty 11 host 10.0.23.8 0.00% down total: 0/10 checks, error: tcp connect timeout 1 table ap:6443 active (1 hosts) 1 host 10.0.23.6 100.00% up total: 10/10 checksdoas relayctl show hosts Id Type Name Avlblty Status 6 table db:5432 empty 8 host 10.0.23.9 0.00% down total: 0/10 checks, error: tcp connect timeout 9 host 10.0.23.10 0.00% down total: 0/10 checks, error: tcp connect timeout 7 table cache2:11211 empty 10 host 10.0.23.11 0.00% down total: 0/10 checks, error: tcp connect timeout 8 table api:9000 empty 11 host 10.0.23.8 0.00% down total: 0/10 checks, error: tcp connect timeout 1 table ap:6443 active (1 hosts) 1 host 10.0.23.6 100.00% up total: 10/10 checks With diff: $ doas relayctl show summary Id Type Name Avlblty Checks Status 1 rdr db-redirect down 6 table db:5432 empty 8 host 10.0.23.9 0.00% 0/2 down 9 host 10.0.23.10 0.00% 0/2 down 2 rdr cache-redirect down 7 table cache2:11211 empty 10 host 10.0.23.11 0.00% 0/2 down 3 rdr api-redirect down 8 table api:9000 empty 11 host 10.0.23.8 0.00% 0/2 down 1 relay https-gateway-way-to-lon active 1 table ap:6443 active 1 host 10.0.23.6 100.00% 2/2 up $ doas relayctl -v show summary Id Type Name Avlblty Checks Status 1 rdr db-redirect down 6 table db:5432 empty 8 host 10.0.23.9 0.00% 0/3 down (tcp connect timeout) 9 host 10.0.23.10 0.00% 0/3 down (tcp connect timeout) 2 rdr cache-redirect down 7 table cache2:11211 empty 10 host 10.0.23.11 0.00% 0/3 down (tcp connect timeout) 3 rdr api-redirect down 8 table api:9000 empty 11 host 10.0.23.8 0.00% 0/3 down (tcp connect timeout) 1 relay https-gateway-way-to-lon active 1 table ap:6443 active (1 hosts) 1 host 10.0.23.6 100.00% 3/3 up $ doas relayctl show hosts Id Type Name Avlblty Checks Status 8 host 10.0.23.9 0.00% 0/5 down 9 host 10.0.23.10 0.00% 0/5 down 10 host 10.0.23.11 0.00% 0/5 down 11 host 10.0.23.8 0.00% 0/5 down 1 host 10.0.23.6 100.00% 5/5 up 2 host 10.0.23.8 0.00% 0/5 down 3 host 10.0.23.6 0.00% 0/5 down 4 host 10.0.23.7 0.00% 0/5 down 5 host 10.0.23.9 0.00% 0/5 down 6 host 10.0.23.10 0.00% 0/5 down 7 host 10.0.23.11 0.00% 0/5 down $ doas relayctl -v show hosts Id Type Name Avlblty Checks Status 8 host 10.0.23.9 0.00% 0/6 down (tcp connect timeout) 9 host 10.0.23.10 0.00% 0/6 down (tcp connect timeout) 10 host 10.0.23.11 0.00% 0/6 down (tcp connect timeout) 11 host 10.0.23.8 0.00% 0/6 down (tcp connect timeout) 1 host 10.0.23.6 100.00% 6/6 up 2 host 10.0.23.8 0.00% 0/6 down (tcp connect timeout) 3 host 10.0.23.6 0.00% 0/6 down (tls connect failed) 4 host 10.0.23.7 0.00% 0/6 down (tcp connect timeout) 5 host 10.0.23.9 0.00% 0/6 down (tcp connect timeout) 6 host 10.0.23.10 0.00% 0/6 down (tcp connect timeout) 7 host 10.0.23.11 0.00% 0/6 down (tcp connect timeout) $ doas relayctl show relays Id Type Name Avlblty Checks Status 1 relay https-gateway-way-to-lon active 2 relay https-api-gateway active 3 relay wiki-frontend active 4 relay ssh-tunnel active 5 relay cache-service active $ doas relayctl monitor host_status: id: 3 status: down host_status: id: 1 status: up host_status: id: 5 status: down host_status: id: 10 status: down host_status: id: 9 status: down $ doas relayctl -v monitor sync: imsg type 38 len 0 peerid 4294967295 pid 1 sync: timestamp: 1785577623, Sat Aug 1 11:47:03 2026 host_status: imsg type 37 len 32 peerid 4294967295 pid 1 host_status: timestamp: 1785577623, Sat Aug 1 11:47:03 2026 host_status: id: 3 status: down host_status: imsg type 37 len 32 peerid 4294967295 pid 1 host_status: timestamp: 1785577623, Sat Aug 1 11:47:03 2026 host_status: id: 1 status: up commit 900841a571028d4dd0dce801a3abcfdbfa95141c Author: Rafael Sadowski Date: Sat Aug 1 09:52:26 2026 +0200 relayctl: rework show output and add -v flag - Added -v flag for more detailed output. - Replaced tab separators with space-padded, truncated columns (%.Ns) for stable alignment. - Added a new "Checks" column for host checks (N/N), replacing the total: N/N checks sub-line. - Shortened redirect to rdr in the Type column to stay under 80 columns. - Without -v: compact status (down); with -v: error inlined (down (tcp connect timeout)). - Table status shows host count (N hosts) only with -v. - retries sub-line only appears with show hosts -v. - show hosts no longer lists tables; use show summary for that. diff --git a/relayctl.8 b/relayctl.8 index 96e68a5..fcdf043 100644 --- a/relayctl.8 +++ b/relayctl.8 @@ -23,6 +23,7 @@ .Nd control the relay daemon .Sh SYNOPSIS .Nm +.Op Fl v .Op Fl s Ar socket .Ar command .Op Ar argument ... @@ -42,6 +43,8 @@ instead of the default .Pa /var/run/relayd.sock to communicate with .Xr relayd 8 . +.It Fl v +Verbose output. .El .Pp The following commands are available: @@ -78,7 +81,7 @@ the backup table as well. .It Cm reload Reload the configuration file. .It Cm show hosts -Show detailed status of hosts and tables. +Show detailed status of hosts. It will also print the last error for failed host checks; see the .Sx DIAGNOSTICS diff --git a/relayctl.c b/relayctl.c index b76d9fc..a47bf7a 100644 --- a/relayctl.c +++ b/relayctl.c @@ -50,6 +50,7 @@ char *print_host_status(int, int); char *print_table_status(int, int); char *print_relay_status(int); void print_statistics(struct ctl_stats [PROC_MAX_INSTANCES+1]); +void print_imsg(struct imsg *, const char *); struct imsgname { int type; @@ -82,13 +83,14 @@ struct imsgname imsgunknown = { struct imsgbuf *ibuf; int error = 0; +int verbose = 0; __dead void usage(void) { extern char *__progname; - fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n", + fprintf(stderr, "usage: %s [-v] [-s socket] command [argument ...]\n", __progname); exit(1); } @@ -101,16 +103,19 @@ main(int argc, char *argv[]) struct imsg imsg; int ctl_sock; int done = 0; - int n, verbose = 0; + int n, relayd_log_level = 0; int ch; const char *sockname; sockname = RELAYD_SOCKET; - while ((ch = getopt(argc, argv, "s:")) != -1) { + while ((ch = getopt(argc, argv, "vs:")) != -1) { switch (ch) { case 's': sockname = optarg; break; + case 'v': + verbose++; + break; default: usage(); /* NOTREACHED */ @@ -163,8 +168,8 @@ main(int argc, char *argv[]) case SHOW_RELAYS: case SHOW_ROUTERS: imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0); - printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n", - "Id", "Type", "Name", "Avlblty"); + printf("%-4s %-6s %-24.24s %-7.7s %-8.8s %s\n", + "Id", "Type", "Name", "Avlblty", "Checks", "Status"); break; case SHOW_SESSIONS: imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0); @@ -212,11 +217,11 @@ main(int argc, char *argv[]) imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); break; case LOG_VERBOSE: - verbose = 2; + relayd_log_level = 2; /* FALLTHROUGH */ case LOG_BRIEF: imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, - &verbose, sizeof(verbose)); + &relayd_log_level, sizeof(relayd_log_level)); printf("logging request sent.\n"); done = 1; break; @@ -294,8 +299,7 @@ monitor_host_status(struct imsg *imsg) if (imsg_get_data(imsg, &cs, sizeof(cs)) == -1) errx(1, "imsg_get_data ctl status error"); - printf("\tid: %u\n", cs.id); - printf("\tstate: "); + printf("host_status: id: %u status: ", cs.id); switch (cs.up) { case HOST_UP: printf("up\n"); @@ -316,25 +320,19 @@ monitor_id(struct imsg *imsg) if (imsg_get_data(imsg, &id, sizeof(id)) == -1) errx(1, "imsg_get_data ctl id error"); - printf("\tid: %u\n", id.id); + printf("monitor_id: id: %u\n", id.id); if (strlen(id.name)) - printf("\tname: %s\n", id.name); + printf("name: %s\n", id.name); } int monitor(struct imsg *imsg) { - time_t now; int done = 0; struct imsgname *imn; - now = time(NULL); - imn = monitor_lookup(imsg_get_type(imsg)); - printf("%s: imsg type %u len %zu peerid %u pid %d\n", imn->name, - imsg_get_type(imsg), imsg_get_len(imsg), imsg_get_id(imsg), - imsg_get_pid(imsg)); - printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now)); + print_imsg(imsg, imn->name); if (imn->type == -1) done = 1; if (imn->func != NULL) @@ -352,8 +350,10 @@ show_summary_msg(struct imsg *imsg, int type) struct relay rlay; struct router rt; struct netroute nr; - struct ctl_stats stats[PROC_MAX_INSTANCES]; + struct ctl_stats stats[PROC_MAX_INSTANCES + 1]; char name[HOST_NAME_MAX + 1]; + char checks[16]; + char status[128]; switch (imsg_get_type(imsg)) { case IMSG_CTL_RDR: @@ -362,18 +362,18 @@ show_summary_msg(struct imsg *imsg, int type) if (imsg_get_data(imsg, &rdr, sizeof(rdr)) == -1) errx(1, "imsg_get_data rdr error"); rdr.conf.name[sizeof(rdr.conf.name) - 1] = '\0'; - printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", - rdr.conf.id, "redirect", rdr.conf.name, "", + printf("%-4u %-6s %-24.24s %-7.7s %-8.8s %s\n", + rdr.conf.id, "rdr", rdr.conf.name, "", "", print_rdr_status(rdr.conf.flags)); break; case IMSG_CTL_TABLE: - if (!(type == SHOW_SUM || type == SHOW_HOSTS)) + if (type != SHOW_SUM) break; if (imsg_get_data(imsg, &table, sizeof(table)) == -1) errx(1, "imsg_get_data table error"); table.conf.name[sizeof(table.conf.name) - 1] = '\0'; - printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", - table.conf.id, "table", table.conf.name, "", + printf("%-4u %-6s %-24.24s %-7.7s %-8.8s %s\n", + table.conf.id, "table", table.conf.name, "", "", print_table_status(table.up, table.conf.flags)); break; case IMSG_CTL_HOST: @@ -387,19 +387,24 @@ show_summary_msg(struct imsg *imsg, int type) host.conf.name, host.conf.parentid); else strlcpy(name, host.conf.name, sizeof(name)); - printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", + if (host.check_cnt) + snprintf(checks, sizeof(checks), "%lu/%lu", + host.up_cnt, host.check_cnt); + else + checks[0] = '\0'; + if (host.he && host.up == HOST_DOWN && verbose) + snprintf(status, sizeof(status), "%s (%s)", + print_host_status(host.up, host.flags), + host_error(host.he)); + else + strlcpy(status, print_host_status(host.up, host.flags), + sizeof(status)); + printf("%-4u %-6s %-24.24s %-7.7s %-8.8s %s\n", host.conf.id, "host", name, print_availability(host.check_cnt, host.up_cnt), - print_host_status(host.up, host.flags)); - if (type == SHOW_HOSTS && host.check_cnt) { - printf("\t%8s\ttotal: %lu/%lu checks", - "", host.up_cnt, host.check_cnt); - if (host.retry_cnt) - printf(", %d retries", host.retry_cnt); - if (host.he && host.up == HOST_DOWN) - printf(", error: %s", host_error(host.he)); - printf("\n"); - } + checks, status); + if (type == SHOW_HOSTS && verbose && host.retry_cnt) + printf("\t%8s\t%d retries\n", "", host.retry_cnt); break; case IMSG_CTL_RELAY: if (!(type == SHOW_SUM || type == SHOW_RELAYS)) @@ -407,8 +412,8 @@ show_summary_msg(struct imsg *imsg, int type) if (imsg_get_data(imsg, &rlay, sizeof(rlay)) == -1) errx(1, "imsg_get_data relay error"); rlay.rl_conf.name[sizeof(rlay.rl_conf.name) - 1] = '\0'; - printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", - rlay.rl_conf.id, "relay", rlay.rl_conf.name, "", + printf("%-4u %-6s %-24.24s %-7.7s %-8.8s %s\n", + rlay.rl_conf.id, "relay", rlay.rl_conf.name, "", "", print_relay_status(rlay.rl_conf.flags)); break; case IMSG_CTL_RDR_STATS: @@ -433,8 +438,8 @@ show_summary_msg(struct imsg *imsg, int type) errx(1, "imsg_get_data router error"); rt.rt_conf.name[sizeof(rt.rt_conf.name) - 1] = '\0'; rt.rt_conf.label[sizeof(rt.rt_conf.label) - 1] = '\0'; - printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", - rt.rt_conf.id, "router", rt.rt_conf.name, "", + printf("%-4u %-6s %-24.24s %-7.7s %-8.8s %s\n", + rt.rt_conf.id, "router", rt.rt_conf.name, "", "", print_relay_status(rt.rt_conf.flags)); if (type != SHOW_ROUTERS) break; @@ -483,7 +488,7 @@ show_session_msg(struct imsg *imsg) getmonotime(&tv_now); print_time(&tv_now, &con.se_tv_start, a, sizeof(a)); print_time(&tv_now, &con.se_tv_last, b, sizeof(b)); - printf("\tage %s, idle %s, relay %u, pid %u", + printf("age %s, idle %s, relay %u, pid %u", a, b, con.se_relayid, con.se_pid); /* XXX grab tagname instead of tag id */ if (con.se_tag) @@ -540,8 +545,10 @@ print_table_status(int up, int fl) snprintf(buf, sizeof(buf) - 1, "disabled"); } else if (!up) { snprintf(buf, sizeof(buf) - 1, "empty"); - } else + } else if (verbose) snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up); + else + snprintf(buf, sizeof(buf) - 1, "active"); return (buf); } @@ -600,3 +607,19 @@ print_statistics(struct ctl_stats stats[PROC_MAX_INSTANCES+1]) "", crs.avg, crs.interval, crs.avg_hour, crs.avg_day); } + +void +print_imsg(struct imsg *imsg, const char *name) +{ + time_t now; + + if (!verbose) + return; + + now = time(NULL); + + printf("%s: imsg type %u len %zu peerid %u pid %d\n", name, + imsg_get_type(imsg), imsg_get_len(imsg), imsg_get_id(imsg), + imsg_get_pid(imsg)); + printf("%s: timestamp: %lld, %s", name, (long long)now, ctime(&now)); +}