xref: /netbsd/external/bsd/libbind/dist/isc/ctl_srvr.c (revision 6550d01e)
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