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