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