1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3*0Sstevel@tonic-gate * Use is subject to license terms. 4*0Sstevel@tonic-gate */ 5*0Sstevel@tonic-gate 6*0Sstevel@tonic-gate #if !defined(lint) && !defined(SABER) 7*0Sstevel@tonic-gate static const char rcsid[] = "$Id: ctl_clnt.c,v 8.18 2002/07/08 05:10:23 marka Exp $"; 8*0Sstevel@tonic-gate #endif /* not lint */ 9*0Sstevel@tonic-gate 10*0Sstevel@tonic-gate /* 11*0Sstevel@tonic-gate * Copyright (c) 1998,1999 by Internet Software Consortium. 12*0Sstevel@tonic-gate * 13*0Sstevel@tonic-gate * Permission to use, copy, modify, and distribute this software for any 14*0Sstevel@tonic-gate * purpose with or without fee is hereby granted, provided that the above 15*0Sstevel@tonic-gate * copyright notice and this permission notice appear in all copies. 16*0Sstevel@tonic-gate * 17*0Sstevel@tonic-gate * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 18*0Sstevel@tonic-gate * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 19*0Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 20*0Sstevel@tonic-gate * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 21*0Sstevel@tonic-gate * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 22*0Sstevel@tonic-gate * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 23*0Sstevel@tonic-gate * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 24*0Sstevel@tonic-gate * SOFTWARE. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* Extern. */ 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #include "port_before.h" 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <sys/param.h> 34*0Sstevel@tonic-gate #include <sys/file.h> 35*0Sstevel@tonic-gate #include <sys/socket.h> 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate #include <netinet/in.h> 38*0Sstevel@tonic-gate #include <arpa/nameser.h> 39*0Sstevel@tonic-gate #include <arpa/inet.h> 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate #include <ctype.h> 42*0Sstevel@tonic-gate #include <errno.h> 43*0Sstevel@tonic-gate #include <stdio.h> 44*0Sstevel@tonic-gate #include <stdlib.h> 45*0Sstevel@tonic-gate #include <string.h> 46*0Sstevel@tonic-gate #include <time.h> 47*0Sstevel@tonic-gate #include <unistd.h> 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate #include <isc/assertions.h> 50*0Sstevel@tonic-gate #include <isc/ctl.h> 51*0Sstevel@tonic-gate #include <isc/eventlib.h> 52*0Sstevel@tonic-gate #include <isc/list.h> 53*0Sstevel@tonic-gate #include <isc/memcluster.h> 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gate #include "ctl_p.h" 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate #include "port_after.h" 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate /* Constants. */ 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate /* Macros. */ 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate #define donefunc_p(ctx) ((ctx).donefunc != NULL) 65*0Sstevel@tonic-gate #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \ 66*0Sstevel@tonic-gate isdigit((unsigned char)(line[1])) && \ 67*0Sstevel@tonic-gate isdigit((unsigned char)(line[2]))) 68*0Sstevel@tonic-gate #define arpacont_p(line) (line[3] == '-') 69*0Sstevel@tonic-gate #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \ 70*0Sstevel@tonic-gate line[3] == '\r' || line[3] == '\0') 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate /* Types. */ 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate enum state { 75*0Sstevel@tonic-gate initializing = 0, connecting, connected, destroyed 76*0Sstevel@tonic-gate }; 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate struct ctl_tran { 79*0Sstevel@tonic-gate LINK(struct ctl_tran) link; 80*0Sstevel@tonic-gate LINK(struct ctl_tran) wlink; 81*0Sstevel@tonic-gate struct ctl_cctx * ctx; 82*0Sstevel@tonic-gate struct ctl_buf outbuf; 83*0Sstevel@tonic-gate ctl_clntdone donefunc; 84*0Sstevel@tonic-gate void * uap; 85*0Sstevel@tonic-gate }; 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate struct ctl_cctx { 88*0Sstevel@tonic-gate enum state state; 89*0Sstevel@tonic-gate evContext ev; 90*0Sstevel@tonic-gate int sock; 91*0Sstevel@tonic-gate ctl_logfunc logger; 92*0Sstevel@tonic-gate ctl_clntdone donefunc; 93*0Sstevel@tonic-gate void * uap; 94*0Sstevel@tonic-gate evConnID coID; 95*0Sstevel@tonic-gate evTimerID tiID; 96*0Sstevel@tonic-gate evFileID rdID; 97*0Sstevel@tonic-gate evStreamID wrID; 98*0Sstevel@tonic-gate struct ctl_buf inbuf; 99*0Sstevel@tonic-gate struct timespec timeout; 100*0Sstevel@tonic-gate LIST(struct ctl_tran) tran; 101*0Sstevel@tonic-gate LIST(struct ctl_tran) wtran; 102*0Sstevel@tonic-gate }; 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate /* Forward. */ 105*0Sstevel@tonic-gate 106*0Sstevel@tonic-gate static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int); 107*0Sstevel@tonic-gate static void start_write(struct ctl_cctx *); 108*0Sstevel@tonic-gate static void destroy(struct ctl_cctx *, int); 109*0Sstevel@tonic-gate static void error(struct ctl_cctx *); 110*0Sstevel@tonic-gate static void new_state(struct ctl_cctx *, enum state); 111*0Sstevel@tonic-gate static void conn_done(evContext, void *, int, 112*0Sstevel@tonic-gate const void *, int, 113*0Sstevel@tonic-gate const void *, int); 114*0Sstevel@tonic-gate static void write_done(evContext, void *, int, int); 115*0Sstevel@tonic-gate static void start_read(struct ctl_cctx *); 116*0Sstevel@tonic-gate static void stop_read(struct ctl_cctx *); 117*0Sstevel@tonic-gate static void readable(evContext, void *, int, int); 118*0Sstevel@tonic-gate static void start_timer(struct ctl_cctx *); 119*0Sstevel@tonic-gate static void stop_timer(struct ctl_cctx *); 120*0Sstevel@tonic-gate static void touch_timer(struct ctl_cctx *); 121*0Sstevel@tonic-gate static void timer(evContext, void *, 122*0Sstevel@tonic-gate struct timespec, struct timespec); 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gate /* Private data. */ 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate static const char * const state_names[] = { 127*0Sstevel@tonic-gate "initializing", "connecting", "connected", "destroyed" 128*0Sstevel@tonic-gate }; 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate /* Public. */ 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate /* 133*0Sstevel@tonic-gate * void 134*0Sstevel@tonic-gate * ctl_client() 135*0Sstevel@tonic-gate * create, condition, and connect to a listener on the control port. 136*0Sstevel@tonic-gate */ 137*0Sstevel@tonic-gate struct ctl_cctx * 138*0Sstevel@tonic-gate ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len, 139*0Sstevel@tonic-gate const struct sockaddr *sap, size_t sap_len, 140*0Sstevel@tonic-gate ctl_clntdone donefunc, void *uap, 141*0Sstevel@tonic-gate u_int timeout, ctl_logfunc logger) 142*0Sstevel@tonic-gate { 143*0Sstevel@tonic-gate static const char me[] = "ctl_client"; 144*0Sstevel@tonic-gate static const int on = 1; 145*0Sstevel@tonic-gate struct ctl_cctx *ctx; 146*0Sstevel@tonic-gate struct sockaddr *captmp; 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate if (logger == NULL) 149*0Sstevel@tonic-gate logger = ctl_logger; 150*0Sstevel@tonic-gate ctx = memget(sizeof *ctx); 151*0Sstevel@tonic-gate if (ctx == NULL) { 152*0Sstevel@tonic-gate (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); 153*0Sstevel@tonic-gate goto fatal; 154*0Sstevel@tonic-gate } 155*0Sstevel@tonic-gate ctx->state = initializing; 156*0Sstevel@tonic-gate ctx->ev = lev; 157*0Sstevel@tonic-gate ctx->logger = logger; 158*0Sstevel@tonic-gate ctx->timeout = evConsTime(timeout, 0); 159*0Sstevel@tonic-gate ctx->donefunc = donefunc; 160*0Sstevel@tonic-gate ctx->uap = uap; 161*0Sstevel@tonic-gate ctx->coID.opaque = NULL; 162*0Sstevel@tonic-gate ctx->tiID.opaque = NULL; 163*0Sstevel@tonic-gate ctx->rdID.opaque = NULL; 164*0Sstevel@tonic-gate ctx->wrID.opaque = NULL; 165*0Sstevel@tonic-gate buffer_init(ctx->inbuf); 166*0Sstevel@tonic-gate INIT_LIST(ctx->tran); 167*0Sstevel@tonic-gate INIT_LIST(ctx->wtran); 168*0Sstevel@tonic-gate ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); 169*0Sstevel@tonic-gate if (ctx->sock > evHighestFD(ctx->ev)) { 170*0Sstevel@tonic-gate ctx->sock = -1; 171*0Sstevel@tonic-gate errno = ENOTSOCK; 172*0Sstevel@tonic-gate } 173*0Sstevel@tonic-gate if (ctx->sock < 0) { 174*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: socket: %s", 175*0Sstevel@tonic-gate me, strerror(errno)); 176*0Sstevel@tonic-gate goto fatal; 177*0Sstevel@tonic-gate } 178*0Sstevel@tonic-gate if (cap != NULL) { 179*0Sstevel@tonic-gate if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, 180*0Sstevel@tonic-gate (const char *)&on, sizeof on) != 0) { 181*0Sstevel@tonic-gate (*ctx->logger)(ctl_warning, 182*0Sstevel@tonic-gate "%s: setsockopt(REUSEADDR): %s", 183*0Sstevel@tonic-gate me, strerror(errno)); 184*0Sstevel@tonic-gate } 185*0Sstevel@tonic-gate DE_CONST(cap, captmp); 186*0Sstevel@tonic-gate if (bind(ctx->sock, captmp, cap_len) < 0) { 187*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: bind: %s", me, 188*0Sstevel@tonic-gate strerror(errno)); 189*0Sstevel@tonic-gate goto fatal; 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate } 192*0Sstevel@tonic-gate if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len, 193*0Sstevel@tonic-gate conn_done, ctx, &ctx->coID) < 0) { 194*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s", 195*0Sstevel@tonic-gate me, ctx->sock, strerror(errno)); 196*0Sstevel@tonic-gate fatal: 197*0Sstevel@tonic-gate if (ctx != NULL) { 198*0Sstevel@tonic-gate if (ctx->sock >= 0) 199*0Sstevel@tonic-gate close(ctx->sock); 200*0Sstevel@tonic-gate memput(ctx, sizeof *ctx); 201*0Sstevel@tonic-gate } 202*0Sstevel@tonic-gate return (NULL); 203*0Sstevel@tonic-gate } 204*0Sstevel@tonic-gate new_state(ctx, connecting); 205*0Sstevel@tonic-gate return (ctx); 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate /* 209*0Sstevel@tonic-gate * void 210*0Sstevel@tonic-gate * ctl_endclient(ctx) 211*0Sstevel@tonic-gate * close a client and release all of its resources. 212*0Sstevel@tonic-gate */ 213*0Sstevel@tonic-gate void 214*0Sstevel@tonic-gate ctl_endclient(struct ctl_cctx *ctx) { 215*0Sstevel@tonic-gate if (ctx->state != destroyed) 216*0Sstevel@tonic-gate destroy(ctx, 0); 217*0Sstevel@tonic-gate memput(ctx, sizeof *ctx); 218*0Sstevel@tonic-gate } 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate /* 221*0Sstevel@tonic-gate * int 222*0Sstevel@tonic-gate * ctl_command(ctx, cmd, len, donefunc, uap) 223*0Sstevel@tonic-gate * Queue a transaction, which will begin with sending cmd 224*0Sstevel@tonic-gate * and complete by calling donefunc with the answer. 225*0Sstevel@tonic-gate */ 226*0Sstevel@tonic-gate int 227*0Sstevel@tonic-gate ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len, 228*0Sstevel@tonic-gate ctl_clntdone donefunc, void *uap) 229*0Sstevel@tonic-gate { 230*0Sstevel@tonic-gate struct ctl_tran *tran; 231*0Sstevel@tonic-gate char *pc; 232*0Sstevel@tonic-gate unsigned int n; 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate switch (ctx->state) { 235*0Sstevel@tonic-gate case destroyed: 236*0Sstevel@tonic-gate errno = ENOTCONN; 237*0Sstevel@tonic-gate return (-1); 238*0Sstevel@tonic-gate case connecting: 239*0Sstevel@tonic-gate case connected: 240*0Sstevel@tonic-gate break; 241*0Sstevel@tonic-gate default: 242*0Sstevel@tonic-gate abort(); 243*0Sstevel@tonic-gate } 244*0Sstevel@tonic-gate if (len >= MAX_LINELEN) { 245*0Sstevel@tonic-gate errno = EMSGSIZE; 246*0Sstevel@tonic-gate return (-1); 247*0Sstevel@tonic-gate } 248*0Sstevel@tonic-gate tran = new_tran(ctx, donefunc, uap, 1); 249*0Sstevel@tonic-gate if (tran == NULL) 250*0Sstevel@tonic-gate return (-1); 251*0Sstevel@tonic-gate if (ctl_bufget(&tran->outbuf, ctx->logger) < 0) 252*0Sstevel@tonic-gate return (-1); 253*0Sstevel@tonic-gate memcpy(tran->outbuf.text, cmd, len); 254*0Sstevel@tonic-gate tran->outbuf.used = len; 255*0Sstevel@tonic-gate for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++) 256*0Sstevel@tonic-gate if (!isascii((unsigned char)*pc) || 257*0Sstevel@tonic-gate !isprint((unsigned char)*pc)) 258*0Sstevel@tonic-gate *pc = '\040'; 259*0Sstevel@tonic-gate start_write(ctx); 260*0Sstevel@tonic-gate return (0); 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate /* Private. */ 264*0Sstevel@tonic-gate 265*0Sstevel@tonic-gate static struct ctl_tran * 266*0Sstevel@tonic-gate new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) { 267*0Sstevel@tonic-gate struct ctl_tran *new = memget(sizeof *new); 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate if (new == NULL) 270*0Sstevel@tonic-gate return (NULL); 271*0Sstevel@tonic-gate new->ctx = ctx; 272*0Sstevel@tonic-gate buffer_init(new->outbuf); 273*0Sstevel@tonic-gate new->donefunc = donefunc; 274*0Sstevel@tonic-gate new->uap = uap; 275*0Sstevel@tonic-gate INIT_LINK(new, link); 276*0Sstevel@tonic-gate INIT_LINK(new, wlink); 277*0Sstevel@tonic-gate APPEND(ctx->tran, new, link); 278*0Sstevel@tonic-gate if (w) 279*0Sstevel@tonic-gate APPEND(ctx->wtran, new, wlink); 280*0Sstevel@tonic-gate return (new); 281*0Sstevel@tonic-gate } 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate static void 284*0Sstevel@tonic-gate start_write(struct ctl_cctx *ctx) { 285*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::start_write"; 286*0Sstevel@tonic-gate struct ctl_tran *tran; 287*0Sstevel@tonic-gate struct iovec iov[2], *iovp = iov; 288*0Sstevel@tonic-gate char * tmp; 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate REQUIRE(ctx->state == connecting || ctx->state == connected); 291*0Sstevel@tonic-gate /* If there is a write in progress, don't try to write more yet. */ 292*0Sstevel@tonic-gate if (ctx->wrID.opaque != NULL) 293*0Sstevel@tonic-gate return; 294*0Sstevel@tonic-gate /* If there are no trans, make sure timer is off, and we're done. */ 295*0Sstevel@tonic-gate if (EMPTY(ctx->wtran)) { 296*0Sstevel@tonic-gate if (ctx->tiID.opaque != NULL) 297*0Sstevel@tonic-gate stop_timer(ctx); 298*0Sstevel@tonic-gate return; 299*0Sstevel@tonic-gate } 300*0Sstevel@tonic-gate /* Pull it off the head of the write queue. */ 301*0Sstevel@tonic-gate tran = HEAD(ctx->wtran); 302*0Sstevel@tonic-gate UNLINK(ctx->wtran, tran, wlink); 303*0Sstevel@tonic-gate /* Since there are some trans, make sure timer is successfully "on". */ 304*0Sstevel@tonic-gate if (ctx->tiID.opaque != NULL) 305*0Sstevel@tonic-gate touch_timer(ctx); 306*0Sstevel@tonic-gate else 307*0Sstevel@tonic-gate start_timer(ctx); 308*0Sstevel@tonic-gate if (ctx->state == destroyed) 309*0Sstevel@tonic-gate return; 310*0Sstevel@tonic-gate /* Marshall a newline-terminated message and clock it out. */ 311*0Sstevel@tonic-gate *iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used); 312*0Sstevel@tonic-gate DE_CONST("\r\n", tmp); 313*0Sstevel@tonic-gate *iovp++ = evConsIovec(tmp, 2); 314*0Sstevel@tonic-gate if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov, 315*0Sstevel@tonic-gate write_done, tran, &ctx->wrID) < 0) { 316*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evWrite: %s", me, 317*0Sstevel@tonic-gate strerror(errno)); 318*0Sstevel@tonic-gate error(ctx); 319*0Sstevel@tonic-gate return; 320*0Sstevel@tonic-gate } 321*0Sstevel@tonic-gate if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) { 322*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me, 323*0Sstevel@tonic-gate strerror(errno)); 324*0Sstevel@tonic-gate error(ctx); 325*0Sstevel@tonic-gate return; 326*0Sstevel@tonic-gate } 327*0Sstevel@tonic-gate } 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate static void 330*0Sstevel@tonic-gate destroy(struct ctl_cctx *ctx, int notify) { 331*0Sstevel@tonic-gate struct ctl_tran *this, *next; 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate if (ctx->sock != -1) { 334*0Sstevel@tonic-gate (void) close(ctx->sock); 335*0Sstevel@tonic-gate ctx->sock = -1; 336*0Sstevel@tonic-gate } 337*0Sstevel@tonic-gate switch (ctx->state) { 338*0Sstevel@tonic-gate case connecting: 339*0Sstevel@tonic-gate REQUIRE(ctx->wrID.opaque == NULL); 340*0Sstevel@tonic-gate REQUIRE(EMPTY(ctx->tran)); 341*0Sstevel@tonic-gate /* 342*0Sstevel@tonic-gate * This test is nec'y since destroy() can be called from 343*0Sstevel@tonic-gate * start_read() while the state is still "connecting". 344*0Sstevel@tonic-gate */ 345*0Sstevel@tonic-gate if (ctx->coID.opaque != NULL) { 346*0Sstevel@tonic-gate (void)evCancelConn(ctx->ev, ctx->coID); 347*0Sstevel@tonic-gate ctx->coID.opaque = NULL; 348*0Sstevel@tonic-gate } 349*0Sstevel@tonic-gate break; 350*0Sstevel@tonic-gate case connected: 351*0Sstevel@tonic-gate REQUIRE(ctx->coID.opaque == NULL); 352*0Sstevel@tonic-gate if (ctx->wrID.opaque != NULL) { 353*0Sstevel@tonic-gate (void)evCancelRW(ctx->ev, ctx->wrID); 354*0Sstevel@tonic-gate ctx->wrID.opaque = NULL; 355*0Sstevel@tonic-gate } 356*0Sstevel@tonic-gate if (ctx->rdID.opaque != NULL) 357*0Sstevel@tonic-gate stop_read(ctx); 358*0Sstevel@tonic-gate break; 359*0Sstevel@tonic-gate case destroyed: 360*0Sstevel@tonic-gate break; 361*0Sstevel@tonic-gate default: 362*0Sstevel@tonic-gate abort(); 363*0Sstevel@tonic-gate } 364*0Sstevel@tonic-gate if (allocated_p(ctx->inbuf)) 365*0Sstevel@tonic-gate ctl_bufput(&ctx->inbuf); 366*0Sstevel@tonic-gate for (this = HEAD(ctx->tran); this != NULL; this = next) { 367*0Sstevel@tonic-gate next = NEXT(this, link); 368*0Sstevel@tonic-gate if (allocated_p(this->outbuf)) 369*0Sstevel@tonic-gate ctl_bufput(&this->outbuf); 370*0Sstevel@tonic-gate if (notify && this->donefunc != NULL) 371*0Sstevel@tonic-gate (*this->donefunc)(ctx, this->uap, NULL, 0); 372*0Sstevel@tonic-gate memput(this, sizeof *this); 373*0Sstevel@tonic-gate } 374*0Sstevel@tonic-gate if (ctx->tiID.opaque != NULL) 375*0Sstevel@tonic-gate stop_timer(ctx); 376*0Sstevel@tonic-gate new_state(ctx, destroyed); 377*0Sstevel@tonic-gate } 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate static void 380*0Sstevel@tonic-gate error(struct ctl_cctx *ctx) { 381*0Sstevel@tonic-gate REQUIRE(ctx->state != destroyed); 382*0Sstevel@tonic-gate destroy(ctx, 1); 383*0Sstevel@tonic-gate } 384*0Sstevel@tonic-gate 385*0Sstevel@tonic-gate static void 386*0Sstevel@tonic-gate new_state(struct ctl_cctx *ctx, enum state new_state) { 387*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::new_state"; 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: %s -> %s", me, 390*0Sstevel@tonic-gate state_names[ctx->state], state_names[new_state]); 391*0Sstevel@tonic-gate ctx->state = new_state; 392*0Sstevel@tonic-gate } 393*0Sstevel@tonic-gate 394*0Sstevel@tonic-gate static void 395*0Sstevel@tonic-gate conn_done(evContext ev, void *uap, int fd, 396*0Sstevel@tonic-gate const void *la, int lalen, 397*0Sstevel@tonic-gate const void *ra, int ralen) 398*0Sstevel@tonic-gate { 399*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::conn_done"; 400*0Sstevel@tonic-gate struct ctl_cctx *ctx = uap; 401*0Sstevel@tonic-gate struct ctl_tran *tran; 402*0Sstevel@tonic-gate 403*0Sstevel@tonic-gate UNUSED(ev); 404*0Sstevel@tonic-gate UNUSED(la); 405*0Sstevel@tonic-gate UNUSED(lalen); 406*0Sstevel@tonic-gate UNUSED(ra); 407*0Sstevel@tonic-gate UNUSED(ralen); 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate ctx->coID.opaque = NULL; 410*0Sstevel@tonic-gate if (fd < 0) { 411*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evConnect: %s", me, 412*0Sstevel@tonic-gate strerror(errno)); 413*0Sstevel@tonic-gate error(ctx); 414*0Sstevel@tonic-gate return; 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate new_state(ctx, connected); 417*0Sstevel@tonic-gate tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0); 418*0Sstevel@tonic-gate if (tran == NULL) { 419*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me, 420*0Sstevel@tonic-gate strerror(errno)); 421*0Sstevel@tonic-gate error(ctx); 422*0Sstevel@tonic-gate return; 423*0Sstevel@tonic-gate } 424*0Sstevel@tonic-gate start_read(ctx); 425*0Sstevel@tonic-gate if (ctx->state == destroyed) { 426*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: start_read failed: %s", 427*0Sstevel@tonic-gate me, strerror(errno)); 428*0Sstevel@tonic-gate error(ctx); 429*0Sstevel@tonic-gate return; 430*0Sstevel@tonic-gate } 431*0Sstevel@tonic-gate } 432*0Sstevel@tonic-gate 433*0Sstevel@tonic-gate static void 434*0Sstevel@tonic-gate write_done(evContext lev, void *uap, int fd, int bytes) { 435*0Sstevel@tonic-gate struct ctl_tran *tran = (struct ctl_tran *)uap; 436*0Sstevel@tonic-gate struct ctl_cctx *ctx = tran->ctx; 437*0Sstevel@tonic-gate 438*0Sstevel@tonic-gate UNUSED(lev); 439*0Sstevel@tonic-gate UNUSED(fd); 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate ctx->wrID.opaque = NULL; 442*0Sstevel@tonic-gate if (ctx->tiID.opaque != NULL) 443*0Sstevel@tonic-gate touch_timer(ctx); 444*0Sstevel@tonic-gate ctl_bufput(&tran->outbuf); 445*0Sstevel@tonic-gate start_write(ctx); 446*0Sstevel@tonic-gate if (bytes < 0) 447*0Sstevel@tonic-gate destroy(ctx, 1); 448*0Sstevel@tonic-gate else 449*0Sstevel@tonic-gate start_read(ctx); 450*0Sstevel@tonic-gate } 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate static void 453*0Sstevel@tonic-gate start_read(struct ctl_cctx *ctx) { 454*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::start_read"; 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate REQUIRE(ctx->state == connecting || ctx->state == connected); 457*0Sstevel@tonic-gate REQUIRE(ctx->rdID.opaque == NULL); 458*0Sstevel@tonic-gate if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx, 459*0Sstevel@tonic-gate &ctx->rdID) < 0) 460*0Sstevel@tonic-gate { 461*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me, 462*0Sstevel@tonic-gate ctx->sock, strerror(errno)); 463*0Sstevel@tonic-gate error(ctx); 464*0Sstevel@tonic-gate return; 465*0Sstevel@tonic-gate } 466*0Sstevel@tonic-gate } 467*0Sstevel@tonic-gate 468*0Sstevel@tonic-gate static void 469*0Sstevel@tonic-gate stop_read(struct ctl_cctx *ctx) { 470*0Sstevel@tonic-gate REQUIRE(ctx->coID.opaque == NULL); 471*0Sstevel@tonic-gate REQUIRE(ctx->rdID.opaque != NULL); 472*0Sstevel@tonic-gate (void)evDeselectFD(ctx->ev, ctx->rdID); 473*0Sstevel@tonic-gate ctx->rdID.opaque = NULL; 474*0Sstevel@tonic-gate } 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate static void 477*0Sstevel@tonic-gate readable(evContext ev, void *uap, int fd, int evmask) { 478*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::readable"; 479*0Sstevel@tonic-gate struct ctl_cctx *ctx = uap; 480*0Sstevel@tonic-gate struct ctl_tran *tran; 481*0Sstevel@tonic-gate ssize_t n; 482*0Sstevel@tonic-gate char *eos; 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate UNUSED(ev); 485*0Sstevel@tonic-gate 486*0Sstevel@tonic-gate REQUIRE(ctx != NULL); 487*0Sstevel@tonic-gate REQUIRE(fd >= 0); 488*0Sstevel@tonic-gate REQUIRE(evmask == EV_READ); 489*0Sstevel@tonic-gate REQUIRE(ctx->state == connected); 490*0Sstevel@tonic-gate REQUIRE(!EMPTY(ctx->tran)); 491*0Sstevel@tonic-gate tran = HEAD(ctx->tran); 492*0Sstevel@tonic-gate if (!allocated_p(ctx->inbuf) && 493*0Sstevel@tonic-gate ctl_bufget(&ctx->inbuf, ctx->logger) < 0) { 494*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: can't get an input buffer", me); 495*0Sstevel@tonic-gate error(ctx); 496*0Sstevel@tonic-gate return; 497*0Sstevel@tonic-gate } 498*0Sstevel@tonic-gate n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used, 499*0Sstevel@tonic-gate MAX_LINELEN - ctx->inbuf.used); 500*0Sstevel@tonic-gate if (n <= 0) { 501*0Sstevel@tonic-gate (*ctx->logger)(ctl_warning, "%s: read: %s", me, 502*0Sstevel@tonic-gate (n == 0) ? "Unexpected EOF" : strerror(errno)); 503*0Sstevel@tonic-gate error(ctx); 504*0Sstevel@tonic-gate return; 505*0Sstevel@tonic-gate } 506*0Sstevel@tonic-gate if (ctx->tiID.opaque != NULL) 507*0Sstevel@tonic-gate touch_timer(ctx); 508*0Sstevel@tonic-gate ctx->inbuf.used += n; 509*0Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me, 510*0Sstevel@tonic-gate n, ctx->inbuf.used); 511*0Sstevel@tonic-gate again: 512*0Sstevel@tonic-gate eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used); 513*0Sstevel@tonic-gate if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') { 514*0Sstevel@tonic-gate int done = 0; 515*0Sstevel@tonic-gate 516*0Sstevel@tonic-gate eos[-1] = '\0'; 517*0Sstevel@tonic-gate if (!arpacode_p(ctx->inbuf.text)) { 518*0Sstevel@tonic-gate /* XXX Doesn't FTP do this sometimes? Is it legal? */ 519*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me, 520*0Sstevel@tonic-gate ctx->inbuf.text); 521*0Sstevel@tonic-gate error(ctx); 522*0Sstevel@tonic-gate return; 523*0Sstevel@tonic-gate } 524*0Sstevel@tonic-gate if (arpadone_p(ctx->inbuf.text)) 525*0Sstevel@tonic-gate done = 1; 526*0Sstevel@tonic-gate else if (arpacont_p(ctx->inbuf.text)) 527*0Sstevel@tonic-gate done = 0; 528*0Sstevel@tonic-gate else { 529*0Sstevel@tonic-gate /* XXX Doesn't FTP do this sometimes? Is it legal? */ 530*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me, 531*0Sstevel@tonic-gate ctx->inbuf.text); 532*0Sstevel@tonic-gate error(ctx); 533*0Sstevel@tonic-gate return; 534*0Sstevel@tonic-gate } 535*0Sstevel@tonic-gate (*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text, 536*0Sstevel@tonic-gate (done ? 0 : CTL_MORE)); 537*0Sstevel@tonic-gate ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1); 538*0Sstevel@tonic-gate if (ctx->inbuf.used == 0) 539*0Sstevel@tonic-gate ctl_bufput(&ctx->inbuf); 540*0Sstevel@tonic-gate else 541*0Sstevel@tonic-gate memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used); 542*0Sstevel@tonic-gate if (done) { 543*0Sstevel@tonic-gate UNLINK(ctx->tran, tran, link); 544*0Sstevel@tonic-gate memput(tran, sizeof *tran); 545*0Sstevel@tonic-gate stop_read(ctx); 546*0Sstevel@tonic-gate start_write(ctx); 547*0Sstevel@tonic-gate return; 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate if (allocated_p(ctx->inbuf)) 550*0Sstevel@tonic-gate goto again; 551*0Sstevel@tonic-gate return; 552*0Sstevel@tonic-gate } 553*0Sstevel@tonic-gate if (ctx->inbuf.used == MAX_LINELEN) { 554*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me, 555*0Sstevel@tonic-gate ctx->inbuf.text); 556*0Sstevel@tonic-gate error(ctx); 557*0Sstevel@tonic-gate } 558*0Sstevel@tonic-gate } 559*0Sstevel@tonic-gate 560*0Sstevel@tonic-gate /* Timer related stuff. */ 561*0Sstevel@tonic-gate 562*0Sstevel@tonic-gate static void 563*0Sstevel@tonic-gate start_timer(struct ctl_cctx *ctx) { 564*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::start_timer"; 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate REQUIRE(ctx->tiID.opaque == NULL); 567*0Sstevel@tonic-gate if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){ 568*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me, 569*0Sstevel@tonic-gate strerror(errno)); 570*0Sstevel@tonic-gate error(ctx); 571*0Sstevel@tonic-gate return; 572*0Sstevel@tonic-gate } 573*0Sstevel@tonic-gate } 574*0Sstevel@tonic-gate 575*0Sstevel@tonic-gate static void 576*0Sstevel@tonic-gate stop_timer(struct ctl_cctx *ctx) { 577*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::stop_timer"; 578*0Sstevel@tonic-gate 579*0Sstevel@tonic-gate REQUIRE(ctx->tiID.opaque != NULL); 580*0Sstevel@tonic-gate if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) { 581*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me, 582*0Sstevel@tonic-gate strerror(errno)); 583*0Sstevel@tonic-gate error(ctx); 584*0Sstevel@tonic-gate return; 585*0Sstevel@tonic-gate } 586*0Sstevel@tonic-gate ctx->tiID.opaque = NULL; 587*0Sstevel@tonic-gate } 588*0Sstevel@tonic-gate 589*0Sstevel@tonic-gate static void 590*0Sstevel@tonic-gate touch_timer(struct ctl_cctx *ctx) { 591*0Sstevel@tonic-gate REQUIRE(ctx->tiID.opaque != NULL); 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate evTouchIdleTimer(ctx->ev, ctx->tiID); 594*0Sstevel@tonic-gate } 595*0Sstevel@tonic-gate 596*0Sstevel@tonic-gate static void 597*0Sstevel@tonic-gate timer(evContext ev, void *uap, struct timespec due, struct timespec itv) { 598*0Sstevel@tonic-gate static const char me[] = "isc/ctl_clnt::timer"; 599*0Sstevel@tonic-gate struct ctl_cctx *ctx = uap; 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate UNUSED(ev); 602*0Sstevel@tonic-gate UNUSED(due); 603*0Sstevel@tonic-gate UNUSED(itv); 604*0Sstevel@tonic-gate 605*0Sstevel@tonic-gate ctx->tiID.opaque = NULL; 606*0Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me, 607*0Sstevel@tonic-gate ctx->timeout.tv_sec, state_names[ctx->state]); 608*0Sstevel@tonic-gate error(ctx); 609*0Sstevel@tonic-gate } 610