17c478bd9Sstevel@tonic-gate /*
2*9525b14bSRao Shoaib  * Copyright (C) 2004, 2005, 2007, 2008  Internet Systems Consortium, Inc. ("ISC")
3*9525b14bSRao Shoaib  * Copyright (C) 1998-2003  Internet Software Consortium.
47c478bd9Sstevel@tonic-gate  *
5*9525b14bSRao Shoaib  * Permission to use, copy, modify, and/or distribute this software for any
67c478bd9Sstevel@tonic-gate  * purpose with or without fee is hereby granted, provided that the above
77c478bd9Sstevel@tonic-gate  * copyright notice and this permission notice appear in all copies.
87c478bd9Sstevel@tonic-gate  *
9*9525b14bSRao Shoaib  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10*9525b14bSRao Shoaib  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11*9525b14bSRao Shoaib  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12*9525b14bSRao Shoaib  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13*9525b14bSRao Shoaib  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14*9525b14bSRao Shoaib  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15*9525b14bSRao Shoaib  * PERFORMANCE OF THIS SOFTWARE.
167c478bd9Sstevel@tonic-gate  */
177c478bd9Sstevel@tonic-gate 
187c478bd9Sstevel@tonic-gate #include "port_before.h"
197c478bd9Sstevel@tonic-gate 
207c478bd9Sstevel@tonic-gate #include <sys/param.h>
217c478bd9Sstevel@tonic-gate #include <sys/file.h>
227c478bd9Sstevel@tonic-gate #include <sys/socket.h>
237c478bd9Sstevel@tonic-gate 
247c478bd9Sstevel@tonic-gate #include <netinet/in.h>
257c478bd9Sstevel@tonic-gate #include <arpa/nameser.h>
267c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <ctype.h>
297c478bd9Sstevel@tonic-gate #include <errno.h>
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <time.h>
347c478bd9Sstevel@tonic-gate #include <unistd.h>
35*9525b14bSRao Shoaib #ifdef HAVE_MEMORY_H
36*9525b14bSRao Shoaib #include <memory.h>
37*9525b14bSRao Shoaib #endif
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include <isc/assertions.h>
407c478bd9Sstevel@tonic-gate #include <isc/ctl.h>
417c478bd9Sstevel@tonic-gate #include <isc/eventlib.h>
427c478bd9Sstevel@tonic-gate #include <isc/list.h>
437c478bd9Sstevel@tonic-gate #include <isc/memcluster.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include "ctl_p.h"
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #include "port_after.h"
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate /* Constants. */
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /* Macros. */
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #define donefunc_p(ctx) ((ctx).donefunc != NULL)
557c478bd9Sstevel@tonic-gate #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \
567c478bd9Sstevel@tonic-gate 			  isdigit((unsigned char)(line[1])) && \
577c478bd9Sstevel@tonic-gate 			  isdigit((unsigned char)(line[2])))
587c478bd9Sstevel@tonic-gate #define arpacont_p(line) (line[3] == '-')
597c478bd9Sstevel@tonic-gate #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \
607c478bd9Sstevel@tonic-gate 			  line[3] == '\r' || line[3] == '\0')
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate /* Types. */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate enum state {
657c478bd9Sstevel@tonic-gate 	initializing = 0, connecting, connected, destroyed
667c478bd9Sstevel@tonic-gate };
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate struct ctl_tran {
697c478bd9Sstevel@tonic-gate 	LINK(struct ctl_tran)	link;
707c478bd9Sstevel@tonic-gate 	LINK(struct ctl_tran)	wlink;
717c478bd9Sstevel@tonic-gate 	struct ctl_cctx *	ctx;
727c478bd9Sstevel@tonic-gate 	struct ctl_buf		outbuf;
737c478bd9Sstevel@tonic-gate 	ctl_clntdone		donefunc;
747c478bd9Sstevel@tonic-gate 	void *			uap;
757c478bd9Sstevel@tonic-gate };
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate struct ctl_cctx {
787c478bd9Sstevel@tonic-gate 	enum state		state;
797c478bd9Sstevel@tonic-gate 	evContext		ev;
807c478bd9Sstevel@tonic-gate 	int			sock;
817c478bd9Sstevel@tonic-gate 	ctl_logfunc		logger;
827c478bd9Sstevel@tonic-gate 	ctl_clntdone		donefunc;
837c478bd9Sstevel@tonic-gate 	void *			uap;
847c478bd9Sstevel@tonic-gate 	evConnID		coID;
857c478bd9Sstevel@tonic-gate 	evTimerID		tiID;
867c478bd9Sstevel@tonic-gate 	evFileID		rdID;
877c478bd9Sstevel@tonic-gate 	evStreamID		wrID;
887c478bd9Sstevel@tonic-gate 	struct ctl_buf		inbuf;
897c478bd9Sstevel@tonic-gate 	struct timespec		timeout;
907c478bd9Sstevel@tonic-gate 	LIST(struct ctl_tran)	tran;
917c478bd9Sstevel@tonic-gate 	LIST(struct ctl_tran)	wtran;
927c478bd9Sstevel@tonic-gate };
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate /* Forward. */
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int);
977c478bd9Sstevel@tonic-gate static void		start_write(struct ctl_cctx *);
987c478bd9Sstevel@tonic-gate static void		destroy(struct ctl_cctx *, int);
997c478bd9Sstevel@tonic-gate static void		error(struct ctl_cctx *);
1007c478bd9Sstevel@tonic-gate static void		new_state(struct ctl_cctx *, enum state);
1017c478bd9Sstevel@tonic-gate static void		conn_done(evContext, void *, int,
1027c478bd9Sstevel@tonic-gate 				  const void *, int,
1037c478bd9Sstevel@tonic-gate 				  const void *, int);
1047c478bd9Sstevel@tonic-gate static void		write_done(evContext, void *, int, int);
1057c478bd9Sstevel@tonic-gate static void		start_read(struct ctl_cctx *);
1067c478bd9Sstevel@tonic-gate static void		stop_read(struct ctl_cctx *);
1077c478bd9Sstevel@tonic-gate static void		readable(evContext, void *, int, int);
1087c478bd9Sstevel@tonic-gate static void		start_timer(struct ctl_cctx *);
1097c478bd9Sstevel@tonic-gate static void		stop_timer(struct ctl_cctx *);
1107c478bd9Sstevel@tonic-gate static void		touch_timer(struct ctl_cctx *);
1117c478bd9Sstevel@tonic-gate static void		timer(evContext, void *,
1127c478bd9Sstevel@tonic-gate 			      struct timespec, struct timespec);
1137c478bd9Sstevel@tonic-gate 
114*9525b14bSRao Shoaib #ifndef HAVE_MEMCHR
115*9525b14bSRao Shoaib static void *
memchr(const void * b,int c,size_t len)116*9525b14bSRao Shoaib memchr(const void *b, int c, size_t len) {
117*9525b14bSRao Shoaib 	const unsigned char *p = b;
118*9525b14bSRao Shoaib 	size_t i;
119*9525b14bSRao Shoaib 
120*9525b14bSRao Shoaib 	for (i = 0; i < len; i++, p++)
121*9525b14bSRao Shoaib 		if (*p == (unsigned char)c)
122*9525b14bSRao Shoaib 			return ((void *)p);
123*9525b14bSRao Shoaib 	return (NULL);
124*9525b14bSRao Shoaib }
125*9525b14bSRao Shoaib #endif
126*9525b14bSRao Shoaib 
1277c478bd9Sstevel@tonic-gate /* Private data. */
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate static const char * const state_names[] = {
1307c478bd9Sstevel@tonic-gate 	"initializing", "connecting", "connected", "destroyed"
1317c478bd9Sstevel@tonic-gate };
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate /* Public. */
1347c478bd9Sstevel@tonic-gate 
135*9525b14bSRao Shoaib /*%
1367c478bd9Sstevel@tonic-gate  * void
1377c478bd9Sstevel@tonic-gate  * ctl_client()
1387c478bd9Sstevel@tonic-gate  *	create, condition, and connect to a listener on the control port.
1397c478bd9Sstevel@tonic-gate  */
1407c478bd9Sstevel@tonic-gate struct ctl_cctx *
ctl_client(evContext lev,const struct sockaddr * cap,size_t cap_len,const struct sockaddr * sap,size_t sap_len,ctl_clntdone donefunc,void * uap,u_int timeout,ctl_logfunc logger)1417c478bd9Sstevel@tonic-gate ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len,
1427c478bd9Sstevel@tonic-gate 	   const struct sockaddr *sap, size_t sap_len,
1437c478bd9Sstevel@tonic-gate 	   ctl_clntdone donefunc, void *uap,
1447c478bd9Sstevel@tonic-gate 	   u_int timeout, ctl_logfunc logger)
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate 	static const char me[] = "ctl_client";
1477c478bd9Sstevel@tonic-gate 	static const int on = 1;
1487c478bd9Sstevel@tonic-gate 	struct ctl_cctx *ctx;
1497c478bd9Sstevel@tonic-gate 	struct sockaddr *captmp;
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	if (logger == NULL)
1527c478bd9Sstevel@tonic-gate 		logger = ctl_logger;
1537c478bd9Sstevel@tonic-gate 	ctx = memget(sizeof *ctx);
1547c478bd9Sstevel@tonic-gate 	if (ctx == NULL) {
1557c478bd9Sstevel@tonic-gate 		(*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
1567c478bd9Sstevel@tonic-gate 		goto fatal;
1577c478bd9Sstevel@tonic-gate 	}
1587c478bd9Sstevel@tonic-gate 	ctx->state = initializing;
1597c478bd9Sstevel@tonic-gate 	ctx->ev = lev;
1607c478bd9Sstevel@tonic-gate 	ctx->logger = logger;
1617c478bd9Sstevel@tonic-gate 	ctx->timeout = evConsTime(timeout, 0);
1627c478bd9Sstevel@tonic-gate 	ctx->donefunc = donefunc;
1637c478bd9Sstevel@tonic-gate 	ctx->uap = uap;
1647c478bd9Sstevel@tonic-gate 	ctx->coID.opaque = NULL;
1657c478bd9Sstevel@tonic-gate 	ctx->tiID.opaque = NULL;
1667c478bd9Sstevel@tonic-gate 	ctx->rdID.opaque = NULL;
1677c478bd9Sstevel@tonic-gate 	ctx->wrID.opaque = NULL;
1687c478bd9Sstevel@tonic-gate 	buffer_init(ctx->inbuf);
1697c478bd9Sstevel@tonic-gate 	INIT_LIST(ctx->tran);
1707c478bd9Sstevel@tonic-gate 	INIT_LIST(ctx->wtran);
1717c478bd9Sstevel@tonic-gate 	ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
1727c478bd9Sstevel@tonic-gate 	if (ctx->sock > evHighestFD(ctx->ev)) {
1737c478bd9Sstevel@tonic-gate 		ctx->sock = -1;
1747c478bd9Sstevel@tonic-gate 		errno = ENOTSOCK;
1757c478bd9Sstevel@tonic-gate 	}
1767c478bd9Sstevel@tonic-gate 	if (ctx->sock < 0) {
1777c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: socket: %s",
1787c478bd9Sstevel@tonic-gate 			       me, strerror(errno));
1797c478bd9Sstevel@tonic-gate 		goto fatal;
1807c478bd9Sstevel@tonic-gate 	}
1817c478bd9Sstevel@tonic-gate 	if (cap != NULL) {
1827c478bd9Sstevel@tonic-gate 		if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
1837c478bd9Sstevel@tonic-gate 			       (const char *)&on, sizeof on) != 0) {
1847c478bd9Sstevel@tonic-gate 			(*ctx->logger)(ctl_warning,
1857c478bd9Sstevel@tonic-gate 				       "%s: setsockopt(REUSEADDR): %s",
1867c478bd9Sstevel@tonic-gate 				       me, strerror(errno));
1877c478bd9Sstevel@tonic-gate 		}
1887c478bd9Sstevel@tonic-gate 		DE_CONST(cap, captmp);
1897c478bd9Sstevel@tonic-gate 		if (bind(ctx->sock, captmp, cap_len) < 0) {
1907c478bd9Sstevel@tonic-gate 			(*ctx->logger)(ctl_error, "%s: bind: %s", me,
1917c478bd9Sstevel@tonic-gate 				       strerror(errno));
1927c478bd9Sstevel@tonic-gate 			goto fatal;
1937c478bd9Sstevel@tonic-gate 		}
1947c478bd9Sstevel@tonic-gate 	}
1957c478bd9Sstevel@tonic-gate 	if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len,
1967c478bd9Sstevel@tonic-gate 		      conn_done, ctx, &ctx->coID) < 0) {
1977c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s",
1987c478bd9Sstevel@tonic-gate 			       me, ctx->sock, strerror(errno));
1997c478bd9Sstevel@tonic-gate  fatal:
2007c478bd9Sstevel@tonic-gate 		if (ctx != NULL) {
2017c478bd9Sstevel@tonic-gate 			if (ctx->sock >= 0)
2027c478bd9Sstevel@tonic-gate 				close(ctx->sock);
2037c478bd9Sstevel@tonic-gate 			memput(ctx, sizeof *ctx);
2047c478bd9Sstevel@tonic-gate 		}
2057c478bd9Sstevel@tonic-gate 		return (NULL);
2067c478bd9Sstevel@tonic-gate 	}
2077c478bd9Sstevel@tonic-gate 	new_state(ctx, connecting);
2087c478bd9Sstevel@tonic-gate 	return (ctx);
2097c478bd9Sstevel@tonic-gate }
2107c478bd9Sstevel@tonic-gate 
211*9525b14bSRao Shoaib /*%
2127c478bd9Sstevel@tonic-gate  * void
2137c478bd9Sstevel@tonic-gate  * ctl_endclient(ctx)
2147c478bd9Sstevel@tonic-gate  *	close a client and release all of its resources.
2157c478bd9Sstevel@tonic-gate  */
2167c478bd9Sstevel@tonic-gate void
ctl_endclient(struct ctl_cctx * ctx)2177c478bd9Sstevel@tonic-gate ctl_endclient(struct ctl_cctx *ctx) {
2187c478bd9Sstevel@tonic-gate 	if (ctx->state != destroyed)
2197c478bd9Sstevel@tonic-gate 		destroy(ctx, 0);
2207c478bd9Sstevel@tonic-gate 	memput(ctx, sizeof *ctx);
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
223*9525b14bSRao Shoaib /*%
2247c478bd9Sstevel@tonic-gate  * int
2257c478bd9Sstevel@tonic-gate  * ctl_command(ctx, cmd, len, donefunc, uap)
2267c478bd9Sstevel@tonic-gate  *	Queue a transaction, which will begin with sending cmd
2277c478bd9Sstevel@tonic-gate  *	and complete by calling donefunc with the answer.
2287c478bd9Sstevel@tonic-gate  */
2297c478bd9Sstevel@tonic-gate int
ctl_command(struct ctl_cctx * ctx,const char * cmd,size_t len,ctl_clntdone donefunc,void * uap)2307c478bd9Sstevel@tonic-gate ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
2317c478bd9Sstevel@tonic-gate 	    ctl_clntdone donefunc, void *uap)
2327c478bd9Sstevel@tonic-gate {
2337c478bd9Sstevel@tonic-gate 	struct ctl_tran *tran;
2347c478bd9Sstevel@tonic-gate 	char *pc;
2357c478bd9Sstevel@tonic-gate 	unsigned int n;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	switch (ctx->state) {
2387c478bd9Sstevel@tonic-gate 	case destroyed:
2397c478bd9Sstevel@tonic-gate 		errno = ENOTCONN;
2407c478bd9Sstevel@tonic-gate 		return (-1);
2417c478bd9Sstevel@tonic-gate 	case connecting:
2427c478bd9Sstevel@tonic-gate 	case connected:
2437c478bd9Sstevel@tonic-gate 		break;
2447c478bd9Sstevel@tonic-gate 	default:
2457c478bd9Sstevel@tonic-gate 		abort();
2467c478bd9Sstevel@tonic-gate 	}
247*9525b14bSRao Shoaib 	if (len >= (size_t)MAX_LINELEN) {
2487c478bd9Sstevel@tonic-gate 		errno = EMSGSIZE;
2497c478bd9Sstevel@tonic-gate 		return (-1);
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate 	tran = new_tran(ctx, donefunc, uap, 1);
2527c478bd9Sstevel@tonic-gate 	if (tran == NULL)
2537c478bd9Sstevel@tonic-gate 		return (-1);
2547c478bd9Sstevel@tonic-gate 	if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
2557c478bd9Sstevel@tonic-gate 		return (-1);
2567c478bd9Sstevel@tonic-gate 	memcpy(tran->outbuf.text, cmd, len);
2577c478bd9Sstevel@tonic-gate 	tran->outbuf.used = len;
2587c478bd9Sstevel@tonic-gate 	for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++)
2597c478bd9Sstevel@tonic-gate 		if (!isascii((unsigned char)*pc) ||
2607c478bd9Sstevel@tonic-gate 		    !isprint((unsigned char)*pc))
2617c478bd9Sstevel@tonic-gate 			*pc = '\040';
2627c478bd9Sstevel@tonic-gate 	start_write(ctx);
2637c478bd9Sstevel@tonic-gate 	return (0);
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate /* Private. */
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate static struct ctl_tran *
new_tran(struct ctl_cctx * ctx,ctl_clntdone donefunc,void * uap,int w)2697c478bd9Sstevel@tonic-gate new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) {
2707c478bd9Sstevel@tonic-gate 	struct ctl_tran *new = memget(sizeof *new);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	if (new == NULL)
2737c478bd9Sstevel@tonic-gate 		return (NULL);
2747c478bd9Sstevel@tonic-gate 	new->ctx = ctx;
2757c478bd9Sstevel@tonic-gate 	buffer_init(new->outbuf);
2767c478bd9Sstevel@tonic-gate 	new->donefunc = donefunc;
2777c478bd9Sstevel@tonic-gate 	new->uap = uap;
2787c478bd9Sstevel@tonic-gate 	INIT_LINK(new, link);
2797c478bd9Sstevel@tonic-gate 	INIT_LINK(new, wlink);
2807c478bd9Sstevel@tonic-gate 	APPEND(ctx->tran, new, link);
2817c478bd9Sstevel@tonic-gate 	if (w)
2827c478bd9Sstevel@tonic-gate 		APPEND(ctx->wtran, new, wlink);
2837c478bd9Sstevel@tonic-gate 	return (new);
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate static void
start_write(struct ctl_cctx * ctx)2877c478bd9Sstevel@tonic-gate start_write(struct ctl_cctx *ctx) {
2887c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::start_write";
2897c478bd9Sstevel@tonic-gate 	struct ctl_tran *tran;
2907c478bd9Sstevel@tonic-gate 	struct iovec iov[2], *iovp = iov;
2917c478bd9Sstevel@tonic-gate 	char * tmp;
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->state == connecting || ctx->state == connected);
2947c478bd9Sstevel@tonic-gate 	/* If there is a write in progress, don't try to write more yet. */
2957c478bd9Sstevel@tonic-gate 	if (ctx->wrID.opaque != NULL)
2967c478bd9Sstevel@tonic-gate 		return;
2977c478bd9Sstevel@tonic-gate 	/* If there are no trans, make sure timer is off, and we're done. */
2987c478bd9Sstevel@tonic-gate 	if (EMPTY(ctx->wtran)) {
2997c478bd9Sstevel@tonic-gate 		if (ctx->tiID.opaque != NULL)
3007c478bd9Sstevel@tonic-gate 			stop_timer(ctx);
3017c478bd9Sstevel@tonic-gate 		return;
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 	/* Pull it off the head of the write queue. */
3047c478bd9Sstevel@tonic-gate 	tran = HEAD(ctx->wtran);
3057c478bd9Sstevel@tonic-gate 	UNLINK(ctx->wtran, tran, wlink);
3067c478bd9Sstevel@tonic-gate 	/* Since there are some trans, make sure timer is successfully "on". */
3077c478bd9Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
3087c478bd9Sstevel@tonic-gate 		touch_timer(ctx);
3097c478bd9Sstevel@tonic-gate 	else
3107c478bd9Sstevel@tonic-gate 		start_timer(ctx);
3117c478bd9Sstevel@tonic-gate 	if (ctx->state == destroyed)
3127c478bd9Sstevel@tonic-gate 		return;
3137c478bd9Sstevel@tonic-gate 	/* Marshall a newline-terminated message and clock it out. */
3147c478bd9Sstevel@tonic-gate 	*iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used);
3157c478bd9Sstevel@tonic-gate 	DE_CONST("\r\n", tmp);
3167c478bd9Sstevel@tonic-gate 	*iovp++ = evConsIovec(tmp, 2);
3177c478bd9Sstevel@tonic-gate 	if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov,
3187c478bd9Sstevel@tonic-gate 		    write_done, tran, &ctx->wrID) < 0) {
3197c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evWrite: %s", me,
3207c478bd9Sstevel@tonic-gate 			       strerror(errno));
3217c478bd9Sstevel@tonic-gate 		error(ctx);
3227c478bd9Sstevel@tonic-gate 		return;
3237c478bd9Sstevel@tonic-gate 	}
3247c478bd9Sstevel@tonic-gate 	if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
3257c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
3267c478bd9Sstevel@tonic-gate 			       strerror(errno));
3277c478bd9Sstevel@tonic-gate 		error(ctx);
3287c478bd9Sstevel@tonic-gate 		return;
3297c478bd9Sstevel@tonic-gate 	}
3307c478bd9Sstevel@tonic-gate }
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate static void
destroy(struct ctl_cctx * ctx,int notify)3337c478bd9Sstevel@tonic-gate destroy(struct ctl_cctx *ctx, int notify) {
3347c478bd9Sstevel@tonic-gate 	struct ctl_tran *this, *next;
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	if (ctx->sock != -1) {
3377c478bd9Sstevel@tonic-gate 		(void) close(ctx->sock);
3387c478bd9Sstevel@tonic-gate 		ctx->sock = -1;
3397c478bd9Sstevel@tonic-gate 	}
3407c478bd9Sstevel@tonic-gate 	switch (ctx->state) {
3417c478bd9Sstevel@tonic-gate 	case connecting:
3427c478bd9Sstevel@tonic-gate 		REQUIRE(ctx->wrID.opaque == NULL);
3437c478bd9Sstevel@tonic-gate 		REQUIRE(EMPTY(ctx->tran));
3447c478bd9Sstevel@tonic-gate 		/*
3457c478bd9Sstevel@tonic-gate 		 * This test is nec'y since destroy() can be called from
3467c478bd9Sstevel@tonic-gate 		 * start_read() while the state is still "connecting".
3477c478bd9Sstevel@tonic-gate 		 */
3487c478bd9Sstevel@tonic-gate 		if (ctx->coID.opaque != NULL) {
3497c478bd9Sstevel@tonic-gate 			(void)evCancelConn(ctx->ev, ctx->coID);
3507c478bd9Sstevel@tonic-gate 			ctx->coID.opaque = NULL;
3517c478bd9Sstevel@tonic-gate 		}
3527c478bd9Sstevel@tonic-gate 		break;
3537c478bd9Sstevel@tonic-gate 	case connected:
3547c478bd9Sstevel@tonic-gate 		REQUIRE(ctx->coID.opaque == NULL);
3557c478bd9Sstevel@tonic-gate 		if (ctx->wrID.opaque != NULL) {
3567c478bd9Sstevel@tonic-gate 			(void)evCancelRW(ctx->ev, ctx->wrID);
3577c478bd9Sstevel@tonic-gate 			ctx->wrID.opaque = NULL;
3587c478bd9Sstevel@tonic-gate 		}
3597c478bd9Sstevel@tonic-gate 		if (ctx->rdID.opaque != NULL)
3607c478bd9Sstevel@tonic-gate 			stop_read(ctx);
3617c478bd9Sstevel@tonic-gate 		break;
3627c478bd9Sstevel@tonic-gate 	case destroyed:
3637c478bd9Sstevel@tonic-gate 		break;
3647c478bd9Sstevel@tonic-gate 	default:
3657c478bd9Sstevel@tonic-gate 		abort();
3667c478bd9Sstevel@tonic-gate 	}
3677c478bd9Sstevel@tonic-gate 	if (allocated_p(ctx->inbuf))
3687c478bd9Sstevel@tonic-gate 		ctl_bufput(&ctx->inbuf);
3697c478bd9Sstevel@tonic-gate 	for (this = HEAD(ctx->tran); this != NULL; this = next) {
3707c478bd9Sstevel@tonic-gate 		next = NEXT(this, link);
3717c478bd9Sstevel@tonic-gate 		if (allocated_p(this->outbuf))
3727c478bd9Sstevel@tonic-gate 			ctl_bufput(&this->outbuf);
3737c478bd9Sstevel@tonic-gate 		if (notify && this->donefunc != NULL)
3747c478bd9Sstevel@tonic-gate 			(*this->donefunc)(ctx, this->uap, NULL, 0);
3757c478bd9Sstevel@tonic-gate 		memput(this, sizeof *this);
3767c478bd9Sstevel@tonic-gate 	}
3777c478bd9Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
3787c478bd9Sstevel@tonic-gate 		stop_timer(ctx);
3797c478bd9Sstevel@tonic-gate 	new_state(ctx, destroyed);
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate static void
error(struct ctl_cctx * ctx)3837c478bd9Sstevel@tonic-gate error(struct ctl_cctx *ctx) {
3847c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->state != destroyed);
3857c478bd9Sstevel@tonic-gate 	destroy(ctx, 1);
3867c478bd9Sstevel@tonic-gate }
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate static void
new_state(struct ctl_cctx * ctx,enum state new_state)3897c478bd9Sstevel@tonic-gate new_state(struct ctl_cctx *ctx, enum state new_state) {
3907c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::new_state";
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	(*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
3937c478bd9Sstevel@tonic-gate 		       state_names[ctx->state], state_names[new_state]);
3947c478bd9Sstevel@tonic-gate 	ctx->state = new_state;
3957c478bd9Sstevel@tonic-gate }
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate static void
conn_done(evContext ev,void * uap,int fd,const void * la,int lalen,const void * ra,int ralen)3987c478bd9Sstevel@tonic-gate conn_done(evContext ev, void *uap, int fd,
3997c478bd9Sstevel@tonic-gate 	  const void *la, int lalen,
4007c478bd9Sstevel@tonic-gate 	  const void *ra, int ralen)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::conn_done";
4037c478bd9Sstevel@tonic-gate 	struct ctl_cctx *ctx = uap;
4047c478bd9Sstevel@tonic-gate 	struct ctl_tran *tran;
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	UNUSED(ev);
4077c478bd9Sstevel@tonic-gate 	UNUSED(la);
4087c478bd9Sstevel@tonic-gate 	UNUSED(lalen);
4097c478bd9Sstevel@tonic-gate 	UNUSED(ra);
4107c478bd9Sstevel@tonic-gate 	UNUSED(ralen);
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	ctx->coID.opaque = NULL;
4137c478bd9Sstevel@tonic-gate 	if (fd < 0) {
4147c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
4157c478bd9Sstevel@tonic-gate 			       strerror(errno));
4167c478bd9Sstevel@tonic-gate 		error(ctx);
4177c478bd9Sstevel@tonic-gate 		return;
4187c478bd9Sstevel@tonic-gate 	}
4197c478bd9Sstevel@tonic-gate 	new_state(ctx, connected);
4207c478bd9Sstevel@tonic-gate 	tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
4217c478bd9Sstevel@tonic-gate 	if (tran == NULL) {
4227c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
4237c478bd9Sstevel@tonic-gate 			       strerror(errno));
4247c478bd9Sstevel@tonic-gate 		error(ctx);
4257c478bd9Sstevel@tonic-gate 		return;
4267c478bd9Sstevel@tonic-gate 	}
4277c478bd9Sstevel@tonic-gate 	start_read(ctx);
4287c478bd9Sstevel@tonic-gate 	if (ctx->state == destroyed) {
4297c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: start_read failed: %s",
4307c478bd9Sstevel@tonic-gate 			       me, strerror(errno));
4317c478bd9Sstevel@tonic-gate 		error(ctx);
4327c478bd9Sstevel@tonic-gate 		return;
4337c478bd9Sstevel@tonic-gate 	}
4347c478bd9Sstevel@tonic-gate }
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate static void
write_done(evContext lev,void * uap,int fd,int bytes)4377c478bd9Sstevel@tonic-gate write_done(evContext lev, void *uap, int fd, int bytes) {
4387c478bd9Sstevel@tonic-gate 	struct ctl_tran *tran = (struct ctl_tran *)uap;
4397c478bd9Sstevel@tonic-gate 	struct ctl_cctx *ctx = tran->ctx;
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	UNUSED(lev);
4427c478bd9Sstevel@tonic-gate 	UNUSED(fd);
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	ctx->wrID.opaque = NULL;
4457c478bd9Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
4467c478bd9Sstevel@tonic-gate 		touch_timer(ctx);
4477c478bd9Sstevel@tonic-gate 	ctl_bufput(&tran->outbuf);
4487c478bd9Sstevel@tonic-gate 	start_write(ctx);
4497c478bd9Sstevel@tonic-gate 	if (bytes < 0)
4507c478bd9Sstevel@tonic-gate 		destroy(ctx, 1);
4517c478bd9Sstevel@tonic-gate 	else
4527c478bd9Sstevel@tonic-gate 		start_read(ctx);
4537c478bd9Sstevel@tonic-gate }
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate static void
start_read(struct ctl_cctx * ctx)4567c478bd9Sstevel@tonic-gate start_read(struct ctl_cctx *ctx) {
4577c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::start_read";
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->state == connecting || ctx->state == connected);
4607c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->rdID.opaque == NULL);
4617c478bd9Sstevel@tonic-gate 	if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx,
4627c478bd9Sstevel@tonic-gate 		       &ctx->rdID) < 0)
4637c478bd9Sstevel@tonic-gate 	{
4647c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
4657c478bd9Sstevel@tonic-gate 			       ctx->sock, strerror(errno));
4667c478bd9Sstevel@tonic-gate 		error(ctx);
4677c478bd9Sstevel@tonic-gate 		return;
4687c478bd9Sstevel@tonic-gate 	}
4697c478bd9Sstevel@tonic-gate }
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate static void
stop_read(struct ctl_cctx * ctx)4727c478bd9Sstevel@tonic-gate stop_read(struct ctl_cctx *ctx) {
4737c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->coID.opaque == NULL);
4747c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->rdID.opaque != NULL);
4757c478bd9Sstevel@tonic-gate 	(void)evDeselectFD(ctx->ev, ctx->rdID);
4767c478bd9Sstevel@tonic-gate 	ctx->rdID.opaque = NULL;
4777c478bd9Sstevel@tonic-gate }
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate static void
readable(evContext ev,void * uap,int fd,int evmask)4807c478bd9Sstevel@tonic-gate readable(evContext ev, void *uap, int fd, int evmask) {
4817c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::readable";
4827c478bd9Sstevel@tonic-gate 	struct ctl_cctx *ctx = uap;
4837c478bd9Sstevel@tonic-gate 	struct ctl_tran *tran;
4847c478bd9Sstevel@tonic-gate 	ssize_t n;
4857c478bd9Sstevel@tonic-gate 	char *eos;
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	UNUSED(ev);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	REQUIRE(ctx != NULL);
4907c478bd9Sstevel@tonic-gate 	REQUIRE(fd >= 0);
4917c478bd9Sstevel@tonic-gate 	REQUIRE(evmask == EV_READ);
4927c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->state == connected);
4937c478bd9Sstevel@tonic-gate 	REQUIRE(!EMPTY(ctx->tran));
4947c478bd9Sstevel@tonic-gate 	tran = HEAD(ctx->tran);
4957c478bd9Sstevel@tonic-gate 	if (!allocated_p(ctx->inbuf) &&
4967c478bd9Sstevel@tonic-gate 	    ctl_bufget(&ctx->inbuf, ctx->logger) < 0) {
4977c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: can't get an input buffer", me);
4987c478bd9Sstevel@tonic-gate 		error(ctx);
4997c478bd9Sstevel@tonic-gate 		return;
5007c478bd9Sstevel@tonic-gate 	}
5017c478bd9Sstevel@tonic-gate 	n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
5027c478bd9Sstevel@tonic-gate 		 MAX_LINELEN - ctx->inbuf.used);
5037c478bd9Sstevel@tonic-gate 	if (n <= 0) {
5047c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_warning, "%s: read: %s", me,
5057c478bd9Sstevel@tonic-gate 			       (n == 0) ? "Unexpected EOF" : strerror(errno));
5067c478bd9Sstevel@tonic-gate 		error(ctx);
5077c478bd9Sstevel@tonic-gate 		return;
5087c478bd9Sstevel@tonic-gate 	}
5097c478bd9Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
5107c478bd9Sstevel@tonic-gate 		touch_timer(ctx);
5117c478bd9Sstevel@tonic-gate 	ctx->inbuf.used += n;
5127c478bd9Sstevel@tonic-gate 	(*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
5137c478bd9Sstevel@tonic-gate 		       n, ctx->inbuf.used);
5147c478bd9Sstevel@tonic-gate  again:
5157c478bd9Sstevel@tonic-gate 	eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
5167c478bd9Sstevel@tonic-gate 	if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
5177c478bd9Sstevel@tonic-gate 		int done = 0;
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 		eos[-1] = '\0';
5207c478bd9Sstevel@tonic-gate 		if (!arpacode_p(ctx->inbuf.text)) {
5217c478bd9Sstevel@tonic-gate 			/* XXX Doesn't FTP do this sometimes? Is it legal? */
5227c478bd9Sstevel@tonic-gate 			(*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me,
5237c478bd9Sstevel@tonic-gate 				       ctx->inbuf.text);
5247c478bd9Sstevel@tonic-gate 			error(ctx);
5257c478bd9Sstevel@tonic-gate 			return;
5267c478bd9Sstevel@tonic-gate 		}
5277c478bd9Sstevel@tonic-gate 		if (arpadone_p(ctx->inbuf.text))
5287c478bd9Sstevel@tonic-gate 			done = 1;
5297c478bd9Sstevel@tonic-gate 		else if (arpacont_p(ctx->inbuf.text))
5307c478bd9Sstevel@tonic-gate 			done = 0;
5317c478bd9Sstevel@tonic-gate 		else {
5327c478bd9Sstevel@tonic-gate 			/* XXX Doesn't FTP do this sometimes? Is it legal? */
5337c478bd9Sstevel@tonic-gate 			(*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
5347c478bd9Sstevel@tonic-gate 				       ctx->inbuf.text);
5357c478bd9Sstevel@tonic-gate 			error(ctx);
5367c478bd9Sstevel@tonic-gate 			return;
5377c478bd9Sstevel@tonic-gate 		}
5387c478bd9Sstevel@tonic-gate 		(*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text,
5397c478bd9Sstevel@tonic-gate 				  (done ? 0 : CTL_MORE));
5407c478bd9Sstevel@tonic-gate 		ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1);
541*9525b14bSRao Shoaib 		if (ctx->inbuf.used == 0U)
5427c478bd9Sstevel@tonic-gate 			ctl_bufput(&ctx->inbuf);
5437c478bd9Sstevel@tonic-gate 		else
5447c478bd9Sstevel@tonic-gate 			memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
5457c478bd9Sstevel@tonic-gate 		if (done) {
5467c478bd9Sstevel@tonic-gate 			UNLINK(ctx->tran, tran, link);
5477c478bd9Sstevel@tonic-gate 			memput(tran, sizeof *tran);
5487c478bd9Sstevel@tonic-gate 			stop_read(ctx);
5497c478bd9Sstevel@tonic-gate 			start_write(ctx);
5507c478bd9Sstevel@tonic-gate 			return;
5517c478bd9Sstevel@tonic-gate 		}
5527c478bd9Sstevel@tonic-gate 		if (allocated_p(ctx->inbuf))
5537c478bd9Sstevel@tonic-gate 			goto again;
5547c478bd9Sstevel@tonic-gate 		return;
5557c478bd9Sstevel@tonic-gate 	}
556*9525b14bSRao Shoaib 	if (ctx->inbuf.used == (size_t)MAX_LINELEN) {
5577c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
5587c478bd9Sstevel@tonic-gate 			       ctx->inbuf.text);
5597c478bd9Sstevel@tonic-gate 		error(ctx);
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate /* Timer related stuff. */
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate static void
start_timer(struct ctl_cctx * ctx)5667c478bd9Sstevel@tonic-gate start_timer(struct ctl_cctx *ctx) {
5677c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::start_timer";
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->tiID.opaque == NULL);
5707c478bd9Sstevel@tonic-gate 	if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){
5717c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me,
5727c478bd9Sstevel@tonic-gate 			       strerror(errno));
5737c478bd9Sstevel@tonic-gate 		error(ctx);
5747c478bd9Sstevel@tonic-gate 		return;
5757c478bd9Sstevel@tonic-gate 	}
5767c478bd9Sstevel@tonic-gate }
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate static void
stop_timer(struct ctl_cctx * ctx)5797c478bd9Sstevel@tonic-gate stop_timer(struct ctl_cctx *ctx) {
5807c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::stop_timer";
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->tiID.opaque != NULL);
5837c478bd9Sstevel@tonic-gate 	if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
5847c478bd9Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
5857c478bd9Sstevel@tonic-gate 			       strerror(errno));
5867c478bd9Sstevel@tonic-gate 		error(ctx);
5877c478bd9Sstevel@tonic-gate 		return;
5887c478bd9Sstevel@tonic-gate 	}
5897c478bd9Sstevel@tonic-gate 	ctx->tiID.opaque = NULL;
5907c478bd9Sstevel@tonic-gate }
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate static void
touch_timer(struct ctl_cctx * ctx)5937c478bd9Sstevel@tonic-gate touch_timer(struct ctl_cctx *ctx) {
5947c478bd9Sstevel@tonic-gate 	REQUIRE(ctx->tiID.opaque != NULL);
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	evTouchIdleTimer(ctx->ev, ctx->tiID);
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate static void
timer(evContext ev,void * uap,struct timespec due,struct timespec itv)6007c478bd9Sstevel@tonic-gate timer(evContext ev, void *uap, struct timespec due, struct timespec itv) {
6017c478bd9Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::timer";
6027c478bd9Sstevel@tonic-gate 	struct ctl_cctx *ctx = uap;
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	UNUSED(ev);
6057c478bd9Sstevel@tonic-gate 	UNUSED(due);
6067c478bd9Sstevel@tonic-gate 	UNUSED(itv);
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	ctx->tiID.opaque = NULL;
6097c478bd9Sstevel@tonic-gate 	(*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me,
6107c478bd9Sstevel@tonic-gate 		       ctx->timeout.tv_sec, state_names[ctx->state]);
6117c478bd9Sstevel@tonic-gate 	error(ctx);
6127c478bd9Sstevel@tonic-gate }
613*9525b14bSRao Shoaib 
614*9525b14bSRao Shoaib /*! \file */
615