1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #if !defined(lint) && !defined(SABER) 7 static const char rcsid[] = "$Id: ctl_srvr.c,v 8.26 2002/07/08 05:10:25 marka Exp $"; 8 #endif /* not lint */ 9 10 /* 11 * Copyright (c) 1998,1999 by Internet Software Consortium. 12 * 13 * Permission to use, copy, modify, and distribute this software for any 14 * purpose with or without fee is hereby granted, provided that the above 15 * copyright notice and this permission notice appear in all copies. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 18 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 20 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 21 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 22 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 23 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 24 * SOFTWARE. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* Extern. */ 30 31 #include "port_before.h" 32 33 #include <sys/param.h> 34 #include <sys/file.h> 35 #include <sys/socket.h> 36 #include <sys/un.h> 37 38 #include <netinet/in.h> 39 #include <arpa/nameser.h> 40 #include <arpa/inet.h> 41 42 #include <ctype.h> 43 #include <errno.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 #include <unistd.h> 49 #include <fcntl.h> 50 51 #include <isc/assertions.h> 52 #include <isc/ctl.h> 53 #include <isc/eventlib.h> 54 #include <isc/list.h> 55 #include <isc/logging.h> 56 #include <isc/memcluster.h> 57 58 #include "ctl_p.h" 59 60 #include "port_after.h" 61 62 #ifdef SPRINTF_CHAR 63 # define SPRINTF(x) strlen(sprintf/**/x) 64 #else 65 # define SPRINTF(x) ((size_t)sprintf x) 66 #endif 67 68 /* Macros. */ 69 70 #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL) 71 #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \ 72 tmp, sizeof tmp, ctx->logger) 73 74 /* Types. */ 75 76 enum state { 77 available = 0, initializing, writing, reading, reading_data, 78 processing, idling, quitting, closing 79 }; 80 81 union sa_un { 82 struct sockaddr_in in; 83 #ifndef NO_SOCKADDR_UN 84 struct sockaddr_un un; 85 #endif 86 }; 87 88 struct ctl_sess { 89 LINK(struct ctl_sess) link; 90 struct ctl_sctx * ctx; 91 enum state state; 92 int sock; 93 union sa_un sa; 94 evFileID rdID; 95 evStreamID wrID; 96 evTimerID rdtiID; 97 evTimerID wrtiID; 98 struct ctl_buf inbuf; 99 struct ctl_buf outbuf; 100 const struct ctl_verb * verb; 101 u_int helpcode; 102 const void * respctx; 103 u_int respflags; 104 ctl_srvrdone donefunc; 105 void * uap; 106 void * csctx; 107 }; 108 109 struct ctl_sctx { 110 evContext ev; 111 void * uctx; 112 u_int unkncode; 113 u_int timeoutcode; 114 const struct ctl_verb * verbs; 115 const struct ctl_verb * connverb; 116 int sock; 117 int max_sess; 118 int cur_sess; 119 struct timespec timeout; 120 ctl_logfunc logger; 121 evConnID acID; 122 LIST(struct ctl_sess) sess; 123 }; 124 125 /* Forward. */ 126 127 static void ctl_accept(evContext, void *, int, 128 const void *, int, 129 const void *, int); 130 static void ctl_close(struct ctl_sess *); 131 static void ctl_new_state(struct ctl_sess *, 132 enum state, 133 const char *); 134 static void ctl_start_read(struct ctl_sess *); 135 static void ctl_stop_read(struct ctl_sess *); 136 static void ctl_readable(evContext, void *, int, int); 137 static void ctl_rdtimeout(evContext, void *, 138 struct timespec, 139 struct timespec); 140 static void ctl_wrtimeout(evContext, void *, 141 struct timespec, 142 struct timespec); 143 static void ctl_docommand(struct ctl_sess *); 144 static void ctl_writedone(evContext, void *, int, int); 145 static void ctl_morehelp(struct ctl_sctx *, 146 struct ctl_sess *, 147 const struct ctl_verb *, 148 const char *, 149 u_int, const void *, void *); 150 static void ctl_signal_done(struct ctl_sctx *, 151 struct ctl_sess *); 152 153 /* Private data. */ 154 155 static const char * state_names[] = { 156 "available", "initializing", "writing", "reading", 157 "reading_data", "processing", "idling", "quitting", "closing" 158 }; 159 160 static const char space[] = " "; 161 162 static const struct ctl_verb fakehelpverb = { 163 "fakehelp", ctl_morehelp , NULL 164 }; 165 166 /* Public. */ 167 168 /* 169 * void 170 * ctl_server() 171 * create, condition, and start a listener on the control port. 172 */ 173 struct ctl_sctx * 174 ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len, 175 const struct ctl_verb *verbs, 176 u_int unkncode, u_int timeoutcode, 177 u_int timeout, int backlog, int max_sess, 178 ctl_logfunc logger, void *uctx) 179 { 180 static const char me[] = "ctl_server"; 181 static const int on = 1; 182 const struct ctl_verb *connverb; 183 struct ctl_sctx *ctx; 184 int save_errno; 185 186 if (logger == NULL) 187 logger = ctl_logger; 188 for (connverb = verbs; 189 connverb->name != NULL && connverb->func != NULL; 190 connverb++) 191 if (connverb->name[0] == '\0') 192 break; 193 if (connverb->func == NULL) { 194 (*logger)(ctl_error, "%s: no connection verb found", me); 195 return (NULL); 196 } 197 ctx = memget(sizeof *ctx); 198 if (ctx == NULL) { 199 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); 200 return (NULL); 201 } 202 ctx->ev = lev; 203 ctx->uctx = uctx; 204 ctx->unkncode = unkncode; 205 ctx->timeoutcode = timeoutcode; 206 ctx->verbs = verbs; 207 ctx->timeout = evConsTime(timeout, 0); 208 ctx->logger = logger; 209 ctx->connverb = connverb; 210 ctx->max_sess = max_sess; 211 ctx->cur_sess = 0; 212 INIT_LIST(ctx->sess); 213 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); 214 if (ctx->sock > evHighestFD(ctx->ev)) { 215 ctx->sock = -1; 216 errno = ENOTSOCK; 217 } 218 if (ctx->sock < 0) { 219 save_errno = errno; 220 (*ctx->logger)(ctl_error, "%s: socket: %s", 221 me, strerror(errno)); 222 memput(ctx, sizeof *ctx); 223 errno = save_errno; 224 return (NULL); 225 } 226 if (ctx->sock > evHighestFD(lev)) { 227 close(ctx->sock); 228 (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD"); 229 errno = ENFILE; 230 memput(ctx, sizeof *ctx); 231 return (NULL); 232 } 233 #ifdef NO_UNIX_REUSEADDR 234 if (sap->sa_family != AF_UNIX) 235 #endif 236 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, 237 (const char *)&on, sizeof on) != 0) { 238 (*ctx->logger)(ctl_warning, 239 "%s: setsockopt(REUSEADDR): %s", 240 me, strerror(errno)); 241 } 242 if (bind(ctx->sock, sap, sap_len) < 0) { 243 char tmp[MAX_NTOP]; 244 save_errno = errno; 245 (*ctx->logger)(ctl_error, "%s: bind: %s: %s", 246 me, ctl_sa_ntop((const struct sockaddr *)sap, 247 tmp, sizeof tmp, ctx->logger), 248 strerror(save_errno)); 249 close(ctx->sock); 250 memput(ctx, sizeof *ctx); 251 errno = save_errno; 252 return (NULL); 253 } 254 if (fcntl(ctx->sock, F_SETFD, 1) < 0) { 255 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, 256 strerror(errno)); 257 } 258 if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx, 259 &ctx->acID) < 0) { 260 save_errno = errno; 261 (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s", 262 me, ctx->sock, strerror(errno)); 263 close(ctx->sock); 264 memput(ctx, sizeof *ctx); 265 errno = save_errno; 266 return (NULL); 267 } 268 (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d", 269 me, ctx, ctx->sock); 270 return (ctx); 271 } 272 273 /* 274 * void 275 * ctl_endserver(ctx) 276 * if the control listener is open, close it. clean out all eventlib 277 * stuff. close all active sessions. 278 */ 279 void 280 ctl_endserver(struct ctl_sctx *ctx) { 281 static const char me[] = "ctl_endserver"; 282 struct ctl_sess *this, *next; 283 284 (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p", 285 me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess); 286 if (ctx->acID.opaque != NULL) { 287 (void)evCancelConn(ctx->ev, ctx->acID); 288 ctx->acID.opaque = NULL; 289 } 290 if (ctx->sock != -1) { 291 (void) close(ctx->sock); 292 ctx->sock = -1; 293 } 294 for (this = HEAD(ctx->sess); this != NULL; this = next) { 295 next = NEXT(this, link); 296 ctl_close(this); 297 } 298 memput(ctx, sizeof *ctx); 299 } 300 301 /* 302 * If body is non-NULL then it we add a "." line after it. 303 * Caller must have escaped lines with leading ".". 304 */ 305 void 306 ctl_response(struct ctl_sess *sess, u_int code, const char *text, 307 u_int flags, const void *respctx, ctl_srvrdone donefunc, 308 void *uap, const char *body, size_t bodylen) 309 { 310 static const char me[] = "ctl_response"; 311 struct iovec iov[3], *iovp = iov; 312 struct ctl_sctx *ctx = sess->ctx; 313 char tmp[MAX_NTOP], *pc; 314 int n; 315 316 REQUIRE(sess->state == initializing || 317 sess->state == processing || 318 sess->state == reading_data || 319 sess->state == writing); 320 REQUIRE(sess->wrtiID.opaque == NULL); 321 REQUIRE(sess->wrID.opaque == NULL); 322 ctl_new_state(sess, writing, me); 323 sess->donefunc = donefunc; 324 sess->uap = uap; 325 if (!allocated_p(sess->outbuf) && 326 ctl_bufget(&sess->outbuf, ctx->logger) < 0) { 327 (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer", 328 me, address_expr); 329 goto untimely; 330 } 331 if (sizeof "000-\r\n" + strlen(text) > MAX_LINELEN) { 332 (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing", 333 me, address_expr); 334 goto untimely; 335 } 336 sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n", 337 code, (flags & CTL_MORE) != 0 ? '-' : ' ', 338 text)); 339 for (pc = sess->outbuf.text, n = 0; 340 n < (int)sess->outbuf.used-2; pc++, n++) 341 if (!isascii((unsigned char)*pc) || 342 !isprint((unsigned char)*pc)) 343 *pc = '\040'; 344 *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used); 345 if (body != NULL) { 346 char *tmp; 347 DE_CONST(body, tmp); 348 *iovp++ = evConsIovec(tmp, bodylen); 349 DE_CONST(".\r\n", tmp); 350 *iovp++ = evConsIovec(tmp, 3); 351 } 352 (*ctx->logger)(ctl_debug, "%s: [%d] %s", me, 353 sess->outbuf.used, sess->outbuf.text); 354 if (evWrite(ctx->ev, sess->sock, iov, iovp - iov, 355 ctl_writedone, sess, &sess->wrID) < 0) { 356 (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me, 357 address_expr, strerror(errno)); 358 goto untimely; 359 } 360 if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout, 361 &sess->wrtiID) < 0) 362 { 363 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, 364 address_expr, strerror(errno)); 365 goto untimely; 366 } 367 if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) { 368 (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me, 369 address_expr, strerror(errno)); 370 untimely: 371 ctl_signal_done(ctx, sess); 372 ctl_close(sess); 373 return; 374 } 375 sess->respctx = respctx; 376 sess->respflags = flags; 377 } 378 379 void 380 ctl_sendhelp(struct ctl_sess *sess, u_int code) { 381 static const char me[] = "ctl_sendhelp"; 382 struct ctl_sctx *ctx = sess->ctx; 383 384 sess->helpcode = code; 385 sess->verb = &fakehelpverb; 386 ctl_morehelp(ctx, sess, NULL, me, CTL_MORE, 387 (const void *)ctx->verbs, NULL); 388 } 389 390 void * 391 ctl_getcsctx(struct ctl_sess *sess) { 392 return (sess->csctx); 393 } 394 395 void * 396 ctl_setcsctx(struct ctl_sess *sess, void *csctx) { 397 void *old = sess->csctx; 398 399 sess->csctx = csctx; 400 return (old); 401 } 402 403 /* Private functions. */ 404 405 static void 406 ctl_accept(evContext lev, void *uap, int fd, 407 const void *lav, int lalen, 408 const void *rav, int ralen) 409 { 410 static const char me[] = "ctl_accept"; 411 struct ctl_sctx *ctx = uap; 412 struct ctl_sess *sess = NULL; 413 char tmp[MAX_NTOP]; 414 415 UNUSED(lev); 416 UNUSED(lalen); 417 UNUSED(ralen); 418 419 if (fd < 0) { 420 (*ctx->logger)(ctl_error, "%s: accept: %s", 421 me, strerror(errno)); 422 return; 423 } 424 if (ctx->cur_sess == ctx->max_sess) { 425 (*ctx->logger)(ctl_error, "%s: %s: too many control sessions", 426 me, ctl_sa_ntop((const struct sockaddr *)rav, 427 tmp, sizeof tmp, 428 ctx->logger)); 429 (void) close(fd); 430 return; 431 } 432 sess = memget(sizeof *sess); 433 if (sess == NULL) { 434 (*ctx->logger)(ctl_error, "%s: memget: %s", me, 435 strerror(errno)); 436 (void) close(fd); 437 return; 438 } 439 if (fcntl(fd, F_SETFD, 1) < 0) { 440 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, 441 strerror(errno)); 442 } 443 ctx->cur_sess++; 444 INIT_LINK(sess, link); 445 APPEND(ctx->sess, sess, link); 446 sess->ctx = ctx; 447 sess->sock = fd; 448 sess->wrID.opaque = NULL; 449 sess->rdID.opaque = NULL; 450 sess->wrtiID.opaque = NULL; 451 sess->rdtiID.opaque = NULL; 452 sess->respctx = NULL; 453 sess->csctx = NULL; 454 if (((const struct sockaddr *)rav)->sa_family == AF_UNIX) 455 ctl_sa_copy((const struct sockaddr *)lav, 456 (struct sockaddr *)&sess->sa); 457 else 458 ctl_sa_copy((const struct sockaddr *)rav, 459 (struct sockaddr *)&sess->sa); 460 sess->donefunc = NULL; 461 buffer_init(sess->inbuf); 462 buffer_init(sess->outbuf); 463 sess->state = available; 464 ctl_new_state(sess, initializing, me); 465 sess->verb = ctx->connverb; 466 (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)", 467 me, address_expr, sess->sock); 468 (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0, 469 (const struct sockaddr *)rav, ctx->uctx); 470 } 471 472 static void 473 ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason) 474 { 475 static const char me[] = "ctl_new_state"; 476 struct ctl_sctx *ctx = sess->ctx; 477 char tmp[MAX_NTOP]; 478 479 (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)", 480 me, address_expr, 481 state_names[sess->state], 482 state_names[new_state], reason); 483 sess->state = new_state; 484 } 485 486 static void 487 ctl_close(struct ctl_sess *sess) { 488 static const char me[] = "ctl_close"; 489 struct ctl_sctx *ctx = sess->ctx; 490 char tmp[MAX_NTOP]; 491 492 REQUIRE(sess->state == initializing || 493 sess->state == writing || 494 sess->state == reading || 495 sess->state == processing || 496 sess->state == reading_data || 497 sess->state == idling); 498 REQUIRE(sess->sock != -1); 499 if (sess->state == reading || sess->state == reading_data) 500 ctl_stop_read(sess); 501 else if (sess->state == writing) { 502 if (sess->wrID.opaque != NULL) { 503 (void) evCancelRW(ctx->ev, sess->wrID); 504 sess->wrID.opaque = NULL; 505 } 506 if (sess->wrtiID.opaque != NULL) { 507 (void) evClearIdleTimer(ctx->ev, sess->wrtiID); 508 sess->wrtiID.opaque = NULL; 509 } 510 } 511 ctl_new_state(sess, closing, me); 512 (void) close(sess->sock); 513 if (allocated_p(sess->inbuf)) 514 ctl_bufput(&sess->inbuf); 515 if (allocated_p(sess->outbuf)) 516 ctl_bufput(&sess->outbuf); 517 (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)", 518 me, address_expr, sess->sock); 519 UNLINK(ctx->sess, sess, link); 520 memput(sess, sizeof *sess); 521 ctx->cur_sess--; 522 } 523 524 static void 525 ctl_start_read(struct ctl_sess *sess) { 526 static const char me[] = "ctl_start_read"; 527 struct ctl_sctx *ctx = sess->ctx; 528 char tmp[MAX_NTOP]; 529 530 REQUIRE(sess->state == initializing || 531 sess->state == writing || 532 sess->state == processing || 533 sess->state == idling); 534 REQUIRE(sess->rdtiID.opaque == NULL); 535 REQUIRE(sess->rdID.opaque == NULL); 536 sess->inbuf.used = 0; 537 if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout, 538 &sess->rdtiID) < 0) 539 { 540 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, 541 address_expr, strerror(errno)); 542 ctl_close(sess); 543 return; 544 } 545 if (evSelectFD(ctx->ev, sess->sock, EV_READ, 546 ctl_readable, sess, &sess->rdID) < 0) { 547 (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me, 548 address_expr, strerror(errno)); 549 return; 550 } 551 ctl_new_state(sess, reading, me); 552 } 553 554 static void 555 ctl_stop_read(struct ctl_sess *sess) { 556 static const char me[] = "ctl_stop_read"; 557 struct ctl_sctx *ctx = sess->ctx; 558 559 REQUIRE(sess->state == reading || sess->state == reading_data); 560 REQUIRE(sess->rdID.opaque != NULL); 561 (void) evDeselectFD(ctx->ev, sess->rdID); 562 sess->rdID.opaque = NULL; 563 if (sess->rdtiID.opaque != NULL) { 564 (void) evClearIdleTimer(ctx->ev, sess->rdtiID); 565 sess->rdtiID.opaque = NULL; 566 } 567 ctl_new_state(sess, idling, me); 568 } 569 570 static void 571 ctl_readable(evContext lev, void *uap, int fd, int evmask) { 572 static const char me[] = "ctl_readable"; 573 struct ctl_sess *sess = uap; 574 struct ctl_sctx *ctx = sess->ctx; 575 char *eos, tmp[MAX_NTOP]; 576 ssize_t n; 577 578 REQUIRE(sess != NULL); 579 REQUIRE(fd >= 0); 580 REQUIRE(evmask == EV_READ); 581 REQUIRE(sess->state == reading || sess->state == reading_data); 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 == 0) 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 == 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