sd: remove 'ssd' driver support
[unleashed/tickless.git] / usr / src / lib / libresolv2 / common / isc / ctl_srvr.c
blobc6de27cf73dcb7db723caad30faa6b33323e3aff
1 /*
2 * Copyright (C) 2004-2006, 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_srvr.c,v 1.10 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>
27 #include <sys/un.h>
29 #include <netinet/in.h>
30 #include <arpa/nameser.h>
31 #include <arpa/inet.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #ifdef HAVE_MEMORY_H
42 #include <memory.h>
43 #endif
45 #include <isc/assertions.h>
46 #include <isc/ctl.h>
47 #include <isc/eventlib.h>
48 #include <isc/list.h>
49 #include <isc/logging.h>
50 #include <isc/memcluster.h>
52 #include "ctl_p.h"
54 #include "port_after.h"
56 #ifdef SPRINTF_CHAR
57 # define SPRINTF(x) strlen(sprintf/**/x)
58 #else
59 # define SPRINTF(x) ((size_t)sprintf x)
60 #endif
62 /* Macros. */
64 #define lastverb_p(verb) (verb->name == NULL || verb->func == NULL)
65 #define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \
66 tmp, sizeof tmp, ctx->logger)
68 /* Types. */
70 enum state {
71 available = 0, initializing, writing, reading, reading_data,
72 processing, idling, quitting, closing
75 union sa_un {
76 struct sockaddr_in in;
77 #ifndef NO_SOCKADDR_UN
78 struct sockaddr_un un;
79 #endif
82 struct ctl_sess {
83 LINK(struct ctl_sess) link;
84 struct ctl_sctx * ctx;
85 enum state state;
86 int sock;
87 union sa_un sa;
88 evFileID rdID;
89 evStreamID wrID;
90 evTimerID rdtiID;
91 evTimerID wrtiID;
92 struct ctl_buf inbuf;
93 struct ctl_buf outbuf;
94 const struct ctl_verb * verb;
95 u_int helpcode;
96 const void * respctx;
97 u_int respflags;
98 ctl_srvrdone donefunc;
99 void * uap;
100 void * csctx;
103 struct ctl_sctx {
104 evContext ev;
105 void * uctx;
106 u_int unkncode;
107 u_int timeoutcode;
108 const struct ctl_verb * verbs;
109 const struct ctl_verb * connverb;
110 int sock;
111 int max_sess;
112 int cur_sess;
113 struct timespec timeout;
114 ctl_logfunc logger;
115 evConnID acID;
116 LIST(struct ctl_sess) sess;
119 /* Forward. */
121 static void ctl_accept(evContext, void *, int,
122 const void *, int,
123 const void *, int);
124 static void ctl_close(struct ctl_sess *);
125 static void ctl_new_state(struct ctl_sess *,
126 enum state,
127 const char *);
128 static void ctl_start_read(struct ctl_sess *);
129 static void ctl_stop_read(struct ctl_sess *);
130 static void ctl_readable(evContext, void *, int, int);
131 static void ctl_rdtimeout(evContext, void *,
132 struct timespec,
133 struct timespec);
134 static void ctl_wrtimeout(evContext, void *,
135 struct timespec,
136 struct timespec);
137 static void ctl_docommand(struct ctl_sess *);
138 static void ctl_writedone(evContext, void *, int, int);
139 static void ctl_morehelp(struct ctl_sctx *,
140 struct ctl_sess *,
141 const struct ctl_verb *,
142 const char *,
143 u_int, const void *, void *);
144 static void ctl_signal_done(struct ctl_sctx *,
145 struct ctl_sess *);
147 /* Private data. */
149 static const char * state_names[] = {
150 "available", "initializing", "writing", "reading",
151 "reading_data", "processing", "idling", "quitting", "closing"
154 static const char space[] = " ";
156 static const struct ctl_verb fakehelpverb = {
157 "fakehelp", ctl_morehelp , NULL
160 /* Public. */
163 * void
164 * ctl_server()
165 * create, condition, and start a listener on the control port.
167 struct ctl_sctx *
168 ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len,
169 const struct ctl_verb *verbs,
170 u_int unkncode, u_int timeoutcode,
171 u_int timeout, int backlog, int max_sess,
172 ctl_logfunc logger, void *uctx)
174 static const char me[] = "ctl_server";
175 static const int on = 1;
176 const struct ctl_verb *connverb;
177 struct ctl_sctx *ctx;
178 int save_errno;
180 if (logger == NULL)
181 logger = ctl_logger;
182 for (connverb = verbs;
183 connverb->name != NULL && connverb->func != NULL;
184 connverb++)
185 if (connverb->name[0] == '\0')
186 break;
187 if (connverb->func == NULL) {
188 (*logger)(ctl_error, "%s: no connection verb found", me);
189 return (NULL);
191 ctx = memget(sizeof *ctx);
192 if (ctx == NULL) {
193 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
194 return (NULL);
196 ctx->ev = lev;
197 ctx->uctx = uctx;
198 ctx->unkncode = unkncode;
199 ctx->timeoutcode = timeoutcode;
200 ctx->verbs = verbs;
201 ctx->timeout = evConsTime(timeout, 0);
202 ctx->logger = logger;
203 ctx->connverb = connverb;
204 ctx->max_sess = max_sess;
205 ctx->cur_sess = 0;
206 INIT_LIST(ctx->sess);
207 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
208 if (ctx->sock > evHighestFD(ctx->ev)) {
209 ctx->sock = -1;
210 errno = ENOTSOCK;
212 if (ctx->sock < 0) {
213 save_errno = errno;
214 (*ctx->logger)(ctl_error, "%s: socket: %s",
215 me, strerror(errno));
216 memput(ctx, sizeof *ctx);
217 errno = save_errno;
218 return (NULL);
220 if (ctx->sock > evHighestFD(lev)) {
221 close(ctx->sock);
222 (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD");
223 errno = ENFILE;
224 memput(ctx, sizeof *ctx);
225 return (NULL);
227 #ifdef NO_UNIX_REUSEADDR
228 if (sap->sa_family != AF_UNIX)
229 #endif
230 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
231 (const char *)&on, sizeof on) != 0) {
232 (*ctx->logger)(ctl_warning,
233 "%s: setsockopt(REUSEADDR): %s",
234 me, strerror(errno));
236 if (bind(ctx->sock, sap, sap_len) < 0) {
237 char tmp[MAX_NTOP];
238 save_errno = errno;
239 (*ctx->logger)(ctl_error, "%s: bind: %s: %s",
240 me, ctl_sa_ntop((const struct sockaddr *)sap,
241 tmp, sizeof tmp, ctx->logger),
242 strerror(save_errno));
243 close(ctx->sock);
244 memput(ctx, sizeof *ctx);
245 errno = save_errno;
246 return (NULL);
248 if (fcntl(ctx->sock, F_SETFD, 1) < 0) {
249 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
250 strerror(errno));
252 if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx,
253 &ctx->acID) < 0) {
254 save_errno = errno;
255 (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s",
256 me, ctx->sock, strerror(errno));
257 close(ctx->sock);
258 memput(ctx, sizeof *ctx);
259 errno = save_errno;
260 return (NULL);
262 (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d",
263 me, ctx, ctx->sock);
264 return (ctx);
268 * void
269 * ctl_endserver(ctx)
270 * if the control listener is open, close it. clean out all eventlib
271 * stuff. close all active sessions.
273 void
274 ctl_endserver(struct ctl_sctx *ctx) {
275 static const char me[] = "ctl_endserver";
276 struct ctl_sess *this, *next;
278 (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p",
279 me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess);
280 if (ctx->acID.opaque != NULL) {
281 (void)evCancelConn(ctx->ev, ctx->acID);
282 ctx->acID.opaque = NULL;
284 if (ctx->sock != -1) {
285 (void) close(ctx->sock);
286 ctx->sock = -1;
288 for (this = HEAD(ctx->sess); this != NULL; this = next) {
289 next = NEXT(this, link);
290 ctl_close(this);
292 memput(ctx, sizeof *ctx);
296 * If body is non-NULL then it we add a "." line after it.
297 * Caller must have escaped lines with leading ".".
299 void
300 ctl_response(struct ctl_sess *sess, u_int code, const char *text,
301 u_int flags, const void *respctx, ctl_srvrdone donefunc,
302 void *uap, const char *body, size_t bodylen)
304 static const char me[] = "ctl_response";
305 struct iovec iov[3], *iovp = iov;
306 struct ctl_sctx *ctx = sess->ctx;
307 char tmp[MAX_NTOP], *pc;
308 int n;
310 REQUIRE(sess->state == initializing ||
311 sess->state == processing ||
312 sess->state == reading_data ||
313 sess->state == writing);
314 REQUIRE(sess->wrtiID.opaque == NULL);
315 REQUIRE(sess->wrID.opaque == NULL);
316 ctl_new_state(sess, writing, me);
317 sess->donefunc = donefunc;
318 sess->uap = uap;
319 if (!allocated_p(sess->outbuf) &&
320 ctl_bufget(&sess->outbuf, ctx->logger) < 0) {
321 (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer",
322 me, address_expr);
323 goto untimely;
325 if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) {
326 (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing",
327 me, address_expr);
328 goto untimely;
330 sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n",
331 code, (flags & CTL_MORE) != 0 ? '-' : ' ',
332 text));
333 for (pc = sess->outbuf.text, n = 0;
334 n < (int)sess->outbuf.used-2; pc++, n++)
335 if (!isascii((unsigned char)*pc) ||
336 !isprint((unsigned char)*pc))
337 *pc = '\040';
338 *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used);
339 if (body != NULL) {
340 char *tmp;
341 DE_CONST(body, tmp);
342 *iovp++ = evConsIovec(tmp, bodylen);
343 DE_CONST(".\r\n", tmp);
344 *iovp++ = evConsIovec(tmp, 3);
346 (*ctx->logger)(ctl_debug, "%s: [%d] %s", me,
347 sess->outbuf.used, sess->outbuf.text);
348 if (evWrite(ctx->ev, sess->sock, iov, iovp - iov,
349 ctl_writedone, sess, &sess->wrID) < 0) {
350 (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me,
351 address_expr, strerror(errno));
352 goto untimely;
354 if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout,
355 &sess->wrtiID) < 0)
357 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
358 address_expr, strerror(errno));
359 goto untimely;
361 if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) {
362 (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me,
363 address_expr, strerror(errno));
364 untimely:
365 ctl_signal_done(ctx, sess);
366 ctl_close(sess);
367 return;
369 sess->respctx = respctx;
370 sess->respflags = flags;
373 void
374 ctl_sendhelp(struct ctl_sess *sess, u_int code) {
375 static const char me[] = "ctl_sendhelp";
376 struct ctl_sctx *ctx = sess->ctx;
378 sess->helpcode = code;
379 sess->verb = &fakehelpverb;
380 ctl_morehelp(ctx, sess, NULL, me, CTL_MORE,
381 (const void *)ctx->verbs, NULL);
384 void *
385 ctl_getcsctx(struct ctl_sess *sess) {
386 return (sess->csctx);
389 void *
390 ctl_setcsctx(struct ctl_sess *sess, void *csctx) {
391 void *old = sess->csctx;
393 sess->csctx = csctx;
394 return (old);
397 /* Private functions. */
399 static void
400 ctl_accept(evContext lev, void *uap, int fd,
401 const void *lav, int lalen,
402 const void *rav, int ralen)
404 static const char me[] = "ctl_accept";
405 struct ctl_sctx *ctx = uap;
406 struct ctl_sess *sess = NULL;
407 char tmp[MAX_NTOP];
409 UNUSED(lev);
410 UNUSED(lalen);
411 UNUSED(ralen);
413 if (fd < 0) {
414 (*ctx->logger)(ctl_error, "%s: accept: %s",
415 me, strerror(errno));
416 return;
418 if (ctx->cur_sess == ctx->max_sess) {
419 (*ctx->logger)(ctl_error, "%s: %s: too many control sessions",
420 me, ctl_sa_ntop((const struct sockaddr *)rav,
421 tmp, sizeof tmp,
422 ctx->logger));
423 (void) close(fd);
424 return;
426 sess = memget(sizeof *sess);
427 if (sess == NULL) {
428 (*ctx->logger)(ctl_error, "%s: memget: %s", me,
429 strerror(errno));
430 (void) close(fd);
431 return;
433 if (fcntl(fd, F_SETFD, 1) < 0) {
434 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
435 strerror(errno));
437 ctx->cur_sess++;
438 INIT_LINK(sess, link);
439 APPEND(ctx->sess, sess, link);
440 sess->ctx = ctx;
441 sess->sock = fd;
442 sess->wrID.opaque = NULL;
443 sess->rdID.opaque = NULL;
444 sess->wrtiID.opaque = NULL;
445 sess->rdtiID.opaque = NULL;
446 sess->respctx = NULL;
447 sess->csctx = NULL;
448 if (((const struct sockaddr *)rav)->sa_family == AF_UNIX)
449 ctl_sa_copy((const struct sockaddr *)lav,
450 (struct sockaddr *)&sess->sa);
451 else
452 ctl_sa_copy((const struct sockaddr *)rav,
453 (struct sockaddr *)&sess->sa);
454 sess->donefunc = NULL;
455 buffer_init(sess->inbuf);
456 buffer_init(sess->outbuf);
457 sess->state = available;
458 ctl_new_state(sess, initializing, me);
459 sess->verb = ctx->connverb;
460 (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)",
461 me, address_expr, sess->sock);
462 (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0,
463 (const struct sockaddr *)rav, ctx->uctx);
466 static void
467 ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason)
469 static const char me[] = "ctl_new_state";
470 struct ctl_sctx *ctx = sess->ctx;
471 char tmp[MAX_NTOP];
473 (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)",
474 me, address_expr,
475 state_names[sess->state],
476 state_names[new_state], reason);
477 sess->state = new_state;
480 static void
481 ctl_close(struct ctl_sess *sess) {
482 static const char me[] = "ctl_close";
483 struct ctl_sctx *ctx = sess->ctx;
484 char tmp[MAX_NTOP];
486 REQUIRE(sess->state == initializing ||
487 sess->state == writing ||
488 sess->state == reading ||
489 sess->state == processing ||
490 sess->state == reading_data ||
491 sess->state == idling);
492 REQUIRE(sess->sock != -1);
493 if (sess->state == reading || sess->state == reading_data)
494 ctl_stop_read(sess);
495 else if (sess->state == writing) {
496 if (sess->wrID.opaque != NULL) {
497 (void) evCancelRW(ctx->ev, sess->wrID);
498 sess->wrID.opaque = NULL;
500 if (sess->wrtiID.opaque != NULL) {
501 (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
502 sess->wrtiID.opaque = NULL;
505 ctl_new_state(sess, closing, me);
506 (void) close(sess->sock);
507 if (allocated_p(sess->inbuf))
508 ctl_bufput(&sess->inbuf);
509 if (allocated_p(sess->outbuf))
510 ctl_bufput(&sess->outbuf);
511 (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)",
512 me, address_expr, sess->sock);
513 UNLINK(ctx->sess, sess, link);
514 memput(sess, sizeof *sess);
515 ctx->cur_sess--;
518 static void
519 ctl_start_read(struct ctl_sess *sess) {
520 static const char me[] = "ctl_start_read";
521 struct ctl_sctx *ctx = sess->ctx;
522 char tmp[MAX_NTOP];
524 REQUIRE(sess->state == initializing ||
525 sess->state == writing ||
526 sess->state == processing ||
527 sess->state == idling);
528 REQUIRE(sess->rdtiID.opaque == NULL);
529 REQUIRE(sess->rdID.opaque == NULL);
530 sess->inbuf.used = 0;
531 if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout,
532 &sess->rdtiID) < 0)
534 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
535 address_expr, strerror(errno));
536 ctl_close(sess);
537 return;
539 if (evSelectFD(ctx->ev, sess->sock, EV_READ,
540 ctl_readable, sess, &sess->rdID) < 0) {
541 (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me,
542 address_expr, strerror(errno));
543 return;
545 ctl_new_state(sess, reading, me);
548 static void
549 ctl_stop_read(struct ctl_sess *sess) {
550 static const char me[] = "ctl_stop_read";
551 struct ctl_sctx *ctx = sess->ctx;
553 REQUIRE(sess->state == reading || sess->state == reading_data);
554 REQUIRE(sess->rdID.opaque != NULL);
555 (void) evDeselectFD(ctx->ev, sess->rdID);
556 sess->rdID.opaque = NULL;
557 if (sess->rdtiID.opaque != NULL) {
558 (void) evClearIdleTimer(ctx->ev, sess->rdtiID);
559 sess->rdtiID.opaque = NULL;
561 ctl_new_state(sess, idling, me);
564 static void
565 ctl_readable(evContext lev, void *uap, int fd, int evmask) {
566 static const char me[] = "ctl_readable";
567 struct ctl_sess *sess = uap;
568 struct ctl_sctx *ctx;
569 char *eos, tmp[MAX_NTOP];
570 ssize_t n;
572 REQUIRE(sess != NULL);
573 REQUIRE(fd >= 0);
574 REQUIRE(evmask == EV_READ);
575 REQUIRE(sess->state == reading || sess->state == reading_data);
577 ctx = sess->ctx;
578 evTouchIdleTimer(lev, sess->rdtiID);
579 if (!allocated_p(sess->inbuf) &&
580 ctl_bufget(&sess->inbuf, ctx->logger) < 0) {
581 (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer",
582 me, address_expr);
583 ctl_close(sess);
584 return;
586 n = read(sess->sock, sess->inbuf.text + sess->inbuf.used,
587 MAX_LINELEN - sess->inbuf.used);
588 if (n <= 0) {
589 (*ctx->logger)(ctl_debug, "%s: %s: read: %s",
590 me, address_expr,
591 (n == 0) ? "Unexpected EOF" : strerror(errno));
592 ctl_close(sess);
593 return;
595 sess->inbuf.used += n;
596 eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used);
597 if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') {
598 eos[-1] = '\0';
599 if ((sess->respflags & CTL_DATA) != 0) {
600 INSIST(sess->verb != NULL);
601 (*sess->verb->func)(sess->ctx, sess, sess->verb,
602 sess->inbuf.text,
603 CTL_DATA, sess->respctx,
604 sess->ctx->uctx);
605 } else {
606 ctl_stop_read(sess);
607 ctl_docommand(sess);
609 sess->inbuf.used -= ((eos - sess->inbuf.text) + 1);
610 if (sess->inbuf.used == 0U)
611 ctl_bufput(&sess->inbuf);
612 else
613 memmove(sess->inbuf.text, eos + 1, sess->inbuf.used);
614 return;
616 if (sess->inbuf.used == (size_t)MAX_LINELEN) {
617 (*ctx->logger)(ctl_error, "%s: %s: line too long, closing",
618 me, address_expr);
619 ctl_close(sess);
623 static void
624 ctl_wrtimeout(evContext lev, void *uap,
625 struct timespec due,
626 struct timespec itv)
628 static const char me[] = "ctl_wrtimeout";
629 struct ctl_sess *sess = uap;
630 struct ctl_sctx *ctx = sess->ctx;
631 char tmp[MAX_NTOP];
633 UNUSED(lev);
634 UNUSED(due);
635 UNUSED(itv);
637 REQUIRE(sess->state == writing);
638 sess->wrtiID.opaque = NULL;
639 (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing",
640 me, address_expr);
641 if (sess->wrID.opaque != NULL) {
642 (void) evCancelRW(ctx->ev, sess->wrID);
643 sess->wrID.opaque = NULL;
645 ctl_signal_done(ctx, sess);
646 ctl_new_state(sess, processing, me);
647 ctl_close(sess);
650 static void
651 ctl_rdtimeout(evContext lev, void *uap,
652 struct timespec due,
653 struct timespec itv)
655 static const char me[] = "ctl_rdtimeout";
656 struct ctl_sess *sess = uap;
657 struct ctl_sctx *ctx = sess->ctx;
658 char tmp[MAX_NTOP];
660 UNUSED(lev);
661 UNUSED(due);
662 UNUSED(itv);
664 REQUIRE(sess->state == reading);
665 sess->rdtiID.opaque = NULL;
666 (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing",
667 me, address_expr);
668 if (sess->state == reading || sess->state == reading_data)
669 ctl_stop_read(sess);
670 ctl_signal_done(ctx, sess);
671 ctl_new_state(sess, processing, me);
672 ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL,
673 NULL, NULL, NULL, 0);
676 static void
677 ctl_docommand(struct ctl_sess *sess) {
678 static const char me[] = "ctl_docommand";
679 char *name, *rest, tmp[MAX_NTOP];
680 struct ctl_sctx *ctx = sess->ctx;
681 const struct ctl_verb *verb;
683 REQUIRE(allocated_p(sess->inbuf));
684 (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]",
685 me, address_expr,
686 sess->inbuf.text, (u_int)sess->inbuf.used);
687 ctl_new_state(sess, processing, me);
688 name = sess->inbuf.text + strspn(sess->inbuf.text, space);
689 rest = name + strcspn(name, space);
690 if (*rest != '\0') {
691 *rest++ = '\0';
692 rest += strspn(rest, space);
694 for (verb = ctx->verbs;
695 verb != NULL && verb->name != NULL && verb->func != NULL;
696 verb++)
697 if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0)
698 break;
699 if (verb != NULL && verb->name != NULL && verb->func != NULL) {
700 sess->verb = verb;
701 (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx);
702 } else {
703 char buf[1100];
705 if (sizeof "Unrecognized command \"\" (args \"\")" +
706 strlen(name) + strlen(rest) > sizeof buf)
707 strcpy(buf, "Unrecognized command (buf ovf)");
708 else
709 sprintf(buf,
710 "Unrecognized command \"%s\" (args \"%s\")",
711 name, rest);
712 ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL,
713 NULL, 0);
717 static void
718 ctl_writedone(evContext lev, void *uap, int fd, int bytes) {
719 static const char me[] = "ctl_writedone";
720 struct ctl_sess *sess = uap;
721 struct ctl_sctx *ctx = sess->ctx;
722 char tmp[MAX_NTOP];
723 int save_errno = errno;
725 UNUSED(lev);
726 UNUSED(uap);
728 REQUIRE(sess->state == writing);
729 REQUIRE(fd == sess->sock);
730 REQUIRE(sess->wrtiID.opaque != NULL);
731 sess->wrID.opaque = NULL;
732 (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
733 sess->wrtiID.opaque = NULL;
734 if (bytes < 0) {
735 (*ctx->logger)(ctl_error, "%s: %s: %s",
736 me, address_expr, strerror(save_errno));
737 ctl_close(sess);
738 return;
741 INSIST(allocated_p(sess->outbuf));
742 ctl_bufput(&sess->outbuf);
743 if ((sess->respflags & CTL_EXIT) != 0) {
744 ctl_signal_done(ctx, sess);
745 ctl_close(sess);
746 return;
747 } else if ((sess->respflags & CTL_MORE) != 0) {
748 INSIST(sess->verb != NULL);
749 (*sess->verb->func)(sess->ctx, sess, sess->verb, "",
750 CTL_MORE, sess->respctx, sess->ctx->uctx);
751 } else {
752 ctl_signal_done(ctx, sess);
753 ctl_start_read(sess);
757 static void
758 ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess,
759 const struct ctl_verb *verb, const char *text,
760 u_int respflags, const void *respctx, void *uctx)
762 const struct ctl_verb *this = respctx, *next = this + 1;
764 UNUSED(ctx);
765 UNUSED(verb);
766 UNUSED(text);
767 UNUSED(uctx);
769 REQUIRE(!lastverb_p(this));
770 REQUIRE((respflags & CTL_MORE) != 0);
771 if (lastverb_p(next))
772 respflags &= ~CTL_MORE;
773 ctl_response(sess, sess->helpcode, this->help, respflags, next,
774 NULL, NULL, NULL, 0);
777 static void
778 ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) {
779 if (sess->donefunc != NULL) {
780 (*sess->donefunc)(ctx, sess, sess->uap);
781 sess->donefunc = NULL;
785 /*! \file */