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