xref: /openbsd/usr.sbin/unwindctl/unwindctl.c (revision 6371cd0b)
1 /*	$OpenBSD: unwindctl.c,v 1.29 2021/11/10 20:24:22 bket 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 *
prio2str(int prio)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 	case RTP_PROPOSAL_PPP:
71 		return "PPP";
72 	}
73 	return "OTHER";
74 }
75 
76 __dead void
usage(void)77 usage(void)
78 {
79 	extern char *__progname;
80 
81 	fprintf(stderr, "usage: %s [-s socket] command [argument ...]\n",
82 	    __progname);
83 	exit(1);
84 }
85 
86 int
main(int argc,char * argv[])87 main(int argc, char *argv[])
88 {
89 	struct sockaddr_un		 sun;
90 	struct parse_result		*res;
91 	struct imsg			 imsg;
92 	struct ctl_resolver_info	*cri;
93 	int				 ctl_sock;
94 	int				 done = 0;
95 	int				 i, j, k, n, verbose = 0;
96 	int				 ch, column_offset;
97 	char				*sockname;
98 
99 	sockname = _PATH_UNWIND_SOCKET;
100 	while ((ch = getopt(argc, argv, "s:")) != -1) {
101 		switch (ch) {
102 		case 's':
103 			sockname = optarg;
104 			break;
105 		default:
106 			usage();
107 		}
108 	}
109 	argc -= optind;
110 	argv += optind;
111 
112 	/* Parse command line. */
113 	if ((res = parse(argc, argv)) == NULL)
114 		exit(1);
115 
116 	/* Connect to control socket. */
117 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
118 		err(1, "socket");
119 
120 	memset(&sun, 0, sizeof(sun));
121 	sun.sun_family = AF_UNIX;
122 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
123 
124 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
125 		err(1, "connect: %s", sockname);
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 	/* Check for root-only actions */
136 	switch (res->action) {
137 	case LOG_DEBUG:
138 	case LOG_VERBOSE:
139 	case LOG_BRIEF:
140 	case RELOAD:
141 		if (geteuid() != 0)
142 			errx(1, "need root privileges");
143 		break;
144 	default:
145 		break;
146 	}
147 
148 	/* Process user request. */
149 	switch (res->action) {
150 	case LOG_DEBUG:
151 		verbose |= OPT_VERBOSE2;
152 		/* FALLTHROUGH */
153 	case LOG_VERBOSE:
154 		verbose |= OPT_VERBOSE;
155 		/* FALLTHROUGH */
156 	case LOG_BRIEF:
157 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
158 		    &verbose, sizeof(verbose));
159 		printf("logging request sent.\n");
160 		done = 1;
161 		break;
162 	case RELOAD:
163 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
164 		printf("reload request sent.\n");
165 		done = 1;
166 		break;
167 	case STATUS:
168 		imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0);
169 		break;
170 	case AUTOCONF:
171 		imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0);
172 		break;
173 	case MEM:
174 		imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0);
175 		break;
176 	default:
177 		usage();
178 	}
179 
180 	while (ibuf->w.queued)
181 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
182 			err(1, "write error");
183 
184 	while (!done) {
185 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
186 			errx(1, "imsg_read error");
187 		if (n == 0)
188 			errx(1, "pipe closed");
189 
190 		while (!done) {
191 			if ((n = imsg_get(ibuf, &imsg)) == -1)
192 				errx(1, "imsg_get error");
193 			if (n == 0)
194 				break;
195 
196 			switch (res->action) {
197 			case STATUS:
198 				done = show_status_msg(&imsg);
199 				break;
200 			case AUTOCONF:
201 				done = show_autoconf_msg(&imsg);
202 				break;
203 			case MEM:
204 				done = show_mem_msg(&imsg);
205 				break;
206 			default:
207 				break;
208 			}
209 			imsg_free(&imsg);
210 		}
211 	}
212 	close(ctl_sock);
213 	free(ibuf);
214 
215 	column_offset = info_cnt / 2;
216 	if (info_cnt % 2 == 1)
217 		column_offset++;
218 
219 	for (i = 0; i < column_offset; i++) {
220 		for (j = 0; j < 2; j++) {
221 			k = i + j * column_offset;
222 			if (k >= info_cnt)
223 				break;
224 
225 			cri = &info[k];
226 			printf("%d. %-15s %10s, ", k + 1,
227 			    uw_resolver_type_str[cri->type],
228 			    uw_resolver_state_str[cri->state]);
229 			if (cri->median == 0)
230 				printf("%5s", "N/A");
231 			else if (cri->median == INT64_MAX)
232 				printf("%5s", "Inf");
233 			else
234 				printf("%3lldms", cri->median);
235 			if (j == 0)
236 				printf("   ");
237 		}
238 		printf("\n");
239 	}
240 
241 	if (info_cnt)
242 		histogram_header();
243 	for (i = 0; i < info_cnt; i++) {
244 		cri = &info[i];
245 		print_histogram(uw_resolver_type_short[cri->type],
246 		    cri->histogram, nitems(cri->histogram));
247 		print_histogram("", cri->latest_histogram,
248 		    nitems(cri->latest_histogram));
249 	}
250 	return (0);
251 }
252 
253 int
show_status_msg(struct imsg * imsg)254 show_status_msg(struct imsg *imsg)
255 {
256 	static char			 fwd_line[80];
257 
258 	switch (imsg->hdr.type) {
259 	case IMSG_CTL_RESOLVER_INFO:
260 		memcpy(&info[info_cnt++], imsg->data, sizeof(info[0]));
261 		break;
262 	case IMSG_CTL_END:
263 		if (fwd_line[0] != '\0')
264 			printf("%s\n", fwd_line);
265 		return (1);
266 	default:
267 		break;
268 	}
269 
270 	return (0);
271 }
272 
273 int
show_autoconf_msg(struct imsg * imsg)274 show_autoconf_msg(struct imsg *imsg)
275 {
276 	static int			 autoconf_forwarders, last_src;
277 	static int			 label_len, line_len;
278 	static uint32_t			 last_if_index;
279 	static char			 fwd_line[80];
280 	struct ctl_forwarder_info	*cfi;
281 	char				 ifnamebuf[IFNAMSIZ];
282 	char				*if_name;
283 
284 	switch (imsg->hdr.type) {
285 	case IMSG_CTL_AUTOCONF_RESOLVER_INFO:
286 		cfi = imsg->data;
287 		if (!autoconf_forwarders++)
288 			printf("autoconfiguration forwarders:\n");
289 		if (cfi->if_index != last_if_index || cfi->src != last_src) {
290 			if_name = if_indextoname(cfi->if_index, ifnamebuf);
291 			if (fwd_line[0] != '\0') {
292 				printf("%s\n", fwd_line);
293 				fwd_line[0] = '\0';
294 			}
295 			label_len = snprintf(fwd_line, sizeof(fwd_line),
296 			    "%6s[%s]:", prio2str(cfi->src),
297 			    if_name ? if_name : "unknown");
298 			line_len = label_len;
299 			last_if_index = cfi->if_index;
300 			last_src = cfi->src;
301 		}
302 
303 		if (line_len + 1 + strlen(cfi->ip) > sizeof(fwd_line)) {
304 			printf("%s\n", fwd_line);
305 			snprintf(fwd_line, sizeof(fwd_line), "%*s", label_len,
306 			    " ");
307 		}
308 		strlcat(fwd_line, " ", sizeof(fwd_line));
309 		line_len = strlcat(fwd_line, cfi->ip, sizeof(fwd_line));
310 		break;
311 	case IMSG_CTL_END:
312 		if (fwd_line[0] != '\0')
313 			printf("%s\n", fwd_line);
314 		return (1);
315 	default:
316 		break;
317 	}
318 
319 	return (0);
320 }
321 
322 void
histogram_header(void)323 histogram_header(void)
324 {
325 	const char	 head[] = "histograms: lifetime[ms], decaying[ms]";
326 	char	 	 buf[10];
327 	size_t	 	 i;
328 
329 	printf("\n%*s%*s\n%*s", 5, "",
330 	    (int)(72/2 + (sizeof(head)-1)/2), head, 6, "");
331 	for(i = 0; i < nitems(histogram_limits) - 1; i++) {
332 		snprintf(buf, sizeof(buf), "<%lld", histogram_limits[i]);
333 		printf("%6s", buf);
334 	}
335 	printf("%6s\n", ">");
336 }
337 
338 void
print_histogram(const char * name,int64_t histogram[],size_t n)339 print_histogram(const char *name, int64_t histogram[], size_t n)
340 {
341 	size_t	 i;
342 
343 	printf("%5s ", name);
344 	for(i = 0; i < n; i++)
345 		printf("%6lld", histogram[i]);
346 	printf("\n");
347 }
348 
349 int
show_mem_msg(struct imsg * imsg)350 show_mem_msg(struct imsg *imsg)
351 {
352 	struct ctl_mem_info	*cmi;
353 
354 	switch (imsg->hdr.type) {
355 	case IMSG_CTL_MEM_INFO:
356 		cmi = imsg->data;
357 		printf("msg-cache:   %zu / %zu (%.2f%%)\n", cmi->msg_cache_used,
358 		    cmi->msg_cache_max, 100.0 * cmi->msg_cache_used /
359 		    cmi->msg_cache_max);
360 		printf("rrset-cache: %zu / %zu (%.2f%%)\n",
361 		    cmi->rrset_cache_used, cmi->rrset_cache_max, 100.0 *
362 		    cmi->rrset_cache_used / cmi->rrset_cache_max);
363 		printf("key-cache: %zu / %zu (%.2f%%)\n", cmi->key_cache_used,
364 		    cmi->key_cache_max, 100.0 * cmi->key_cache_used /
365 		    cmi->key_cache_max);
366 		printf("neg-cache: %zu / %zu (%.2f%%)\n", cmi->neg_cache_used,
367 		    cmi->neg_cache_max, 100.0 * cmi->neg_cache_used /
368 		    cmi->neg_cache_max);
369 		break;
370 	default:
371 		break;
372 	}
373 
374 	return 1;
375 }
376