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