sd: remove 'ssd' driver support
[unleashed/tickless.git] / usr / src / lib / libresolv2 / common / isc / ctl_clnt.c
blob05e55e686cd3588132951fa32006da370d813154
1 /*
2 * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1998-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 static const char rcsid[] = "$Id: ctl_clnt.c,v 1.11 2008/11/14 02:36:51 marka Exp $";
20 /* Extern. */
22 #include "port_before.h"
24 #include <sys/param.h>
25 #include <sys/file.h>
26 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/nameser.h>
30 #include <arpa/inet.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 #ifdef HAVE_MEMORY_H
40 #include <memory.h>
41 #endif
43 #include <isc/assertions.h>
44 #include <isc/ctl.h>
45 #include <isc/eventlib.h>
46 #include <isc/list.h>
47 #include <isc/memcluster.h>
49 #include "ctl_p.h"
51 #include "port_after.h"
53 /* Constants. */
56 /* Macros. */
58 #define donefunc_p(ctx) ((ctx).donefunc != NULL)
59 #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \
60 isdigit((unsigned char)(line[1])) && \
61 isdigit((unsigned char)(line[2])))
62 #define arpacont_p(line) (line[3] == '-')
63 #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \
64 line[3] == '\r' || line[3] == '\0')
66 /* Types. */
68 enum state {
69 initializing = 0, connecting, connected, destroyed
72 struct ctl_tran {
73 LINK(struct ctl_tran) link;
74 LINK(struct ctl_tran) wlink;
75 struct ctl_cctx * ctx;
76 struct ctl_buf outbuf;
77 ctl_clntdone donefunc;
78 void * uap;
81 struct ctl_cctx {
82 enum state state;
83 evContext ev;
84 int sock;
85 ctl_logfunc logger;
86 ctl_clntdone donefunc;
87 void * uap;
88 evConnID coID;
89 evTimerID tiID;
90 evFileID rdID;
91 evStreamID wrID;
92 struct ctl_buf inbuf;
93 struct timespec timeout;
94 LIST(struct ctl_tran) tran;
95 LIST(struct ctl_tran) wtran;
98 /* Forward. */
100 static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int);
101 static void start_write(struct ctl_cctx *);
102 static void destroy(struct ctl_cctx *, int);
103 static void error(struct ctl_cctx *);
104 static void new_state(struct ctl_cctx *, enum state);
105 static void conn_done(evContext, void *, int,
106 const void *, int,
107 const void *, int);
108 static void write_done(evContext, void *, int, int);
109 static void start_read(struct ctl_cctx *);
110 static void stop_read(struct ctl_cctx *);
111 static void readable(evContext, void *, int, int);
112 static void start_timer(struct ctl_cctx *);
113 static void stop_timer(struct ctl_cctx *);
114 static void touch_timer(struct ctl_cctx *);
115 static void timer(evContext, void *,
116 struct timespec, struct timespec);
118 #ifndef HAVE_MEMCHR
119 static void *
120 memchr(const void *b, int c, size_t len) {
121 const unsigned char *p = b;
122 size_t i;
124 for (i = 0; i < len; i++, p++)
125 if (*p == (unsigned char)c)
126 return ((void *)p);
127 return (NULL);
129 #endif
131 /* Private data. */
133 static const char * const state_names[] = {
134 "initializing", "connecting", "connected", "destroyed"
137 /* Public. */
140 * void
141 * ctl_client()
142 * create, condition, and connect to a listener on the control port.
144 struct ctl_cctx *
145 ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len,
146 const struct sockaddr *sap, size_t sap_len,
147 ctl_clntdone donefunc, void *uap,
148 u_int timeout, ctl_logfunc logger)
150 static const char me[] = "ctl_client";
151 static const int on = 1;
152 struct ctl_cctx *ctx;
153 struct sockaddr *captmp;
155 if (logger == NULL)
156 logger = ctl_logger;
157 ctx = memget(sizeof *ctx);
158 if (ctx == NULL) {
159 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
160 goto fatal;
162 ctx->state = initializing;
163 ctx->ev = lev;
164 ctx->logger = logger;
165 ctx->timeout = evConsTime(timeout, 0);
166 ctx->donefunc = donefunc;
167 ctx->uap = uap;
168 ctx->coID.opaque = NULL;
169 ctx->tiID.opaque = NULL;
170 ctx->rdID.opaque = NULL;
171 ctx->wrID.opaque = NULL;
172 buffer_init(ctx->inbuf);
173 INIT_LIST(ctx->tran);
174 INIT_LIST(ctx->wtran);
175 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
176 if (ctx->sock > evHighestFD(ctx->ev)) {
177 ctx->sock = -1;
178 errno = ENOTSOCK;
180 if (ctx->sock < 0) {
181 (*ctx->logger)(ctl_error, "%s: socket: %s",
182 me, strerror(errno));
183 goto fatal;
185 if (cap != NULL) {
186 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
187 (const char *)&on, sizeof on) != 0) {
188 (*ctx->logger)(ctl_warning,
189 "%s: setsockopt(REUSEADDR): %s",
190 me, strerror(errno));
192 DE_CONST(cap, captmp);
193 if (bind(ctx->sock, captmp, cap_len) < 0) {
194 (*ctx->logger)(ctl_error, "%s: bind: %s", me,
195 strerror(errno));
196 goto fatal;
199 if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len,
200 conn_done, ctx, &ctx->coID) < 0) {
201 (*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s",
202 me, ctx->sock, strerror(errno));
203 fatal:
204 if (ctx != NULL) {
205 if (ctx->sock >= 0)
206 close(ctx->sock);
207 memput(ctx, sizeof *ctx);
209 return (NULL);
211 new_state(ctx, connecting);
212 return (ctx);
216 * void
217 * ctl_endclient(ctx)
218 * close a client and release all of its resources.
220 void
221 ctl_endclient(struct ctl_cctx *ctx) {
222 if (ctx->state != destroyed)
223 destroy(ctx, 0);
224 memput(ctx, sizeof *ctx);
228 * int
229 * ctl_command(ctx, cmd, len, donefunc, uap)
230 * Queue a transaction, which will begin with sending cmd
231 * and complete by calling donefunc with the answer.
234 ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
235 ctl_clntdone donefunc, void *uap)
237 struct ctl_tran *tran;
238 char *pc;
239 unsigned int n;
241 switch (ctx->state) {
242 case destroyed:
243 errno = ENOTCONN;
244 return (-1);
245 case connecting:
246 case connected:
247 break;
248 default:
249 abort();
251 if (len >= (size_t)MAX_LINELEN) {
252 errno = EMSGSIZE;
253 return (-1);
255 tran = new_tran(ctx, donefunc, uap, 1);
256 if (tran == NULL)
257 return (-1);
258 if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
259 return (-1);
260 memcpy(tran->outbuf.text, cmd, len);
261 tran->outbuf.used = len;
262 for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++)
263 if (!isascii((unsigned char)*pc) ||
264 !isprint((unsigned char)*pc))
265 *pc = '\040';
266 start_write(ctx);
267 return (0);
270 /* Private. */
272 static struct ctl_tran *
273 new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) {
274 struct ctl_tran *new = memget(sizeof *new);
276 if (new == NULL)
277 return (NULL);
278 new->ctx = ctx;
279 buffer_init(new->outbuf);
280 new->donefunc = donefunc;
281 new->uap = uap;
282 INIT_LINK(new, link);
283 INIT_LINK(new, wlink);
284 APPEND(ctx->tran, new, link);
285 if (w)
286 APPEND(ctx->wtran, new, wlink);
287 return (new);
290 static void
291 start_write(struct ctl_cctx *ctx) {
292 static const char me[] = "isc/ctl_clnt::start_write";
293 struct ctl_tran *tran;
294 struct iovec iov[2], *iovp = iov;
295 char * tmp;
297 REQUIRE(ctx->state == connecting || ctx->state == connected);
298 /* If there is a write in progress, don't try to write more yet. */
299 if (ctx->wrID.opaque != NULL)
300 return;
301 /* If there are no trans, make sure timer is off, and we're done. */
302 if (EMPTY(ctx->wtran)) {
303 if (ctx->tiID.opaque != NULL)
304 stop_timer(ctx);
305 return;
307 /* Pull it off the head of the write queue. */
308 tran = HEAD(ctx->wtran);
309 UNLINK(ctx->wtran, tran, wlink);
310 /* Since there are some trans, make sure timer is successfully "on". */
311 if (ctx->tiID.opaque != NULL)
312 touch_timer(ctx);
313 else
314 start_timer(ctx);
315 if (ctx->state == destroyed)
316 return;
317 /* Marshall a newline-terminated message and clock it out. */
318 *iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used);
319 DE_CONST("\r\n", tmp);
320 *iovp++ = evConsIovec(tmp, 2);
321 if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov,
322 write_done, tran, &ctx->wrID) < 0) {
323 (*ctx->logger)(ctl_error, "%s: evWrite: %s", me,
324 strerror(errno));
325 error(ctx);
326 return;
328 if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
329 (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
330 strerror(errno));
331 error(ctx);
332 return;
336 static void
337 destroy(struct ctl_cctx *ctx, int notify) {
338 struct ctl_tran *this, *next;
340 if (ctx->sock != -1) {
341 (void) close(ctx->sock);
342 ctx->sock = -1;
344 switch (ctx->state) {
345 case connecting:
346 REQUIRE(ctx->wrID.opaque == NULL);
347 REQUIRE(EMPTY(ctx->tran));
349 * This test is nec'y since destroy() can be called from
350 * start_read() while the state is still "connecting".
352 if (ctx->coID.opaque != NULL) {
353 (void)evCancelConn(ctx->ev, ctx->coID);
354 ctx->coID.opaque = NULL;
356 break;
357 case connected:
358 REQUIRE(ctx->coID.opaque == NULL);
359 if (ctx->wrID.opaque != NULL) {
360 (void)evCancelRW(ctx->ev, ctx->wrID);
361 ctx->wrID.opaque = NULL;
363 if (ctx->rdID.opaque != NULL)
364 stop_read(ctx);
365 break;
366 case destroyed:
367 break;
368 default:
369 abort();
371 if (allocated_p(ctx->inbuf))
372 ctl_bufput(&ctx->inbuf);
373 for (this = HEAD(ctx->tran); this != NULL; this = next) {
374 next = NEXT(this, link);
375 if (allocated_p(this->outbuf))
376 ctl_bufput(&this->outbuf);
377 if (notify && this->donefunc != NULL)
378 (*this->donefunc)(ctx, this->uap, NULL, 0);
379 memput(this, sizeof *this);
381 if (ctx->tiID.opaque != NULL)
382 stop_timer(ctx);
383 new_state(ctx, destroyed);
386 static void
387 error(struct ctl_cctx *ctx) {
388 REQUIRE(ctx->state != destroyed);
389 destroy(ctx, 1);
392 static void
393 new_state(struct ctl_cctx *ctx, enum state new_state) {
394 static const char me[] = "isc/ctl_clnt::new_state";
396 (*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
397 state_names[ctx->state], state_names[new_state]);
398 ctx->state = new_state;
401 static void
402 conn_done(evContext ev, void *uap, int fd,
403 const void *la, int lalen,
404 const void *ra, int ralen)
406 static const char me[] = "isc/ctl_clnt::conn_done";
407 struct ctl_cctx *ctx = uap;
408 struct ctl_tran *tran;
410 UNUSED(ev);
411 UNUSED(la);
412 UNUSED(lalen);
413 UNUSED(ra);
414 UNUSED(ralen);
416 ctx->coID.opaque = NULL;
417 if (fd < 0) {
418 (*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
419 strerror(errno));
420 error(ctx);
421 return;
423 new_state(ctx, connected);
424 tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
425 if (tran == NULL) {
426 (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
427 strerror(errno));
428 error(ctx);
429 return;
431 start_read(ctx);
432 if (ctx->state == destroyed) {
433 (*ctx->logger)(ctl_error, "%s: start_read failed: %s",
434 me, strerror(errno));
435 error(ctx);
436 return;
440 static void
441 write_done(evContext lev, void *uap, int fd, int bytes) {
442 struct ctl_tran *tran = (struct ctl_tran *)uap;
443 struct ctl_cctx *ctx = tran->ctx;
445 UNUSED(lev);
446 UNUSED(fd);
448 ctx->wrID.opaque = NULL;
449 if (ctx->tiID.opaque != NULL)
450 touch_timer(ctx);
451 ctl_bufput(&tran->outbuf);
452 start_write(ctx);
453 if (bytes < 0)
454 destroy(ctx, 1);
455 else
456 start_read(ctx);
459 static void
460 start_read(struct ctl_cctx *ctx) {
461 static const char me[] = "isc/ctl_clnt::start_read";
463 REQUIRE(ctx->state == connecting || ctx->state == connected);
464 REQUIRE(ctx->rdID.opaque == NULL);
465 if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx,
466 &ctx->rdID) < 0)
468 (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
469 ctx->sock, strerror(errno));
470 error(ctx);
471 return;
475 static void
476 stop_read(struct ctl_cctx *ctx) {
477 REQUIRE(ctx->coID.opaque == NULL);
478 REQUIRE(ctx->rdID.opaque != NULL);
479 (void)evDeselectFD(ctx->ev, ctx->rdID);
480 ctx->rdID.opaque = NULL;
483 static void
484 readable(evContext ev, void *uap, int fd, int evmask) {
485 static const char me[] = "isc/ctl_clnt::readable";
486 struct ctl_cctx *ctx = uap;
487 struct ctl_tran *tran;
488 ssize_t n;
489 char *eos;
491 UNUSED(ev);
493 REQUIRE(ctx != NULL);
494 REQUIRE(fd >= 0);
495 REQUIRE(evmask == EV_READ);
496 REQUIRE(ctx->state == connected);
497 REQUIRE(!EMPTY(ctx->tran));
498 tran = HEAD(ctx->tran);
499 if (!allocated_p(ctx->inbuf) &&
500 ctl_bufget(&ctx->inbuf, ctx->logger) < 0) {
501 (*ctx->logger)(ctl_error, "%s: can't get an input buffer", me);
502 error(ctx);
503 return;
505 n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
506 MAX_LINELEN - ctx->inbuf.used);
507 if (n <= 0) {
508 (*ctx->logger)(ctl_warning, "%s: read: %s", me,
509 (n == 0) ? "Unexpected EOF" : strerror(errno));
510 error(ctx);
511 return;
513 if (ctx->tiID.opaque != NULL)
514 touch_timer(ctx);
515 ctx->inbuf.used += n;
516 (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
517 n, ctx->inbuf.used);
518 again:
519 eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
520 if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
521 int done = 0;
523 eos[-1] = '\0';
524 if (!arpacode_p(ctx->inbuf.text)) {
525 /* XXX Doesn't FTP do this sometimes? Is it legal? */
526 (*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me,
527 ctx->inbuf.text);
528 error(ctx);
529 return;
531 if (arpadone_p(ctx->inbuf.text))
532 done = 1;
533 else if (arpacont_p(ctx->inbuf.text))
534 done = 0;
535 else {
536 /* XXX Doesn't FTP do this sometimes? Is it legal? */
537 (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
538 ctx->inbuf.text);
539 error(ctx);
540 return;
542 (*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text,
543 (done ? 0 : CTL_MORE));
544 ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1);
545 if (ctx->inbuf.used == 0U)
546 ctl_bufput(&ctx->inbuf);
547 else
548 memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
549 if (done) {
550 UNLINK(ctx->tran, tran, link);
551 memput(tran, sizeof *tran);
552 stop_read(ctx);
553 start_write(ctx);
554 return;
556 if (allocated_p(ctx->inbuf))
557 goto again;
558 return;
560 if (ctx->inbuf.used == (size_t)MAX_LINELEN) {
561 (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
562 ctx->inbuf.text);
563 error(ctx);
567 /* Timer related stuff. */
569 static void
570 start_timer(struct ctl_cctx *ctx) {
571 static const char me[] = "isc/ctl_clnt::start_timer";
573 REQUIRE(ctx->tiID.opaque == NULL);
574 if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){
575 (*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me,
576 strerror(errno));
577 error(ctx);
578 return;
582 static void
583 stop_timer(struct ctl_cctx *ctx) {
584 static const char me[] = "isc/ctl_clnt::stop_timer";
586 REQUIRE(ctx->tiID.opaque != NULL);
587 if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
588 (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
589 strerror(errno));
590 error(ctx);
591 return;
593 ctx->tiID.opaque = NULL;
596 static void
597 touch_timer(struct ctl_cctx *ctx) {
598 REQUIRE(ctx->tiID.opaque != NULL);
600 evTouchIdleTimer(ctx->ev, ctx->tiID);
603 static void
604 timer(evContext ev, void *uap, struct timespec due, struct timespec itv) {
605 static const char me[] = "isc/ctl_clnt::timer";
606 struct ctl_cctx *ctx = uap;
608 UNUSED(ev);
609 UNUSED(due);
610 UNUSED(itv);
612 ctx->tiID.opaque = NULL;
613 (*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me,
614 ctx->timeout.tv_sec, state_names[ctx->state]);
615 error(ctx);
618 /*! \file */