1 /* $OpenBSD: unwindctl.c,v 1.34 2024/11/21 13:38:15 claudio 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 case RTP_PROPOSAL_PPP: 71 return "PPP"; 72 } 73 return "OTHER"; 74 } 75 76 __dead 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 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 if (imsgbuf_init(ibuf, ctl_sock) == -1) 133 err(1, NULL); 134 done = 0; 135 136 /* Check for root-only actions */ 137 switch (res->action) { 138 case LOG_DEBUG: 139 case LOG_VERBOSE: 140 case LOG_BRIEF: 141 case RELOAD: 142 if (geteuid() != 0) 143 errx(1, "need root privileges"); 144 break; 145 default: 146 break; 147 } 148 149 /* Process user request. */ 150 switch (res->action) { 151 case LOG_DEBUG: 152 verbose |= OPT_VERBOSE2; 153 /* FALLTHROUGH */ 154 case LOG_VERBOSE: 155 verbose |= OPT_VERBOSE; 156 /* FALLTHROUGH */ 157 case LOG_BRIEF: 158 imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, 159 &verbose, sizeof(verbose)); 160 printf("logging request sent.\n"); 161 done = 1; 162 break; 163 case RELOAD: 164 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 165 printf("reload request sent.\n"); 166 done = 1; 167 break; 168 case STATUS: 169 imsg_compose(ibuf, IMSG_CTL_STATUS, 0, 0, -1, NULL, 0); 170 break; 171 case AUTOCONF: 172 imsg_compose(ibuf, IMSG_CTL_AUTOCONF, 0, 0, -1, NULL, 0); 173 break; 174 case MEM: 175 imsg_compose(ibuf, IMSG_CTL_MEM, 0, 0, -1, NULL, 0); 176 break; 177 default: 178 usage(); 179 } 180 181 if (imsgbuf_flush(ibuf) == -1) 182 err(1, "write error"); 183 184 while (!done) { 185 if ((n = imsgbuf_read(ibuf)) == -1) 186 err(1, "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 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 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 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 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 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