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";
26 #include "port_before.h"
28 #include <sys/param.h>
30 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/nameser.h>
34 #include <arpa/inet.h>
47 #include <isc/assertions.h>
49 #include <isc/eventlib.h>
51 #include <isc/memcluster.h>
55 #include "port_after.h"
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')
73 initializing
= 0, connecting
, connected
, destroyed
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
;
90 ctl_clntdone donefunc
;
97 struct timespec timeout
;
98 LIST(struct ctl_tran
) tran
;
99 LIST(struct ctl_tran
) wtran
;
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,
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
);
124 memchr(const void *b
, int c
, size_t len
) {
125 const unsigned char *p
= b
;
128 for (i
= 0; i
< len
; i
++, p
++)
129 if (*p
== (unsigned char)c
)
137 static const char * const state_names
[] = {
138 "initializing", "connecting", "connected", "destroyed"
146 * create, condition, and connect to a listener on the control port.
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
;
161 ctx
= memget(sizeof *ctx
);
163 (*logger
)(ctl_error
, "%s: getmem: %s", me
, strerror(errno
));
166 ctx
->state
= initializing
;
168 ctx
->logger
= logger
;
169 ctx
->timeout
= evConsTime(timeout
, 0);
170 ctx
->donefunc
= donefunc
;
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
)) {
185 (*ctx
->logger
)(ctl_error
, "%s: socket: %s",
186 me
, strerror(errno
));
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
,
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
));
211 memput(ctx
, sizeof *ctx
);
215 new_state(ctx
, connecting
);
222 * close a client and release all of its resources.
225 ctl_endclient(struct ctl_cctx
*ctx
) {
226 if (ctx
->state
!= destroyed
)
228 memput(ctx
, sizeof *ctx
);
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
;
245 switch (ctx
->state
) {
255 if (len
>= (size_t)MAX_LINELEN
) {
259 tran
= new_tran(ctx
, donefunc
, uap
, 1);
262 if (ctl_bufget(&tran
->outbuf
, ctx
->logger
) < 0)
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
))
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);
283 buffer_init(new->outbuf
);
284 new->donefunc
= donefunc
;
286 INIT_LINK(new, link
);
287 INIT_LINK(new, wlink
);
288 APPEND(ctx
->tran
, new, link
);
290 APPEND(ctx
->wtran
, new, wlink
);
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
;
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
)
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
)
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
)
319 if (ctx
->state
== destroyed
)
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
,
332 if (evTimeRW(ctx
->ev
, ctx
->wrID
, ctx
->tiID
) < 0) {
333 (*ctx
->logger
)(ctl_error
, "%s: evTimeRW: %s", me
,
341 destroy(struct ctl_cctx
*ctx
, int notify
) {
342 struct ctl_tran
*this, *next
;
344 if (ctx
->sock
!= -1) {
345 (void) close(ctx
->sock
);
348 switch (ctx
->state
) {
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
;
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
)
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
)
387 new_state(ctx
, destroyed
);
391 error(struct ctl_cctx
*ctx
) {
392 REQUIRE(ctx
->state
!= destroyed
);
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
;
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
;
420 ctx
->coID
.opaque
= NULL
;
422 (*ctx
->logger
)(ctl_error
, "%s: evConnect: %s", me
,
427 new_state(ctx
, connected
);
428 tran
= new_tran(ctx
, ctx
->donefunc
, ctx
->uap
, 0);
430 (*ctx
->logger
)(ctl_error
, "%s: new_tran failed: %s", me
,
436 if (ctx
->state
== destroyed
) {
437 (*ctx
->logger
)(ctl_error
, "%s: start_read failed: %s",
438 me
, strerror(errno
));
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
;
452 ctx
->wrID
.opaque
= NULL
;
453 if (ctx
->tiID
.opaque
!= NULL
)
455 ctl_bufput(&tran
->outbuf
);
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
,
472 (*ctx
->logger
)(ctl_error
, "%s: evSelect(fd %d): %s", me
,
473 ctx
->sock
, strerror(errno
));
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
;
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
;
497 REQUIRE(ctx
!= NULL
);
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
);
509 n
= read(ctx
->sock
, ctx
->inbuf
.text
+ ctx
->inbuf
.used
,
510 MAX_LINELEN
- ctx
->inbuf
.used
);
512 (*ctx
->logger
)(ctl_warning
, "%s: read: %s", me
,
513 (n
== 0) ? "Unexpected EOF" : strerror(errno
));
517 if (ctx
->tiID
.opaque
!= NULL
)
519 ctx
->inbuf
.used
+= n
;
520 (*ctx
->logger
)(ctl_debug
, "%s: read %d, used %d", me
,
523 eos
= memchr(ctx
->inbuf
.text
, '\n', ctx
->inbuf
.used
);
524 if (eos
!= NULL
&& eos
!= ctx
->inbuf
.text
&& eos
[-1] == '\r') {
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
,
535 if (arpadone_p(ctx
->inbuf
.text
))
537 else if (arpacont_p(ctx
->inbuf
.text
))
540 /* XXX Doesn't FTP do this sometimes? Is it legal? */
541 (*ctx
->logger
)(ctl_error
, "%s: no arpa flag (%s)", me
,
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
);
552 memmove(ctx
->inbuf
.text
, eos
+ 1, ctx
->inbuf
.used
);
554 UNLINK(ctx
->tran
, tran
, link
);
555 memput(tran
, sizeof *tran
);
560 if (allocated_p(ctx
->inbuf
))
564 if (ctx
->inbuf
.used
== (size_t)MAX_LINELEN
) {
565 (*ctx
->logger
)(ctl_error
, "%s: line too long (%-10s...)", me
,
571 /* Timer related stuff. */
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
,
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
,
597 ctx
->tiID
.opaque
= NULL
;
601 touch_timer(struct ctl_cctx
*ctx
) {
602 REQUIRE(ctx
->tiID
.opaque
!= NULL
);
604 evTouchIdleTimer(ctx
->ev
, ctx
->tiID
);
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
;
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
]);