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