Download raw body.
relayctl: rework output and add -v flag
Any feeback or better ideas?
On Sat Aug 01, 2026 at 11:54:43AM +0200, Rafael Sadowski wrote:
> 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 <rafael@sizeofvoid.org>
> 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));
> +}
>
relayctl: rework output and add -v flag