10Sstevel@tonic-gate /* 2*11038SRao.Shoaib@Sun.COM * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC") 3*11038SRao.Shoaib@Sun.COM * Copyright (C) 1998-2003 Internet Software Consortium. 4*11038SRao.Shoaib@Sun.COM * 5*11038SRao.Shoaib@Sun.COM * Permission to use, copy, modify, and/or distribute this software for any 6*11038SRao.Shoaib@Sun.COM * purpose with or without fee is hereby granted, provided that the above 7*11038SRao.Shoaib@Sun.COM * copyright notice and this permission notice appear in all copies. 8*11038SRao.Shoaib@Sun.COM * 9*11038SRao.Shoaib@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10*11038SRao.Shoaib@Sun.COM * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11*11038SRao.Shoaib@Sun.COM * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12*11038SRao.Shoaib@Sun.COM * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13*11038SRao.Shoaib@Sun.COM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14*11038SRao.Shoaib@Sun.COM * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15*11038SRao.Shoaib@Sun.COM * PERFORMANCE OF THIS SOFTWARE. 160Sstevel@tonic-gate */ 170Sstevel@tonic-gate 180Sstevel@tonic-gate #if !defined(lint) && !defined(SABER) 19*11038SRao.Shoaib@Sun.COM static const char rcsid[] = "$Id: ctl_srvr.c,v 1.10 2008/11/14 02:36:51 marka Exp $"; 200Sstevel@tonic-gate #endif /* not lint */ 210Sstevel@tonic-gate 220Sstevel@tonic-gate /* Extern. */ 230Sstevel@tonic-gate 240Sstevel@tonic-gate #include "port_before.h" 250Sstevel@tonic-gate 260Sstevel@tonic-gate #include <sys/param.h> 270Sstevel@tonic-gate #include <sys/file.h> 280Sstevel@tonic-gate #include <sys/socket.h> 290Sstevel@tonic-gate #include <sys/un.h> 300Sstevel@tonic-gate 310Sstevel@tonic-gate #include <netinet/in.h> 320Sstevel@tonic-gate #include <arpa/nameser.h> 330Sstevel@tonic-gate #include <arpa/inet.h> 340Sstevel@tonic-gate 350Sstevel@tonic-gate #include <ctype.h> 360Sstevel@tonic-gate #include <errno.h> 370Sstevel@tonic-gate #include <stdio.h> 380Sstevel@tonic-gate #include <stdlib.h> 390Sstevel@tonic-gate #include <string.h> 400Sstevel@tonic-gate #include <time.h> 410Sstevel@tonic-gate #include <unistd.h> 420Sstevel@tonic-gate #include <fcntl.h> 43*11038SRao.Shoaib@Sun.COM #ifdef HAVE_MEMORY_H 44*11038SRao.Shoaib@Sun.COM #include <memory.h> 45*11038SRao.Shoaib@Sun.COM #endif 460Sstevel@tonic-gate 470Sstevel@tonic-gate #include <isc/assertions.h> 480Sstevel@tonic-gate #include <isc/ctl.h> 490Sstevel@tonic-gate #include <isc/eventlib.h> 500Sstevel@tonic-gate #include <isc/list.h> 510Sstevel@tonic-gate #include <isc/logging.h> 520Sstevel@tonic-gate #include <isc/memcluster.h> 530Sstevel@tonic-gate 540Sstevel@tonic-gate #include "ctl_p.h" 550Sstevel@tonic-gate 560Sstevel@tonic-gate #include "port_after.h" 570Sstevel@tonic-gate 580Sstevel@tonic-gate #ifdef SPRINTF_CHAR 590Sstevel@tonic-gate # define SPRINTF(x) strlen(sprintf/**/x) 600Sstevel@tonic-gate #else 610Sstevel@tonic-gate # define SPRINTF(x) ((size_t)sprintf x) 620Sstevel@tonic-gate #endif 630Sstevel@tonic-gate 640Sstevel@tonic-gate /* Macros. */ 650Sstevel@tonic-gate 660Sstevel@tonic-gate #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL) 670Sstevel@tonic-gate #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \ 680Sstevel@tonic-gate tmp, sizeof tmp, ctx->logger) 690Sstevel@tonic-gate 700Sstevel@tonic-gate /* Types. */ 710Sstevel@tonic-gate 720Sstevel@tonic-gate enum state { 730Sstevel@tonic-gate available = 0, initializing, writing, reading, reading_data, 740Sstevel@tonic-gate processing, idling, quitting, closing 750Sstevel@tonic-gate }; 760Sstevel@tonic-gate 770Sstevel@tonic-gate union sa_un { 780Sstevel@tonic-gate struct sockaddr_in in; 790Sstevel@tonic-gate #ifndef NO_SOCKADDR_UN 800Sstevel@tonic-gate struct sockaddr_un un; 810Sstevel@tonic-gate #endif 820Sstevel@tonic-gate }; 830Sstevel@tonic-gate 840Sstevel@tonic-gate struct ctl_sess { 850Sstevel@tonic-gate LINK(struct ctl_sess) link; 860Sstevel@tonic-gate struct ctl_sctx * ctx; 870Sstevel@tonic-gate enum state state; 880Sstevel@tonic-gate int sock; 890Sstevel@tonic-gate union sa_un sa; 900Sstevel@tonic-gate evFileID rdID; 910Sstevel@tonic-gate evStreamID wrID; 920Sstevel@tonic-gate evTimerID rdtiID; 930Sstevel@tonic-gate evTimerID wrtiID; 940Sstevel@tonic-gate struct ctl_buf inbuf; 950Sstevel@tonic-gate struct ctl_buf outbuf; 960Sstevel@tonic-gate const struct ctl_verb * verb; 970Sstevel@tonic-gate u_int helpcode; 980Sstevel@tonic-gate const void * respctx; 990Sstevel@tonic-gate u_int respflags; 1000Sstevel@tonic-gate ctl_srvrdone donefunc; 1010Sstevel@tonic-gate void * uap; 1020Sstevel@tonic-gate void * csctx; 1030Sstevel@tonic-gate }; 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate struct ctl_sctx { 1060Sstevel@tonic-gate evContext ev; 1070Sstevel@tonic-gate void * uctx; 1080Sstevel@tonic-gate u_int unkncode; 1090Sstevel@tonic-gate u_int timeoutcode; 1100Sstevel@tonic-gate const struct ctl_verb * verbs; 1110Sstevel@tonic-gate const struct ctl_verb * connverb; 1120Sstevel@tonic-gate int sock; 1130Sstevel@tonic-gate int max_sess; 1140Sstevel@tonic-gate int cur_sess; 1150Sstevel@tonic-gate struct timespec timeout; 1160Sstevel@tonic-gate ctl_logfunc logger; 1170Sstevel@tonic-gate evConnID acID; 1180Sstevel@tonic-gate LIST(struct ctl_sess) sess; 1190Sstevel@tonic-gate }; 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate /* Forward. */ 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate static void ctl_accept(evContext, void *, int, 1240Sstevel@tonic-gate const void *, int, 1250Sstevel@tonic-gate const void *, int); 1260Sstevel@tonic-gate static void ctl_close(struct ctl_sess *); 1270Sstevel@tonic-gate static void ctl_new_state(struct ctl_sess *, 1280Sstevel@tonic-gate enum state, 1290Sstevel@tonic-gate const char *); 1300Sstevel@tonic-gate static void ctl_start_read(struct ctl_sess *); 1310Sstevel@tonic-gate static void ctl_stop_read(struct ctl_sess *); 1320Sstevel@tonic-gate static void ctl_readable(evContext, void *, int, int); 1330Sstevel@tonic-gate static void ctl_rdtimeout(evContext, void *, 1340Sstevel@tonic-gate struct timespec, 1350Sstevel@tonic-gate struct timespec); 1360Sstevel@tonic-gate static void ctl_wrtimeout(evContext, void *, 1370Sstevel@tonic-gate struct timespec, 1380Sstevel@tonic-gate struct timespec); 1390Sstevel@tonic-gate static void ctl_docommand(struct ctl_sess *); 1400Sstevel@tonic-gate static void ctl_writedone(evContext, void *, int, int); 1410Sstevel@tonic-gate static void ctl_morehelp(struct ctl_sctx *, 1420Sstevel@tonic-gate struct ctl_sess *, 1430Sstevel@tonic-gate const struct ctl_verb *, 1440Sstevel@tonic-gate const char *, 1450Sstevel@tonic-gate u_int, const void *, void *); 1460Sstevel@tonic-gate static void ctl_signal_done(struct ctl_sctx *, 1470Sstevel@tonic-gate struct ctl_sess *); 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate /* Private data. */ 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate static const char * state_names[] = { 1520Sstevel@tonic-gate "available", "initializing", "writing", "reading", 1530Sstevel@tonic-gate "reading_data", "processing", "idling", "quitting", "closing" 1540Sstevel@tonic-gate }; 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate static const char space[] = " "; 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate static const struct ctl_verb fakehelpverb = { 1590Sstevel@tonic-gate "fakehelp", ctl_morehelp , NULL 1600Sstevel@tonic-gate }; 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate /* Public. */ 1630Sstevel@tonic-gate 164*11038SRao.Shoaib@Sun.COM /*% 1650Sstevel@tonic-gate * void 1660Sstevel@tonic-gate * ctl_server() 1670Sstevel@tonic-gate * create, condition, and start a listener on the control port. 1680Sstevel@tonic-gate */ 1690Sstevel@tonic-gate struct ctl_sctx * 1700Sstevel@tonic-gate ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len, 1710Sstevel@tonic-gate const struct ctl_verb *verbs, 1720Sstevel@tonic-gate u_int unkncode, u_int timeoutcode, 1730Sstevel@tonic-gate u_int timeout, int backlog, int max_sess, 1740Sstevel@tonic-gate ctl_logfunc logger, void *uctx) 1750Sstevel@tonic-gate { 1760Sstevel@tonic-gate static const char me[] = "ctl_server"; 1770Sstevel@tonic-gate static const int on = 1; 1780Sstevel@tonic-gate const struct ctl_verb *connverb; 1790Sstevel@tonic-gate struct ctl_sctx *ctx; 1800Sstevel@tonic-gate int save_errno; 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate if (logger == NULL) 1830Sstevel@tonic-gate logger = ctl_logger; 1840Sstevel@tonic-gate for (connverb = verbs; 1850Sstevel@tonic-gate connverb->name != NULL && connverb->func != NULL; 1860Sstevel@tonic-gate connverb++) 1870Sstevel@tonic-gate if (connverb->name[0] == '\0') 1880Sstevel@tonic-gate break; 1890Sstevel@tonic-gate if (connverb->func == NULL) { 1900Sstevel@tonic-gate (*logger)(ctl_error, "%s: no connection verb found", me); 1910Sstevel@tonic-gate return (NULL); 1920Sstevel@tonic-gate } 1930Sstevel@tonic-gate ctx = memget(sizeof *ctx); 1940Sstevel@tonic-gate if (ctx == NULL) { 1950Sstevel@tonic-gate (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); 1960Sstevel@tonic-gate return (NULL); 1970Sstevel@tonic-gate } 1980Sstevel@tonic-gate ctx->ev = lev; 1990Sstevel@tonic-gate ctx->uctx = uctx; 2000Sstevel@tonic-gate ctx->unkncode = unkncode; 2010Sstevel@tonic-gate ctx->timeoutcode = timeoutcode; 2020Sstevel@tonic-gate ctx->verbs = verbs; 2030Sstevel@tonic-gate ctx->timeout = evConsTime(timeout, 0); 2040Sstevel@tonic-gate ctx->logger = logger; 2050Sstevel@tonic-gate ctx->connverb = connverb; 2060Sstevel@tonic-gate ctx->max_sess = max_sess; 2070Sstevel@tonic-gate ctx->cur_sess = 0; 2080Sstevel@tonic-gate INIT_LIST(ctx->sess); 2090Sstevel@tonic-gate ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); 2100Sstevel@tonic-gate if (ctx->sock > evHighestFD(ctx->ev)) { 2110Sstevel@tonic-gate ctx->sock = -1; 2120Sstevel@tonic-gate errno = ENOTSOCK; 2130Sstevel@tonic-gate } 2140Sstevel@tonic-gate if (ctx->sock < 0) { 2150Sstevel@tonic-gate save_errno = errno; 2160Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: socket: %s", 2170Sstevel@tonic-gate me, strerror(errno)); 2180Sstevel@tonic-gate memput(ctx, sizeof *ctx); 2190Sstevel@tonic-gate errno = save_errno; 2200Sstevel@tonic-gate return (NULL); 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate if (ctx->sock > evHighestFD(lev)) { 2230Sstevel@tonic-gate close(ctx->sock); 2240Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD"); 2250Sstevel@tonic-gate errno = ENFILE; 2260Sstevel@tonic-gate memput(ctx, sizeof *ctx); 2270Sstevel@tonic-gate return (NULL); 2280Sstevel@tonic-gate } 2290Sstevel@tonic-gate #ifdef NO_UNIX_REUSEADDR 2300Sstevel@tonic-gate if (sap->sa_family != AF_UNIX) 2310Sstevel@tonic-gate #endif 2320Sstevel@tonic-gate if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, 2330Sstevel@tonic-gate (const char *)&on, sizeof on) != 0) { 2340Sstevel@tonic-gate (*ctx->logger)(ctl_warning, 2350Sstevel@tonic-gate "%s: setsockopt(REUSEADDR): %s", 2360Sstevel@tonic-gate me, strerror(errno)); 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate if (bind(ctx->sock, sap, sap_len) < 0) { 2390Sstevel@tonic-gate char tmp[MAX_NTOP]; 2400Sstevel@tonic-gate save_errno = errno; 2410Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: bind: %s: %s", 2420Sstevel@tonic-gate me, ctl_sa_ntop((const struct sockaddr *)sap, 2430Sstevel@tonic-gate tmp, sizeof tmp, ctx->logger), 2440Sstevel@tonic-gate strerror(save_errno)); 2450Sstevel@tonic-gate close(ctx->sock); 2460Sstevel@tonic-gate memput(ctx, sizeof *ctx); 2470Sstevel@tonic-gate errno = save_errno; 2480Sstevel@tonic-gate return (NULL); 2490Sstevel@tonic-gate } 2500Sstevel@tonic-gate if (fcntl(ctx->sock, F_SETFD, 1) < 0) { 2510Sstevel@tonic-gate (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, 2520Sstevel@tonic-gate strerror(errno)); 2530Sstevel@tonic-gate } 2540Sstevel@tonic-gate if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx, 2550Sstevel@tonic-gate &ctx->acID) < 0) { 2560Sstevel@tonic-gate save_errno = errno; 2570Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s", 2580Sstevel@tonic-gate me, ctx->sock, strerror(errno)); 2590Sstevel@tonic-gate close(ctx->sock); 2600Sstevel@tonic-gate memput(ctx, sizeof *ctx); 2610Sstevel@tonic-gate errno = save_errno; 2620Sstevel@tonic-gate return (NULL); 2630Sstevel@tonic-gate } 2640Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d", 2650Sstevel@tonic-gate me, ctx, ctx->sock); 2660Sstevel@tonic-gate return (ctx); 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate 269*11038SRao.Shoaib@Sun.COM /*% 2700Sstevel@tonic-gate * void 2710Sstevel@tonic-gate * ctl_endserver(ctx) 2720Sstevel@tonic-gate * if the control listener is open, close it. clean out all eventlib 2730Sstevel@tonic-gate * stuff. close all active sessions. 2740Sstevel@tonic-gate */ 2750Sstevel@tonic-gate void 2760Sstevel@tonic-gate ctl_endserver(struct ctl_sctx *ctx) { 2770Sstevel@tonic-gate static const char me[] = "ctl_endserver"; 2780Sstevel@tonic-gate struct ctl_sess *this, *next; 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p", 2810Sstevel@tonic-gate me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess); 2820Sstevel@tonic-gate if (ctx->acID.opaque != NULL) { 2830Sstevel@tonic-gate (void)evCancelConn(ctx->ev, ctx->acID); 2840Sstevel@tonic-gate ctx->acID.opaque = NULL; 2850Sstevel@tonic-gate } 2860Sstevel@tonic-gate if (ctx->sock != -1) { 2870Sstevel@tonic-gate (void) close(ctx->sock); 2880Sstevel@tonic-gate ctx->sock = -1; 2890Sstevel@tonic-gate } 2900Sstevel@tonic-gate for (this = HEAD(ctx->sess); this != NULL; this = next) { 2910Sstevel@tonic-gate next = NEXT(this, link); 2920Sstevel@tonic-gate ctl_close(this); 2930Sstevel@tonic-gate } 2940Sstevel@tonic-gate memput(ctx, sizeof *ctx); 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate 297*11038SRao.Shoaib@Sun.COM /*% 2980Sstevel@tonic-gate * If body is non-NULL then it we add a "." line after it. 2990Sstevel@tonic-gate * Caller must have escaped lines with leading ".". 3000Sstevel@tonic-gate */ 3010Sstevel@tonic-gate void 3020Sstevel@tonic-gate ctl_response(struct ctl_sess *sess, u_int code, const char *text, 3030Sstevel@tonic-gate u_int flags, const void *respctx, ctl_srvrdone donefunc, 3040Sstevel@tonic-gate void *uap, const char *body, size_t bodylen) 3050Sstevel@tonic-gate { 3060Sstevel@tonic-gate static const char me[] = "ctl_response"; 3070Sstevel@tonic-gate struct iovec iov[3], *iovp = iov; 3080Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 3090Sstevel@tonic-gate char tmp[MAX_NTOP], *pc; 3100Sstevel@tonic-gate int n; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate REQUIRE(sess->state == initializing || 3130Sstevel@tonic-gate sess->state == processing || 3140Sstevel@tonic-gate sess->state == reading_data || 3150Sstevel@tonic-gate sess->state == writing); 3160Sstevel@tonic-gate REQUIRE(sess->wrtiID.opaque == NULL); 3170Sstevel@tonic-gate REQUIRE(sess->wrID.opaque == NULL); 3180Sstevel@tonic-gate ctl_new_state(sess, writing, me); 3190Sstevel@tonic-gate sess->donefunc = donefunc; 3200Sstevel@tonic-gate sess->uap = uap; 3210Sstevel@tonic-gate if (!allocated_p(sess->outbuf) && 3220Sstevel@tonic-gate ctl_bufget(&sess->outbuf, ctx->logger) < 0) { 3230Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer", 3240Sstevel@tonic-gate me, address_expr); 3250Sstevel@tonic-gate goto untimely; 3260Sstevel@tonic-gate } 327*11038SRao.Shoaib@Sun.COM if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) { 3280Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing", 3290Sstevel@tonic-gate me, address_expr); 3300Sstevel@tonic-gate goto untimely; 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n", 3330Sstevel@tonic-gate code, (flags & CTL_MORE) != 0 ? '-' : ' ', 3340Sstevel@tonic-gate text)); 3350Sstevel@tonic-gate for (pc = sess->outbuf.text, n = 0; 3360Sstevel@tonic-gate n < (int)sess->outbuf.used-2; pc++, n++) 3370Sstevel@tonic-gate if (!isascii((unsigned char)*pc) || 3380Sstevel@tonic-gate !isprint((unsigned char)*pc)) 3390Sstevel@tonic-gate *pc = '\040'; 3400Sstevel@tonic-gate *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used); 3410Sstevel@tonic-gate if (body != NULL) { 3420Sstevel@tonic-gate char *tmp; 3430Sstevel@tonic-gate DE_CONST(body, tmp); 3440Sstevel@tonic-gate *iovp++ = evConsIovec(tmp, bodylen); 3450Sstevel@tonic-gate DE_CONST(".\r\n", tmp); 3460Sstevel@tonic-gate *iovp++ = evConsIovec(tmp, 3); 3470Sstevel@tonic-gate } 3480Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: [%d] %s", me, 3490Sstevel@tonic-gate sess->outbuf.used, sess->outbuf.text); 3500Sstevel@tonic-gate if (evWrite(ctx->ev, sess->sock, iov, iovp - iov, 3510Sstevel@tonic-gate ctl_writedone, sess, &sess->wrID) < 0) { 3520Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me, 3530Sstevel@tonic-gate address_expr, strerror(errno)); 3540Sstevel@tonic-gate goto untimely; 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout, 3570Sstevel@tonic-gate &sess->wrtiID) < 0) 3580Sstevel@tonic-gate { 3590Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, 3600Sstevel@tonic-gate address_expr, strerror(errno)); 3610Sstevel@tonic-gate goto untimely; 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) { 3640Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me, 3650Sstevel@tonic-gate address_expr, strerror(errno)); 3660Sstevel@tonic-gate untimely: 3670Sstevel@tonic-gate ctl_signal_done(ctx, sess); 3680Sstevel@tonic-gate ctl_close(sess); 3690Sstevel@tonic-gate return; 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate sess->respctx = respctx; 3720Sstevel@tonic-gate sess->respflags = flags; 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate void 3760Sstevel@tonic-gate ctl_sendhelp(struct ctl_sess *sess, u_int code) { 3770Sstevel@tonic-gate static const char me[] = "ctl_sendhelp"; 3780Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate sess->helpcode = code; 3810Sstevel@tonic-gate sess->verb = &fakehelpverb; 3820Sstevel@tonic-gate ctl_morehelp(ctx, sess, NULL, me, CTL_MORE, 3830Sstevel@tonic-gate (const void *)ctx->verbs, NULL); 3840Sstevel@tonic-gate } 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate void * 3870Sstevel@tonic-gate ctl_getcsctx(struct ctl_sess *sess) { 3880Sstevel@tonic-gate return (sess->csctx); 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate void * 3920Sstevel@tonic-gate ctl_setcsctx(struct ctl_sess *sess, void *csctx) { 3930Sstevel@tonic-gate void *old = sess->csctx; 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate sess->csctx = csctx; 3960Sstevel@tonic-gate return (old); 3970Sstevel@tonic-gate } 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate /* Private functions. */ 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate static void 4020Sstevel@tonic-gate ctl_accept(evContext lev, void *uap, int fd, 4030Sstevel@tonic-gate const void *lav, int lalen, 4040Sstevel@tonic-gate const void *rav, int ralen) 4050Sstevel@tonic-gate { 4060Sstevel@tonic-gate static const char me[] = "ctl_accept"; 4070Sstevel@tonic-gate struct ctl_sctx *ctx = uap; 4080Sstevel@tonic-gate struct ctl_sess *sess = NULL; 4090Sstevel@tonic-gate char tmp[MAX_NTOP]; 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate UNUSED(lev); 4120Sstevel@tonic-gate UNUSED(lalen); 4130Sstevel@tonic-gate UNUSED(ralen); 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate if (fd < 0) { 4160Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: accept: %s", 4170Sstevel@tonic-gate me, strerror(errno)); 4180Sstevel@tonic-gate return; 4190Sstevel@tonic-gate } 4200Sstevel@tonic-gate if (ctx->cur_sess == ctx->max_sess) { 4210Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: too many control sessions", 4220Sstevel@tonic-gate me, ctl_sa_ntop((const struct sockaddr *)rav, 4230Sstevel@tonic-gate tmp, sizeof tmp, 4240Sstevel@tonic-gate ctx->logger)); 4250Sstevel@tonic-gate (void) close(fd); 4260Sstevel@tonic-gate return; 4270Sstevel@tonic-gate } 4280Sstevel@tonic-gate sess = memget(sizeof *sess); 4290Sstevel@tonic-gate if (sess == NULL) { 4300Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: memget: %s", me, 4310Sstevel@tonic-gate strerror(errno)); 4320Sstevel@tonic-gate (void) close(fd); 4330Sstevel@tonic-gate return; 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate if (fcntl(fd, F_SETFD, 1) < 0) { 4360Sstevel@tonic-gate (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, 4370Sstevel@tonic-gate strerror(errno)); 4380Sstevel@tonic-gate } 4390Sstevel@tonic-gate ctx->cur_sess++; 4400Sstevel@tonic-gate INIT_LINK(sess, link); 4410Sstevel@tonic-gate APPEND(ctx->sess, sess, link); 4420Sstevel@tonic-gate sess->ctx = ctx; 4430Sstevel@tonic-gate sess->sock = fd; 4440Sstevel@tonic-gate sess->wrID.opaque = NULL; 4450Sstevel@tonic-gate sess->rdID.opaque = NULL; 4460Sstevel@tonic-gate sess->wrtiID.opaque = NULL; 4470Sstevel@tonic-gate sess->rdtiID.opaque = NULL; 4480Sstevel@tonic-gate sess->respctx = NULL; 4490Sstevel@tonic-gate sess->csctx = NULL; 4500Sstevel@tonic-gate if (((const struct sockaddr *)rav)->sa_family == AF_UNIX) 4510Sstevel@tonic-gate ctl_sa_copy((const struct sockaddr *)lav, 4520Sstevel@tonic-gate (struct sockaddr *)&sess->sa); 4530Sstevel@tonic-gate else 4540Sstevel@tonic-gate ctl_sa_copy((const struct sockaddr *)rav, 4550Sstevel@tonic-gate (struct sockaddr *)&sess->sa); 4560Sstevel@tonic-gate sess->donefunc = NULL; 4570Sstevel@tonic-gate buffer_init(sess->inbuf); 4580Sstevel@tonic-gate buffer_init(sess->outbuf); 4590Sstevel@tonic-gate sess->state = available; 4600Sstevel@tonic-gate ctl_new_state(sess, initializing, me); 4610Sstevel@tonic-gate sess->verb = ctx->connverb; 4620Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)", 4630Sstevel@tonic-gate me, address_expr, sess->sock); 4640Sstevel@tonic-gate (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0, 4650Sstevel@tonic-gate (const struct sockaddr *)rav, ctx->uctx); 4660Sstevel@tonic-gate } 4670Sstevel@tonic-gate 4680Sstevel@tonic-gate static void 4690Sstevel@tonic-gate ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason) 4700Sstevel@tonic-gate { 4710Sstevel@tonic-gate static const char me[] = "ctl_new_state"; 4720Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 4730Sstevel@tonic-gate char tmp[MAX_NTOP]; 4740Sstevel@tonic-gate 4750Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)", 4760Sstevel@tonic-gate me, address_expr, 4770Sstevel@tonic-gate state_names[sess->state], 4780Sstevel@tonic-gate state_names[new_state], reason); 4790Sstevel@tonic-gate sess->state = new_state; 4800Sstevel@tonic-gate } 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate static void 4830Sstevel@tonic-gate ctl_close(struct ctl_sess *sess) { 4840Sstevel@tonic-gate static const char me[] = "ctl_close"; 4850Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 4860Sstevel@tonic-gate char tmp[MAX_NTOP]; 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate REQUIRE(sess->state == initializing || 4890Sstevel@tonic-gate sess->state == writing || 4900Sstevel@tonic-gate sess->state == reading || 4910Sstevel@tonic-gate sess->state == processing || 4920Sstevel@tonic-gate sess->state == reading_data || 4930Sstevel@tonic-gate sess->state == idling); 4940Sstevel@tonic-gate REQUIRE(sess->sock != -1); 4950Sstevel@tonic-gate if (sess->state == reading || sess->state == reading_data) 4960Sstevel@tonic-gate ctl_stop_read(sess); 4970Sstevel@tonic-gate else if (sess->state == writing) { 4980Sstevel@tonic-gate if (sess->wrID.opaque != NULL) { 4990Sstevel@tonic-gate (void) evCancelRW(ctx->ev, sess->wrID); 5000Sstevel@tonic-gate sess->wrID.opaque = NULL; 5010Sstevel@tonic-gate } 5020Sstevel@tonic-gate if (sess->wrtiID.opaque != NULL) { 5030Sstevel@tonic-gate (void) evClearIdleTimer(ctx->ev, sess->wrtiID); 5040Sstevel@tonic-gate sess->wrtiID.opaque = NULL; 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate } 5070Sstevel@tonic-gate ctl_new_state(sess, closing, me); 5080Sstevel@tonic-gate (void) close(sess->sock); 5090Sstevel@tonic-gate if (allocated_p(sess->inbuf)) 5100Sstevel@tonic-gate ctl_bufput(&sess->inbuf); 5110Sstevel@tonic-gate if (allocated_p(sess->outbuf)) 5120Sstevel@tonic-gate ctl_bufput(&sess->outbuf); 5130Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)", 5140Sstevel@tonic-gate me, address_expr, sess->sock); 5150Sstevel@tonic-gate UNLINK(ctx->sess, sess, link); 5160Sstevel@tonic-gate memput(sess, sizeof *sess); 5170Sstevel@tonic-gate ctx->cur_sess--; 5180Sstevel@tonic-gate } 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate static void 5210Sstevel@tonic-gate ctl_start_read(struct ctl_sess *sess) { 5220Sstevel@tonic-gate static const char me[] = "ctl_start_read"; 5230Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 5240Sstevel@tonic-gate char tmp[MAX_NTOP]; 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate REQUIRE(sess->state == initializing || 5270Sstevel@tonic-gate sess->state == writing || 5280Sstevel@tonic-gate sess->state == processing || 5290Sstevel@tonic-gate sess->state == idling); 5300Sstevel@tonic-gate REQUIRE(sess->rdtiID.opaque == NULL); 5310Sstevel@tonic-gate REQUIRE(sess->rdID.opaque == NULL); 5320Sstevel@tonic-gate sess->inbuf.used = 0; 5330Sstevel@tonic-gate if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout, 5340Sstevel@tonic-gate &sess->rdtiID) < 0) 5350Sstevel@tonic-gate { 5360Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, 5370Sstevel@tonic-gate address_expr, strerror(errno)); 5380Sstevel@tonic-gate ctl_close(sess); 5390Sstevel@tonic-gate return; 5400Sstevel@tonic-gate } 5410Sstevel@tonic-gate if (evSelectFD(ctx->ev, sess->sock, EV_READ, 5420Sstevel@tonic-gate ctl_readable, sess, &sess->rdID) < 0) { 5430Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me, 5440Sstevel@tonic-gate address_expr, strerror(errno)); 5450Sstevel@tonic-gate return; 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate ctl_new_state(sess, reading, me); 5480Sstevel@tonic-gate } 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate static void 5510Sstevel@tonic-gate ctl_stop_read(struct ctl_sess *sess) { 5520Sstevel@tonic-gate static const char me[] = "ctl_stop_read"; 5530Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate REQUIRE(sess->state == reading || sess->state == reading_data); 5560Sstevel@tonic-gate REQUIRE(sess->rdID.opaque != NULL); 5570Sstevel@tonic-gate (void) evDeselectFD(ctx->ev, sess->rdID); 5580Sstevel@tonic-gate sess->rdID.opaque = NULL; 5590Sstevel@tonic-gate if (sess->rdtiID.opaque != NULL) { 5600Sstevel@tonic-gate (void) evClearIdleTimer(ctx->ev, sess->rdtiID); 5610Sstevel@tonic-gate sess->rdtiID.opaque = NULL; 5620Sstevel@tonic-gate } 5630Sstevel@tonic-gate ctl_new_state(sess, idling, me); 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate static void 5670Sstevel@tonic-gate ctl_readable(evContext lev, void *uap, int fd, int evmask) { 5680Sstevel@tonic-gate static const char me[] = "ctl_readable"; 5690Sstevel@tonic-gate struct ctl_sess *sess = uap; 570*11038SRao.Shoaib@Sun.COM struct ctl_sctx *ctx; 5710Sstevel@tonic-gate char *eos, tmp[MAX_NTOP]; 5720Sstevel@tonic-gate ssize_t n; 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate REQUIRE(sess != NULL); 5750Sstevel@tonic-gate REQUIRE(fd >= 0); 5760Sstevel@tonic-gate REQUIRE(evmask == EV_READ); 5770Sstevel@tonic-gate REQUIRE(sess->state == reading || sess->state == reading_data); 578*11038SRao.Shoaib@Sun.COM 579*11038SRao.Shoaib@Sun.COM ctx = sess->ctx; 5800Sstevel@tonic-gate evTouchIdleTimer(lev, sess->rdtiID); 5810Sstevel@tonic-gate if (!allocated_p(sess->inbuf) && 5820Sstevel@tonic-gate ctl_bufget(&sess->inbuf, ctx->logger) < 0) { 5830Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer", 5840Sstevel@tonic-gate me, address_expr); 5850Sstevel@tonic-gate ctl_close(sess); 5860Sstevel@tonic-gate return; 5870Sstevel@tonic-gate } 5880Sstevel@tonic-gate n = read(sess->sock, sess->inbuf.text + sess->inbuf.used, 5890Sstevel@tonic-gate MAX_LINELEN - sess->inbuf.used); 5900Sstevel@tonic-gate if (n <= 0) { 5910Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: %s: read: %s", 5920Sstevel@tonic-gate me, address_expr, 5930Sstevel@tonic-gate (n == 0) ? "Unexpected EOF" : strerror(errno)); 5940Sstevel@tonic-gate ctl_close(sess); 5950Sstevel@tonic-gate return; 5960Sstevel@tonic-gate } 5970Sstevel@tonic-gate sess->inbuf.used += n; 5980Sstevel@tonic-gate eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used); 5990Sstevel@tonic-gate if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') { 6000Sstevel@tonic-gate eos[-1] = '\0'; 6010Sstevel@tonic-gate if ((sess->respflags & CTL_DATA) != 0) { 6020Sstevel@tonic-gate INSIST(sess->verb != NULL); 6030Sstevel@tonic-gate (*sess->verb->func)(sess->ctx, sess, sess->verb, 6040Sstevel@tonic-gate sess->inbuf.text, 6050Sstevel@tonic-gate CTL_DATA, sess->respctx, 6060Sstevel@tonic-gate sess->ctx->uctx); 6070Sstevel@tonic-gate } else { 6080Sstevel@tonic-gate ctl_stop_read(sess); 6090Sstevel@tonic-gate ctl_docommand(sess); 6100Sstevel@tonic-gate } 6110Sstevel@tonic-gate sess->inbuf.used -= ((eos - sess->inbuf.text) + 1); 612*11038SRao.Shoaib@Sun.COM if (sess->inbuf.used == 0U) 6130Sstevel@tonic-gate ctl_bufput(&sess->inbuf); 6140Sstevel@tonic-gate else 6150Sstevel@tonic-gate memmove(sess->inbuf.text, eos + 1, sess->inbuf.used); 6160Sstevel@tonic-gate return; 6170Sstevel@tonic-gate } 618*11038SRao.Shoaib@Sun.COM if (sess->inbuf.used == (size_t)MAX_LINELEN) { 6190Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: line too long, closing", 6200Sstevel@tonic-gate me, address_expr); 6210Sstevel@tonic-gate ctl_close(sess); 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate static void 6260Sstevel@tonic-gate ctl_wrtimeout(evContext lev, void *uap, 6270Sstevel@tonic-gate struct timespec due, 6280Sstevel@tonic-gate struct timespec itv) 6290Sstevel@tonic-gate { 6300Sstevel@tonic-gate static const char me[] = "ctl_wrtimeout"; 6310Sstevel@tonic-gate struct ctl_sess *sess = uap; 6320Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 6330Sstevel@tonic-gate char tmp[MAX_NTOP]; 634*11038SRao.Shoaib@Sun.COM 6350Sstevel@tonic-gate UNUSED(lev); 6360Sstevel@tonic-gate UNUSED(due); 6370Sstevel@tonic-gate UNUSED(itv); 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate REQUIRE(sess->state == writing); 6400Sstevel@tonic-gate sess->wrtiID.opaque = NULL; 6410Sstevel@tonic-gate (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing", 6420Sstevel@tonic-gate me, address_expr); 6430Sstevel@tonic-gate if (sess->wrID.opaque != NULL) { 6440Sstevel@tonic-gate (void) evCancelRW(ctx->ev, sess->wrID); 6450Sstevel@tonic-gate sess->wrID.opaque = NULL; 6460Sstevel@tonic-gate } 6470Sstevel@tonic-gate ctl_signal_done(ctx, sess); 6480Sstevel@tonic-gate ctl_new_state(sess, processing, me); 6490Sstevel@tonic-gate ctl_close(sess); 6500Sstevel@tonic-gate } 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate static void 6530Sstevel@tonic-gate ctl_rdtimeout(evContext lev, void *uap, 6540Sstevel@tonic-gate struct timespec due, 6550Sstevel@tonic-gate struct timespec itv) 6560Sstevel@tonic-gate { 6570Sstevel@tonic-gate static const char me[] = "ctl_rdtimeout"; 6580Sstevel@tonic-gate struct ctl_sess *sess = uap; 6590Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 6600Sstevel@tonic-gate char tmp[MAX_NTOP]; 6610Sstevel@tonic-gate 6620Sstevel@tonic-gate UNUSED(lev); 6630Sstevel@tonic-gate UNUSED(due); 6640Sstevel@tonic-gate UNUSED(itv); 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate REQUIRE(sess->state == reading); 6670Sstevel@tonic-gate sess->rdtiID.opaque = NULL; 6680Sstevel@tonic-gate (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing", 6690Sstevel@tonic-gate me, address_expr); 6700Sstevel@tonic-gate if (sess->state == reading || sess->state == reading_data) 6710Sstevel@tonic-gate ctl_stop_read(sess); 6720Sstevel@tonic-gate ctl_signal_done(ctx, sess); 6730Sstevel@tonic-gate ctl_new_state(sess, processing, me); 6740Sstevel@tonic-gate ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL, 6750Sstevel@tonic-gate NULL, NULL, NULL, 0); 6760Sstevel@tonic-gate } 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate static void 6790Sstevel@tonic-gate ctl_docommand(struct ctl_sess *sess) { 6800Sstevel@tonic-gate static const char me[] = "ctl_docommand"; 6810Sstevel@tonic-gate char *name, *rest, tmp[MAX_NTOP]; 6820Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 6830Sstevel@tonic-gate const struct ctl_verb *verb; 6840Sstevel@tonic-gate 6850Sstevel@tonic-gate REQUIRE(allocated_p(sess->inbuf)); 6860Sstevel@tonic-gate (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]", 6870Sstevel@tonic-gate me, address_expr, 6880Sstevel@tonic-gate sess->inbuf.text, (u_int)sess->inbuf.used); 6890Sstevel@tonic-gate ctl_new_state(sess, processing, me); 6900Sstevel@tonic-gate name = sess->inbuf.text + strspn(sess->inbuf.text, space); 6910Sstevel@tonic-gate rest = name + strcspn(name, space); 6920Sstevel@tonic-gate if (*rest != '\0') { 6930Sstevel@tonic-gate *rest++ = '\0'; 6940Sstevel@tonic-gate rest += strspn(rest, space); 6950Sstevel@tonic-gate } 6960Sstevel@tonic-gate for (verb = ctx->verbs; 6970Sstevel@tonic-gate verb != NULL && verb->name != NULL && verb->func != NULL; 6980Sstevel@tonic-gate verb++) 6990Sstevel@tonic-gate if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0) 7000Sstevel@tonic-gate break; 7010Sstevel@tonic-gate if (verb != NULL && verb->name != NULL && verb->func != NULL) { 7020Sstevel@tonic-gate sess->verb = verb; 7030Sstevel@tonic-gate (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx); 7040Sstevel@tonic-gate } else { 7050Sstevel@tonic-gate char buf[1100]; 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate if (sizeof "Unrecognized command \"\" (args \"\")" + 7080Sstevel@tonic-gate strlen(name) + strlen(rest) > sizeof buf) 7090Sstevel@tonic-gate strcpy(buf, "Unrecognized command (buf ovf)"); 7100Sstevel@tonic-gate else 7110Sstevel@tonic-gate sprintf(buf, 7120Sstevel@tonic-gate "Unrecognized command \"%s\" (args \"%s\")", 7130Sstevel@tonic-gate name, rest); 7140Sstevel@tonic-gate ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL, 7150Sstevel@tonic-gate NULL, 0); 7160Sstevel@tonic-gate } 7170Sstevel@tonic-gate } 7180Sstevel@tonic-gate 7190Sstevel@tonic-gate static void 7200Sstevel@tonic-gate ctl_writedone(evContext lev, void *uap, int fd, int bytes) { 7210Sstevel@tonic-gate static const char me[] = "ctl_writedone"; 7220Sstevel@tonic-gate struct ctl_sess *sess = uap; 7230Sstevel@tonic-gate struct ctl_sctx *ctx = sess->ctx; 7240Sstevel@tonic-gate char tmp[MAX_NTOP]; 7250Sstevel@tonic-gate int save_errno = errno; 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate UNUSED(lev); 7280Sstevel@tonic-gate UNUSED(uap); 7290Sstevel@tonic-gate 7300Sstevel@tonic-gate REQUIRE(sess->state == writing); 7310Sstevel@tonic-gate REQUIRE(fd == sess->sock); 7320Sstevel@tonic-gate REQUIRE(sess->wrtiID.opaque != NULL); 7330Sstevel@tonic-gate sess->wrID.opaque = NULL; 7340Sstevel@tonic-gate (void) evClearIdleTimer(ctx->ev, sess->wrtiID); 7350Sstevel@tonic-gate sess->wrtiID.opaque = NULL; 7360Sstevel@tonic-gate if (bytes < 0) { 7370Sstevel@tonic-gate (*ctx->logger)(ctl_error, "%s: %s: %s", 7380Sstevel@tonic-gate me, address_expr, strerror(save_errno)); 7390Sstevel@tonic-gate ctl_close(sess); 7400Sstevel@tonic-gate return; 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate INSIST(allocated_p(sess->outbuf)); 7440Sstevel@tonic-gate ctl_bufput(&sess->outbuf); 7450Sstevel@tonic-gate if ((sess->respflags & CTL_EXIT) != 0) { 7460Sstevel@tonic-gate ctl_signal_done(ctx, sess); 7470Sstevel@tonic-gate ctl_close(sess); 7480Sstevel@tonic-gate return; 7490Sstevel@tonic-gate } else if ((sess->respflags & CTL_MORE) != 0) { 7500Sstevel@tonic-gate INSIST(sess->verb != NULL); 7510Sstevel@tonic-gate (*sess->verb->func)(sess->ctx, sess, sess->verb, "", 7520Sstevel@tonic-gate CTL_MORE, sess->respctx, sess->ctx->uctx); 7530Sstevel@tonic-gate } else { 7540Sstevel@tonic-gate ctl_signal_done(ctx, sess); 7550Sstevel@tonic-gate ctl_start_read(sess); 7560Sstevel@tonic-gate } 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate static void 7600Sstevel@tonic-gate ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess, 7610Sstevel@tonic-gate const struct ctl_verb *verb, const char *text, 7620Sstevel@tonic-gate u_int respflags, const void *respctx, void *uctx) 7630Sstevel@tonic-gate { 7640Sstevel@tonic-gate const struct ctl_verb *this = respctx, *next = this + 1; 7650Sstevel@tonic-gate 7660Sstevel@tonic-gate UNUSED(ctx); 7670Sstevel@tonic-gate UNUSED(verb); 7680Sstevel@tonic-gate UNUSED(text); 7690Sstevel@tonic-gate UNUSED(uctx); 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate REQUIRE(!lastverb_p(this)); 7720Sstevel@tonic-gate REQUIRE((respflags & CTL_MORE) != 0); 7730Sstevel@tonic-gate if (lastverb_p(next)) 7740Sstevel@tonic-gate respflags &= ~CTL_MORE; 7750Sstevel@tonic-gate ctl_response(sess, sess->helpcode, this->help, respflags, next, 7760Sstevel@tonic-gate NULL, NULL, NULL, 0); 7770Sstevel@tonic-gate } 7780Sstevel@tonic-gate 7790Sstevel@tonic-gate static void 7800Sstevel@tonic-gate ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) { 7810Sstevel@tonic-gate if (sess->donefunc != NULL) { 7820Sstevel@tonic-gate (*sess->donefunc)(ctx, sess, sess->uap); 7830Sstevel@tonic-gate sess->donefunc = NULL; 7840Sstevel@tonic-gate } 7850Sstevel@tonic-gate } 786*11038SRao.Shoaib@Sun.COM 787*11038SRao.Shoaib@Sun.COM /*! \file */ 788