1 #if !defined(lint) && !defined(SABER)
2 static const char rcsid[] = "$Id: ctl_srvr.c,v 1.6.18.2 2006/12/07 04:53:02 marka Exp $";
6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1998,1999 by Internet Software Consortium.
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 #include "port_before.h"
26 #include <sys/param.h>
28 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/nameser.h>
33 #include <arpa/inet.h>
44 #include <isc/assertions.h>
46 #include <isc/eventlib.h>
48 #include <isc/logging.h>
49 #include <isc/memcluster.h>
53 #include "port_after.h"
56 # define SPRINTF(x) strlen(sprintf/**/x)
58 # define SPRINTF(x) ((size_t)sprintf x)
63 #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL)
64 #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \
65 tmp, sizeof tmp, ctx->logger)
70 available = 0, initializing, writing, reading, reading_data,
71 processing, idling, quitting, closing
75 struct sockaddr_in in;
76 #ifndef NO_SOCKADDR_UN
77 struct sockaddr_un un;
82 LINK(struct ctl_sess) link;
83 struct ctl_sctx * ctx;
92 struct ctl_buf outbuf;
93 const struct ctl_verb * verb;
97 ctl_srvrdone donefunc;
107 const struct ctl_verb * verbs;
108 const struct ctl_verb * connverb;
112 struct timespec timeout;
115 LIST(struct ctl_sess) sess;
120 static void ctl_accept(evContext, void *, int,
123 static void ctl_close(struct ctl_sess *);
124 static void ctl_new_state(struct ctl_sess *,
127 static void ctl_start_read(struct ctl_sess *);
128 static void ctl_stop_read(struct ctl_sess *);
129 static void ctl_readable(evContext, void *, int, int);
130 static void ctl_rdtimeout(evContext, void *,
133 static void ctl_wrtimeout(evContext, void *,
136 static void ctl_docommand(struct ctl_sess *);
137 static void ctl_writedone(evContext, void *, int, int);
138 static void ctl_morehelp(struct ctl_sctx *,
140 const struct ctl_verb *,
142 u_int, const void *, void *);
143 static void ctl_signal_done(struct ctl_sctx *,
148 static const char * state_names[] = {
149 "available", "initializing", "writing", "reading",
150 "reading_data", "processing", "idling", "quitting", "closing"
153 static const char space[] = " ";
155 static const struct ctl_verb fakehelpverb = {
156 "fakehelp", ctl_morehelp , NULL
164 * create, condition, and start a listener on the control port.
167 ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len,
168 const struct ctl_verb *verbs,
169 u_int unkncode, u_int timeoutcode,
170 u_int timeout, int backlog, int max_sess,
171 ctl_logfunc logger, void *uctx)
173 static const char me[] = "ctl_server";
174 static const int on = 1;
175 const struct ctl_verb *connverb;
176 struct ctl_sctx *ctx;
181 for (connverb = verbs;
182 connverb->name != NULL && connverb->func != NULL;
184 if (connverb->name[0] == '\0')
186 if (connverb->func == NULL) {
187 (*logger)(ctl_error, "%s: no connection verb found", me);
190 ctx = memget(sizeof *ctx);
192 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
197 ctx->unkncode = unkncode;
198 ctx->timeoutcode = timeoutcode;
200 ctx->timeout = evConsTime(timeout, 0);
201 ctx->logger = logger;
202 ctx->connverb = connverb;
203 ctx->max_sess = max_sess;
205 INIT_LIST(ctx->sess);
206 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
207 if (ctx->sock > evHighestFD(ctx->ev)) {
213 (*ctx->logger)(ctl_error, "%s: socket: %s",
214 me, strerror(errno));
215 memput(ctx, sizeof *ctx);
219 if (ctx->sock > evHighestFD(lev)) {
221 (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD");
223 memput(ctx, sizeof *ctx);
226 #ifdef NO_UNIX_REUSEADDR
227 if (sap->sa_family != AF_UNIX)
229 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
230 (const char *)&on, sizeof on) != 0) {
231 (*ctx->logger)(ctl_warning,
232 "%s: setsockopt(REUSEADDR): %s",
233 me, strerror(errno));
235 if (bind(ctx->sock, sap, sap_len) < 0) {
238 (*ctx->logger)(ctl_error, "%s: bind: %s: %s",
239 me, ctl_sa_ntop((const struct sockaddr *)sap,
240 tmp, sizeof tmp, ctx->logger),
241 strerror(save_errno));
243 memput(ctx, sizeof *ctx);
247 if (fcntl(ctx->sock, F_SETFD, 1) < 0) {
248 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
251 if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx,
254 (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s",
255 me, ctx->sock, strerror(errno));
257 memput(ctx, sizeof *ctx);
261 (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d",
269 * if the control listener is open, close it. clean out all eventlib
270 * stuff. close all active sessions.
273 ctl_endserver(struct ctl_sctx *ctx) {
274 static const char me[] = "ctl_endserver";
275 struct ctl_sess *this, *next;
277 (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p",
278 me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess);
279 if (ctx->acID.opaque != NULL) {
280 (void)evCancelConn(ctx->ev, ctx->acID);
281 ctx->acID.opaque = NULL;
283 if (ctx->sock != -1) {
284 (void) close(ctx->sock);
287 for (this = HEAD(ctx->sess); this != NULL; this = next) {
288 next = NEXT(this, link);
291 memput(ctx, sizeof *ctx);
295 * If body is non-NULL then it we add a "." line after it.
296 * Caller must have escaped lines with leading ".".
299 ctl_response(struct ctl_sess *sess, u_int code, const char *text,
300 u_int flags, const void *respctx, ctl_srvrdone donefunc,
301 void *uap, const char *body, size_t bodylen)
303 static const char me[] = "ctl_response";
304 struct iovec iov[3], *iovp = iov;
305 struct ctl_sctx *ctx = sess->ctx;
306 char tmp[MAX_NTOP], *pc;
309 REQUIRE(sess->state == initializing ||
310 sess->state == processing ||
311 sess->state == reading_data ||
312 sess->state == writing);
313 REQUIRE(sess->wrtiID.opaque == NULL);
314 REQUIRE(sess->wrID.opaque == NULL);
315 ctl_new_state(sess, writing, me);
316 sess->donefunc = donefunc;
318 if (!allocated_p(sess->outbuf) &&
319 ctl_bufget(&sess->outbuf, ctx->logger) < 0) {
320 (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer",
324 if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) {
325 (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing",
329 sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n",
330 code, (flags & CTL_MORE) != 0 ? '-' : ' ',
332 for (pc = sess->outbuf.text, n = 0;
333 n < (int)sess->outbuf.used-2; pc++, n++)
334 if (!isascii((unsigned char)*pc) ||
335 !isprint((unsigned char)*pc))
337 *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used);
341 *iovp++ = evConsIovec(tmp, bodylen);
342 DE_CONST(".\r\n", tmp);
343 *iovp++ = evConsIovec(tmp, 3);
345 (*ctx->logger)(ctl_debug, "%s: [%d] %s", me,
346 sess->outbuf.used, sess->outbuf.text);
347 if (evWrite(ctx->ev, sess->sock, iov, iovp - iov,
348 ctl_writedone, sess, &sess->wrID) < 0) {
349 (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me,
350 address_expr, strerror(errno));
353 if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout,
356 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
357 address_expr, strerror(errno));
360 if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) {
361 (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me,
362 address_expr, strerror(errno));
364 ctl_signal_done(ctx, sess);
368 sess->respctx = respctx;
369 sess->respflags = flags;
373 ctl_sendhelp(struct ctl_sess *sess, u_int code) {
374 static const char me[] = "ctl_sendhelp";
375 struct ctl_sctx *ctx = sess->ctx;
377 sess->helpcode = code;
378 sess->verb = &fakehelpverb;
379 ctl_morehelp(ctx, sess, NULL, me, CTL_MORE,
380 (const void *)ctx->verbs, NULL);
384 ctl_getcsctx(struct ctl_sess *sess) {
385 return (sess->csctx);
389 ctl_setcsctx(struct ctl_sess *sess, void *csctx) {
390 void *old = sess->csctx;
396 /* Private functions. */
399 ctl_accept(evContext lev, void *uap, int fd,
400 const void *lav, int lalen,
401 const void *rav, int ralen)
403 static const char me[] = "ctl_accept";
404 struct ctl_sctx *ctx = uap;
405 struct ctl_sess *sess = NULL;
413 (*ctx->logger)(ctl_error, "%s: accept: %s",
414 me, strerror(errno));
417 if (ctx->cur_sess == ctx->max_sess) {
418 (*ctx->logger)(ctl_error, "%s: %s: too many control sessions",
419 me, ctl_sa_ntop((const struct sockaddr *)rav,
425 sess = memget(sizeof *sess);
427 (*ctx->logger)(ctl_error, "%s: memget: %s", me,
432 if (fcntl(fd, F_SETFD, 1) < 0) {
433 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
437 INIT_LINK(sess, link);
438 APPEND(ctx->sess, sess, link);
441 sess->wrID.opaque = NULL;
442 sess->rdID.opaque = NULL;
443 sess->wrtiID.opaque = NULL;
444 sess->rdtiID.opaque = NULL;
445 sess->respctx = NULL;
447 if (((const struct sockaddr *)rav)->sa_family == AF_UNIX)
448 ctl_sa_copy((const struct sockaddr *)lav,
449 (struct sockaddr *)&sess->sa);
451 ctl_sa_copy((const struct sockaddr *)rav,
452 (struct sockaddr *)&sess->sa);
453 sess->donefunc = NULL;
454 buffer_init(sess->inbuf);
455 buffer_init(sess->outbuf);
456 sess->state = available;
457 ctl_new_state(sess, initializing, me);
458 sess->verb = ctx->connverb;
459 (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)",
460 me, address_expr, sess->sock);
461 (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0,
462 (const struct sockaddr *)rav, ctx->uctx);
466 ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason)
468 static const char me[] = "ctl_new_state";
469 struct ctl_sctx *ctx = sess->ctx;
472 (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)",
474 state_names[sess->state],
475 state_names[new_state], reason);
476 sess->state = new_state;
480 ctl_close(struct ctl_sess *sess) {
481 static const char me[] = "ctl_close";
482 struct ctl_sctx *ctx = sess->ctx;
485 REQUIRE(sess->state == initializing ||
486 sess->state == writing ||
487 sess->state == reading ||
488 sess->state == processing ||
489 sess->state == reading_data ||
490 sess->state == idling);
491 REQUIRE(sess->sock != -1);
492 if (sess->state == reading || sess->state == reading_data)
494 else if (sess->state == writing) {
495 if (sess->wrID.opaque != NULL) {
496 (void) evCancelRW(ctx->ev, sess->wrID);
497 sess->wrID.opaque = NULL;
499 if (sess->wrtiID.opaque != NULL) {
500 (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
501 sess->wrtiID.opaque = NULL;
504 ctl_new_state(sess, closing, me);
505 (void) close(sess->sock);
506 if (allocated_p(sess->inbuf))
507 ctl_bufput(&sess->inbuf);
508 if (allocated_p(sess->outbuf))
509 ctl_bufput(&sess->outbuf);
510 (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)",
511 me, address_expr, sess->sock);
512 UNLINK(ctx->sess, sess, link);
513 memput(sess, sizeof *sess);
518 ctl_start_read(struct ctl_sess *sess) {
519 static const char me[] = "ctl_start_read";
520 struct ctl_sctx *ctx = sess->ctx;
523 REQUIRE(sess->state == initializing ||
524 sess->state == writing ||
525 sess->state == processing ||
526 sess->state == idling);
527 REQUIRE(sess->rdtiID.opaque == NULL);
528 REQUIRE(sess->rdID.opaque == NULL);
529 sess->inbuf.used = 0;
530 if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout,
533 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
534 address_expr, strerror(errno));
538 if (evSelectFD(ctx->ev, sess->sock, EV_READ,
539 ctl_readable, sess, &sess->rdID) < 0) {
540 (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me,
541 address_expr, strerror(errno));
544 ctl_new_state(sess, reading, me);
548 ctl_stop_read(struct ctl_sess *sess) {
549 static const char me[] = "ctl_stop_read";
550 struct ctl_sctx *ctx = sess->ctx;
552 REQUIRE(sess->state == reading || sess->state == reading_data);
553 REQUIRE(sess->rdID.opaque != NULL);
554 (void) evDeselectFD(ctx->ev, sess->rdID);
555 sess->rdID.opaque = NULL;
556 if (sess->rdtiID.opaque != NULL) {
557 (void) evClearIdleTimer(ctx->ev, sess->rdtiID);
558 sess->rdtiID.opaque = NULL;
560 ctl_new_state(sess, idling, me);
564 ctl_readable(evContext lev, void *uap, int fd, int evmask) {
565 static const char me[] = "ctl_readable";
566 struct ctl_sess *sess = uap;
567 struct ctl_sctx *ctx;
568 char *eos, tmp[MAX_NTOP];
571 REQUIRE(sess != NULL);
573 REQUIRE(evmask == EV_READ);
574 REQUIRE(sess->state == reading || sess->state == reading_data);
577 evTouchIdleTimer(lev, sess->rdtiID);
578 if (!allocated_p(sess->inbuf) &&
579 ctl_bufget(&sess->inbuf, ctx->logger) < 0) {
580 (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer",
585 n = read(sess->sock, sess->inbuf.text + sess->inbuf.used,
586 MAX_LINELEN - sess->inbuf.used);
588 (*ctx->logger)(ctl_debug, "%s: %s: read: %s",
590 (n == 0) ? "Unexpected EOF" : strerror(errno));
594 sess->inbuf.used += n;
595 eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used);
596 if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') {
598 if ((sess->respflags & CTL_DATA) != 0) {
599 INSIST(sess->verb != NULL);
600 (*sess->verb->func)(sess->ctx, sess, sess->verb,
602 CTL_DATA, sess->respctx,
608 sess->inbuf.used -= ((eos - sess->inbuf.text) + 1);
609 if (sess->inbuf.used == 0U)
610 ctl_bufput(&sess->inbuf);
612 memmove(sess->inbuf.text, eos + 1, sess->inbuf.used);
615 if (sess->inbuf.used == (size_t)MAX_LINELEN) {
616 (*ctx->logger)(ctl_error, "%s: %s: line too long, closing",
623 ctl_wrtimeout(evContext lev, void *uap,
627 static const char me[] = "ctl_wrtimeout";
628 struct ctl_sess *sess = uap;
629 struct ctl_sctx *ctx = sess->ctx;
636 REQUIRE(sess->state == writing);
637 sess->wrtiID.opaque = NULL;
638 (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing",
640 if (sess->wrID.opaque != NULL) {
641 (void) evCancelRW(ctx->ev, sess->wrID);
642 sess->wrID.opaque = NULL;
644 ctl_signal_done(ctx, sess);
645 ctl_new_state(sess, processing, me);
650 ctl_rdtimeout(evContext lev, void *uap,
654 static const char me[] = "ctl_rdtimeout";
655 struct ctl_sess *sess = uap;
656 struct ctl_sctx *ctx = sess->ctx;
663 REQUIRE(sess->state == reading);
664 sess->rdtiID.opaque = NULL;
665 (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing",
667 if (sess->state == reading || sess->state == reading_data)
669 ctl_signal_done(ctx, sess);
670 ctl_new_state(sess, processing, me);
671 ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL,
672 NULL, NULL, NULL, 0);
676 ctl_docommand(struct ctl_sess *sess) {
677 static const char me[] = "ctl_docommand";
678 char *name, *rest, tmp[MAX_NTOP];
679 struct ctl_sctx *ctx = sess->ctx;
680 const struct ctl_verb *verb;
682 REQUIRE(allocated_p(sess->inbuf));
683 (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]",
685 sess->inbuf.text, (u_int)sess->inbuf.used);
686 ctl_new_state(sess, processing, me);
687 name = sess->inbuf.text + strspn(sess->inbuf.text, space);
688 rest = name + strcspn(name, space);
691 rest += strspn(rest, space);
693 for (verb = ctx->verbs;
694 verb != NULL && verb->name != NULL && verb->func != NULL;
696 if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0)
698 if (verb != NULL && verb->name != NULL && verb->func != NULL) {
700 (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx);
704 if (sizeof "Unrecognized command \"\" (args \"\")" +
705 strlen(name) + strlen(rest) > sizeof buf)
706 strcpy(buf, "Unrecognized command (buf ovf)");
709 "Unrecognized command \"%s\" (args \"%s\")",
711 ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL,
717 ctl_writedone(evContext lev, void *uap, int fd, int bytes) {
718 static const char me[] = "ctl_writedone";
719 struct ctl_sess *sess = uap;
720 struct ctl_sctx *ctx = sess->ctx;
722 int save_errno = errno;
727 REQUIRE(sess->state == writing);
728 REQUIRE(fd == sess->sock);
729 REQUIRE(sess->wrtiID.opaque != NULL);
730 sess->wrID.opaque = NULL;
731 (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
732 sess->wrtiID.opaque = NULL;
734 (*ctx->logger)(ctl_error, "%s: %s: %s",
735 me, address_expr, strerror(save_errno));
740 INSIST(allocated_p(sess->outbuf));
741 ctl_bufput(&sess->outbuf);
742 if ((sess->respflags & CTL_EXIT) != 0) {
743 ctl_signal_done(ctx, sess);
746 } else if ((sess->respflags & CTL_MORE) != 0) {
747 INSIST(sess->verb != NULL);
748 (*sess->verb->func)(sess->ctx, sess, sess->verb, "",
749 CTL_MORE, sess->respctx, sess->ctx->uctx);
751 ctl_signal_done(ctx, sess);
752 ctl_start_read(sess);
757 ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess,
758 const struct ctl_verb *verb, const char *text,
759 u_int respflags, const void *respctx, void *uctx)
761 const struct ctl_verb *this = respctx, *next = this + 1;
768 REQUIRE(!lastverb_p(this));
769 REQUIRE((respflags & CTL_MORE) != 0);
770 if (lastverb_p(next))
771 respflags &= ~CTL_MORE;
772 ctl_response(sess, sess->helpcode, this->help, respflags, next,
773 NULL, NULL, NULL, 0);
777 ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) {
778 if (sess->donefunc != NULL) {
779 (*sess->donefunc)(ctx, sess, sess->uap);
780 sess->donefunc = NULL;