/*
* Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1998-2003 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*%
* void
* ctl_endclient(ctx)
* close a client and release all of its resources.
*/
void
ctl_endclient(struct ctl_cctx *ctx) {
if (ctx->state != destroyed)
destroy(ctx, 0);
memput(ctx, sizeof *ctx);
}
/*%
* int
* ctl_command(ctx, cmd, len, donefunc, uap)
* Queue a transaction, which will begin with sending cmd
* and complete by calling donefunc with the answer.
*/
int
ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
ctl_clntdone donefunc, void *uap)
{
struct ctl_tran *tran;
char *pc;
unsigned int n;
switch (ctx->state) {
case destroyed:
errno = ENOTCONN;
return (-1);
case connecting:
case connected:
break;
default:
abort();
}
if (len >= (size_t)MAX_LINELEN) {
errno = EMSGSIZE;
return (-1);
}
tran = new_tran(ctx, donefunc, uap, 1);
if (tran == NULL)
return (-1);
if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
return (-1);
memcpy(tran->outbuf.text, cmd, len);
tran->outbuf.used = len;
for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++)
if (!isascii((unsigned char)*pc) ||
!isprint((unsigned char)*pc))
*pc = '\040';
start_write(ctx);
return (0);
}
REQUIRE(ctx->state == connecting || ctx->state == connected);
/* If there is a write in progress, don't try to write more yet. */
if (ctx->wrID.opaque != NULL)
return;
/* If there are no trans, make sure timer is off, and we're done. */
if (EMPTY(ctx->wtran)) {
if (ctx->tiID.opaque != NULL)
stop_timer(ctx);
return;
}
/* Pull it off the head of the write queue. */
tran = HEAD(ctx->wtran);
UNLINK(ctx->wtran, tran, wlink);
/* Since there are some trans, make sure timer is successfully "on". */
if (ctx->tiID.opaque != NULL)
touch_timer(ctx);
else
start_timer(ctx);
if (ctx->state == destroyed)
return;
/* Marshall a newline-terminated message and clock it out. */
*iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used);
DE_CONST("\r\n", tmp);
*iovp++ = evConsIovec(tmp, 2);
if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov,
write_done, tran, &ctx->wrID) < 0) {
(*ctx->logger)(ctl_error, "%s: evWrite: %s", me,
strerror(errno));
error(ctx);
return;
}
if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
(*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
strerror(errno));
error(ctx);
return;
}
}
if (ctx->sock != -1) {
(void) close(ctx->sock);
ctx->sock = -1;
}
switch (ctx->state) {
case connecting:
REQUIRE(ctx->wrID.opaque == NULL);
REQUIRE(EMPTY(ctx->tran));
/*
* This test is nec'y since destroy() can be called from
* start_read() while the state is still "connecting".
*/
if (ctx->coID.opaque != NULL) {
(void)evCancelConn(ctx->ev, ctx->coID);
ctx->coID.opaque = NULL;
}
break;
case connected:
REQUIRE(ctx->coID.opaque == NULL);
if (ctx->wrID.opaque != NULL) {
(void)evCancelRW(ctx->ev, ctx->wrID);
ctx->wrID.opaque = NULL;
}
if (ctx->rdID.opaque != NULL)
stop_read(ctx);
break;
case destroyed:
break;
default:
abort();
}
if (allocated_p(ctx->inbuf))
ctl_bufput(&ctx->inbuf);
for (this = HEAD(ctx->tran); this != NULL; this = next) {
next = NEXT(this, link);
if (allocated_p(this->outbuf))
ctl_bufput(&this->outbuf);
if (notify && this->donefunc != NULL)
(*this->donefunc)(ctx, this->uap, NULL, 0);
memput(this, sizeof *this);
}
if (ctx->tiID.opaque != NULL)
stop_timer(ctx);
new_state(ctx, destroyed);
}