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