xref: /openbsd-src/usr.sbin/unwindctl/unwindctl.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: unwindctl.c,v 1.28 2021/09/08 11:38:39 tobhe Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <net/if.h>
28 #include <net/if_media.h>
29 #include <net/if_types.h>
30 #include <net/route.h>
31 
32 #include <err.h>
33 #include <errno.h>
34 #include <event.h>
35 #include <imsg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "unwind.h"
42 #include "frontend.h"
43 #include "resolver.h"
44 #include "parser.h"
45 
46 __dead void	 usage(void);
47 int		 show_status_msg(struct imsg *);
48 int		 show_autoconf_msg(struct imsg *);
49 int		 show_mem_msg(struct imsg *);
50 void		 histogram_header(void);
51 void		 print_histogram(const char *name, int64_t[], size_t);
52 const char	*prio2str(int);
53 
54 struct imsgbuf		*ibuf;
55 int		 	 info_cnt;
56 struct ctl_resolver_info info[UW_RES_NONE];
57 
58 const char *
59 prio2str(int prio)
60 {
61 	switch(prio) {
62 	case RTP_PROPOSAL_DHCLIENT:
63 		return "DHCP";
64 	case RTP_PROPOSAL_SLAAC:
65 		return "SLAAC";
66 	case RTP_PROPOSAL_STATIC:
67 		return "STATIC";
68 	case RTP_PROPOSAL_UMB:
69 		return "UMB";
70 	}
71 	return "OTHER";
72 }
73 
74 __dead void
75 usage(void)
76 {
77 	extern char *__progname;
78 
79 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
80 	    __progname);
81 	exit(1);
82 }
83 
84 int
85 main(int argc, char *argv[])
86 {
87 	struct sockaddr_un		 sun;
88 	struct parse_result		*res;
89 	struct imsg			 imsg;
90 	struct ctl_resolver_info	*cri;
91 	int				 ctl_sock;
92 	int				 done = 0;
93 	int				 i, j, k, n, verbose = 0;
94 	int				 ch, column_offset;
95 	char				*sockname;
96 
97 	sockname = _PATH_UNWIND_SOCKET;
98 	while ((ch = getopt(argc, argv, "s:")) != -1) {
99 		switch (ch) {
100 		case 's':
101 			sockname = optarg;
102 			break;
103 		default:
104 			usage();
105 		}
106 	}
107 	argc -= optind;
108 	argv += optind;
109 
110 	/* Parse command line. */
111 	if ((res = parse(argc, argv)) == NULL)
112 		exit(1);
113 
114 	/* Connect to control socket. */
115 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
116 		err(1, "socket");
117 
118 	memset(&sun, 0, sizeof(sun));
119 	sun.sun_family = AF_UNIX;
120 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
121 
122 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
123 		err(1, "connect: %s", sockname);
124 
125 	if (pledge("stdio", NULL) == -1)
126 		err(1, "pledge");
127 
128 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
129 		err(1, NULL);
130 	imsg_init(ibuf, ctl_sock);
131 	done = 0;
132 
133 	/* Check for root-only actions */
134 	switch (res->action) {
135 	case LOG_DEBUG:
136 	case LOG_VERBOSE:
137 	case LOG_BRIEF:
138 	case RELOAD:
139 		if (geteuid() != 0)
140 			errx(1, "need root privileges");
141 		break;
142 	default:
143 		break;
144 	}
145 
146 	/* Process user request. */
147 	switch (res->action) {
148 	case LOG_DEBUG:
149 		verbose |= OPT_VERBOSE2;
150 		/* FALLTHROUGH */
151 	case LOG_VERBOSE:
152 		verbose |= OPT_VERBOSE;
153 		/* FALLTHROUGH */
154 	case LOG_BRIEF:
155 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
156 		    &verbose, sizeof(verbose));
157 		printf("logging request sent.\n");
158 		done = 1;
159 		break;
160 	case RELOAD:
161 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
162 		printf("reload request sent.\n");
163 		done = 1;
164 		break;
165 	case STATUS:
166 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
167 		break;
168 	case AUTOCONF:
169 		imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0);
170 		break;
171 	case MEM:
172 		imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0);
173 		break;
174 	default:
175 		usage();
176 	}
177 
178 	while (ibuf->w.queued)
179 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
180 			err(1, "write error");
181 
182 	while (!done) {
183 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
184 			errx(1, "imsg_read error");
185 		if (n == 0)
186 			errx(1, "pipe closed");
187 
188 		while (!done) {
189 			if ((n = imsg_get(ibuf, &imsg)) == -1)
190 				errx(1, "imsg_get error");
191 			if (n == 0)
192 				break;
193 
194 			switch (res->action) {
195 			case STATUS:
196 				done = show_status_msg(&imsg);
197 				break;
198 			case AUTOCONF:
199 				done = show_autoconf_msg(&imsg);
200 				break;
201 			case MEM:
202 				done = show_mem_msg(&imsg);
203 				break;
204 			default:
205 				break;
206 			}
207 			imsg_free(&imsg);
208 		}
209 	}
210 	close(ctl_sock);
211 	free(ibuf);
212 
213 	column_offset = info_cnt / 2;
214 	if (info_cnt % 2 == 1)
215 		column_offset++;
216 
217 	for (i = 0; i < column_offset; i++) {
218 		for (j = 0; j < 2; j++) {
219 			k = i + j * column_offset;
220 			if (k >= info_cnt)
221 				break;
222 
223 			cri = &info[k];
224 			printf("%d. %-15s %10s, ", k + 1,
225 			    uw_resolver_type_str[cri->type],
226 			    uw_resolver_state_str[cri->state]);
227 			if (cri->median == 0)
228 				printf("%5s", "N/A");
229 			else if (cri->median == INT64_MAX)
230 				printf("%5s", "Inf");
231 			else
232 				printf("%3lldms", cri->median);
233 			if (j == 0)
234 				printf("   ");
235 		}
236 		printf("\n");
237 	}
238 
239 	if (info_cnt)
240 		histogram_header();
241 	for (i = 0; i < info_cnt; i++) {
242 		cri = &info[i];
243 		print_histogram(uw_resolver_type_short[cri->type],
244 		    cri->histogram, nitems(cri->histogram));
245 		print_histogram("", cri->latest_histogram,
246 		    nitems(cri->latest_histogram));
247 	}
248 	return (0);
249 }
250 
251 int
252 show_status_msg(struct imsg *imsg)
253 {
254 	static char			 fwd_line[80];
255 
256 	switch (imsg->hdr.type) {
257 	case IMSG_CTL_RESOLVER_INFO:
258 		memcpy(&info[info_cnt++], imsg->data, sizeof(info[0]));
259 		break;
260 	case IMSG_CTL_END:
261 		if (fwd_line[0] != '\0')
262 			printf("%s\n", fwd_line);
263 		return (1);
264 	default:
265 		break;
266 	}
267 
268 	return (0);
269 }
270 
271 int
272 show_autoconf_msg(struct imsg *imsg)
273 {
274 	static int			 autoconf_forwarders, last_src;
275 	static int			 label_len, line_len;
276 	static uint32_t			 last_if_index;
277 	static char			 fwd_line[80];
278 	struct ctl_forwarder_info	*cfi;
279 	char				 ifnamebuf[IFNAMSIZ];
280 	char				*if_name;
281 
282 	switch (imsg->hdr.type) {
283 	case IMSG_CTL_AUTOCONF_RESOLVER_INFO:
284 		cfi = imsg->data;
285 		if (!autoconf_forwarders++)
286 			printf("autoconfiguration forwarders:\n");
287 		if (cfi->if_index != last_if_index || cfi->src != last_src) {
288 			if_name = if_indextoname(cfi->if_index, ifnamebuf);
289 			if (fwd_line[0] != '\0') {
290 				printf("%s\n", fwd_line);
291 				fwd_line[0] = '\0';
292 			}
293 			label_len = snprintf(fwd_line, sizeof(fwd_line),
294 			    "%6s[%s]:", prio2str(cfi->src),
295 			    if_name ? if_name : "unknown");
296 			line_len = label_len;
297 			last_if_index = cfi->if_index;
298 			last_src = cfi->src;
299 		}
300 
301 		if (line_len + 1 + strlen(cfi->ip) > sizeof(fwd_line)) {
302 			printf("%s\n", fwd_line);
303 			snprintf(fwd_line, sizeof(fwd_line), "%*s", label_len,
304 			    " ");
305 		}
306 		strlcat(fwd_line, " ", sizeof(fwd_line));
307 		line_len = strlcat(fwd_line, cfi->ip, sizeof(fwd_line));
308 		break;
309 	case IMSG_CTL_END:
310 		if (fwd_line[0] != '\0')
311 			printf("%s\n", fwd_line);
312 		return (1);
313 	default:
314 		break;
315 	}
316 
317 	return (0);
318 }
319 
320 void
321 histogram_header(void)
322 {
323 	const char	 head[] = "histograms: lifetime[ms], decaying[ms]";
324 	char	 	 buf[10];
325 	size_t	 	 i;
326 
327 	printf("\n%*s%*s\n%*s", 5, "",
328 	    (int)(72/2 + (sizeof(head)-1)/2), head, 6, "");
329 	for(i = 0; i < nitems(histogram_limits) - 1; i++) {
330 		snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]);
331 		printf("%6s", buf);
332 	}
333 	printf("%6s\n", ">");
334 }
335 
336 void
337 print_histogram(const char *name, int64_t histogram[], size_t n)
338 {
339 	size_t	 i;
340 
341 	printf("%5s ", name);
342 	for(i = 0; i < n; i++)
343 		printf("%6lld", histogram[i]);
344 	printf("\n");
345 }
346 
347 int
348 show_mem_msg(struct imsg *imsg)
349 {
350 	struct ctl_mem_info	*cmi;
351 
352 	switch (imsg->hdr.type) {
353 	case IMSG_CTL_MEM_INFO:
354 		cmi = imsg->data;
355 		printf("msg-cache:   %zu / %zu (%.2f%%)\n", cmi->msg_cache_used,
356 		    cmi->msg_cache_max, 100.0 * cmi->msg_cache_used /
357 		    cmi->msg_cache_max);
358 		printf("rrset-cache: %zu / %zu (%.2f%%)\n",
359 		    cmi->rrset_cache_used, cmi->rrset_cache_max, 100.0 *
360 		    cmi->rrset_cache_used / cmi->rrset_cache_max);
361 		printf("key-cache: %zu / %zu (%.2f%%)\n", cmi->key_cache_used,
362 		    cmi->key_cache_max, 100.0 * cmi->key_cache_used /
363 		    cmi->key_cache_max);
364 		printf("neg-cache: %zu / %zu (%.2f%%)\n", cmi->neg_cache_used,
365 		    cmi->neg_cache_max, 100.0 * cmi->neg_cache_used /
366 		    cmi->neg_cache_max);
367 		break;
368 	default:
369 		break;
370 	}
371 
372 	return 1;
373 }
374