1 /* 2 * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1998-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "port_before.h" 19 20 #include <sys/param.h> 21 #include <sys/file.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 25 #include <netinet/in.h> 26 #include <arpa/nameser.h> 27 #include <arpa/inet.h> 28 29 #include <ctype.h> 30 #include <errno.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <time.h> 35 #include <unistd.h> 36 #include <fcntl.h> 37 #ifdef HAVE_MEMORY_H 38 #include <memory.h> 39 #endif 40 41 #include <isc/assertions.h> 42 #include <isc/ctl.h> 43 #include <isc/eventlib.h> 44 #include <isc/list.h> 45 #include <isc/logging.h> 46 #include <isc/memcluster.h> 47 48 #include "ctl_p.h" 49 50 #include "port_after.h" 51 52 #ifdef SPRINTF_CHAR 53 # define SPRINTF(x) strlen(sprintf/**/x) 54 #else 55 # define SPRINTF(x) ((size_t)sprintf x) 56 #endif 57 58 /* Macros. */ 59 60 #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL) 61 #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \ 62 tmp, sizeof tmp, ctx->logger) 63 64 /* Types. */ 65 66 enum state { 67 available = 0, initializing, writing, reading, reading_data, 68 processing, idling, quitting, closing 69 }; 70 71 union sa_un { 72 struct sockaddr_in in; 73 #ifndef NO_SOCKADDR_UN 74 struct sockaddr_un un; 75 #endif 76 }; 77 78 struct ctl_sess { 79 LINK(struct ctl_sess) link; 80 struct ctl_sctx * ctx; 81 enum state state; 82 int sock; 83 union sa_un sa; 84 evFileID rdID; 85 evStreamID wrID; 86 evTimerID rdtiID; 87 evTimerID wrtiID; 88 struct ctl_buf inbuf; 89 struct ctl_buf outbuf; 90 const struct ctl_verb * verb; 91 u_int helpcode; 92 const void * respctx; 93 u_int respflags; 94 ctl_srvrdone donefunc; 95 void * uap; 96 void * csctx; 97 }; 98 99 struct ctl_sctx { 100 evContext ev; 101 void * uctx; 102 u_int unkncode; 103 u_int timeoutcode; 104 const struct ctl_verb * verbs; 105 const struct ctl_verb * connverb; 106 int sock; 107 int max_sess; 108 int cur_sess; 109 struct timespec timeout; 110 ctl_logfunc logger; 111 evConnID acID; 112 LIST(struct ctl_sess) sess; 113 }; 114 115 /* Forward. */ 116 117 static void ctl_accept(evContext, void *, int, 118 const void *, int, 119 const void *, int); 120 static void ctl_close(struct ctl_sess *); 121 static void ctl_new_state(struct ctl_sess *, 122 enum state, 123 const char *); 124 static void ctl_start_read(struct ctl_sess *); 125 static void ctl_stop_read(struct ctl_sess *); 126 static void ctl_readable(evContext, void *, int, int); 127 static void ctl_rdtimeout(evContext, void *, 128 struct timespec, 129 struct timespec); 130 static void ctl_wrtimeout(evContext, void *, 131 struct timespec, 132 struct timespec); 133 static void ctl_docommand(struct ctl_sess *); 134 static void ctl_writedone(evContext, void *, int, int); 135 static void ctl_morehelp(struct ctl_sctx *, 136 struct ctl_sess *, 137 const struct ctl_verb *, 138 const char *, 139 u_int, const void *, void *); 140 static void ctl_signal_done(struct ctl_sctx *, 141 struct ctl_sess *); 142 143 /* Private data. */ 144 145 static const char * state_names[] = { 146 "available", "initializing", "writing", "reading", 147 "reading_data", "processing", "idling", "quitting", "closing" 148 }; 149 150 static const char space[] = " "; 151 152 static const struct ctl_verb fakehelpverb = { 153 "fakehelp", ctl_morehelp , NULL 154 }; 155 156 /* Public. */ 157 158 /*% 159 * void 160 * ctl_server() 161 * create, condition, and start a listener on the control port. 162 */ 163 struct ctl_sctx * 164 ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len, 165 const struct ctl_verb *verbs, 166 u_int unkncode, u_int timeoutcode, 167 u_int timeout, int backlog, int max_sess, 168 ctl_logfunc logger, void *uctx) 169 { 170 static const char me[] = "ctl_server"; 171 static const int on = 1; 172 const struct ctl_verb *connverb; 173 struct ctl_sctx *ctx; 174 int save_errno; 175 176 if (logger == NULL) 177 logger = ctl_logger; 178 for (connverb = verbs; 179 connverb->name != NULL && connverb->func != NULL; 180 connverb++) 181 if (connverb->name[0] == '\0') 182 break; 183 if (connverb->func == NULL) { 184 (*logger)(ctl_error, "%s: no connection verb found", me); 185 return (NULL); 186 } 187 ctx = memget(sizeof *ctx); 188 if (ctx == NULL) { 189 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); 190 return (NULL); 191 } 192 ctx->ev = lev; 193 ctx->uctx = uctx; 194 ctx->unkncode = unkncode; 195 ctx->timeoutcode = timeoutcode; 196 ctx->verbs = verbs; 197 ctx->timeout = evConsTime(timeout, 0); 198 ctx->logger = logger; 199 ctx->connverb = connverb; 200 ctx->max_sess = max_sess; 201 ctx->cur_sess = 0; 202 INIT_LIST(ctx->sess); 203 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); 204 if (ctx->sock > evHighestFD(ctx->ev)) { 205 ctx->sock = -1; 206 errno = ENOTSOCK; 207 } 208 if (ctx->sock < 0) { 209 save_errno = errno; 210 (*ctx->logger)(ctl_error, "%s: socket: %s", 211 me, strerror(errno)); 212 memput(ctx, sizeof *ctx); 213 errno = save_errno; 214 return (NULL); 215 } 216 if (ctx->sock > evHighestFD(lev)) { 217 close(ctx->sock); 218 (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD"); 219 errno = ENFILE; 220 memput(ctx, sizeof *ctx); 221 return (NULL); 222 } 223 #ifdef NO_UNIX_REUSEADDR 224 if (sap->sa_family != AF_UNIX) 225 #endif 226 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, 227 (const char *)&on, sizeof on) != 0) { 228 (*ctx->logger)(ctl_warning, 229 "%s: setsockopt(REUSEADDR): %s", 230 me, strerror(errno)); 231 } 232 if (bind(ctx->sock, sap, sap_len) < 0) { 233 char tmp[MAX_NTOP]; 234 save_errno = errno; 235 (*ctx->logger)(ctl_error, "%s: bind: %s: %s", 236 me, ctl_sa_ntop((const struct sockaddr *)sap, 237 tmp, sizeof tmp, ctx->logger), 238 strerror(save_errno)); 239 close(ctx->sock); 240 memput(ctx, sizeof *ctx); 241 errno = save_errno; 242 return (NULL); 243 } 244 if (fcntl(ctx->sock, F_SETFD, 1) < 0) { 245 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, 246 strerror(errno)); 247 } 248 if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx, 249 &ctx->acID) < 0) { 250 save_errno = errno; 251 (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s", 252 me, ctx->sock, strerror(errno)); 253 close(ctx->sock); 254 memput(ctx, sizeof *ctx); 255 errno = save_errno; 256 return (NULL); 257 } 258 (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d", 259 me, ctx, ctx->sock); 260 return (ctx); 261 } 262 263 /*% 264 * void 265 * ctl_endserver(ctx) 266 * if the control listener is open, close it. clean out all eventlib 267 * stuff. close all active sessions. 268 */ 269 void 270 ctl_endserver(struct ctl_sctx *ctx) { 271 static const char me[] = "ctl_endserver"; 272 struct ctl_sess *this, *next; 273 274 (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p", 275 me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess); 276 if (ctx->acID.opaque != NULL) { 277 (void)evCancelConn(ctx->ev, ctx->acID); 278 ctx->acID.opaque = NULL; 279 } 280 if (ctx->sock != -1) { 281 (void) close(ctx->sock); 282 ctx->sock = -1; 283 } 284 for (this = HEAD(ctx->sess); this != NULL; this = next) { 285 next = NEXT(this, link); 286 ctl_close(this); 287 } 288 memput(ctx, sizeof *ctx); 289 } 290 291 /*% 292 * If body is non-NULL then it we add a "." line after it. 293 * Caller must have escaped lines with leading ".". 294 */ 295 void 296 ctl_response(struct ctl_sess *sess, u_int code, const char *text, 297 u_int flags, const void *respctx, ctl_srvrdone donefunc, 298 void *uap, const char *body, size_t bodylen) 299 { 300 static const char me[] = "ctl_response"; 301 struct iovec iov[3], *iovp = iov; 302 struct ctl_sctx *ctx = sess->ctx; 303 char tmp[MAX_NTOP], *pc; 304 int n; 305 306 REQUIRE(sess->state == initializing || 307 sess->state == processing || 308 sess->state == reading_data || 309 sess->state == writing); 310 REQUIRE(sess->wrtiID.opaque == NULL); 311 REQUIRE(sess->wrID.opaque == NULL); 312 ctl_new_state(sess, writing, me); 313 sess->donefunc = donefunc; 314 sess->uap = uap; 315 if (!allocated_p(sess->outbuf) && 316 ctl_bufget(&sess->outbuf, ctx->logger) < 0) { 317 (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer", 318 me, address_expr); 319 goto untimely; 320 } 321 if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) { 322 (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing", 323 me, address_expr); 324 goto untimely; 325 } 326 sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n", 327 code, (flags & CTL_MORE) != 0 ? '-' : ' ', 328 text)); 329 for (pc = sess->outbuf.text, n = 0; 330 n < (int)sess->outbuf.used-2; pc++, n++) 331 if (!isascii((unsigned char)*pc) || 332 !isprint((unsigned char)*pc)) 333 *pc = '\040'; 334 *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used); 335 if (body != NULL) { 336 char *tmp; 337 DE_CONST(body, tmp); 338 *iovp++ = evConsIovec(tmp, bodylen); 339 DE_CONST(".\r\n", tmp); 340 *iovp++ = evConsIovec(tmp, 3); 341 } 342 (*ctx->logger)(ctl_debug, "%s: [%d] %s", me, 343 sess->outbuf.used, sess->outbuf.text); 344 if (evWrite(ctx->ev, sess->sock, iov, iovp - iov, 345 ctl_writedone, sess, &sess->wrID) < 0) { 346 (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me, 347 address_expr, strerror(errno)); 348 goto untimely; 349 } 350 if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout, 351 &sess->wrtiID) < 0) 352 { 353 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, 354 address_expr, strerror(errno)); 355 goto untimely; 356 } 357 if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) { 358 (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me, 359 address_expr, strerror(errno)); 360 untimely: 361 ctl_signal_done(ctx, sess); 362 ctl_close(sess); 363 return; 364 } 365 sess->respctx = respctx; 366 sess->respflags = flags; 367 } 368 369 void 370 ctl_sendhelp(struct ctl_sess *sess, u_int code) { 371 static const char me[] = "ctl_sendhelp"; 372 struct ctl_sctx *ctx = sess->ctx; 373 374 sess->helpcode = code; 375 sess->verb = &fakehelpverb; 376 ctl_morehelp(ctx, sess, NULL, me, CTL_MORE, 377 (const void *)ctx->verbs, NULL); 378 } 379 380 void * 381 ctl_getcsctx(struct ctl_sess *sess) { 382 return (sess->csctx); 383 } 384 385 void * 386 ctl_setcsctx(struct ctl_sess *sess, void *csctx) { 387 void *old = sess->csctx; 388 389 sess->csctx = csctx; 390 return (old); 391 } 392 393 /* Private functions. */ 394 395 static void 396 ctl_accept(evContext lev, void *uap, int fd, 397 const void *lav, int lalen, 398 const void *rav, int ralen) 399 { 400 static const char me[] = "ctl_accept"; 401 struct ctl_sctx *ctx = uap; 402 struct ctl_sess *sess = NULL; 403 char tmp[MAX_NTOP]; 404 405 UNUSED(lev); 406 UNUSED(lalen); 407 UNUSED(ralen); 408 409 if (fd < 0) { 410 (*ctx->logger)(ctl_error, "%s: accept: %s", 411 me, strerror(errno)); 412 return; 413 } 414 if (ctx->cur_sess == ctx->max_sess) { 415 (*ctx->logger)(ctl_error, "%s: %s: too many control sessions", 416 me, ctl_sa_ntop((const struct sockaddr *)rav, 417 tmp, sizeof tmp, 418 ctx->logger)); 419 (void) close(fd); 420 return; 421 } 422 sess = memget(sizeof *sess); 423 if (sess == NULL) { 424 (*ctx->logger)(ctl_error, "%s: memget: %s", me, 425 strerror(errno)); 426 (void) close(fd); 427 return; 428 } 429 if (fcntl(fd, F_SETFD, 1) < 0) { 430 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, 431 strerror(errno)); 432 } 433 ctx->cur_sess++; 434 INIT_LINK(sess, link); 435 APPEND(ctx->sess, sess, link); 436 sess->ctx = ctx; 437 sess->sock = fd; 438 sess->wrID.opaque = NULL; 439 sess->rdID.opaque = NULL; 440 sess->wrtiID.opaque = NULL; 441 sess->rdtiID.opaque = NULL; 442 sess->respctx = NULL; 443 sess->csctx = NULL; 444 if (((const struct sockaddr *)rav)->sa_family == AF_UNIX) 445 ctl_sa_copy((const struct sockaddr *)lav, 446 (struct sockaddr *)&sess->sa); 447 else 448 ctl_sa_copy((const struct sockaddr *)rav, 449 (struct sockaddr *)&sess->sa); 450 sess->donefunc = NULL; 451 buffer_init(sess->inbuf); 452 buffer_init(sess->outbuf); 453 sess->state = available; 454 ctl_new_state(sess, initializing, me); 455 sess->verb = ctx->connverb; 456 (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)", 457 me, address_expr, sess->sock); 458 (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0, 459 (const struct sockaddr *)rav, ctx->uctx); 460 } 461 462 static void 463 ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason) 464 { 465 static const char me[] = "ctl_new_state"; 466 struct ctl_sctx *ctx = sess->ctx; 467 char tmp[MAX_NTOP]; 468 469 (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)", 470 me, address_expr, 471 state_names[sess->state], 472 state_names[new_state], reason); 473 sess->state = new_state; 474 } 475 476 static void 477 ctl_close(struct ctl_sess *sess) { 478 static const char me[] = "ctl_close"; 479 struct ctl_sctx *ctx = sess->ctx; 480 char tmp[MAX_NTOP]; 481 482 REQUIRE(sess->state == initializing || 483 sess->state == writing || 484 sess->state == reading || 485 sess->state == processing || 486 sess->state == reading_data || 487 sess->state == idling); 488 REQUIRE(sess->sock != -1); 489 if (sess->state == reading || sess->state == reading_data) 490 ctl_stop_read(sess); 491 else if (sess->state == writing) { 492 if (sess->wrID.opaque != NULL) { 493 (void) evCancelRW(ctx->ev, sess->wrID); 494 sess->wrID.opaque = NULL; 495 } 496 if (sess->wrtiID.opaque != NULL) { 497 (void) evClearIdleTimer(ctx->ev, sess->wrtiID); 498 sess->wrtiID.opaque = NULL; 499 } 500 } 501 ctl_new_state(sess, closing, me); 502 (void) close(sess->sock); 503 if (allocated_p(sess->inbuf)) 504 ctl_bufput(&sess->inbuf); 505 if (allocated_p(sess->outbuf)) 506 ctl_bufput(&sess->outbuf); 507 (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)", 508 me, address_expr, sess->sock); 509 UNLINK(ctx->sess, sess, link); 510 memput(sess, sizeof *sess); 511 ctx->cur_sess--; 512 } 513 514 static void 515 ctl_start_read(struct ctl_sess *sess) { 516 static const char me[] = "ctl_start_read"; 517 struct ctl_sctx *ctx = sess->ctx; 518 char tmp[MAX_NTOP]; 519 520 REQUIRE(sess->state == initializing || 521 sess->state == writing || 522 sess->state == processing || 523 sess->state == idling); 524 REQUIRE(sess->rdtiID.opaque == NULL); 525 REQUIRE(sess->rdID.opaque == NULL); 526 sess->inbuf.used = 0; 527 if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout, 528 &sess->rdtiID) < 0) 529 { 530 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, 531 address_expr, strerror(errno)); 532 ctl_close(sess); 533 return; 534 } 535 if (evSelectFD(ctx->ev, sess->sock, EV_READ, 536 ctl_readable, sess, &sess->rdID) < 0) { 537 (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me, 538 address_expr, strerror(errno)); 539 return; 540 } 541 ctl_new_state(sess, reading, me); 542 } 543 544 static void 545 ctl_stop_read(struct ctl_sess *sess) { 546 static const char me[] = "ctl_stop_read"; 547 struct ctl_sctx *ctx = sess->ctx; 548 549 REQUIRE(sess->state == reading || sess->state == reading_data); 550 REQUIRE(sess->rdID.opaque != NULL); 551 (void) evDeselectFD(ctx->ev, sess->rdID); 552 sess->rdID.opaque = NULL; 553 if (sess->rdtiID.opaque != NULL) { 554 (void) evClearIdleTimer(ctx->ev, sess->rdtiID); 555 sess->rdtiID.opaque = NULL; 556 } 557 ctl_new_state(sess, idling, me); 558 } 559 560 static void 561 ctl_readable(evContext lev, void *uap, int fd, int evmask) { 562 static const char me[] = "ctl_readable"; 563 struct ctl_sess *sess = uap; 564 struct ctl_sctx *ctx; 565 char *eos, tmp[MAX_NTOP]; 566 ssize_t n; 567 568 REQUIRE(sess != NULL); 569 REQUIRE(fd >= 0); 570 REQUIRE(evmask == EV_READ); 571 REQUIRE(sess->state == reading || sess->state == reading_data); 572 573 ctx = sess->ctx; 574 evTouchIdleTimer(lev, sess->rdtiID); 575 if (!allocated_p(sess->inbuf) && 576 ctl_bufget(&sess->inbuf, ctx->logger) < 0) { 577 (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer", 578 me, address_expr); 579 ctl_close(sess); 580 return; 581 } 582 n = read(sess->sock, sess->inbuf.text + sess->inbuf.used, 583 MAX_LINELEN - sess->inbuf.used); 584 if (n <= 0) { 585 (*ctx->logger)(ctl_debug, "%s: %s: read: %s", 586 me, address_expr, 587 (n == 0) ? "Unexpected EOF" : strerror(errno)); 588 ctl_close(sess); 589 return; 590 } 591 sess->inbuf.used += n; 592 eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used); 593 if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') { 594 eos[-1] = '\0'; 595 if ((sess->respflags & CTL_DATA) != 0) { 596 INSIST(sess->verb != NULL); 597 (*sess->verb->func)(sess->ctx, sess, sess->verb, 598 sess->inbuf.text, 599 CTL_DATA, sess->respctx, 600 sess->ctx->uctx); 601 } else { 602 ctl_stop_read(sess); 603 ctl_docommand(sess); 604 } 605 sess->inbuf.used -= ((eos - sess->inbuf.text) + 1); 606 if (sess->inbuf.used == 0U) 607 ctl_bufput(&sess->inbuf); 608 else 609 memmove(sess->inbuf.text, eos + 1, sess->inbuf.used); 610 return; 611 } 612 if (sess->inbuf.used == (size_t)MAX_LINELEN) { 613 (*ctx->logger)(ctl_error, "%s: %s: line too long, closing", 614 me, address_expr); 615 ctl_close(sess); 616 } 617 } 618 619 static void 620 ctl_wrtimeout(evContext lev, void *uap, 621 struct timespec due, 622 struct timespec itv) 623 { 624 static const char me[] = "ctl_wrtimeout"; 625 struct ctl_sess *sess = uap; 626 struct ctl_sctx *ctx = sess->ctx; 627 char tmp[MAX_NTOP]; 628 629 UNUSED(lev); 630 UNUSED(due); 631 UNUSED(itv); 632 633 REQUIRE(sess->state == writing); 634 sess->wrtiID.opaque = NULL; 635 (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing", 636 me, address_expr); 637 if (sess->wrID.opaque != NULL) { 638 (void) evCancelRW(ctx->ev, sess->wrID); 639 sess->wrID.opaque = NULL; 640 } 641 ctl_signal_done(ctx, sess); 642 ctl_new_state(sess, processing, me); 643 ctl_close(sess); 644 } 645 646 static void 647 ctl_rdtimeout(evContext lev, void *uap, 648 struct timespec due, 649 struct timespec itv) 650 { 651 static const char me[] = "ctl_rdtimeout"; 652 struct ctl_sess *sess = uap; 653 struct ctl_sctx *ctx = sess->ctx; 654 char tmp[MAX_NTOP]; 655 656 UNUSED(lev); 657 UNUSED(due); 658 UNUSED(itv); 659 660 REQUIRE(sess->state == reading); 661 sess->rdtiID.opaque = NULL; 662 (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing", 663 me, address_expr); 664 if (sess->state == reading || sess->state == reading_data) 665 ctl_stop_read(sess); 666 ctl_signal_done(ctx, sess); 667 ctl_new_state(sess, processing, me); 668 ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL, 669 NULL, NULL, NULL, 0); 670 } 671 672 static void 673 ctl_docommand(struct ctl_sess *sess) { 674 static const char me[] = "ctl_docommand"; 675 char *name, *rest, tmp[MAX_NTOP]; 676 struct ctl_sctx *ctx = sess->ctx; 677 const struct ctl_verb *verb; 678 679 REQUIRE(allocated_p(sess->inbuf)); 680 (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]", 681 me, address_expr, 682 sess->inbuf.text, (u_int)sess->inbuf.used); 683 ctl_new_state(sess, processing, me); 684 name = sess->inbuf.text + strspn(sess->inbuf.text, space); 685 rest = name + strcspn(name, space); 686 if (*rest != '\0') { 687 *rest++ = '\0'; 688 rest += strspn(rest, space); 689 } 690 for (verb = ctx->verbs; 691 verb != NULL && verb->name != NULL && verb->func != NULL; 692 verb++) 693 if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0) 694 break; 695 if (verb != NULL && verb->name != NULL && verb->func != NULL) { 696 sess->verb = verb; 697 (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx); 698 } else { 699 char buf[1100]; 700 701 if (sizeof "Unrecognized command \"\" (args \"\")" + 702 strlen(name) + strlen(rest) > sizeof buf) 703 strcpy(buf, "Unrecognized command (buf ovf)"); 704 else 705 sprintf(buf, 706 "Unrecognized command \"%s\" (args \"%s\")", 707 name, rest); 708 ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL, 709 NULL, 0); 710 } 711 } 712 713 static void 714 ctl_writedone(evContext lev, void *uap, int fd, int bytes) { 715 static const char me[] = "ctl_writedone"; 716 struct ctl_sess *sess = uap; 717 struct ctl_sctx *ctx = sess->ctx; 718 char tmp[MAX_NTOP]; 719 int save_errno = errno; 720 721 UNUSED(lev); 722 UNUSED(uap); 723 724 REQUIRE(sess->state == writing); 725 REQUIRE(fd == sess->sock); 726 REQUIRE(sess->wrtiID.opaque != NULL); 727 sess->wrID.opaque = NULL; 728 (void) evClearIdleTimer(ctx->ev, sess->wrtiID); 729 sess->wrtiID.opaque = NULL; 730 if (bytes < 0) { 731 (*ctx->logger)(ctl_error, "%s: %s: %s", 732 me, address_expr, strerror(save_errno)); 733 ctl_close(sess); 734 return; 735 } 736 737 INSIST(allocated_p(sess->outbuf)); 738 ctl_bufput(&sess->outbuf); 739 if ((sess->respflags & CTL_EXIT) != 0) { 740 ctl_signal_done(ctx, sess); 741 ctl_close(sess); 742 return; 743 } else if ((sess->respflags & CTL_MORE) != 0) { 744 INSIST(sess->verb != NULL); 745 (*sess->verb->func)(sess->ctx, sess, sess->verb, "", 746 CTL_MORE, sess->respctx, sess->ctx->uctx); 747 } else { 748 ctl_signal_done(ctx, sess); 749 ctl_start_read(sess); 750 } 751 } 752 753 static void 754 ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess, 755 const struct ctl_verb *verb, const char *text, 756 u_int respflags, const void *respctx, void *uctx) 757 { 758 const struct ctl_verb *this = respctx, *next = this + 1; 759 760 UNUSED(ctx); 761 UNUSED(verb); 762 UNUSED(text); 763 UNUSED(uctx); 764 765 REQUIRE(!lastverb_p(this)); 766 REQUIRE((respflags & CTL_MORE) != 0); 767 if (lastverb_p(next)) 768 respflags &= ~CTL_MORE; 769 ctl_response(sess, sess->helpcode, this->help, respflags, next, 770 NULL, NULL, NULL, 0); 771 } 772 773 static void 774 ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) { 775 if (sess->donefunc != NULL) { 776 (*sess->donefunc)(ctx, sess, sess->uap); 777 sess->donefunc = NULL; 778 } 779 } 780 781 /*! \file */ 782