Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: ctl_clnt.c,v 1.1.1.2 2012/09/09 16:08:03 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2004, 2005, 2007, 2008  Internet Systems Consortium, Inc. ("ISC")
      5  * Copyright (C) 1998-2003  Internet Software Consortium.
      6  *
      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.
     10  *
     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.
     18  */
     19 
     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 ";
     22 #endif /* not lint */
     23 
     24 /* Extern. */
     25 
     26 #include "port_before.h"
     27 
     28 #include <sys/param.h>
     29 #include <sys/file.h>
     30 #include <sys/socket.h>
     31 
     32 #include <netinet/in.h>
     33 #include <arpa/nameser.h>
     34 #include <arpa/inet.h>
     35 
     36 #include <ctype.h>
     37 #include <errno.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <time.h>
     42 #include <unistd.h>
     43 #ifdef HAVE_MEMORY_H
     44 #include <memory.h>
     45 #endif
     46 
     47 #include <isc/assertions.h>
     48 #include <isc/ctl.h>
     49 #include <isc/eventlib.h>
     50 #include <isc/list.h>
     51 #include <isc/memcluster.h>
     52 
     53 #include "ctl_p.h"
     54 
     55 #include "port_after.h"
     56 
     57 /* Constants. */
     58 
     59 
     60 /* Macros. */
     61 
     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')
     69 
     70 /* Types. */
     71 
     72 enum state {
     73 	initializing = 0, connecting, connected, destroyed
     74 };
     75 
     76 struct ctl_tran {
     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;
     82 	void *			uap;
     83 };
     84 
     85 struct ctl_cctx {
     86 	enum state		state;
     87 	evContext		ev;
     88 	int			sock;
     89 	ctl_logfunc		logger;
     90 	ctl_clntdone		donefunc;
     91 	void *			uap;
     92 	evConnID		coID;
     93 	evTimerID		tiID;
     94 	evFileID		rdID;
     95 	evStreamID		wrID;
     96 	struct ctl_buf		inbuf;
     97 	struct timespec		timeout;
     98 	LIST(struct ctl_tran)	tran;
     99 	LIST(struct ctl_tran)	wtran;
    100 };
    101 
    102 /* Forward. */
    103 
    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,
    110 				  const void *, int,
    111 				  const 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);
    121 
    122 #ifndef HAVE_MEMCHR
    123 static void *
    124 memchr(const void *b, int c, size_t len) {
    125 	const unsigned char *p = b;
    126 	size_t i;
    127 
    128 	for (i = 0; i < len; i++, p++)
    129 		if (*p == (unsigned char)c)
    130 			return ((void *)p);
    131 	return (NULL);
    132 }
    133 #endif
    134 
    135 /* Private data. */
    136 
    137 static const char * const state_names[] = {
    138 	"initializing", "connecting", "connected", "destroyed"
    139 };
    140 
    141 /* Public. */
    142 
    143 /*%
    144  * void
    145  * ctl_client()
    146  *	create, condition, and connect to a listener on the control port.
    147  */
    148 struct ctl_cctx *
    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)
    153 {
    154 	static const char me[] = "ctl_client";
    155 	static const int on = 1;
    156 	struct ctl_cctx *ctx;
    157 	struct sockaddr *captmp;
    158 
    159 	if (logger == NULL)
    160 		logger = ctl_logger;
    161 	ctx = memget(sizeof *ctx);
    162 	if (ctx == NULL) {
    163 		(*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
    164 		goto fatal;
    165 	}
    166 	ctx->state = initializing;
    167 	ctx->ev = lev;
    168 	ctx->logger = logger;
    169 	ctx->timeout = evConsTime(timeout, 0);
    170 	ctx->donefunc = donefunc;
    171 	ctx->uap = uap;
    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)) {
    181 		ctx->sock = -1;
    182 		errno = ENOTSOCK;
    183 	}
    184 	if (ctx->sock < 0) {
    185 		(*ctx->logger)(ctl_error, "%s: socket: %s",
    186 			       me, strerror(errno));
    187 		goto fatal;
    188 	}
    189 	if (cap != NULL) {
    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));
    195 		}
    196 		DE_CONST(cap, captmp);
    197 		if (bind(ctx->sock, captmp, cap_len) < 0) {
    198 			(*ctx->logger)(ctl_error, "%s: bind: %s", me,
    199 				       strerror(errno));
    200 			goto fatal;
    201 		}
    202 	}
    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));
    207  fatal:
    208 		if (ctx != NULL) {
    209 			if (ctx->sock >= 0)
    210 				close(ctx->sock);
    211 			memput(ctx, sizeof *ctx);
    212 		}
    213 		return (NULL);
    214 	}
    215 	new_state(ctx, connecting);
    216 	return (ctx);
    217 }
    218 
    219 /*%
    220  * void
    221  * ctl_endclient(ctx)
    222  *	close a client and release all of its resources.
    223  */
    224 void
    225 ctl_endclient(struct ctl_cctx *ctx) {
    226 	if (ctx->state != destroyed)
    227 		destroy(ctx, 0);
    228 	memput(ctx, sizeof *ctx);
    229 }
    230 
    231 /*%
    232  * int
    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.
    236  */
    237 int
    238 ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
    239 	    ctl_clntdone donefunc, void *uap)
    240 {
    241 	struct ctl_tran *tran;
    242 	char *pc;
    243 	unsigned int n;
    244 
    245 	switch (ctx->state) {
    246 	case destroyed:
    247 		errno = ENOTCONN;
    248 		return (-1);
    249 	case connecting:
    250 	case connected:
    251 		break;
    252 	default:
    253 		abort();
    254 	}
    255 	if (len >= (size_t)MAX_LINELEN) {
    256 		errno = EMSGSIZE;
    257 		return (-1);
    258 	}
    259 	tran = new_tran(ctx, donefunc, uap, 1);
    260 	if (tran == NULL)
    261 		return (-1);
    262 	if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
    263 		return (-1);
    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))
    269 			*pc = '\040';
    270 	start_write(ctx);
    271 	return (0);
    272 }
    273 
    274 /* Private. */
    275 
    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);
    279 
    280 	if (new == NULL)
    281 		return (NULL);
    282 	new->ctx = ctx;
    283 	buffer_init(new->outbuf);
    284 	new->donefunc = donefunc;
    285 	new->uap = uap;
    286 	INIT_LINK(new, link);
    287 	INIT_LINK(new, wlink);
    288 	APPEND(ctx->tran, new, link);
    289 	if (w)
    290 		APPEND(ctx->wtran, new, wlink);
    291 	return (new);
    292 }
    293 
    294 static void
    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;
    299 	char * tmp;
    300 
    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)
    304 		return;
    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)
    308 			stop_timer(ctx);
    309 		return;
    310 	}
    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)
    316 		touch_timer(ctx);
    317 	else
    318 		start_timer(ctx);
    319 	if (ctx->state == destroyed)
    320 		return;
    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,
    328 			       strerror(errno));
    329 		error(ctx);
    330 		return;
    331 	}
    332 	if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
    333 		(*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
    334 			       strerror(errno));
    335 		error(ctx);
    336 		return;
    337 	}
    338 }
    339 
    340 static void
    341 destroy(struct ctl_cctx *ctx, int notify) {
    342 	struct ctl_tran *this, *next;
    343 
    344 	if (ctx->sock != -1) {
    345 		(void) close(ctx->sock);
    346 		ctx->sock = -1;
    347 	}
    348 	switch (ctx->state) {
    349 	case connecting:
    350 		REQUIRE(ctx->wrID.opaque == NULL);
    351 		REQUIRE(EMPTY(ctx->tran));
    352 		/*
    353 		 * This test is nec'y since destroy() can be called from
    354 		 * start_read() while the state is still "connecting".
    355 		 */
    356 		if (ctx->coID.opaque != NULL) {
    357 			(void)evCancelConn(ctx->ev, ctx->coID);
    358 			ctx->coID.opaque = NULL;
    359 		}
    360 		break;
    361 	case connected:
    362 		REQUIRE(ctx->coID.opaque == NULL);
    363 		if (ctx->wrID.opaque != NULL) {
    364 			(void)evCancelRW(ctx->ev, ctx->wrID);
    365 			ctx->wrID.opaque = NULL;
    366 		}
    367 		if (ctx->rdID.opaque != NULL)
    368 			stop_read(ctx);
    369 		break;
    370 	case destroyed:
    371 		break;
    372 	default:
    373 		abort();
    374 	}
    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);
    384 	}
    385 	if (ctx->tiID.opaque != NULL)
    386 		stop_timer(ctx);
    387 	new_state(ctx, destroyed);
    388 }
    389 
    390 static void
    391 error(struct ctl_cctx *ctx) {
    392 	REQUIRE(ctx->state != destroyed);
    393 	destroy(ctx, 1);
    394 }
    395 
    396 static void
    397 new_state(struct ctl_cctx *ctx, enum state new_state) {
    398 	static const char me[] = "isc/ctl_clnt::new_state";
    399 
    400 	(*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
    401 		       state_names[ctx->state], state_names[new_state]);
    402 	ctx->state = new_state;
    403 }
    404 
    405 static void
    406 conn_done(evContext ev, void *uap, int fd,
    407 	  const void *la, int lalen,
    408 	  const void *ra, int ralen)
    409 {
    410 	static const char me[] = "isc/ctl_clnt::conn_done";
    411 	struct ctl_cctx *ctx = uap;
    412 	struct ctl_tran *tran;
    413 
    414 	UNUSED(ev);
    415 	UNUSED(la);
    416 	UNUSED(lalen);
    417 	UNUSED(ra);
    418 	UNUSED(ralen);
    419 
    420 	ctx->coID.opaque = NULL;
    421 	if (fd < 0) {
    422 		(*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
    423 			       strerror(errno));
    424 		error(ctx);
    425 		return;
    426 	}
    427 	new_state(ctx, connected);
    428 	tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
    429 	if (tran == NULL) {
    430 		(*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
    431 			       strerror(errno));
    432 		error(ctx);
    433 		return;
    434 	}
    435 	start_read(ctx);
    436 	if (ctx->state == destroyed) {
    437 		(*ctx->logger)(ctl_error, "%s: start_read failed: %s",
    438 			       me, strerror(errno));
    439 		error(ctx);
    440 		return;
    441 	}
    442 }
    443 
    444 static void
    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;
    448 
    449 	UNUSED(lev);
    450 	UNUSED(fd);
    451 
    452 	ctx->wrID.opaque = NULL;
    453 	if (ctx->tiID.opaque != NULL)
    454 		touch_timer(ctx);
    455 	ctl_bufput(&tran->outbuf);
    456 	start_write(ctx);
    457 	if (bytes < 0)
    458 		destroy(ctx, 1);
    459 	else
    460 		start_read(ctx);
    461 }
    462 
    463 static void
    464 start_read(struct ctl_cctx *ctx) {
    465 	static const char me[] = "isc/ctl_clnt::start_read";
    466 
    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,
    470 		       &ctx->rdID) < 0)
    471 	{
    472 		(*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
    473 			       ctx->sock, strerror(errno));
    474 		error(ctx);
    475 		return;
    476 	}
    477 }
    478 
    479 static void
    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;
    485 }
    486 
    487 static void
    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;
    492 	ssize_t n;
    493 	char *eos;
    494 
    495 	UNUSED(ev);
    496 
    497 	REQUIRE(ctx != NULL);
    498 	REQUIRE(fd >= 0);
    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);
    506 		error(ctx);
    507 		return;
    508 	}
    509 	n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
    510 		 MAX_LINELEN - ctx->inbuf.used);
    511 	if (n <= 0) {
    512 		(*ctx->logger)(ctl_warning, "%s: read: %s", me,
    513 			       (n == 0) ? "Unexpected EOF" : strerror(errno));
    514 		error(ctx);
    515 		return;
    516 	}
    517 	if (ctx->tiID.opaque != NULL)
    518 		touch_timer(ctx);
    519 	ctx->inbuf.used += n;
    520 	(*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
    521 		       n, ctx->inbuf.used);
    522  again:
    523 	eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
    524 	if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
    525 		int done = 0;
    526 
    527 		eos[-1] = '\0';
    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,
    531 				       ctx->inbuf.text);
    532 			error(ctx);
    533 			return;
    534 		}
    535 		if (arpadone_p(ctx->inbuf.text))
    536 			done = 1;
    537 		else if (arpacont_p(ctx->inbuf.text))
    538 			done = 0;
    539 		else {
    540 			/* XXX Doesn't FTP do this sometimes? Is it legal? */
    541 			(*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
    542 				       ctx->inbuf.text);
    543 			error(ctx);
    544 			return;
    545 		}
    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);
    551 		else
    552 			memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
    553 		if (done) {
    554 			UNLINK(ctx->tran, tran, link);
    555 			memput(tran, sizeof *tran);
    556 			stop_read(ctx);
    557 			start_write(ctx);
    558 			return;
    559 		}
    560 		if (allocated_p(ctx->inbuf))
    561 			goto again;
    562 		return;
    563 	}
    564 	if (ctx->inbuf.used == (size_t)MAX_LINELEN) {
    565 		(*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
    566 			       ctx->inbuf.text);
    567 		error(ctx);
    568 	}
    569 }
    570 
    571 /* Timer related stuff. */
    572 
    573 static void
    574 start_timer(struct ctl_cctx *ctx) {
    575 	static const char me[] = "isc/ctl_clnt::start_timer";
    576 
    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,
    580 			       strerror(errno));
    581 		error(ctx);
    582 		return;
    583 	}
    584 }
    585 
    586 static void
    587 stop_timer(struct ctl_cctx *ctx) {
    588 	static const char me[] = "isc/ctl_clnt::stop_timer";
    589 
    590 	REQUIRE(ctx->tiID.opaque != NULL);
    591 	if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
    592 		(*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
    593 			       strerror(errno));
    594 		error(ctx);
    595 		return;
    596 	}
    597 	ctx->tiID.opaque = NULL;
    598 }
    599 
    600 static void
    601 touch_timer(struct ctl_cctx *ctx) {
    602 	REQUIRE(ctx->tiID.opaque != NULL);
    603 
    604 	evTouchIdleTimer(ctx->ev, ctx->tiID);
    605 }
    606 
    607 static void
    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;
    611 
    612 	UNUSED(ev);
    613 	UNUSED(due);
    614 	UNUSED(itv);
    615 
    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]);
    619 	error(ctx);
    620 }
    621 
    622 /*! \file */
    623