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