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