xref: /openbsd-src/usr.sbin/relayctl/relayctl.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: relayctl.c,v 1.56 2015/12/05 13:13:11 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
6  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
7  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
8  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/time.h>
26 #include <sys/queue.h>
27 #include <sys/un.h>
28 
29 #include <arpa/inet.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <time.h>
39 #include <imsg.h>
40 
41 #include "relayd.h"
42 #include "parser.h"
43 
44 __dead void	 usage(void);
45 int		 show_summary_msg(struct imsg *, int);
46 int		 show_session_msg(struct imsg *);
47 int		 show_command_output(struct imsg *);
48 char		*print_rdr_status(int);
49 char		*print_host_status(int, int);
50 char		*print_table_status(int, int);
51 char		*print_relay_status(int);
52 void		 print_statistics(struct ctl_stats[RELAY_MAXPROC + 1]);
53 
54 struct imsgname {
55 	int type;
56 	char *name;
57 	void (*func)(struct imsg *);
58 };
59 
60 struct imsgname *monitor_lookup(u_int8_t);
61 void		 monitor_host_status(struct imsg *);
62 void		 monitor_id(struct imsg *);
63 int		 monitor(struct imsg *);
64 
65 struct imsgname imsgs[] = {
66 	{ IMSG_HOST_STATUS,		"host_status",	monitor_host_status },
67 	{ IMSG_CTL_RDR_DISABLE,		"ctl_rdr_disable",	monitor_id },
68 	{ IMSG_CTL_RDR_ENABLE,		"ctl_rdr_enable",	monitor_id },
69 	{ IMSG_CTL_TABLE_DISABLE,	"ctl_table_disable",	monitor_id },
70 	{ IMSG_CTL_TABLE_ENABLE,	"ctl_table_enable",	monitor_id },
71 	{ IMSG_CTL_HOST_DISABLE,	"ctl_host_disable",	monitor_id },
72 	{ IMSG_CTL_HOST_ENABLE,		"ctl_host_enable",	monitor_id },
73 	{ IMSG_CTL_TABLE_CHANGED,	"ctl_table_changed",	monitor_id },
74 	{ IMSG_CTL_PULL_RULESET,	"ctl_pull_ruleset",	monitor_id },
75 	{ IMSG_CTL_PUSH_RULESET,	"ctl_push_ruleset",	monitor_id },
76 	{ IMSG_SYNC,			"sync",			NULL },
77 	{ 0,				NULL,			NULL }
78 };
79 struct imsgname imsgunknown = {
80 	-1,				"<unknown>",		NULL
81 };
82 
83 struct imsgbuf	*ibuf;
84 int error = 0;
85 
86 __dead void
87 usage(void)
88 {
89 	extern char *__progname;
90 
91 	fprintf(stderr, "usage: %s command [argument ...]\n", __progname);
92 	exit(1);
93 }
94 
95 int
96 main(int argc, char *argv[])
97 {
98 	struct sockaddr_un	 sun;
99 	struct parse_result	*res;
100 	struct imsg		 imsg;
101 	int			 ctl_sock;
102 	int			 done = 0;
103 	int			 n, verbose = 0;
104 
105 	/* parse options */
106 	if ((res = parse(argc - 1, argv + 1)) == NULL)
107 		exit(1);
108 
109 	/* connect to relayd control socket */
110 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
111 		err(1, "socket");
112 
113 	bzero(&sun, sizeof(sun));
114 	sun.sun_family = AF_UNIX;
115 	(void)strlcpy(sun.sun_path, RELAYD_SOCKET, sizeof(sun.sun_path));
116  reconnect:
117 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
118 		/* Keep retrying if running in monitor mode */
119 		if (res->action == MONITOR &&
120 		    (errno == ENOENT || errno == ECONNREFUSED)) {
121 			usleep(100);
122 			goto reconnect;
123 		}
124 		err(1, "connect: %s", RELAYD_SOCKET);
125 	}
126 
127 	if (pledge("stdio", NULL) == -1)
128 		err(1, "pledge");
129 
130 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
131 		err(1, NULL);
132 	imsg_init(ibuf, ctl_sock);
133 	done = 0;
134 
135 	/* process user request */
136 	switch (res->action) {
137 	case NONE:
138 		usage();
139 		/* not reached */
140 	case SHOW_SUM:
141 	case SHOW_HOSTS:
142 	case SHOW_RDRS:
143 	case SHOW_RELAYS:
144 	case SHOW_ROUTERS:
145 		imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0);
146 		printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n",
147 		    "Id", "Type", "Name", "Avlblty");
148 		break;
149 	case SHOW_SESSIONS:
150 		imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
151 		break;
152 	case RDR_ENABLE:
153 		imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1,
154 		    &res->id, sizeof(res->id));
155 		break;
156 	case RDR_DISABLE:
157 		imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1,
158 		    &res->id, sizeof(res->id));
159 		break;
160 	case TABLE_ENABLE:
161 		imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1,
162 		    &res->id, sizeof(res->id));
163 		break;
164 	case TABLE_DISABLE:
165 		imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1,
166 		    &res->id, sizeof(res->id));
167 		break;
168 	case HOST_ENABLE:
169 		imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1,
170 		    &res->id, sizeof(res->id));
171 		break;
172 	case HOST_DISABLE:
173 		imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1,
174 		    &res->id, sizeof(res->id));
175 		break;
176 	case SHUTDOWN:
177 		imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
178 		break;
179 	case POLL:
180 		imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0);
181 		break;
182 	case LOAD:
183 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
184 		    res->path, strlen(res->path));
185 		done = 1;
186 		break;
187 	case RELOAD:
188 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
189 		done = 1;
190 		break;
191 	case MONITOR:
192 		imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
193 		break;
194 	case LOG_VERBOSE:
195 		verbose = 2;
196 		/* FALLTHROUGH */
197 	case LOG_BRIEF:
198 		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
199 		    &verbose, sizeof(verbose));
200 		printf("logging request sent.\n");
201 		done = 1;
202 		break;
203 	}
204 
205 	while (ibuf->w.queued)
206 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
207 			err(1, "write error");
208 
209 	while (!done) {
210 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
211 			errx(1, "imsg_read error");
212 		if (n == 0)
213 			errx(1, "pipe closed");
214 
215 		while (!done) {
216 			if ((n = imsg_get(ibuf, &imsg)) == -1)
217 				errx(1, "imsg_get error");
218 			if (n == 0)
219 				break;
220 			switch (res->action) {
221 			case SHOW_SUM:
222 			case SHOW_HOSTS:
223 			case SHOW_RDRS:
224 			case SHOW_RELAYS:
225 			case SHOW_ROUTERS:
226 				done = show_summary_msg(&imsg, res->action);
227 				break;
228 			case SHOW_SESSIONS:
229 				done = show_session_msg(&imsg);
230 				break;
231 			case RDR_DISABLE:
232 			case RDR_ENABLE:
233 			case TABLE_DISABLE:
234 			case TABLE_ENABLE:
235 			case HOST_DISABLE:
236 			case HOST_ENABLE:
237 			case POLL:
238 			case SHUTDOWN:
239 				done = show_command_output(&imsg);
240 				break;
241 			case NONE:
242 			case LOG_VERBOSE:
243 			case LOG_BRIEF:
244 			case RELOAD:
245 			case LOAD:
246 				break;
247 			case MONITOR:
248 				done = monitor(&imsg);
249 				break;
250 			}
251 			imsg_free(&imsg);
252 		}
253 	}
254 	close(ctl_sock);
255 	free(ibuf);
256 
257 	return (error ? 1 : 0);
258 }
259 
260 struct imsgname *
261 monitor_lookup(u_int8_t type)
262 {
263 	int i;
264 
265 	for (i = 0; imsgs[i].name != NULL; i++)
266 		if (imsgs[i].type == type)
267 			return (&imsgs[i]);
268 	return (&imsgunknown);
269 }
270 
271 void
272 monitor_host_status(struct imsg *imsg)
273 {
274 	struct ctl_status	 cs;
275 
276 	memcpy(&cs, imsg->data, sizeof(cs));
277 	printf("\tid: %u\n", cs.id);
278 	printf("\tstate: ");
279 	switch (cs.up) {
280 	case HOST_UP:
281 		printf("up\n");
282 		break;
283 	case HOST_DOWN:
284 		printf("down\n");
285 		break;
286 	default:
287 		printf("unknown\n");
288 		break;
289 	}
290 }
291 
292 void
293 monitor_id(struct imsg *imsg)
294 {
295 	struct ctl_id		 id;
296 
297 	memcpy(&id, imsg->data, sizeof(id));
298 	printf("\tid: %u\n", id.id);
299 	if (strlen(id.name))
300 		printf("\tname: %s\n", id.name);
301 }
302 
303 int
304 monitor(struct imsg *imsg)
305 {
306 	time_t			 now;
307 	int			 done = 0;
308 	struct imsgname		*imn;
309 
310 	now = time(NULL);
311 
312 	imn = monitor_lookup(imsg->hdr.type);
313 	printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
314 	    imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
315 	printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now));
316 	if (imn->type == -1)
317 		done = 1;
318 	if (imn->func != NULL)
319 		(*imn->func)(imsg);
320 
321 	return (done);
322 }
323 
324 int
325 show_summary_msg(struct imsg *imsg, int type)
326 {
327 	struct rdr		*rdr;
328 	struct table		*table;
329 	struct host		*host;
330 	struct relay		*rlay;
331 	struct router		*rt;
332 	struct netroute		*nr;
333 	struct ctl_stats	 stats[RELAY_MAXPROC];
334 	char			 name[HOST_NAME_MAX+1];
335 
336 	switch (imsg->hdr.type) {
337 	case IMSG_CTL_RDR:
338 		if (!(type == SHOW_SUM || type == SHOW_RDRS))
339 			break;
340 		rdr = imsg->data;
341 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
342 		    rdr->conf.id, "redirect", rdr->conf.name, "",
343 		    print_rdr_status(rdr->conf.flags));
344 		break;
345 	case IMSG_CTL_TABLE:
346 		if (!(type == SHOW_SUM || type == SHOW_HOSTS))
347 			break;
348 		table = imsg->data;
349 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
350 		    table->conf.id, "table", table->conf.name, "",
351 		    print_table_status(table->up, table->conf.flags));
352 		break;
353 	case IMSG_CTL_HOST:
354 		if (!(type == SHOW_SUM || type == SHOW_HOSTS))
355 			break;
356 		host = imsg->data;
357 		if (host->conf.parentid)
358 			snprintf(name, sizeof(name), "%s parent %u",
359 			    host->conf.name, host->conf.parentid);
360 		else
361 			strlcpy(name, host->conf.name, sizeof(name));
362 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
363 		    host->conf.id, "host", name,
364 		    print_availability(host->check_cnt, host->up_cnt),
365 		    print_host_status(host->up, host->flags));
366 		if (type == SHOW_HOSTS && host->check_cnt) {
367 			printf("\t%8s\ttotal: %lu/%lu checks",
368 			    "", host->up_cnt, host->check_cnt);
369 			if (host->retry_cnt)
370 				printf(", %d retries", host->retry_cnt);
371 			if (host->he && host->up == HOST_DOWN)
372 				printf(", error: %s", host_error(host->he));
373 			printf("\n");
374 		}
375 		break;
376 	case IMSG_CTL_RELAY:
377 		if (!(type == SHOW_SUM || type == SHOW_RELAYS))
378 			break;
379 		rlay = imsg->data;
380 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
381 		    rlay->rl_conf.id, "relay", rlay->rl_conf.name, "",
382 		    print_relay_status(rlay->rl_conf.flags));
383 		break;
384 	case IMSG_CTL_RDR_STATS:
385 		if (type != SHOW_RDRS)
386 			break;
387 		bcopy(imsg->data, &stats[0], sizeof(stats[0]));
388 		stats[1].id = EMPTY_ID;
389 		print_statistics(stats);
390 		break;
391 	case IMSG_CTL_RELAY_STATS:
392 		if (type != SHOW_RELAYS)
393 			break;
394 		bcopy(imsg->data, &stats, sizeof(stats));
395 		print_statistics(stats);
396 		break;
397 	case IMSG_CTL_ROUTER:
398 		if (!(type == SHOW_SUM || type == SHOW_ROUTERS))
399 			break;
400 		rt = imsg->data;
401 		printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n",
402 		    rt->rt_conf.id, "router", rt->rt_conf.name, "",
403 		    print_relay_status(rt->rt_conf.flags));
404 		if (type != SHOW_ROUTERS)
405 			break;
406 		if (rt->rt_conf.rtable)
407 			printf("\t%8s\trtable: %d\n", "", rt->rt_conf.rtable);
408 		if (strlen(rt->rt_conf.label))
409 			printf("\t%8s\trtlabel: %s\n", "", rt->rt_conf.label);
410 		break;
411 	case IMSG_CTL_NETROUTE:
412 		if (type != SHOW_ROUTERS)
413 			break;
414 		nr = imsg->data;
415 		(void)print_host(&nr->nr_conf.ss, name, sizeof(name));
416 		printf("\t%8s\troute: %s/%d\n",
417 		    "", name, nr->nr_conf.prefixlen);
418 		break;
419 	case IMSG_CTL_END:
420 		return (1);
421 	default:
422 		errx(1, "wrong message in summary: %u", imsg->hdr.type);
423 		break;
424 	}
425 	return (0);
426 }
427 
428 int
429 show_session_msg(struct imsg *imsg)
430 {
431 	struct rsession		*con;
432 	char			 a[128], b[128];
433 	struct timeval		 tv_now;
434 
435 	switch (imsg->hdr.type) {
436 	case IMSG_CTL_SESSION:
437 		con = imsg->data;
438 
439 		(void)print_host(&con->se_in.ss, a, sizeof(a));
440 		(void)print_host(&con->se_out.ss, b, sizeof(b));
441 		printf("session %u:%u %s:%u -> %s:%u\t%s\n",
442 		    imsg->hdr.peerid, con->se_id,
443 		    a, ntohs(con->se_in.port), b, ntohs(con->se_out.port),
444 		    con->se_done ? "DONE" : "RUNNING");
445 
446 		getmonotime(&tv_now);
447 		print_time(&tv_now, &con->se_tv_start, a, sizeof(a));
448 		print_time(&tv_now, &con->se_tv_last, b, sizeof(b));
449 		printf("\tage %s, idle %s, relay %u, pid %u",
450 		    a, b, con->se_relayid, con->se_pid);
451 		/* XXX grab tagname instead of tag id */
452 		if (con->se_tag)
453 			printf(", tag (id) %u", con->se_tag);
454 		printf("\n");
455 		break;
456 	case IMSG_CTL_END:
457 		return (1);
458 	default:
459 		errx(1, "wrong message in session: %u", imsg->hdr.type);
460 		break;
461 	}
462 	return (0);
463 }
464 
465 int
466 show_command_output(struct imsg *imsg)
467 {
468 	switch (imsg->hdr.type) {
469 	case IMSG_CTL_OK:
470 		printf("command succeeded\n");
471 		break;
472 	case IMSG_CTL_FAIL:
473 		printf("command failed\n");
474 		error++;
475 		break;
476 	default:
477 		errx(1, "wrong message in summary: %u", imsg->hdr.type);
478 	}
479 	return (1);
480 }
481 
482 char *
483 print_rdr_status(int flags)
484 {
485 	if (flags & F_DISABLE) {
486 		return ("disabled");
487 	} else if (flags & F_DOWN) {
488 		return ("down");
489 	} else if (flags & F_BACKUP) {
490 		return ("active (using backup table)");
491 	} else
492 		return ("active");
493 }
494 
495 char *
496 print_table_status(int up, int fl)
497 {
498 	static char buf[1024];
499 
500 	bzero(buf, sizeof(buf));
501 
502 	if (fl & F_DISABLE) {
503 		snprintf(buf, sizeof(buf) - 1, "disabled");
504 	} else if (!up) {
505 		snprintf(buf, sizeof(buf) - 1, "empty");
506 	} else
507 		snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up);
508 	return (buf);
509 }
510 
511 char *
512 print_host_status(int status, int fl)
513 {
514 	if (fl & F_DISABLE)
515 		return ("disabled");
516 
517 	switch (status) {
518 	case HOST_DOWN:
519 		return ("down");
520 	case HOST_UNKNOWN:
521 		return ("unknown");
522 	case HOST_UP:
523 		return ("up");
524 	default:
525 		errx(1, "invalid status: %d", status);
526 	}
527 }
528 
529 char *
530 print_relay_status(int flags)
531 {
532 	if (flags & F_DISABLE) {
533 		return ("disabled");
534 	} else
535 		return ("active");
536 }
537 
538 void
539 print_statistics(struct ctl_stats stats[RELAY_MAXPROC + 1])
540 {
541 	struct ctl_stats	 crs;
542 	int			 i;
543 
544 	bzero(&crs, sizeof(crs));
545 	crs.interval = stats[0].interval;
546 	for (i = 0; stats[i].id != EMPTY_ID; i++) {
547 		crs.cnt += stats[i].cnt;
548 		crs.last += stats[i].last;
549 		crs.avg += stats[i].avg;
550 		crs.last_hour += stats[i].last_hour;
551 		crs.avg_hour += stats[i].avg_hour;
552 		crs.last_day += stats[i].last_day;
553 		crs.avg_day += stats[i].avg_day;
554 	}
555 	if (crs.cnt == 0)
556 		return;
557 	printf("\t%8s\ttotal: %llu sessions\n"
558 	    "\t%8s\tlast: %u/%llus %u/h %u/d sessions\n"
559 	    "\t%8s\taverage: %u/%llus %u/h %u/d sessions\n",
560 	    "", crs.cnt,
561 	    "", crs.last, crs.interval,
562 	    crs.last_hour, crs.last_day,
563 	    "", crs.avg, crs.interval,
564 	    crs.avg_hour, crs.avg_day);
565 }
566