xref: /openbsd-src/usr.sbin/relayctl/relayctl.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1*f1b790a5Sclaudio /*	$OpenBSD: relayctl.c,v 1.63 2024/11/21 13:38:15 claudio Exp $	*/
2feb9ff76Sreyk 
3feb9ff76Sreyk /*
4583626ebSreyk  * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
536f5dc5eSpyr  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
6feb9ff76Sreyk  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
7feb9ff76Sreyk  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
8feb9ff76Sreyk  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
9feb9ff76Sreyk  *
10feb9ff76Sreyk  * Permission to use, copy, modify, and distribute this software for any
11feb9ff76Sreyk  * purpose with or without fee is hereby granted, provided that the above
12feb9ff76Sreyk  * copyright notice and this permission notice appear in all copies.
13feb9ff76Sreyk  *
14feb9ff76Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15feb9ff76Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16feb9ff76Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17feb9ff76Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18feb9ff76Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19feb9ff76Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20feb9ff76Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21feb9ff76Sreyk  */
22feb9ff76Sreyk 
23feb9ff76Sreyk #include <sys/types.h>
24feb9ff76Sreyk #include <sys/socket.h>
25f04ff968Sreyk #include <sys/time.h>
26feb9ff76Sreyk #include <sys/queue.h>
27feb9ff76Sreyk #include <sys/un.h>
2853967d9eSreyk 
2953967d9eSreyk #include <arpa/inet.h>
30feb9ff76Sreyk 
31f04ff968Sreyk #include <stdio.h>
32f04ff968Sreyk #include <stdlib.h>
33feb9ff76Sreyk #include <err.h>
3453b804d7Sreyk #include <errno.h>
35b2cd1366Sguenther #include <limits.h>
36feb9ff76Sreyk #include <string.h>
37feb9ff76Sreyk #include <unistd.h>
38f04ff968Sreyk #include <time.h>
39f04ff968Sreyk #include <imsg.h>
40e8fb3979Spyr 
41748ceb64Sreyk #include "relayd.h"
42feb9ff76Sreyk #include "parser.h"
43feb9ff76Sreyk 
44feb9ff76Sreyk __dead void	 usage(void);
452edd718bSreyk int		 show_summary_msg(struct imsg *, int);
46fa0d8478Sreyk int		 show_session_msg(struct imsg *);
47feb9ff76Sreyk int		 show_command_output(struct imsg *);
489591a9f7Spyr char		*print_rdr_status(int);
49feb9ff76Sreyk char		*print_host_status(int, int);
50feb9ff76Sreyk char		*print_table_status(int, int);
512edd718bSreyk char		*print_relay_status(int);
528e01c6e3Sreyk void		 print_statistics(struct ctl_stats[PROC_MAX_INSTANCES + 1]);
53feb9ff76Sreyk 
54ee20bc67Sreyk struct imsgname {
55ee20bc67Sreyk 	int type;
56ee20bc67Sreyk 	char *name;
57ee20bc67Sreyk 	void (*func)(struct imsg *);
58ee20bc67Sreyk };
59ee20bc67Sreyk 
60ee20bc67Sreyk struct imsgname *monitor_lookup(u_int8_t);
61ee20bc67Sreyk void		 monitor_host_status(struct imsg *);
62ee20bc67Sreyk void		 monitor_id(struct imsg *);
63ee20bc67Sreyk int		 monitor(struct imsg *);
64ee20bc67Sreyk 
65ee20bc67Sreyk struct imsgname imsgs[] = {
66ee20bc67Sreyk 	{ IMSG_HOST_STATUS,		"host_status",	monitor_host_status },
679591a9f7Spyr 	{ IMSG_CTL_RDR_DISABLE,		"ctl_rdr_disable",	monitor_id },
689591a9f7Spyr 	{ IMSG_CTL_RDR_ENABLE,		"ctl_rdr_enable",	monitor_id },
69ee20bc67Sreyk 	{ IMSG_CTL_TABLE_DISABLE,	"ctl_table_disable",	monitor_id },
70ee20bc67Sreyk 	{ IMSG_CTL_TABLE_ENABLE,	"ctl_table_enable",	monitor_id },
71ee20bc67Sreyk 	{ IMSG_CTL_HOST_DISABLE,	"ctl_host_disable",	monitor_id },
72ee20bc67Sreyk 	{ IMSG_CTL_HOST_ENABLE,		"ctl_host_enable",	monitor_id },
739ea8c990Spyr 	{ IMSG_CTL_TABLE_CHANGED,	"ctl_table_changed",	monitor_id },
749ea8c990Spyr 	{ IMSG_CTL_PULL_RULESET,	"ctl_pull_ruleset",	monitor_id },
759ea8c990Spyr 	{ IMSG_CTL_PUSH_RULESET,	"ctl_push_ruleset",	monitor_id },
76ee20bc67Sreyk 	{ IMSG_SYNC,			"sync",			NULL },
77ee20bc67Sreyk 	{ 0,				NULL,			NULL }
78ee20bc67Sreyk };
79ee20bc67Sreyk struct imsgname imsgunknown = {
80ee20bc67Sreyk 	-1,				"<unknown>",		NULL
81ee20bc67Sreyk };
82ee20bc67Sreyk 
83feb9ff76Sreyk struct imsgbuf	*ibuf;
8498ad2da2Sclaudio int error = 0;
85feb9ff76Sreyk 
86feb9ff76Sreyk __dead void
87feb9ff76Sreyk usage(void)
88feb9ff76Sreyk {
89feb9ff76Sreyk 	extern char *__progname;
90feb9ff76Sreyk 
91ec4c1254Sbenno 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
92ec4c1254Sbenno 	    __progname);
93feb9ff76Sreyk 	exit(1);
94feb9ff76Sreyk }
95feb9ff76Sreyk 
96feb9ff76Sreyk int
97feb9ff76Sreyk main(int argc, char *argv[])
98feb9ff76Sreyk {
99feb9ff76Sreyk 	struct sockaddr_un	 sun;
100feb9ff76Sreyk 	struct parse_result	*res;
101feb9ff76Sreyk 	struct imsg		 imsg;
102feb9ff76Sreyk 	int			 ctl_sock;
103feb9ff76Sreyk 	int			 done = 0;
104f579a0f7Sjsg 	int			 n, verbose = 0;
105ec4c1254Sbenno 	int			 ch;
106ec4c1254Sbenno 	const char		*sockname;
107ec4c1254Sbenno 
108ec4c1254Sbenno 	sockname = RELAYD_SOCKET;
109ec4c1254Sbenno 	while ((ch = getopt(argc, argv, "s:")) != -1) {
110ec4c1254Sbenno 		switch (ch) {
111ec4c1254Sbenno 		case 's':
112ec4c1254Sbenno 			sockname = optarg;
113ec4c1254Sbenno 			break;
114ec4c1254Sbenno 		default:
115ec4c1254Sbenno 			usage();
116ec4c1254Sbenno 			/* NOTREACHED */
117ec4c1254Sbenno 		}
118ec4c1254Sbenno 	}
119ec4c1254Sbenno 	argc -= optind;
120ec4c1254Sbenno 	argv += optind;
121feb9ff76Sreyk 
122feb9ff76Sreyk 	/* parse options */
123ec4c1254Sbenno 	if ((res = parse(argc, argv)) == NULL)
124feb9ff76Sreyk 		exit(1);
125feb9ff76Sreyk 
126748ceb64Sreyk 	/* connect to relayd control socket */
127feb9ff76Sreyk 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
128feb9ff76Sreyk 		err(1, "socket");
129feb9ff76Sreyk 
130feb9ff76Sreyk 	bzero(&sun, sizeof(sun));
131feb9ff76Sreyk 	sun.sun_family = AF_UNIX;
132ec4c1254Sbenno 	if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >=
133ec4c1254Sbenno 	    sizeof(sun.sun_path))
134ec4c1254Sbenno 		errx(1, "socket `%s' too long", sockname);
13553b804d7Sreyk  reconnect:
13653b804d7Sreyk 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
13753b804d7Sreyk 		/* Keep retrying if running in monitor mode */
13853b804d7Sreyk 		if (res->action == MONITOR &&
13953b804d7Sreyk 		    (errno == ENOENT || errno == ECONNREFUSED)) {
14053b804d7Sreyk 			usleep(100);
14153b804d7Sreyk 			goto reconnect;
14253b804d7Sreyk 		}
143ec4c1254Sbenno 		err(1, "connect: %s", sockname);
14453b804d7Sreyk 	}
145feb9ff76Sreyk 
146c754e3edSbenno 	if (pledge("stdio", NULL) == -1)
1474581b8e5Ssemarie 		err(1, "pledge");
148c754e3edSbenno 
149feb9ff76Sreyk 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
150feb9ff76Sreyk 		err(1, NULL);
151*f1b790a5Sclaudio 	if (imsgbuf_init(ibuf, ctl_sock) == -1)
152*f1b790a5Sclaudio 		err(1, NULL);
153feb9ff76Sreyk 	done = 0;
154feb9ff76Sreyk 
155feb9ff76Sreyk 	/* process user request */
156feb9ff76Sreyk 	switch (res->action) {
157feb9ff76Sreyk 	case NONE:
158feb9ff76Sreyk 		usage();
159feb9ff76Sreyk 		/* not reached */
160feb9ff76Sreyk 	case SHOW_SUM:
1612edd718bSreyk 	case SHOW_HOSTS:
162dec6607bSreyk 	case SHOW_RDRS:
1632edd718bSreyk 	case SHOW_RELAYS:
164417c432fSreyk 	case SHOW_ROUTERS:
16530e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
166c84cf264Sreyk 		printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n",
167c84cf264Sreyk 		    "Id", "Type", "Name", "Avlblty");
168feb9ff76Sreyk 		break;
169fa0d8478Sreyk 	case SHOW_SESSIONS:
170fa0d8478Sreyk 		imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
171fa0d8478Sreyk 		break;
1729591a9f7Spyr 	case RDR_ENABLE:
1739591a9f7Spyr 		imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1,
174feb9ff76Sreyk 		    &res->id, sizeof(res->id));
175feb9ff76Sreyk 		break;
1769591a9f7Spyr 	case RDR_DISABLE:
1779591a9f7Spyr 		imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1,
178feb9ff76Sreyk 		    &res->id, sizeof(res->id));
179feb9ff76Sreyk 		break;
180feb9ff76Sreyk 	case TABLE_ENABLE:
18130e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1,
182feb9ff76Sreyk 		    &res->id, sizeof(res->id));
183feb9ff76Sreyk 		break;
184feb9ff76Sreyk 	case TABLE_DISABLE:
18530e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1,
186feb9ff76Sreyk 		    &res->id, sizeof(res->id));
187feb9ff76Sreyk 		break;
188feb9ff76Sreyk 	case HOST_ENABLE:
18930e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1,
190feb9ff76Sreyk 		    &res->id, sizeof(res->id));
191feb9ff76Sreyk 		break;
192feb9ff76Sreyk 	case HOST_DISABLE:
19330e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1,
194feb9ff76Sreyk 		    &res->id, sizeof(res->id));
195feb9ff76Sreyk 		break;
196feb9ff76Sreyk 	case SHUTDOWN:
19730e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
198feb9ff76Sreyk 		break;
199cd65ce7bSpyr 	case POLL:
200cd65ce7bSpyr 		imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0);
201cd65ce7bSpyr 		break;
202a2195becSreyk 	case LOAD:
203a2195becSreyk 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
204a2195becSreyk 		    res->path, strlen(res->path));
205a2195becSreyk 		done = 1;
206a2195becSreyk 		break;
207feb9ff76Sreyk 	case RELOAD:
20830e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
209a2195becSreyk 		done = 1;
210feb9ff76Sreyk 		break;
2111569a65fSpyr 	case MONITOR:
21230e88fcdSpyr 		imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
2131569a65fSpyr 		break;
214f579a0f7Sjsg 	case LOG_VERBOSE:
215f579a0f7Sjsg 		verbose = 2;
216f579a0f7Sjsg 		/* FALLTHROUGH */
217f579a0f7Sjsg 	case LOG_BRIEF:
2180325c666Sreyk 		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
219f579a0f7Sjsg 		    &verbose, sizeof(verbose));
220f579a0f7Sjsg 		printf("logging request sent.\n");
221f579a0f7Sjsg 		done = 1;
222f579a0f7Sjsg 		break;
223feb9ff76Sreyk 	}
224feb9ff76Sreyk 
225dd7efffeSclaudio 	if (imsgbuf_flush(ibuf) == -1)
226feb9ff76Sreyk 		err(1, "write error");
227feb9ff76Sreyk 
228feb9ff76Sreyk 	while (!done) {
229668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
230ef2e27a1Sclaudio 			err(1, "read error");
231feb9ff76Sreyk 		if (n == 0)
232feb9ff76Sreyk 			errx(1, "pipe closed");
233feb9ff76Sreyk 
234feb9ff76Sreyk 		while (!done) {
235feb9ff76Sreyk 			if ((n = imsg_get(ibuf, &imsg)) == -1)
236feb9ff76Sreyk 				errx(1, "imsg_get error");
237feb9ff76Sreyk 			if (n == 0)
238feb9ff76Sreyk 				break;
239feb9ff76Sreyk 			switch (res->action) {
240feb9ff76Sreyk 			case SHOW_SUM:
2412edd718bSreyk 			case SHOW_HOSTS:
242dec6607bSreyk 			case SHOW_RDRS:
2432edd718bSreyk 			case SHOW_RELAYS:
244417c432fSreyk 			case SHOW_ROUTERS:
24572f1a274Sreyk 				done = show_summary_msg(&imsg, res->action);
246feb9ff76Sreyk 				break;
247fa0d8478Sreyk 			case SHOW_SESSIONS:
248fa0d8478Sreyk 				done = show_session_msg(&imsg);
249fa0d8478Sreyk 				break;
2509591a9f7Spyr 			case RDR_DISABLE:
2519591a9f7Spyr 			case RDR_ENABLE:
252feb9ff76Sreyk 			case TABLE_DISABLE:
253feb9ff76Sreyk 			case TABLE_ENABLE:
254feb9ff76Sreyk 			case HOST_DISABLE:
255feb9ff76Sreyk 			case HOST_ENABLE:
256cd65ce7bSpyr 			case POLL:
257feb9ff76Sreyk 			case SHUTDOWN:
258c8bdf044Spyr 				done = show_command_output(&imsg);
259c8bdf044Spyr 				break;
260feb9ff76Sreyk 			case NONE:
261f579a0f7Sjsg 			case LOG_VERBOSE:
262f579a0f7Sjsg 			case LOG_BRIEF:
263a2195becSreyk 			case RELOAD:
264a2195becSreyk 			case LOAD:
265feb9ff76Sreyk 				break;
2661569a65fSpyr 			case MONITOR:
2671569a65fSpyr 				done = monitor(&imsg);
2681569a65fSpyr 				break;
269feb9ff76Sreyk 			}
270feb9ff76Sreyk 			imsg_free(&imsg);
271feb9ff76Sreyk 		}
272feb9ff76Sreyk 	}
273feb9ff76Sreyk 	close(ctl_sock);
274feb9ff76Sreyk 	free(ibuf);
275feb9ff76Sreyk 
27698ad2da2Sclaudio 	return (error ? 1 : 0);
277feb9ff76Sreyk }
278feb9ff76Sreyk 
279ee20bc67Sreyk struct imsgname *
280ee20bc67Sreyk monitor_lookup(u_int8_t type)
281ee20bc67Sreyk {
282ee20bc67Sreyk 	int i;
283ee20bc67Sreyk 
284ee20bc67Sreyk 	for (i = 0; imsgs[i].name != NULL; i++)
285ee20bc67Sreyk 		if (imsgs[i].type == type)
286ee20bc67Sreyk 			return (&imsgs[i]);
287ee20bc67Sreyk 	return (&imsgunknown);
288ee20bc67Sreyk }
289ee20bc67Sreyk 
290ee20bc67Sreyk void
291ee20bc67Sreyk monitor_host_status(struct imsg *imsg)
292ee20bc67Sreyk {
293ee20bc67Sreyk 	struct ctl_status	 cs;
294ee20bc67Sreyk 
295ee20bc67Sreyk 	memcpy(&cs, imsg->data, sizeof(cs));
296ee20bc67Sreyk 	printf("\tid: %u\n", cs.id);
297ee20bc67Sreyk 	printf("\tstate: ");
298ee20bc67Sreyk 	switch (cs.up) {
299ee20bc67Sreyk 	case HOST_UP:
300ee20bc67Sreyk 		printf("up\n");
301ee20bc67Sreyk 		break;
302ee20bc67Sreyk 	case HOST_DOWN:
303ee20bc67Sreyk 		printf("down\n");
304ee20bc67Sreyk 		break;
305ee20bc67Sreyk 	default:
306ee20bc67Sreyk 		printf("unknown\n");
307ee20bc67Sreyk 		break;
308ee20bc67Sreyk 	}
309ee20bc67Sreyk }
310ee20bc67Sreyk 
311ee20bc67Sreyk void
312ee20bc67Sreyk monitor_id(struct imsg *imsg)
313ee20bc67Sreyk {
314ee20bc67Sreyk 	struct ctl_id		 id;
315ee20bc67Sreyk 
316ee20bc67Sreyk 	memcpy(&id, imsg->data, sizeof(id));
317ee20bc67Sreyk 	printf("\tid: %u\n", id.id);
318e9955165Sreyk 	if (strlen(id.name))
319e9955165Sreyk 		printf("\tname: %s\n", id.name);
320ee20bc67Sreyk }
321ee20bc67Sreyk 
322feb9ff76Sreyk int
3231569a65fSpyr monitor(struct imsg *imsg)
3241569a65fSpyr {
3251569a65fSpyr 	time_t			 now;
326ee20bc67Sreyk 	int			 done = 0;
327ee20bc67Sreyk 	struct imsgname		*imn;
3281569a65fSpyr 
3291569a65fSpyr 	now = time(NULL);
330ee20bc67Sreyk 
331ee20bc67Sreyk 	imn = monitor_lookup(imsg->hdr.type);
332ee20bc67Sreyk 	printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
333ee20bc67Sreyk 	    imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
3342954f9abSguenther 	printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now));
335ee20bc67Sreyk 	if (imn->type == -1)
3361569a65fSpyr 		done = 1;
337ee20bc67Sreyk 	if (imn->func != NULL)
338ee20bc67Sreyk 		(*imn->func)(imsg);
339ee20bc67Sreyk 
3401569a65fSpyr 	return (done);
3411569a65fSpyr }
3421569a65fSpyr 
3431569a65fSpyr int
3442edd718bSreyk show_summary_msg(struct imsg *imsg, int type)
345feb9ff76Sreyk {
3469591a9f7Spyr 	struct rdr		*rdr;
347feb9ff76Sreyk 	struct table		*table;
348feb9ff76Sreyk 	struct host		*host;
3492edd718bSreyk 	struct relay		*rlay;
350417c432fSreyk 	struct router		*rt;
351417c432fSreyk 	struct netroute		*nr;
3528e01c6e3Sreyk 	struct ctl_stats	 stats[PROC_MAX_INSTANCES];
353b9fc9a72Sderaadt 	char			 name[HOST_NAME_MAX+1];
354feb9ff76Sreyk 
355feb9ff76Sreyk 	switch (imsg->hdr.type) {
3569591a9f7Spyr 	case IMSG_CTL_RDR:
357417c432fSreyk 		if (!(type == SHOW_SUM || type == SHOW_RDRS))
3582edd718bSreyk 			break;
3599591a9f7Spyr 		rdr = imsg->data;
360c84cf264Sreyk 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
3619591a9f7Spyr 		    rdr->conf.id, "redirect", rdr->conf.name, "",
3629591a9f7Spyr 		    print_rdr_status(rdr->conf.flags));
363feb9ff76Sreyk 		break;
364feb9ff76Sreyk 	case IMSG_CTL_TABLE:
365417c432fSreyk 		if (!(type == SHOW_SUM || type == SHOW_HOSTS))
3662edd718bSreyk 			break;
367feb9ff76Sreyk 		table = imsg->data;
368c84cf264Sreyk 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
369c84cf264Sreyk 		    table->conf.id, "table", table->conf.name, "",
370ec60fe22Spyr 		    print_table_status(table->up, table->conf.flags));
371feb9ff76Sreyk 		break;
372feb9ff76Sreyk 	case IMSG_CTL_HOST:
373417c432fSreyk 		if (!(type == SHOW_SUM || type == SHOW_HOSTS))
3742edd718bSreyk 			break;
375feb9ff76Sreyk 		host = imsg->data;
3764d6382cdSreyk 		if (host->conf.parentid)
3774d6382cdSreyk 			snprintf(name, sizeof(name), "%s parent %u",
3784d6382cdSreyk 			    host->conf.name, host->conf.parentid);
3794d6382cdSreyk 		else
3804d6382cdSreyk 			strlcpy(name, host->conf.name, sizeof(name));
381c84cf264Sreyk 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
3824d6382cdSreyk 		    host->conf.id, "host", name,
3832edd718bSreyk 		    print_availability(host->check_cnt, host->up_cnt),
384feb9ff76Sreyk 		    print_host_status(host->up, host->flags));
3852edd718bSreyk 		if (type == SHOW_HOSTS && host->check_cnt) {
386c84cf264Sreyk 			printf("\t%8s\ttotal: %lu/%lu checks",
3872edd718bSreyk 			    "", host->up_cnt, host->check_cnt);
3882edd718bSreyk 			if (host->retry_cnt)
3892edd718bSreyk 				printf(", %d retries", host->retry_cnt);
390c0dc99f6Sreyk 			if (host->he && host->up == HOST_DOWN)
391c0dc99f6Sreyk 				printf(", error: %s", host_error(host->he));
3922edd718bSreyk 			printf("\n");
3932edd718bSreyk 		}
3942edd718bSreyk 		break;
3952edd718bSreyk 	case IMSG_CTL_RELAY:
396417c432fSreyk 		if (!(type == SHOW_SUM || type == SHOW_RELAYS))
3972edd718bSreyk 			break;
3982edd718bSreyk 		rlay = imsg->data;
399c84cf264Sreyk 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
4004a5b9b3eSreyk 		    rlay->rl_conf.id, "relay", rlay->rl_conf.name, "",
4014a5b9b3eSreyk 		    print_relay_status(rlay->rl_conf.flags));
4022edd718bSreyk 		break;
403dec6607bSreyk 	case IMSG_CTL_RDR_STATS:
404dec6607bSreyk 		if (type != SHOW_RDRS)
405dec6607bSreyk 			break;
406dec6607bSreyk 		bcopy(imsg->data, &stats[0], sizeof(stats[0]));
407dec6607bSreyk 		stats[1].id = EMPTY_ID;
408dec6607bSreyk 		print_statistics(stats);
409dec6607bSreyk 		break;
410dec6607bSreyk 	case IMSG_CTL_RELAY_STATS:
4112edd718bSreyk 		if (type != SHOW_RELAYS)
4122edd718bSreyk 			break;
4132edd718bSreyk 		bcopy(imsg->data, &stats, sizeof(stats));
414dec6607bSreyk 		print_statistics(stats);
415feb9ff76Sreyk 		break;
416417c432fSreyk 	case IMSG_CTL_ROUTER:
417417c432fSreyk 		if (!(type == SHOW_SUM || type == SHOW_ROUTERS))
418417c432fSreyk 			break;
419417c432fSreyk 		rt = imsg->data;
420417c432fSreyk 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
421417c432fSreyk 		    rt->rt_conf.id, "router", rt->rt_conf.name, "",
422417c432fSreyk 		    print_relay_status(rt->rt_conf.flags));
423417c432fSreyk 		if (type != SHOW_ROUTERS)
424417c432fSreyk 			break;
425417c432fSreyk 		if (rt->rt_conf.rtable)
426417c432fSreyk 			printf("\t%8s\trtable: %d\n", "", rt->rt_conf.rtable);
427417c432fSreyk 		if (strlen(rt->rt_conf.label))
428417c432fSreyk 			printf("\t%8s\trtlabel: %s\n", "", rt->rt_conf.label);
429417c432fSreyk 		break;
430417c432fSreyk 	case IMSG_CTL_NETROUTE:
431417c432fSreyk 		if (type != SHOW_ROUTERS)
432417c432fSreyk 			break;
433417c432fSreyk 		nr = imsg->data;
434417c432fSreyk 		(void)print_host(&nr->nr_conf.ss, name, sizeof(name));
435417c432fSreyk 		printf("\t%8s\troute: %s/%d\n",
436417c432fSreyk 		    "", name, nr->nr_conf.prefixlen);
437417c432fSreyk 		break;
438feb9ff76Sreyk 	case IMSG_CTL_END:
439feb9ff76Sreyk 		return (1);
440feb9ff76Sreyk 	default:
441feb9ff76Sreyk 		errx(1, "wrong message in summary: %u", imsg->hdr.type);
442feb9ff76Sreyk 		break;
443feb9ff76Sreyk 	}
444feb9ff76Sreyk 	return (0);
445feb9ff76Sreyk }
446feb9ff76Sreyk 
447feb9ff76Sreyk int
448fa0d8478Sreyk show_session_msg(struct imsg *imsg)
449fa0d8478Sreyk {
450f4a6e73bSreyk 	struct rsession		*con;
451fa0d8478Sreyk 	char			 a[128], b[128];
452fa0d8478Sreyk 	struct timeval		 tv_now;
453fa0d8478Sreyk 
454fa0d8478Sreyk 	switch (imsg->hdr.type) {
455fa0d8478Sreyk 	case IMSG_CTL_SESSION:
456fa0d8478Sreyk 		con = imsg->data;
457fa0d8478Sreyk 
458f8eb77d7Sthib 		(void)print_host(&con->se_in.ss, a, sizeof(a));
459f8eb77d7Sthib 		(void)print_host(&con->se_out.ss, b, sizeof(b));
460fa0d8478Sreyk 		printf("session %u:%u %s:%u -> %s:%u\t%s\n",
461f8eb77d7Sthib 		    imsg->hdr.peerid, con->se_id,
462f8eb77d7Sthib 		    a, ntohs(con->se_in.port), b, ntohs(con->se_out.port),
463f8eb77d7Sthib 		    con->se_done ? "DONE" : "RUNNING");
464fa0d8478Sreyk 
465fd1841a3Sreyk 		getmonotime(&tv_now);
466f8eb77d7Sthib 		print_time(&tv_now, &con->se_tv_start, a, sizeof(a));
467f8eb77d7Sthib 		print_time(&tv_now, &con->se_tv_last, b, sizeof(b));
46886b74329Sreyk 		printf("\tage %s, idle %s, relay %u, pid %u",
46986b74329Sreyk 		    a, b, con->se_relayid, con->se_pid);
470cb8b0e56Sreyk 		/* XXX grab tagname instead of tag id */
471cb8b0e56Sreyk 		if (con->se_tag)
472cb8b0e56Sreyk 			printf(", tag (id) %u", con->se_tag);
473aadc8ea4Sreyk 		printf("\n");
474fa0d8478Sreyk 		break;
475fa0d8478Sreyk 	case IMSG_CTL_END:
476fa0d8478Sreyk 		return (1);
477fa0d8478Sreyk 	default:
478fa0d8478Sreyk 		errx(1, "wrong message in session: %u", imsg->hdr.type);
479fa0d8478Sreyk 		break;
480fa0d8478Sreyk 	}
481fa0d8478Sreyk 	return (0);
482fa0d8478Sreyk }
483fa0d8478Sreyk 
484fa0d8478Sreyk int
485feb9ff76Sreyk show_command_output(struct imsg *imsg)
486feb9ff76Sreyk {
487feb9ff76Sreyk 	switch (imsg->hdr.type) {
488feb9ff76Sreyk 	case IMSG_CTL_OK:
489feb9ff76Sreyk 		printf("command succeeded\n");
490feb9ff76Sreyk 		break;
491feb9ff76Sreyk 	case IMSG_CTL_FAIL:
492feb9ff76Sreyk 		printf("command failed\n");
49398ad2da2Sclaudio 		error++;
494feb9ff76Sreyk 		break;
495feb9ff76Sreyk 	default:
496feb9ff76Sreyk 		errx(1, "wrong message in summary: %u", imsg->hdr.type);
497feb9ff76Sreyk 	}
498feb9ff76Sreyk 	return (1);
499feb9ff76Sreyk }
500feb9ff76Sreyk 
501feb9ff76Sreyk char *
5029591a9f7Spyr print_rdr_status(int flags)
503feb9ff76Sreyk {
504feb9ff76Sreyk 	if (flags & F_DISABLE) {
505feb9ff76Sreyk 		return ("disabled");
506feb9ff76Sreyk 	} else if (flags & F_DOWN) {
507feb9ff76Sreyk 		return ("down");
508feb9ff76Sreyk 	} else if (flags & F_BACKUP) {
509feb9ff76Sreyk 		return ("active (using backup table)");
510feb9ff76Sreyk 	} else
511feb9ff76Sreyk 		return ("active");
512feb9ff76Sreyk }
513feb9ff76Sreyk 
514feb9ff76Sreyk char *
515feb9ff76Sreyk print_table_status(int up, int fl)
516feb9ff76Sreyk {
517feb9ff76Sreyk 	static char buf[1024];
518feb9ff76Sreyk 
519feb9ff76Sreyk 	bzero(buf, sizeof(buf));
520feb9ff76Sreyk 
521feb9ff76Sreyk 	if (fl & F_DISABLE) {
522feb9ff76Sreyk 		snprintf(buf, sizeof(buf) - 1, "disabled");
523feb9ff76Sreyk 	} else if (!up) {
524feb9ff76Sreyk 		snprintf(buf, sizeof(buf) - 1, "empty");
525feb9ff76Sreyk 	} else
526c0dc99f6Sreyk 		snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up);
527feb9ff76Sreyk 	return (buf);
528feb9ff76Sreyk }
529feb9ff76Sreyk 
530feb9ff76Sreyk char *
531feb9ff76Sreyk print_host_status(int status, int fl)
532feb9ff76Sreyk {
533feb9ff76Sreyk 	if (fl & F_DISABLE)
534feb9ff76Sreyk 		return ("disabled");
535feb9ff76Sreyk 
536feb9ff76Sreyk 	switch (status) {
537feb9ff76Sreyk 	case HOST_DOWN:
538feb9ff76Sreyk 		return ("down");
539feb9ff76Sreyk 	case HOST_UNKNOWN:
540feb9ff76Sreyk 		return ("unknown");
541feb9ff76Sreyk 	case HOST_UP:
542feb9ff76Sreyk 		return ("up");
543feb9ff76Sreyk 	default:
544feb9ff76Sreyk 		errx(1, "invalid status: %d", status);
545feb9ff76Sreyk 	}
546feb9ff76Sreyk }
5472edd718bSreyk 
5482edd718bSreyk char *
5492edd718bSreyk print_relay_status(int flags)
5502edd718bSreyk {
5512edd718bSreyk 	if (flags & F_DISABLE) {
5522edd718bSreyk 		return ("disabled");
5532edd718bSreyk 	} else
5542edd718bSreyk 		return ("active");
5552edd718bSreyk }
5562edd718bSreyk 
557dec6607bSreyk void
5588e01c6e3Sreyk print_statistics(struct ctl_stats stats[PROC_MAX_INSTANCES + 1])
559dec6607bSreyk {
560dec6607bSreyk 	struct ctl_stats	 crs;
561dec6607bSreyk 	int			 i;
562dec6607bSreyk 
563dec6607bSreyk 	bzero(&crs, sizeof(crs));
564dec6607bSreyk 	crs.interval = stats[0].interval;
565dec6607bSreyk 	for (i = 0; stats[i].id != EMPTY_ID; i++) {
566dec6607bSreyk 		crs.cnt += stats[i].cnt;
567dec6607bSreyk 		crs.last += stats[i].last;
568dec6607bSreyk 		crs.avg += stats[i].avg;
569dec6607bSreyk 		crs.last_hour += stats[i].last_hour;
570dec6607bSreyk 		crs.avg_hour += stats[i].avg_hour;
571dec6607bSreyk 		crs.last_day += stats[i].last_day;
572dec6607bSreyk 		crs.avg_day += stats[i].avg_day;
573dec6607bSreyk 	}
574dec6607bSreyk 	if (crs.cnt == 0)
575dec6607bSreyk 		return;
576dec6607bSreyk 	printf("\t%8s\ttotal: %llu sessions\n"
577b02f4fdbSbenno 	    "\t%8s\tlast: %u/%llus %u/h %u/d sessions\n"
578b02f4fdbSbenno 	    "\t%8s\taverage: %u/%llus %u/h %u/d sessions\n",
579dec6607bSreyk 	    "", crs.cnt,
580dec6607bSreyk 	    "", crs.last, crs.interval,
581dec6607bSreyk 	    crs.last_hour, crs.last_day,
582dec6607bSreyk 	    "", crs.avg, crs.interval,
583dec6607bSreyk 	    crs.avg_hour, crs.avg_day);
584dec6607bSreyk }
585