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