xref: /openbsd/usr.sbin/unwindctl/unwindctl.c (revision 6371cd0b)
1*6371cd0bSbket /*	$OpenBSD: unwindctl.c,v 1.29 2021/11/10 20:24:22 bket Exp $	*/
25c077b0fSflorian 
35c077b0fSflorian /*
45c077b0fSflorian  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
55c077b0fSflorian  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
65c077b0fSflorian  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
75c077b0fSflorian  *
85c077b0fSflorian  * Permission to use, copy, modify, and distribute this software for any
95c077b0fSflorian  * purpose with or without fee is hereby granted, provided that the above
105c077b0fSflorian  * copyright notice and this permission notice appear in all copies.
115c077b0fSflorian  *
125c077b0fSflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
135c077b0fSflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
145c077b0fSflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
155c077b0fSflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
165c077b0fSflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
175c077b0fSflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
185c077b0fSflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
195c077b0fSflorian  */
205c077b0fSflorian 
215c077b0fSflorian #include <sys/types.h>
225c077b0fSflorian #include <sys/queue.h>
235c077b0fSflorian #include <sys/socket.h>
245c077b0fSflorian #include <sys/un.h>
255c077b0fSflorian #include <netinet/in.h>
265c077b0fSflorian #include <arpa/inet.h>
275c077b0fSflorian #include <net/if.h>
285c077b0fSflorian #include <net/if_media.h>
295c077b0fSflorian #include <net/if_types.h>
3028ba4729Sflorian #include <net/route.h>
315c077b0fSflorian 
325c077b0fSflorian #include <err.h>
335c077b0fSflorian #include <errno.h>
345c077b0fSflorian #include <event.h>
355c077b0fSflorian #include <imsg.h>
365c077b0fSflorian #include <stdio.h>
375c077b0fSflorian #include <stdlib.h>
385c077b0fSflorian #include <string.h>
395c077b0fSflorian #include <unistd.h>
405c077b0fSflorian 
415c077b0fSflorian #include "unwind.h"
425c077b0fSflorian #include "frontend.h"
435c077b0fSflorian #include "resolver.h"
445c077b0fSflorian #include "parser.h"
455c077b0fSflorian 
465c077b0fSflorian __dead void	 usage(void);
475c077b0fSflorian int		 show_status_msg(struct imsg *);
4815fe126bSflorian int		 show_autoconf_msg(struct imsg *);
49c071f090Sflorian int		 show_mem_msg(struct imsg *);
502db6800fSotto void		 histogram_header(void);
512db6800fSotto void		 print_histogram(const char *name, int64_t[], size_t);
5228d3b5f7Stobhe const char	*prio2str(int);
535c077b0fSflorian 
545c077b0fSflorian struct imsgbuf		*ibuf;
55608e5d72Sotto int		 	 info_cnt;
562db6800fSotto struct ctl_resolver_info info[UW_RES_NONE];
575c077b0fSflorian 
5828d3b5f7Stobhe const char *
prio2str(int prio)5928d3b5f7Stobhe prio2str(int prio)
6028d3b5f7Stobhe {
6128d3b5f7Stobhe 	switch(prio) {
6228d3b5f7Stobhe 	case RTP_PROPOSAL_DHCLIENT:
6328d3b5f7Stobhe 		return "DHCP";
6428d3b5f7Stobhe 	case RTP_PROPOSAL_SLAAC:
6528d3b5f7Stobhe 		return "SLAAC";
6628d3b5f7Stobhe 	case RTP_PROPOSAL_STATIC:
6728d3b5f7Stobhe 		return "STATIC";
6828d3b5f7Stobhe 	case RTP_PROPOSAL_UMB:
6928d3b5f7Stobhe 		return "UMB";
70*6371cd0bSbket 	case RTP_PROPOSAL_PPP:
71*6371cd0bSbket 		return "PPP";
7228d3b5f7Stobhe 	}
7328d3b5f7Stobhe 	return "OTHER";
7428d3b5f7Stobhe }
7528d3b5f7Stobhe 
765c077b0fSflorian __dead void
usage(void)775c077b0fSflorian usage(void)
785c077b0fSflorian {
795c077b0fSflorian 	extern char *__progname;
805c077b0fSflorian 
815c077b0fSflorian 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
825c077b0fSflorian 	    __progname);
835c077b0fSflorian 	exit(1);
845c077b0fSflorian }
855c077b0fSflorian 
865c077b0fSflorian int
main(int argc,char * argv[])875c077b0fSflorian main(int argc, char *argv[])
885c077b0fSflorian {
895c077b0fSflorian 	struct sockaddr_un		 sun;
905c077b0fSflorian 	struct parse_result		*res;
915c077b0fSflorian 	struct imsg			 imsg;
92608e5d72Sotto 	struct ctl_resolver_info	*cri;
935c077b0fSflorian 	int				 ctl_sock;
945c077b0fSflorian 	int				 done = 0;
95608e5d72Sotto 	int				 i, j, k, n, verbose = 0;
96608e5d72Sotto 	int				 ch, column_offset;
97608e5d72Sotto 	char				*sockname;
985c077b0fSflorian 
99ee5be195Sflorian 	sockname = _PATH_UNWIND_SOCKET;
1005c077b0fSflorian 	while ((ch = getopt(argc, argv, "s:")) != -1) {
1015c077b0fSflorian 		switch (ch) {
1025c077b0fSflorian 		case 's':
1035c077b0fSflorian 			sockname = optarg;
1045c077b0fSflorian 			break;
1055c077b0fSflorian 		default:
1065c077b0fSflorian 			usage();
1075c077b0fSflorian 		}
1085c077b0fSflorian 	}
1095c077b0fSflorian 	argc -= optind;
1105c077b0fSflorian 	argv += optind;
1115c077b0fSflorian 
1125c077b0fSflorian 	/* Parse command line. */
1135c077b0fSflorian 	if ((res = parse(argc, argv)) == NULL)
1145c077b0fSflorian 		exit(1);
1155c077b0fSflorian 
1165c077b0fSflorian 	/* Connect to control socket. */
1175c077b0fSflorian 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
1185c077b0fSflorian 		err(1, "socket");
1195c077b0fSflorian 
1205c077b0fSflorian 	memset(&sun, 0, sizeof(sun));
1215c077b0fSflorian 	sun.sun_family = AF_UNIX;
1225c077b0fSflorian 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
123cee51daaSflorian 
1245c077b0fSflorian 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
1255c077b0fSflorian 		err(1, "connect: %s", sockname);
1265c077b0fSflorian 
1275c077b0fSflorian 	if (pledge("stdio", NULL) == -1)
1285c077b0fSflorian 		err(1, "pledge");
1295c077b0fSflorian 
1305c077b0fSflorian 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
1315c077b0fSflorian 		err(1, NULL);
1325c077b0fSflorian 	imsg_init(ibuf, ctl_sock);
1335c077b0fSflorian 	done = 0;
1345c077b0fSflorian 
1353a1cc939Ssolene 	/* Check for root-only actions */
1363a1cc939Ssolene 	switch (res->action) {
1373a1cc939Ssolene 	case LOG_DEBUG:
1383a1cc939Ssolene 	case LOG_VERBOSE:
1393a1cc939Ssolene 	case LOG_BRIEF:
1403a1cc939Ssolene 	case RELOAD:
1413a1cc939Ssolene 		if (geteuid() != 0)
1423a1cc939Ssolene 			errx(1, "need root privileges");
1433a1cc939Ssolene 		break;
1443a1cc939Ssolene 	default:
1453a1cc939Ssolene 		break;
1463a1cc939Ssolene 	}
1473a1cc939Ssolene 
1485c077b0fSflorian 	/* Process user request. */
1495c077b0fSflorian 	switch (res->action) {
1505c077b0fSflorian 	case LOG_DEBUG:
1515c077b0fSflorian 		verbose |= OPT_VERBOSE2;
1525c077b0fSflorian 		/* FALLTHROUGH */
1535c077b0fSflorian 	case LOG_VERBOSE:
1545c077b0fSflorian 		verbose |= OPT_VERBOSE;
1555c077b0fSflorian 		/* FALLTHROUGH */
1565c077b0fSflorian 	case LOG_BRIEF:
1575c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
1585c077b0fSflorian 		    &verbose, sizeof(verbose));
1595c077b0fSflorian 		printf("logging request sent.\n");
1605c077b0fSflorian 		done = 1;
1615c077b0fSflorian 		break;
1625c077b0fSflorian 	case RELOAD:
1635c077b0fSflorian 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
1645c077b0fSflorian 		printf("reload request sent.\n");
1655c077b0fSflorian 		done = 1;
1665c077b0fSflorian 		break;
1675c077b0fSflorian 	case STATUS:
168f6a28683Sotto 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
1692db6800fSotto 		break;
17015fe126bSflorian 	case AUTOCONF:
17115fe126bSflorian 		imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0);
17215fe126bSflorian 		break;
173c071f090Sflorian 	case MEM:
174c071f090Sflorian 		imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0);
175c071f090Sflorian 		break;
1765c077b0fSflorian 	default:
1775c077b0fSflorian 		usage();
1785c077b0fSflorian 	}
1795c077b0fSflorian 
1805c077b0fSflorian 	while (ibuf->w.queued)
1815c077b0fSflorian 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
1825c077b0fSflorian 			err(1, "write error");
1835c077b0fSflorian 
1845c077b0fSflorian 	while (!done) {
1855c077b0fSflorian 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
1865c077b0fSflorian 			errx(1, "imsg_read error");
1875c077b0fSflorian 		if (n == 0)
1885c077b0fSflorian 			errx(1, "pipe closed");
1895c077b0fSflorian 
1905c077b0fSflorian 		while (!done) {
1915c077b0fSflorian 			if ((n = imsg_get(ibuf, &imsg)) == -1)
1925c077b0fSflorian 				errx(1, "imsg_get error");
1935c077b0fSflorian 			if (n == 0)
1945c077b0fSflorian 				break;
1955c077b0fSflorian 
1965c077b0fSflorian 			switch (res->action) {
1975c077b0fSflorian 			case STATUS:
1985c077b0fSflorian 				done = show_status_msg(&imsg);
1995c077b0fSflorian 				break;
20015fe126bSflorian 			case AUTOCONF:
20115fe126bSflorian 				done = show_autoconf_msg(&imsg);
20215fe126bSflorian 				break;
203c071f090Sflorian 			case MEM:
204c071f090Sflorian 				done = show_mem_msg(&imsg);
205c071f090Sflorian 				break;
2065c077b0fSflorian 			default:
2075c077b0fSflorian 				break;
2085c077b0fSflorian 			}
2095c077b0fSflorian 			imsg_free(&imsg);
2105c077b0fSflorian 		}
2115c077b0fSflorian 	}
2125c077b0fSflorian 	close(ctl_sock);
2135c077b0fSflorian 	free(ibuf);
2145c077b0fSflorian 
215608e5d72Sotto 	column_offset = info_cnt / 2;
216608e5d72Sotto 	if (info_cnt % 2 == 1)
217608e5d72Sotto 		column_offset++;
218608e5d72Sotto 
219608e5d72Sotto 	for (i = 0; i < column_offset; i++) {
220608e5d72Sotto 		for (j = 0; j < 2; j++) {
221608e5d72Sotto 			k = i + j * column_offset;
222608e5d72Sotto 			if (k >= info_cnt)
22315fe126bSflorian 				break;
224608e5d72Sotto 
225608e5d72Sotto 			cri = &info[k];
226608e5d72Sotto 			printf("%d. %-15s %10s, ", k + 1,
227608e5d72Sotto 			    uw_resolver_type_str[cri->type],
228608e5d72Sotto 			    uw_resolver_state_str[cri->state]);
229608e5d72Sotto 			if (cri->median == 0)
230608e5d72Sotto 				printf("%5s", "N/A");
231608e5d72Sotto 			else if (cri->median == INT64_MAX)
232608e5d72Sotto 				printf("%5s", "Inf");
233608e5d72Sotto 			else
234608e5d72Sotto 				printf("%3lldms", cri->median);
235608e5d72Sotto 			if (j == 0)
236608e5d72Sotto 				printf("   ");
23715fe126bSflorian 		}
238608e5d72Sotto 		printf("\n");
239608e5d72Sotto 	}
240608e5d72Sotto 
241608e5d72Sotto 	if (info_cnt)
242608e5d72Sotto 		histogram_header();
243608e5d72Sotto 	for (i = 0; i < info_cnt; i++) {
244608e5d72Sotto 		cri = &info[i];
245608e5d72Sotto 		print_histogram(uw_resolver_type_short[cri->type],
246608e5d72Sotto 		    cri->histogram, nitems(cri->histogram));
247608e5d72Sotto 		print_histogram("", cri->latest_histogram,
248608e5d72Sotto 		    nitems(cri->latest_histogram));
2492db6800fSotto 	}
2505c077b0fSflorian 	return (0);
2515c077b0fSflorian }
2525c077b0fSflorian 
2535c077b0fSflorian int
show_status_msg(struct imsg * imsg)2545c077b0fSflorian show_status_msg(struct imsg *imsg)
2555c077b0fSflorian {
2561ceebf76Sflorian 	static char			 fwd_line[80];
2575c077b0fSflorian 
2585c077b0fSflorian 	switch (imsg->hdr.type) {
2595c077b0fSflorian 	case IMSG_CTL_RESOLVER_INFO:
260608e5d72Sotto 		memcpy(&info[info_cnt++], imsg->data, sizeof(info[0]));
2615c077b0fSflorian 		break;
26215fe126bSflorian 	case IMSG_CTL_END:
26315fe126bSflorian 		if (fwd_line[0] != '\0')
26415fe126bSflorian 			printf("%s\n", fwd_line);
26515fe126bSflorian 		return (1);
26615fe126bSflorian 	default:
26715fe126bSflorian 		break;
26815fe126bSflorian 	}
26915fe126bSflorian 
27015fe126bSflorian 	return (0);
27115fe126bSflorian }
27215fe126bSflorian 
27315fe126bSflorian int
show_autoconf_msg(struct imsg * imsg)27415fe126bSflorian show_autoconf_msg(struct imsg *imsg)
27515fe126bSflorian {
27615fe126bSflorian 	static int			 autoconf_forwarders, last_src;
27715fe126bSflorian 	static int			 label_len, line_len;
27815fe126bSflorian 	static uint32_t			 last_if_index;
27915fe126bSflorian 	static char			 fwd_line[80];
28015fe126bSflorian 	struct ctl_forwarder_info	*cfi;
28115fe126bSflorian 	char				 ifnamebuf[IFNAMSIZ];
28215fe126bSflorian 	char				*if_name;
28315fe126bSflorian 
28415fe126bSflorian 	switch (imsg->hdr.type) {
285ecfbee2cSflorian 	case IMSG_CTL_AUTOCONF_RESOLVER_INFO:
286ecfbee2cSflorian 		cfi = imsg->data;
287ecfbee2cSflorian 		if (!autoconf_forwarders++)
28815fe126bSflorian 			printf("autoconfiguration forwarders:\n");
2891ceebf76Sflorian 		if (cfi->if_index != last_if_index || cfi->src != last_src) {
2906158ed34Sflorian 			if_name = if_indextoname(cfi->if_index, ifnamebuf);
2911ceebf76Sflorian 			if (fwd_line[0] != '\0') {
2921ceebf76Sflorian 				printf("%s\n", fwd_line);
2931ceebf76Sflorian 				fwd_line[0] = '\0';
2941ceebf76Sflorian 			}
2951ceebf76Sflorian 			label_len = snprintf(fwd_line, sizeof(fwd_line),
29628d3b5f7Stobhe 			    "%6s[%s]:", prio2str(cfi->src),
29728d3b5f7Stobhe 			    if_name ? if_name : "unknown");
2981ceebf76Sflorian 			line_len = label_len;
2991ceebf76Sflorian 			last_if_index = cfi->if_index;
3001ceebf76Sflorian 			last_src = cfi->src;
3011ceebf76Sflorian 		}
3021ceebf76Sflorian 
3031ceebf76Sflorian 		if (line_len + 1 + strlen(cfi->ip) > sizeof(fwd_line)) {
3041ceebf76Sflorian 			printf("%s\n", fwd_line);
3051ceebf76Sflorian 			snprintf(fwd_line, sizeof(fwd_line), "%*s", label_len,
3061ceebf76Sflorian 			    " ");
3071ceebf76Sflorian 		}
3081ceebf76Sflorian 		strlcat(fwd_line, " ", sizeof(fwd_line));
3091ceebf76Sflorian 		line_len = strlcat(fwd_line, cfi->ip, sizeof(fwd_line));
310ecfbee2cSflorian 		break;
3115c077b0fSflorian 	case IMSG_CTL_END:
3121ceebf76Sflorian 		if (fwd_line[0] != '\0')
3131ceebf76Sflorian 			printf("%s\n", fwd_line);
3145c077b0fSflorian 		return (1);
3155c077b0fSflorian 	default:
3165c077b0fSflorian 		break;
3175c077b0fSflorian 	}
3185c077b0fSflorian 
3195c077b0fSflorian 	return (0);
3205c077b0fSflorian }
3215c077b0fSflorian 
3225c077b0fSflorian void
histogram_header(void)3232db6800fSotto histogram_header(void)
3245c077b0fSflorian {
3254b642e9eSotto 	const char	 head[] = "histograms: lifetime[ms], decaying[ms]";
3265c077b0fSflorian 	char	 	 buf[10];
3272db6800fSotto 	size_t	 	 i;
3285c077b0fSflorian 
3294b642e9eSotto 	printf("\n%*s%*s\n%*s", 5, "",
330608e5d72Sotto 	    (int)(72/2 + (sizeof(head)-1)/2), head, 6, "");
331943f8c8cSotto 	for(i = 0; i < nitems(histogram_limits) - 1; i++) {
3325c077b0fSflorian 		snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]);
3335c077b0fSflorian 		printf("%6s", buf);
3345c077b0fSflorian 	}
3355c077b0fSflorian 	printf("%6s\n", ">");
3362db6800fSotto }
3372db6800fSotto 
3382db6800fSotto void
print_histogram(const char * name,int64_t histogram[],size_t n)3392db6800fSotto print_histogram(const char *name, int64_t histogram[], size_t n)
3402db6800fSotto {
3412db6800fSotto 	size_t	 i;
3422db6800fSotto 
343608e5d72Sotto 	printf("%5s ", name);
3442db6800fSotto 	for(i = 0; i < n; i++)
3455c077b0fSflorian 		printf("%6lld", histogram[i]);
3465c077b0fSflorian 	printf("\n");
3475c077b0fSflorian }
348c071f090Sflorian 
349c071f090Sflorian int
show_mem_msg(struct imsg * imsg)350c071f090Sflorian show_mem_msg(struct imsg *imsg)
351c071f090Sflorian {
352c071f090Sflorian 	struct ctl_mem_info	*cmi;
353c071f090Sflorian 
354c071f090Sflorian 	switch (imsg->hdr.type) {
355c071f090Sflorian 	case IMSG_CTL_MEM_INFO:
356c071f090Sflorian 		cmi = imsg->data;
357c071f090Sflorian 		printf("msg-cache:   %zu / %zu (%.2f%%)\n", cmi->msg_cache_used,
358c071f090Sflorian 		    cmi->msg_cache_max, 100.0 * cmi->msg_cache_used /
359c071f090Sflorian 		    cmi->msg_cache_max);
360c071f090Sflorian 		printf("rrset-cache: %zu / %zu (%.2f%%)\n",
361c071f090Sflorian 		    cmi->rrset_cache_used, cmi->rrset_cache_max, 100.0 *
362c071f090Sflorian 		    cmi->rrset_cache_used / cmi->rrset_cache_max);
363c071f090Sflorian 		printf("key-cache: %zu / %zu (%.2f%%)\n", cmi->key_cache_used,
364c071f090Sflorian 		    cmi->key_cache_max, 100.0 * cmi->key_cache_used /
365c071f090Sflorian 		    cmi->key_cache_max);
366c071f090Sflorian 		printf("neg-cache: %zu / %zu (%.2f%%)\n", cmi->neg_cache_used,
367c071f090Sflorian 		    cmi->neg_cache_max, 100.0 * cmi->neg_cache_used /
368c071f090Sflorian 		    cmi->neg_cache_max);
369c071f090Sflorian 		break;
370c071f090Sflorian 	default:
371c071f090Sflorian 		break;
372c071f090Sflorian 	}
373c071f090Sflorian 
374c071f090Sflorian 	return 1;
375c071f090Sflorian }
376