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 *
memchr(const void * b,int c,size_t len)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 *
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)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
ctl_endclient(struct ctl_cctx * ctx)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
ctl_command(struct ctl_cctx * ctx,const char * cmd,size_t len,ctl_clntdone donefunc,void * uap)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 *
new_tran(struct ctl_cctx * ctx,ctl_clntdone donefunc,void * uap,int w)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
start_write(struct ctl_cctx * ctx)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
destroy(struct ctl_cctx * ctx,int notify)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
error(struct ctl_cctx * ctx)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
new_state(struct ctl_cctx * ctx,enum state new_state)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
conn_done(evContext ev,void * uap,int fd,const void * la,int lalen,const void * ra,int ralen)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
write_done(evContext lev,void * uap,int fd,int bytes)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
start_read(struct ctl_cctx * ctx)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
stop_read(struct ctl_cctx * ctx)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
readable(evContext ev,void * uap,int fd,int evmask)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
start_timer(struct ctl_cctx * ctx)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
stop_timer(struct ctl_cctx * ctx)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
touch_timer(struct ctl_cctx * ctx)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
timer(evContext ev,void * uap,struct timespec due,struct timespec itv)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