1 /* $OpenBSD: relayctl.c,v 1.56 2015/12/05 13:13:11 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org> 5 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 6 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 7 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 8 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 #include <sys/time.h> 26 #include <sys/queue.h> 27 #include <sys/un.h> 28 29 #include <arpa/inet.h> 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <limits.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <time.h> 39 #include <imsg.h> 40 41 #include "relayd.h" 42 #include "parser.h" 43 44 __dead void usage(void); 45 int show_summary_msg(struct imsg *, int); 46 int show_session_msg(struct imsg *); 47 int show_command_output(struct imsg *); 48 char *print_rdr_status(int); 49 char *print_host_status(int, int); 50 char *print_table_status(int, int); 51 char *print_relay_status(int); 52 void print_statistics(struct ctl_stats[RELAY_MAXPROC + 1]); 53 54 struct imsgname { 55 int type; 56 char *name; 57 void (*func)(struct imsg *); 58 }; 59 60 struct imsgname *monitor_lookup(u_int8_t); 61 void monitor_host_status(struct imsg *); 62 void monitor_id(struct imsg *); 63 int monitor(struct imsg *); 64 65 struct imsgname imsgs[] = { 66 { IMSG_HOST_STATUS, "host_status", monitor_host_status }, 67 { IMSG_CTL_RDR_DISABLE, "ctl_rdr_disable", monitor_id }, 68 { IMSG_CTL_RDR_ENABLE, "ctl_rdr_enable", monitor_id }, 69 { IMSG_CTL_TABLE_DISABLE, "ctl_table_disable", monitor_id }, 70 { IMSG_CTL_TABLE_ENABLE, "ctl_table_enable", monitor_id }, 71 { IMSG_CTL_HOST_DISABLE, "ctl_host_disable", monitor_id }, 72 { IMSG_CTL_HOST_ENABLE, "ctl_host_enable", monitor_id }, 73 { IMSG_CTL_TABLE_CHANGED, "ctl_table_changed", monitor_id }, 74 { IMSG_CTL_PULL_RULESET, "ctl_pull_ruleset", monitor_id }, 75 { IMSG_CTL_PUSH_RULESET, "ctl_push_ruleset", monitor_id }, 76 { IMSG_SYNC, "sync", NULL }, 77 { 0, NULL, NULL } 78 }; 79 struct imsgname imsgunknown = { 80 -1, "<unknown>", NULL 81 }; 82 83 struct imsgbuf *ibuf; 84 int error = 0; 85 86 __dead void 87 usage(void) 88 { 89 extern char *__progname; 90 91 fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 92 exit(1); 93 } 94 95 int 96 main(int argc, char *argv[]) 97 { 98 struct sockaddr_un sun; 99 struct parse_result *res; 100 struct imsg imsg; 101 int ctl_sock; 102 int done = 0; 103 int n, verbose = 0; 104 105 /* parse options */ 106 if ((res = parse(argc - 1, argv + 1)) == NULL) 107 exit(1); 108 109 /* connect to relayd control socket */ 110 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 111 err(1, "socket"); 112 113 bzero(&sun, sizeof(sun)); 114 sun.sun_family = AF_UNIX; 115 (void)strlcpy(sun.sun_path, RELAYD_SOCKET, sizeof(sun.sun_path)); 116 reconnect: 117 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 118 /* Keep retrying if running in monitor mode */ 119 if (res->action == MONITOR && 120 (errno == ENOENT || errno == ECONNREFUSED)) { 121 usleep(100); 122 goto reconnect; 123 } 124 err(1, "connect: %s", RELAYD_SOCKET); 125 } 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 /* process user request */ 136 switch (res->action) { 137 case NONE: 138 usage(); 139 /* not reached */ 140 case SHOW_SUM: 141 case SHOW_HOSTS: 142 case SHOW_RDRS: 143 case SHOW_RELAYS: 144 case SHOW_ROUTERS: 145 imsg_compose(ibuf, IMSG_CTL_SHOW_SUM, 0, 0, -1, NULL, 0); 146 printf("%-4s\t%-8s\t%-24s\t%-7s\tStatus\n", 147 "Id", "Type", "Name", "Avlblty"); 148 break; 149 case SHOW_SESSIONS: 150 imsg_compose(ibuf, IMSG_CTL_SESSION, 0, 0, -1, NULL, 0); 151 break; 152 case RDR_ENABLE: 153 imsg_compose(ibuf, IMSG_CTL_RDR_ENABLE, 0, 0, -1, 154 &res->id, sizeof(res->id)); 155 break; 156 case RDR_DISABLE: 157 imsg_compose(ibuf, IMSG_CTL_RDR_DISABLE, 0, 0, -1, 158 &res->id, sizeof(res->id)); 159 break; 160 case TABLE_ENABLE: 161 imsg_compose(ibuf, IMSG_CTL_TABLE_ENABLE, 0, 0, -1, 162 &res->id, sizeof(res->id)); 163 break; 164 case TABLE_DISABLE: 165 imsg_compose(ibuf, IMSG_CTL_TABLE_DISABLE, 0, 0, -1, 166 &res->id, sizeof(res->id)); 167 break; 168 case HOST_ENABLE: 169 imsg_compose(ibuf, IMSG_CTL_HOST_ENABLE, 0, 0, -1, 170 &res->id, sizeof(res->id)); 171 break; 172 case HOST_DISABLE: 173 imsg_compose(ibuf, IMSG_CTL_HOST_DISABLE, 0, 0, -1, 174 &res->id, sizeof(res->id)); 175 break; 176 case SHUTDOWN: 177 imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0); 178 break; 179 case POLL: 180 imsg_compose(ibuf, IMSG_CTL_POLL, 0, 0, -1, NULL, 0); 181 break; 182 case LOAD: 183 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, 184 res->path, strlen(res->path)); 185 done = 1; 186 break; 187 case RELOAD: 188 imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); 189 done = 1; 190 break; 191 case MONITOR: 192 imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); 193 break; 194 case LOG_VERBOSE: 195 verbose = 2; 196 /* FALLTHROUGH */ 197 case LOG_BRIEF: 198 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 199 &verbose, sizeof(verbose)); 200 printf("logging request sent.\n"); 201 done = 1; 202 break; 203 } 204 205 while (ibuf->w.queued) 206 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 207 err(1, "write error"); 208 209 while (!done) { 210 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 211 errx(1, "imsg_read error"); 212 if (n == 0) 213 errx(1, "pipe closed"); 214 215 while (!done) { 216 if ((n = imsg_get(ibuf, &imsg)) == -1) 217 errx(1, "imsg_get error"); 218 if (n == 0) 219 break; 220 switch (res->action) { 221 case SHOW_SUM: 222 case SHOW_HOSTS: 223 case SHOW_RDRS: 224 case SHOW_RELAYS: 225 case SHOW_ROUTERS: 226 done = show_summary_msg(&imsg, res->action); 227 break; 228 case SHOW_SESSIONS: 229 done = show_session_msg(&imsg); 230 break; 231 case RDR_DISABLE: 232 case RDR_ENABLE: 233 case TABLE_DISABLE: 234 case TABLE_ENABLE: 235 case HOST_DISABLE: 236 case HOST_ENABLE: 237 case POLL: 238 case SHUTDOWN: 239 done = show_command_output(&imsg); 240 break; 241 case NONE: 242 case LOG_VERBOSE: 243 case LOG_BRIEF: 244 case RELOAD: 245 case LOAD: 246 break; 247 case MONITOR: 248 done = monitor(&imsg); 249 break; 250 } 251 imsg_free(&imsg); 252 } 253 } 254 close(ctl_sock); 255 free(ibuf); 256 257 return (error ? 1 : 0); 258 } 259 260 struct imsgname * 261 monitor_lookup(u_int8_t type) 262 { 263 int i; 264 265 for (i = 0; imsgs[i].name != NULL; i++) 266 if (imsgs[i].type == type) 267 return (&imsgs[i]); 268 return (&imsgunknown); 269 } 270 271 void 272 monitor_host_status(struct imsg *imsg) 273 { 274 struct ctl_status cs; 275 276 memcpy(&cs, imsg->data, sizeof(cs)); 277 printf("\tid: %u\n", cs.id); 278 printf("\tstate: "); 279 switch (cs.up) { 280 case HOST_UP: 281 printf("up\n"); 282 break; 283 case HOST_DOWN: 284 printf("down\n"); 285 break; 286 default: 287 printf("unknown\n"); 288 break; 289 } 290 } 291 292 void 293 monitor_id(struct imsg *imsg) 294 { 295 struct ctl_id id; 296 297 memcpy(&id, imsg->data, sizeof(id)); 298 printf("\tid: %u\n", id.id); 299 if (strlen(id.name)) 300 printf("\tname: %s\n", id.name); 301 } 302 303 int 304 monitor(struct imsg *imsg) 305 { 306 time_t now; 307 int done = 0; 308 struct imsgname *imn; 309 310 now = time(NULL); 311 312 imn = monitor_lookup(imsg->hdr.type); 313 printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name, 314 imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid); 315 printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now)); 316 if (imn->type == -1) 317 done = 1; 318 if (imn->func != NULL) 319 (*imn->func)(imsg); 320 321 return (done); 322 } 323 324 int 325 show_summary_msg(struct imsg *imsg, int type) 326 { 327 struct rdr *rdr; 328 struct table *table; 329 struct host *host; 330 struct relay *rlay; 331 struct router *rt; 332 struct netroute *nr; 333 struct ctl_stats stats[RELAY_MAXPROC]; 334 char name[HOST_NAME_MAX+1]; 335 336 switch (imsg->hdr.type) { 337 case IMSG_CTL_RDR: 338 if (!(type == SHOW_SUM || type == SHOW_RDRS)) 339 break; 340 rdr = imsg->data; 341 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 342 rdr->conf.id, "redirect", rdr->conf.name, "", 343 print_rdr_status(rdr->conf.flags)); 344 break; 345 case IMSG_CTL_TABLE: 346 if (!(type == SHOW_SUM || type == SHOW_HOSTS)) 347 break; 348 table = imsg->data; 349 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 350 table->conf.id, "table", table->conf.name, "", 351 print_table_status(table->up, table->conf.flags)); 352 break; 353 case IMSG_CTL_HOST: 354 if (!(type == SHOW_SUM || type == SHOW_HOSTS)) 355 break; 356 host = imsg->data; 357 if (host->conf.parentid) 358 snprintf(name, sizeof(name), "%s parent %u", 359 host->conf.name, host->conf.parentid); 360 else 361 strlcpy(name, host->conf.name, sizeof(name)); 362 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 363 host->conf.id, "host", name, 364 print_availability(host->check_cnt, host->up_cnt), 365 print_host_status(host->up, host->flags)); 366 if (type == SHOW_HOSTS && host->check_cnt) { 367 printf("\t%8s\ttotal: %lu/%lu checks", 368 "", host->up_cnt, host->check_cnt); 369 if (host->retry_cnt) 370 printf(", %d retries", host->retry_cnt); 371 if (host->he && host->up == HOST_DOWN) 372 printf(", error: %s", host_error(host->he)); 373 printf("\n"); 374 } 375 break; 376 case IMSG_CTL_RELAY: 377 if (!(type == SHOW_SUM || type == SHOW_RELAYS)) 378 break; 379 rlay = imsg->data; 380 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 381 rlay->rl_conf.id, "relay", rlay->rl_conf.name, "", 382 print_relay_status(rlay->rl_conf.flags)); 383 break; 384 case IMSG_CTL_RDR_STATS: 385 if (type != SHOW_RDRS) 386 break; 387 bcopy(imsg->data, &stats[0], sizeof(stats[0])); 388 stats[1].id = EMPTY_ID; 389 print_statistics(stats); 390 break; 391 case IMSG_CTL_RELAY_STATS: 392 if (type != SHOW_RELAYS) 393 break; 394 bcopy(imsg->data, &stats, sizeof(stats)); 395 print_statistics(stats); 396 break; 397 case IMSG_CTL_ROUTER: 398 if (!(type == SHOW_SUM || type == SHOW_ROUTERS)) 399 break; 400 rt = imsg->data; 401 printf("%-4u\t%-8s\t%-24s\t%-7s\t%s\n", 402 rt->rt_conf.id, "router", rt->rt_conf.name, "", 403 print_relay_status(rt->rt_conf.flags)); 404 if (type != SHOW_ROUTERS) 405 break; 406 if (rt->rt_conf.rtable) 407 printf("\t%8s\trtable: %d\n", "", rt->rt_conf.rtable); 408 if (strlen(rt->rt_conf.label)) 409 printf("\t%8s\trtlabel: %s\n", "", rt->rt_conf.label); 410 break; 411 case IMSG_CTL_NETROUTE: 412 if (type != SHOW_ROUTERS) 413 break; 414 nr = imsg->data; 415 (void)print_host(&nr->nr_conf.ss, name, sizeof(name)); 416 printf("\t%8s\troute: %s/%d\n", 417 "", name, nr->nr_conf.prefixlen); 418 break; 419 case IMSG_CTL_END: 420 return (1); 421 default: 422 errx(1, "wrong message in summary: %u", imsg->hdr.type); 423 break; 424 } 425 return (0); 426 } 427 428 int 429 show_session_msg(struct imsg *imsg) 430 { 431 struct rsession *con; 432 char a[128], b[128]; 433 struct timeval tv_now; 434 435 switch (imsg->hdr.type) { 436 case IMSG_CTL_SESSION: 437 con = imsg->data; 438 439 (void)print_host(&con->se_in.ss, a, sizeof(a)); 440 (void)print_host(&con->se_out.ss, b, sizeof(b)); 441 printf("session %u:%u %s:%u -> %s:%u\t%s\n", 442 imsg->hdr.peerid, con->se_id, 443 a, ntohs(con->se_in.port), b, ntohs(con->se_out.port), 444 con->se_done ? "DONE" : "RUNNING"); 445 446 getmonotime(&tv_now); 447 print_time(&tv_now, &con->se_tv_start, a, sizeof(a)); 448 print_time(&tv_now, &con->se_tv_last, b, sizeof(b)); 449 printf("\tage %s, idle %s, relay %u, pid %u", 450 a, b, con->se_relayid, con->se_pid); 451 /* XXX grab tagname instead of tag id */ 452 if (con->se_tag) 453 printf(", tag (id) %u", con->se_tag); 454 printf("\n"); 455 break; 456 case IMSG_CTL_END: 457 return (1); 458 default: 459 errx(1, "wrong message in session: %u", imsg->hdr.type); 460 break; 461 } 462 return (0); 463 } 464 465 int 466 show_command_output(struct imsg *imsg) 467 { 468 switch (imsg->hdr.type) { 469 case IMSG_CTL_OK: 470 printf("command succeeded\n"); 471 break; 472 case IMSG_CTL_FAIL: 473 printf("command failed\n"); 474 error++; 475 break; 476 default: 477 errx(1, "wrong message in summary: %u", imsg->hdr.type); 478 } 479 return (1); 480 } 481 482 char * 483 print_rdr_status(int flags) 484 { 485 if (flags & F_DISABLE) { 486 return ("disabled"); 487 } else if (flags & F_DOWN) { 488 return ("down"); 489 } else if (flags & F_BACKUP) { 490 return ("active (using backup table)"); 491 } else 492 return ("active"); 493 } 494 495 char * 496 print_table_status(int up, int fl) 497 { 498 static char buf[1024]; 499 500 bzero(buf, sizeof(buf)); 501 502 if (fl & F_DISABLE) { 503 snprintf(buf, sizeof(buf) - 1, "disabled"); 504 } else if (!up) { 505 snprintf(buf, sizeof(buf) - 1, "empty"); 506 } else 507 snprintf(buf, sizeof(buf) - 1, "active (%d hosts)", up); 508 return (buf); 509 } 510 511 char * 512 print_host_status(int status, int fl) 513 { 514 if (fl & F_DISABLE) 515 return ("disabled"); 516 517 switch (status) { 518 case HOST_DOWN: 519 return ("down"); 520 case HOST_UNKNOWN: 521 return ("unknown"); 522 case HOST_UP: 523 return ("up"); 524 default: 525 errx(1, "invalid status: %d", status); 526 } 527 } 528 529 char * 530 print_relay_status(int flags) 531 { 532 if (flags & F_DISABLE) { 533 return ("disabled"); 534 } else 535 return ("active"); 536 } 537 538 void 539 print_statistics(struct ctl_stats stats[RELAY_MAXPROC + 1]) 540 { 541 struct ctl_stats crs; 542 int i; 543 544 bzero(&crs, sizeof(crs)); 545 crs.interval = stats[0].interval; 546 for (i = 0; stats[i].id != EMPTY_ID; i++) { 547 crs.cnt += stats[i].cnt; 548 crs.last += stats[i].last; 549 crs.avg += stats[i].avg; 550 crs.last_hour += stats[i].last_hour; 551 crs.avg_hour += stats[i].avg_hour; 552 crs.last_day += stats[i].last_day; 553 crs.avg_day += stats[i].avg_day; 554 } 555 if (crs.cnt == 0) 556 return; 557 printf("\t%8s\ttotal: %llu sessions\n" 558 "\t%8s\tlast: %u/%llus %u/h %u/d sessions\n" 559 "\t%8s\taverage: %u/%llus %u/h %u/d sessions\n", 560 "", crs.cnt, 561 "", crs.last, crs.interval, 562 crs.last_hour, crs.last_day, 563 "", crs.avg, crs.interval, 564 crs.avg_hour, crs.avg_day); 565 } 566