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