Home | History | Annotate | Line # | Download | only in isc
ctl_srvr.c revision 1.1.1.1
      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