No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / libbind / dist / isc / ctl_srvr.c
blob7458e3db824cd2c28ae791af3af29a7262ddfeb8
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004-2006, 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_srvr.c,v 1.10 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>
31 #include <sys/un.h>
33 #include <netinet/in.h>
34 #include <arpa/nameser.h>
35 #include <arpa/inet.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #ifdef HAVE_MEMORY_H
46 #include <memory.h>
47 #endif
49 #include <isc/assertions.h>
50 #include <isc/ctl.h>
51 #include <isc/eventlib.h>
52 #include <isc/list.h>
53 #include <isc/logging.h>
54 #include <isc/memcluster.h>
56 #include "ctl_p.h"
58 #include "port_after.h"
60 #ifdef SPRINTF_CHAR
61 # define SPRINTF(x) strlen(sprintf/**/x)
62 #else
63 # define SPRINTF(x) ((size_t)sprintf x)
64 #endif
66 /* Macros. */
68 #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL)
69 #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \
70 tmp, sizeof tmp, ctx->logger)
72 /* Types. */
74 enum state {
75 available = 0, initializing, writing, reading, reading_data,
76 processing, idling, quitting, closing
79 union sa_un {
80 struct sockaddr_in in;
81 #ifndef NO_SOCKADDR_UN
82 struct sockaddr_un un;
83 #endif
86 struct ctl_sess {
87 LINK(struct ctl_sess) link;
88 struct ctl_sctx * ctx;
89 enum state state;
90 int sock;
91 union sa_un sa;
92 evFileID rdID;
93 evStreamID wrID;
94 evTimerID rdtiID;
95 evTimerID wrtiID;
96 struct ctl_buf inbuf;
97 struct ctl_buf outbuf;
98 const struct ctl_verb * verb;
99 u_int helpcode;
100 const void * respctx;
101 u_int respflags;
102 ctl_srvrdone donefunc;
103 void * uap;
104 void * csctx;
107 struct ctl_sctx {
108 evContext ev;
109 void * uctx;
110 u_int unkncode;
111 u_int timeoutcode;
112 const struct ctl_verb * verbs;
113 const struct ctl_verb * connverb;
114 int sock;
115 int max_sess;
116 int cur_sess;
117 struct timespec timeout;
118 ctl_logfunc logger;
119 evConnID acID;
120 LIST(struct ctl_sess) sess;
123 /* Forward. */
125 static void ctl_accept(evContext, void *, int,
126 const void *, int,
127 const void *, int);
128 static void ctl_close(struct ctl_sess *);
129 static void ctl_new_state(struct ctl_sess *,
130 enum state,
131 const char *);
132 static void ctl_start_read(struct ctl_sess *);
133 static void ctl_stop_read(struct ctl_sess *);
134 static void ctl_readable(evContext, void *, int, int);
135 static void ctl_rdtimeout(evContext, void *,
136 struct timespec,
137 struct timespec);
138 static void ctl_wrtimeout(evContext, void *,
139 struct timespec,
140 struct timespec);
141 static void ctl_docommand(struct ctl_sess *);
142 static void ctl_writedone(evContext, void *, int, int);
143 static void ctl_morehelp(struct ctl_sctx *,
144 struct ctl_sess *,
145 const struct ctl_verb *,
146 const char *,
147 u_int, const void *, void *);
148 static void ctl_signal_done(struct ctl_sctx *,
149 struct ctl_sess *);
151 /* Private data. */
153 static const char * state_names[] = {
154 "available", "initializing", "writing", "reading",
155 "reading_data", "processing", "idling", "quitting", "closing"
158 static const char space[] = " ";
160 static const struct ctl_verb fakehelpverb = {
161 "fakehelp", ctl_morehelp , NULL
164 /* Public. */
167 * void
168 * ctl_server()
169 * create, condition, and start a listener on the control port.
171 struct ctl_sctx *
172 ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len,
173 const struct ctl_verb *verbs,
174 u_int unkncode, u_int timeoutcode,
175 u_int timeout, int backlog, int max_sess,
176 ctl_logfunc logger, void *uctx)
178 static const char me[] = "ctl_server";
179 static const int on = 1;
180 const struct ctl_verb *connverb;
181 struct ctl_sctx *ctx;
182 int save_errno;
184 if (logger == NULL)
185 logger = ctl_logger;
186 for (connverb = verbs;
187 connverb->name != NULL && connverb->func != NULL;
188 connverb++)
189 if (connverb->name[0] == '\0')
190 break;
191 if (connverb->func == NULL) {
192 (*logger)(ctl_error, "%s: no connection verb found", me);
193 return (NULL);
195 ctx = memget(sizeof *ctx);
196 if (ctx == NULL) {
197 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
198 return (NULL);
200 ctx->ev = lev;
201 ctx->uctx = uctx;
202 ctx->unkncode = unkncode;
203 ctx->timeoutcode = timeoutcode;
204 ctx->verbs = verbs;
205 ctx->timeout = evConsTime(timeout, 0);
206 ctx->logger = logger;
207 ctx->connverb = connverb;
208 ctx->max_sess = max_sess;
209 ctx->cur_sess = 0;
210 INIT_LIST(ctx->sess);
211 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
212 if (ctx->sock > evHighestFD(ctx->ev)) {
213 ctx->sock = -1;
214 errno = ENOTSOCK;
216 if (ctx->sock < 0) {
217 save_errno = errno;
218 (*ctx->logger)(ctl_error, "%s: socket: %s",
219 me, strerror(errno));
220 memput(ctx, sizeof *ctx);
221 errno = save_errno;
222 return (NULL);
224 if (ctx->sock > evHighestFD(lev)) {
225 close(ctx->sock);
226 (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD");
227 errno = ENFILE;
228 memput(ctx, sizeof *ctx);
229 return (NULL);
231 #ifdef NO_UNIX_REUSEADDR
232 if (sap->sa_family != AF_UNIX)
233 #endif
234 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
235 (const char *)&on, sizeof on) != 0) {
236 (*ctx->logger)(ctl_warning,
237 "%s: setsockopt(REUSEADDR): %s",
238 me, strerror(errno));
240 if (bind(ctx->sock, sap, sap_len) < 0) {
241 char tmp[MAX_NTOP];
242 save_errno = errno;
243 (*ctx->logger)(ctl_error, "%s: bind: %s: %s",
244 me, ctl_sa_ntop((const struct sockaddr *)sap,
245 tmp, sizeof tmp, ctx->logger),
246 strerror(save_errno));
247 close(ctx->sock);
248 memput(ctx, sizeof *ctx);
249 errno = save_errno;
250 return (NULL);
252 if (fcntl(ctx->sock, F_SETFD, 1) < 0) {
253 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
254 strerror(errno));
256 if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx,
257 &ctx->acID) < 0) {
258 save_errno = errno;
259 (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s",
260 me, ctx->sock, strerror(errno));
261 close(ctx->sock);
262 memput(ctx, sizeof *ctx);
263 errno = save_errno;
264 return (NULL);
266 (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d",
267 me, ctx, ctx->sock);
268 return (ctx);
272 * void
273 * ctl_endserver(ctx)
274 * if the control listener is open, close it. clean out all eventlib
275 * stuff. close all active sessions.
277 void
278 ctl_endserver(struct ctl_sctx *ctx) {
279 static const char me[] = "ctl_endserver";
280 struct ctl_sess *this, *next;
282 (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p",
283 me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess);
284 if (ctx->acID.opaque != NULL) {
285 (void)evCancelConn(ctx->ev, ctx->acID);
286 ctx->acID.opaque = NULL;
288 if (ctx->sock != -1) {
289 (void) close(ctx->sock);
290 ctx->sock = -1;
292 for (this = HEAD(ctx->sess); this != NULL; this = next) {
293 next = NEXT(this, link);
294 ctl_close(this);
296 memput(ctx, sizeof *ctx);
300 * If body is non-NULL then it we add a "." line after it.
301 * Caller must have escaped lines with leading ".".
303 void
304 ctl_response(struct ctl_sess *sess, u_int code, const char *text,
305 u_int flags, const void *respctx, ctl_srvrdone donefunc,
306 void *uap, const char *body, size_t bodylen)
308 static const char me[] = "ctl_response";
309 struct iovec iov[3], *iovp = iov;
310 struct ctl_sctx *ctx = sess->ctx;
311 char tmp[MAX_NTOP], *pc;
312 int n;
314 REQUIRE(sess->state == initializing ||
315 sess->state == processing ||
316 sess->state == reading_data ||
317 sess->state == writing);
318 REQUIRE(sess->wrtiID.opaque == NULL);
319 REQUIRE(sess->wrID.opaque == NULL);
320 ctl_new_state(sess, writing, me);
321 sess->donefunc = donefunc;
322 sess->uap = uap;
323 if (!allocated_p(sess->outbuf) &&
324 ctl_bufget(&sess->outbuf, ctx->logger) < 0) {
325 (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer",
326 me, address_expr);
327 goto untimely;
329 if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) {
330 (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing",
331 me, address_expr);
332 goto untimely;
334 sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n",
335 code, (flags & CTL_MORE) != 0 ? '-' : ' ',
336 text));
337 for (pc = sess->outbuf.text, n = 0;
338 n < (int)sess->outbuf.used-2; pc++, n++)
339 if (!isascii((unsigned char)*pc) ||
340 !isprint((unsigned char)*pc))
341 *pc = '\040';
342 *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used);
343 if (body != NULL) {
344 char *tmp;
345 DE_CONST(body, tmp);
346 *iovp++ = evConsIovec(tmp, bodylen);
347 DE_CONST(".\r\n", tmp);
348 *iovp++ = evConsIovec(tmp, 3);
350 (*ctx->logger)(ctl_debug, "%s: [%d] %s", me,
351 sess->outbuf.used, sess->outbuf.text);
352 if (evWrite(ctx->ev, sess->sock, iov, iovp - iov,
353 ctl_writedone, sess, &sess->wrID) < 0) {
354 (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me,
355 address_expr, strerror(errno));
356 goto untimely;
358 if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout,
359 &sess->wrtiID) < 0)
361 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
362 address_expr, strerror(errno));
363 goto untimely;
365 if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) {
366 (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me,
367 address_expr, strerror(errno));
368 untimely:
369 ctl_signal_done(ctx, sess);
370 ctl_close(sess);
371 return;
373 sess->respctx = respctx;
374 sess->respflags = flags;
377 void
378 ctl_sendhelp(struct ctl_sess *sess, u_int code) {
379 static const char me[] = "ctl_sendhelp";
380 struct ctl_sctx *ctx = sess->ctx;
382 sess->helpcode = code;
383 sess->verb = &fakehelpverb;
384 ctl_morehelp(ctx, sess, NULL, me, CTL_MORE,
385 (const void *)ctx->verbs, NULL);
388 void *
389 ctl_getcsctx(struct ctl_sess *sess) {
390 return (sess->csctx);
393 void *
394 ctl_setcsctx(struct ctl_sess *sess, void *csctx) {
395 void *old = sess->csctx;
397 sess->csctx = csctx;
398 return (old);
401 /* Private functions. */
403 static void
404 ctl_accept(evContext lev, void *uap, int fd,
405 const void *lav, int lalen,
406 const void *rav, int ralen)
408 static const char me[] = "ctl_accept";
409 struct ctl_sctx *ctx = uap;
410 struct ctl_sess *sess = NULL;
411 char tmp[MAX_NTOP];
413 UNUSED(lev);
414 UNUSED(lalen);
415 UNUSED(ralen);
417 if (fd < 0) {
418 (*ctx->logger)(ctl_error, "%s: accept: %s",
419 me, strerror(errno));
420 return;
422 if (ctx->cur_sess == ctx->max_sess) {
423 (*ctx->logger)(ctl_error, "%s: %s: too many control sessions",
424 me, ctl_sa_ntop((const struct sockaddr *)rav,
425 tmp, sizeof tmp,
426 ctx->logger));
427 (void) close(fd);
428 return;
430 sess = memget(sizeof *sess);
431 if (sess == NULL) {
432 (*ctx->logger)(ctl_error, "%s: memget: %s", me,
433 strerror(errno));
434 (void) close(fd);
435 return;
437 if (fcntl(fd, F_SETFD, 1) < 0) {
438 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
439 strerror(errno));
441 ctx->cur_sess++;
442 INIT_LINK(sess, link);
443 APPEND(ctx->sess, sess, link);
444 sess->ctx = ctx;
445 sess->sock = fd;
446 sess->wrID.opaque = NULL;
447 sess->rdID.opaque = NULL;
448 sess->wrtiID.opaque = NULL;
449 sess->rdtiID.opaque = NULL;
450 sess->respctx = NULL;
451 sess->csctx = NULL;
452 if (((const struct sockaddr *)rav)->sa_family == AF_UNIX)
453 ctl_sa_copy((const struct sockaddr *)lav,
454 (struct sockaddr *)&sess->sa);
455 else
456 ctl_sa_copy((const struct sockaddr *)rav,
457 (struct sockaddr *)&sess->sa);
458 sess->donefunc = NULL;
459 buffer_init(sess->inbuf);
460 buffer_init(sess->outbuf);
461 sess->state = available;
462 ctl_new_state(sess, initializing, me);
463 sess->verb = ctx->connverb;
464 (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)",
465 me, address_expr, sess->sock);
466 (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0,
467 (const struct sockaddr *)rav, ctx->uctx);
470 static void
471 ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason)
473 static const char me[] = "ctl_new_state";
474 struct ctl_sctx *ctx = sess->ctx;
475 char tmp[MAX_NTOP];
477 (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)",
478 me, address_expr,
479 state_names[sess->state],
480 state_names[new_state], reason);
481 sess->state = new_state;
484 static void
485 ctl_close(struct ctl_sess *sess) {
486 static const char me[] = "ctl_close";
487 struct ctl_sctx *ctx = sess->ctx;
488 char tmp[MAX_NTOP];
490 REQUIRE(sess->state == initializing ||
491 sess->state == writing ||
492 sess->state == reading ||
493 sess->state == processing ||
494 sess->state == reading_data ||
495 sess->state == idling);
496 REQUIRE(sess->sock != -1);
497 if (sess->state == reading || sess->state == reading_data)
498 ctl_stop_read(sess);
499 else if (sess->state == writing) {
500 if (sess->wrID.opaque != NULL) {
501 (void) evCancelRW(ctx->ev, sess->wrID);
502 sess->wrID.opaque = NULL;
504 if (sess->wrtiID.opaque != NULL) {
505 (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
506 sess->wrtiID.opaque = NULL;
509 ctl_new_state(sess, closing, me);
510 (void) close(sess->sock);
511 if (allocated_p(sess->inbuf))
512 ctl_bufput(&sess->inbuf);
513 if (allocated_p(sess->outbuf))
514 ctl_bufput(&sess->outbuf);
515 (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)",
516 me, address_expr, sess->sock);
517 UNLINK(ctx->sess, sess, link);
518 memput(sess, sizeof *sess);
519 ctx->cur_sess--;
522 static void
523 ctl_start_read(struct ctl_sess *sess) {
524 static const char me[] = "ctl_start_read";
525 struct ctl_sctx *ctx = sess->ctx;
526 char tmp[MAX_NTOP];
528 REQUIRE(sess->state == initializing ||
529 sess->state == writing ||
530 sess->state == processing ||
531 sess->state == idling);
532 REQUIRE(sess->rdtiID.opaque == NULL);
533 REQUIRE(sess->rdID.opaque == NULL);
534 sess->inbuf.used = 0;
535 if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout,
536 &sess->rdtiID) < 0)
538 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
539 address_expr, strerror(errno));
540 ctl_close(sess);
541 return;
543 if (evSelectFD(ctx->ev, sess->sock, EV_READ,
544 ctl_readable, sess, &sess->rdID) < 0) {
545 (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me,
546 address_expr, strerror(errno));
547 return;
549 ctl_new_state(sess, reading, me);
552 static void
553 ctl_stop_read(struct ctl_sess *sess) {
554 static const char me[] = "ctl_stop_read";
555 struct ctl_sctx *ctx = sess->ctx;
557 REQUIRE(sess->state == reading || sess->state == reading_data);
558 REQUIRE(sess->rdID.opaque != NULL);
559 (void) evDeselectFD(ctx->ev, sess->rdID);
560 sess->rdID.opaque = NULL;
561 if (sess->rdtiID.opaque != NULL) {
562 (void) evClearIdleTimer(ctx->ev, sess->rdtiID);
563 sess->rdtiID.opaque = NULL;
565 ctl_new_state(sess, idling, me);
568 static void
569 ctl_readable(evContext lev, void *uap, int fd, int evmask) {
570 static const char me[] = "ctl_readable";
571 struct ctl_sess *sess = uap;
572 struct ctl_sctx *ctx;
573 char *eos, tmp[MAX_NTOP];
574 ssize_t n;
576 REQUIRE(sess != NULL);
577 REQUIRE(fd >= 0);
578 REQUIRE(evmask == EV_READ);
579 REQUIRE(sess->state == reading || sess->state == reading_data);
581 ctx = sess->ctx;
582 evTouchIdleTimer(lev, sess->rdtiID);
583 if (!allocated_p(sess->inbuf) &&
584 ctl_bufget(&sess->inbuf, ctx->logger) < 0) {
585 (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer",
586 me, address_expr);
587 ctl_close(sess);
588 return;
590 n = read(sess->sock, sess->inbuf.text + sess->inbuf.used,
591 MAX_LINELEN - sess->inbuf.used);
592 if (n <= 0) {
593 (*ctx->logger)(ctl_debug, "%s: %s: read: %s",
594 me, address_expr,
595 (n == 0) ? "Unexpected EOF" : strerror(errno));
596 ctl_close(sess);
597 return;
599 sess->inbuf.used += n;
600 eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used);
601 if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') {
602 eos[-1] = '\0';
603 if ((sess->respflags & CTL_DATA) != 0) {
604 INSIST(sess->verb != NULL);
605 (*sess->verb->func)(sess->ctx, sess, sess->verb,
606 sess->inbuf.text,
607 CTL_DATA, sess->respctx,
608 sess->ctx->uctx);
609 } else {
610 ctl_stop_read(sess);
611 ctl_docommand(sess);
613 sess->inbuf.used -= ((eos - sess->inbuf.text) + 1);
614 if (sess->inbuf.used == 0U)
615 ctl_bufput(&sess->inbuf);
616 else
617 memmove(sess->inbuf.text, eos + 1, sess->inbuf.used);
618 return;
620 if (sess->inbuf.used == (size_t)MAX_LINELEN) {
621 (*ctx->logger)(ctl_error, "%s: %s: line too long, closing",
622 me, address_expr);
623 ctl_close(sess);
627 static void
628 ctl_wrtimeout(evContext lev, void *uap,
629 struct timespec due,
630 struct timespec itv)
632 static const char me[] = "ctl_wrtimeout";
633 struct ctl_sess *sess = uap;
634 struct ctl_sctx *ctx = sess->ctx;
635 char tmp[MAX_NTOP];
637 UNUSED(lev);
638 UNUSED(due);
639 UNUSED(itv);
641 REQUIRE(sess->state == writing);
642 sess->wrtiID.opaque = NULL;
643 (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing",
644 me, address_expr);
645 if (sess->wrID.opaque != NULL) {
646 (void) evCancelRW(ctx->ev, sess->wrID);
647 sess->wrID.opaque = NULL;
649 ctl_signal_done(ctx, sess);
650 ctl_new_state(sess, processing, me);
651 ctl_close(sess);
654 static void
655 ctl_rdtimeout(evContext lev, void *uap,
656 struct timespec due,
657 struct timespec itv)
659 static const char me[] = "ctl_rdtimeout";
660 struct ctl_sess *sess = uap;
661 struct ctl_sctx *ctx = sess->ctx;
662 char tmp[MAX_NTOP];
664 UNUSED(lev);
665 UNUSED(due);
666 UNUSED(itv);
668 REQUIRE(sess->state == reading);
669 sess->rdtiID.opaque = NULL;
670 (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing",
671 me, address_expr);
672 if (sess->state == reading || sess->state == reading_data)
673 ctl_stop_read(sess);
674 ctl_signal_done(ctx, sess);
675 ctl_new_state(sess, processing, me);
676 ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL,
677 NULL, NULL, NULL, 0);
680 static void
681 ctl_docommand(struct ctl_sess *sess) {
682 static const char me[] = "ctl_docommand";
683 char *name, *rest, tmp[MAX_NTOP];
684 struct ctl_sctx *ctx = sess->ctx;
685 const struct ctl_verb *verb;
687 REQUIRE(allocated_p(sess->inbuf));
688 (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]",
689 me, address_expr,
690 sess->inbuf.text, (u_int)sess->inbuf.used);
691 ctl_new_state(sess, processing, me);
692 name = sess->inbuf.text + strspn(sess->inbuf.text, space);
693 rest = name + strcspn(name, space);
694 if (*rest != '\0') {
695 *rest++ = '\0';
696 rest += strspn(rest, space);
698 for (verb = ctx->verbs;
699 verb != NULL && verb->name != NULL && verb->func != NULL;
700 verb++)
701 if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0)
702 break;
703 if (verb != NULL && verb->name != NULL && verb->func != NULL) {
704 sess->verb = verb;
705 (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx);
706 } else {
707 char buf[1100];
709 if (sizeof "Unrecognized command \"\" (args \"\")" +
710 strlen(name) + strlen(rest) > sizeof buf)
711 strcpy(buf, "Unrecognized command (buf ovf)");
712 else
713 sprintf(buf,
714 "Unrecognized command \"%s\" (args \"%s\")",
715 name, rest);
716 ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL,
717 NULL, 0);
721 static void
722 ctl_writedone(evContext lev, void *uap, int fd, int bytes) {
723 static const char me[] = "ctl_writedone";
724 struct ctl_sess *sess = uap;
725 struct ctl_sctx *ctx = sess->ctx;
726 char tmp[MAX_NTOP];
727 int save_errno = errno;
729 UNUSED(lev);
730 UNUSED(uap);
732 REQUIRE(sess->state == writing);
733 REQUIRE(fd == sess->sock);
734 REQUIRE(sess->wrtiID.opaque != NULL);
735 sess->wrID.opaque = NULL;
736 (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
737 sess->wrtiID.opaque = NULL;
738 if (bytes < 0) {
739 (*ctx->logger)(ctl_error, "%s: %s: %s",
740 me, address_expr, strerror(save_errno));
741 ctl_close(sess);
742 return;
745 INSIST(allocated_p(sess->outbuf));
746 ctl_bufput(&sess->outbuf);
747 if ((sess->respflags & CTL_EXIT) != 0) {
748 ctl_signal_done(ctx, sess);
749 ctl_close(sess);
750 return;
751 } else if ((sess->respflags & CTL_MORE) != 0) {
752 INSIST(sess->verb != NULL);
753 (*sess->verb->func)(sess->ctx, sess, sess->verb, "",
754 CTL_MORE, sess->respctx, sess->ctx->uctx);
755 } else {
756 ctl_signal_done(ctx, sess);
757 ctl_start_read(sess);
761 static void
762 ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess,
763 const struct ctl_verb *verb, const char *text,
764 u_int respflags, const void *respctx, void *uctx)
766 const struct ctl_verb *this = respctx, *next = this + 1;
768 UNUSED(ctx);
769 UNUSED(verb);
770 UNUSED(text);
771 UNUSED(uctx);
773 REQUIRE(!lastverb_p(this));
774 REQUIRE((respflags & CTL_MORE) != 0);
775 if (lastverb_p(next))
776 respflags &= ~CTL_MORE;
777 ctl_response(sess, sess->helpcode, this->help, respflags, next,
778 NULL, NULL, NULL, 0);
781 static void
782 ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) {
783 if (sess->donefunc != NULL) {
784 (*sess->donefunc)(ctx, sess, sess->uap);
785 sess->donefunc = NULL;
789 /*! \file */