#include <sys/sysmacros.h>
#include <stropts.h>
#include <limits.h>
#include <mdb/mdb.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_context.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb_frame.h>
typedef struct pipe_data {
mdb_iobsvc_f *pipe_rdsvc;
mdb_iob_t *pipe_rdiob;
mdb_iobsvc_f *pipe_wrsvc;
mdb_iob_t *pipe_wriob;
char pipe_buf[BUFSIZ];
mdb_iob_ctx_t pipe_ctx;
uint_t pipe_rdndx;
uint_t pipe_wrndx;
uint_t pipe_free;
uint_t pipe_used;
} pipe_data_t;
static ssize_t
pio_read(mdb_io_t *io, void *buf, size_t nbytes)
{
pipe_data_t *pd = io->io_data;
size_t n, nleft;
if (nbytes == 0)
return (0);
for (nleft = nbytes; nleft == nbytes; nleft -= n) {
if (pd->pipe_used == 0) {
if (pd->pipe_wriob != NULL) {
pd->pipe_wrsvc(pd->pipe_rdiob,
pd->pipe_wriob, &pd->pipe_ctx);
}
if (pd->pipe_used == 0)
break;
}
n = MIN(pd->pipe_used, nleft);
if (BUFSIZ - pd->pipe_rdndx < n) {
size_t n1 = BUFSIZ - pd->pipe_rdndx;
size_t n2 = n - n1;
ASSERT(n2 <= pd->pipe_wrndx);
bcopy(&pd->pipe_buf[pd->pipe_rdndx], buf, n1);
buf = (char *)buf + n1;
bcopy(&pd->pipe_buf[0], buf, n2);
buf = (char *)buf + n2;
} else {
bcopy(&pd->pipe_buf[pd->pipe_rdndx], buf, n);
buf = (char *)buf + n;
}
pd->pipe_rdndx = (pd->pipe_rdndx + n) % BUFSIZ;
pd->pipe_free += n;
pd->pipe_used -= n;
}
if (nleft == nbytes) {
if (pd->pipe_wriob != NULL)
return (set_errno(EAGAIN));
else
return (0);
}
return (nbytes - nleft);
}
static ssize_t
pio_write(mdb_io_t *io, const void *buf, size_t nbytes)
{
pipe_data_t *pd = io->io_data;
size_t n, nleft;
if (pd->pipe_rdiob == NULL)
return (set_errno(EPIPE));
for (nleft = nbytes; nleft != 0; nleft -= n) {
if (pd->pipe_free == 0) {
pd->pipe_rdsvc(pd->pipe_rdiob,
pd->pipe_wriob, &pd->pipe_ctx);
if (pd->pipe_free == 0)
break;
}
n = MIN(pd->pipe_free, nleft);
if (BUFSIZ - pd->pipe_wrndx < n) {
size_t n1 = BUFSIZ - pd->pipe_wrndx;
size_t n2 = n - n1;
ASSERT(n2 <= pd->pipe_rdndx);
bcopy(buf, &pd->pipe_buf[pd->pipe_wrndx], n1);
buf = (const char *)buf + n1;
bcopy(buf, &pd->pipe_buf[0], n2);
buf = (const char *)buf + n2;
} else {
bcopy(buf, &pd->pipe_buf[pd->pipe_wrndx], n);
buf = (const char *)buf + n;
}
pd->pipe_wrndx = (pd->pipe_wrndx + n) % BUFSIZ;
pd->pipe_free -= n;
pd->pipe_used += n;
}
if (nleft == nbytes && nbytes != 0)
return (set_errno(EAGAIN));
return (nbytes - nleft);
}
static int
pio_ctl(mdb_io_t *io, int req, void *arg)
{
pipe_data_t *pd = io->io_data;
if (io->io_next != NULL)
return (IOP_CTL(io->io_next, req, arg));
if (req != I_FLUSH || (intptr_t)arg != FLUSHW)
return (set_errno(ENOTSUP));
if (pd->pipe_used != 0)
pd->pipe_rdsvc(pd->pipe_rdiob, pd->pipe_wriob, &pd->pipe_ctx);
return (0);
}
static void
pio_close(mdb_io_t *io)
{
mdb_free(io->io_data, sizeof (pipe_data_t));
}
static const char *
pio_name(mdb_io_t *io)
{
return ("(pipeline)");
}
static void
pio_link(mdb_io_t *io, mdb_iob_t *iob)
{
pipe_data_t *pd = io->io_data;
if (io->io_next == NULL) {
if (iob->iob_flags & MDB_IOB_RDONLY)
pd->pipe_rdiob = iob;
else
pd->pipe_wriob = iob;
} else
IOP_LINK(io->io_next, iob);
}
static void
pio_unlink(mdb_io_t *io, mdb_iob_t *iob)
{
pipe_data_t *volatile pd = io->io_data;
if (io->io_next == NULL) {
if (pd->pipe_wriob == iob) {
pd->pipe_wriob = NULL;
if (pd->pipe_used == 0 && pd->pipe_ctx.ctx_data == NULL)
return;
do {
if (pd->pipe_rdiob == NULL)
break;
if (mdb_iob_err(pd->pipe_rdiob) != 0) {
if (pd->pipe_ctx.ctx_wptr != NULL) {
mdb_frame_pop(
pd->pipe_ctx.ctx_wptr,
MDB_ERR_ABORT);
pd->pipe_ctx.ctx_wptr = NULL;
}
break;
}
if (pd->pipe_ctx.ctx_data == NULL ||
setjmp(*mdb_context_getpcb(
pd->pipe_ctx.ctx_data)) == 0) {
pd->pipe_rdsvc(pd->pipe_rdiob,
pd->pipe_wriob, &pd->pipe_ctx);
}
} while (pd->pipe_used != 0);
if (pd->pipe_ctx.ctx_data != NULL) {
mdb_context_destroy(pd->pipe_ctx.ctx_data);
pd->pipe_ctx.ctx_data = NULL;
}
} else if (pd->pipe_rdiob == iob)
pd->pipe_rdiob = NULL;
} else
IOP_UNLINK(io->io_next, iob);
}
static const mdb_io_ops_t pipeio_ops = {
.io_read = pio_read,
.io_write = pio_write,
.io_seek = no_io_seek,
.io_ctl = pio_ctl,
.io_close = pio_close,
.io_name = pio_name,
.io_link = pio_link,
.io_unlink = pio_unlink,
.io_setattr = no_io_setattr,
.io_suspend = no_io_suspend,
.io_resume = no_io_resume,
};
mdb_io_t *
mdb_pipeio_create(mdb_iobsvc_f *rdsvc, mdb_iobsvc_f *wrsvc)
{
mdb_io_t *io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
pipe_data_t *pd = mdb_zalloc(sizeof (pipe_data_t), UM_SLEEP);
ASSERT(rdsvc != NULL && wrsvc != NULL);
pd->pipe_rdsvc = rdsvc;
pd->pipe_wrsvc = wrsvc;
pd->pipe_free = BUFSIZ;
io->io_ops = &pipeio_ops;
io->io_data = pd;
io->io_next = NULL;
io->io_refcnt = 0;
return (io);
}
int
mdb_iob_isapipe(mdb_iob_t *iob)
{
mdb_io_t *io;
for (io = iob->iob_iop; io != NULL; io = io->io_next) {
if (io->io_ops == &pipeio_ops)
return (1);
}
return (0);
}