Import imsg from OpenBSD's libutil.
imsg provides functions for communication between processes using sockets. imsg is used by programs in OpenBSD such as ypldap.
This commit is contained in:
parent
b76e673f66
commit
70623c80d4
309
lib/libopenbsd/imsg-buffer.c
Normal file
309
lib/libopenbsd/imsg-buffer.c
Normal file
@ -0,0 +1,309 @@
|
||||
/* $OpenBSD: imsg-buffer.c,v 1.7 2015/07/12 18:40:49 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "imsg.h"
|
||||
|
||||
int ibuf_realloc(struct ibuf *, size_t);
|
||||
void ibuf_enqueue(struct msgbuf *, struct ibuf *);
|
||||
void ibuf_dequeue(struct msgbuf *, struct ibuf *);
|
||||
|
||||
struct ibuf *
|
||||
ibuf_open(size_t len)
|
||||
{
|
||||
struct ibuf *buf;
|
||||
|
||||
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
|
||||
return (NULL);
|
||||
if ((buf->buf = malloc(len)) == NULL) {
|
||||
free(buf);
|
||||
return (NULL);
|
||||
}
|
||||
buf->size = buf->max = len;
|
||||
buf->fd = -1;
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
struct ibuf *
|
||||
ibuf_dynamic(size_t len, size_t max)
|
||||
{
|
||||
struct ibuf *buf;
|
||||
|
||||
if (max < len)
|
||||
return (NULL);
|
||||
|
||||
if ((buf = ibuf_open(len)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
if (max > 0)
|
||||
buf->max = max;
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_realloc(struct ibuf *buf, size_t len)
|
||||
{
|
||||
u_char *b;
|
||||
|
||||
/* on static buffers max is eq size and so the following fails */
|
||||
if (buf->wpos + len > buf->max) {
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
b = realloc(buf->buf, buf->wpos + len);
|
||||
if (b == NULL)
|
||||
return (-1);
|
||||
buf->buf = b;
|
||||
buf->size = buf->wpos + len;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add(struct ibuf *buf, const void *data, size_t len)
|
||||
{
|
||||
if (buf->wpos + len > buf->size)
|
||||
if (ibuf_realloc(buf, len) == -1)
|
||||
return (-1);
|
||||
|
||||
memcpy(buf->buf + buf->wpos, data, len);
|
||||
buf->wpos += len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void *
|
||||
ibuf_reserve(struct ibuf *buf, size_t len)
|
||||
{
|
||||
void *b;
|
||||
|
||||
if (buf->wpos + len > buf->size)
|
||||
if (ibuf_realloc(buf, len) == -1)
|
||||
return (NULL);
|
||||
|
||||
b = buf->buf + buf->wpos;
|
||||
buf->wpos += len;
|
||||
return (b);
|
||||
}
|
||||
|
||||
void *
|
||||
ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
|
||||
{
|
||||
/* only allowed to seek in already written parts */
|
||||
if (pos + len > buf->wpos)
|
||||
return (NULL);
|
||||
|
||||
return (buf->buf + pos);
|
||||
}
|
||||
|
||||
size_t
|
||||
ibuf_size(struct ibuf *buf)
|
||||
{
|
||||
return (buf->wpos);
|
||||
}
|
||||
|
||||
size_t
|
||||
ibuf_left(struct ibuf *buf)
|
||||
{
|
||||
return (buf->max - buf->wpos);
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
|
||||
{
|
||||
ibuf_enqueue(msgbuf, buf);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_write(struct msgbuf *msgbuf)
|
||||
{
|
||||
struct iovec iov[IOV_MAX];
|
||||
struct ibuf *buf;
|
||||
unsigned int i = 0;
|
||||
ssize_t n;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
|
||||
if (i >= IOV_MAX)
|
||||
break;
|
||||
iov[i].iov_base = buf->buf + buf->rpos;
|
||||
iov[i].iov_len = buf->wpos - buf->rpos;
|
||||
i++;
|
||||
}
|
||||
|
||||
again:
|
||||
if ((n = writev(msgbuf->fd, iov, i)) == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
if (errno == ENOBUFS)
|
||||
errno = EAGAIN;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (n == 0) { /* connection closed */
|
||||
errno = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
msgbuf_drain(msgbuf, n);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_free(struct ibuf *buf)
|
||||
{
|
||||
free(buf->buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_init(struct msgbuf *msgbuf)
|
||||
{
|
||||
msgbuf->queued = 0;
|
||||
msgbuf->fd = -1;
|
||||
TAILQ_INIT(&msgbuf->bufs);
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
|
||||
{
|
||||
struct ibuf *buf, *next;
|
||||
|
||||
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
|
||||
buf = next) {
|
||||
next = TAILQ_NEXT(buf, entry);
|
||||
if (buf->rpos + n >= buf->wpos) {
|
||||
n -= buf->wpos - buf->rpos;
|
||||
ibuf_dequeue(msgbuf, buf);
|
||||
} else {
|
||||
buf->rpos += n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_clear(struct msgbuf *msgbuf)
|
||||
{
|
||||
struct ibuf *buf;
|
||||
|
||||
while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
|
||||
ibuf_dequeue(msgbuf, buf);
|
||||
}
|
||||
|
||||
int
|
||||
msgbuf_write(struct msgbuf *msgbuf)
|
||||
{
|
||||
struct iovec iov[IOV_MAX];
|
||||
struct ibuf *buf;
|
||||
unsigned int i = 0;
|
||||
ssize_t n;
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
char buf[CMSG_SPACE(sizeof(int))];
|
||||
} cmsgbuf;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
|
||||
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
|
||||
if (i >= IOV_MAX)
|
||||
break;
|
||||
iov[i].iov_base = buf->buf + buf->rpos;
|
||||
iov[i].iov_len = buf->wpos - buf->rpos;
|
||||
i++;
|
||||
if (buf->fd != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = i;
|
||||
|
||||
if (buf != NULL && buf->fd != -1) {
|
||||
msg.msg_control = (caddr_t)&cmsgbuf.buf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf.buf);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
*(int *)CMSG_DATA(cmsg) = buf->fd;
|
||||
}
|
||||
|
||||
again:
|
||||
if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
if (errno == ENOBUFS)
|
||||
errno = EAGAIN;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (n == 0) { /* connection closed */
|
||||
errno = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* assumption: fd got sent if sendmsg sent anything
|
||||
* this works because fds are passed one at a time
|
||||
*/
|
||||
if (buf != NULL && buf->fd != -1) {
|
||||
close(buf->fd);
|
||||
buf->fd = -1;
|
||||
}
|
||||
|
||||
msgbuf_drain(msgbuf, n);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
|
||||
{
|
||||
TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
|
||||
msgbuf->queued++;
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
|
||||
{
|
||||
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
|
||||
|
||||
if (buf->fd != -1)
|
||||
close(buf->fd);
|
||||
|
||||
msgbuf->queued--;
|
||||
ibuf_free(buf);
|
||||
}
|
307
lib/libopenbsd/imsg.c
Normal file
307
lib/libopenbsd/imsg.c
Normal file
@ -0,0 +1,307 @@
|
||||
/* $OpenBSD: imsg.c,v 1.10 2015/07/19 07:18:59 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "imsg.h"
|
||||
|
||||
int imsg_fd_overhead = 0;
|
||||
|
||||
int imsg_get_fd(struct imsgbuf *);
|
||||
|
||||
void
|
||||
imsg_init(struct imsgbuf *ibuf, int fd)
|
||||
{
|
||||
msgbuf_init(&ibuf->w);
|
||||
memset(&ibuf->r, 0, sizeof(ibuf->r));
|
||||
ibuf->fd = fd;
|
||||
ibuf->w.fd = fd;
|
||||
ibuf->pid = getpid();
|
||||
TAILQ_INIT(&ibuf->fds);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
imsg_read(struct imsgbuf *ibuf)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
char buf[CMSG_SPACE(sizeof(int) * 1)];
|
||||
} cmsgbuf;
|
||||
struct iovec iov;
|
||||
ssize_t n = -1;
|
||||
int fd;
|
||||
struct imsg_fd *ifd;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
|
||||
|
||||
iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
|
||||
iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = &cmsgbuf.buf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf.buf);
|
||||
|
||||
if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
|
||||
return (-1);
|
||||
|
||||
again:
|
||||
if (getdtablecount() + imsg_fd_overhead +
|
||||
(CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int)
|
||||
>= getdtablesize()) {
|
||||
errno = EAGAIN;
|
||||
free(ifd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
|
||||
if (errno == EMSGSIZE)
|
||||
goto fail;
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
goto fail;
|
||||
goto again;
|
||||
}
|
||||
|
||||
ibuf->r.wpos += n;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
int i;
|
||||
int j;
|
||||
|
||||
/*
|
||||
* We only accept one file descriptor. Due to C
|
||||
* padding rules, our control buffer might contain
|
||||
* more than one fd, and we must close them.
|
||||
*/
|
||||
j = ((char *)cmsg + cmsg->cmsg_len -
|
||||
(char *)CMSG_DATA(cmsg)) / sizeof(int);
|
||||
for (i = 0; i < j; i++) {
|
||||
fd = ((int *)CMSG_DATA(cmsg))[i];
|
||||
if (ifd != NULL) {
|
||||
ifd->fd = fd;
|
||||
TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
|
||||
entry);
|
||||
ifd = NULL;
|
||||
} else
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
/* we do not handle other ctl data level */
|
||||
}
|
||||
|
||||
fail:
|
||||
if (ifd)
|
||||
free(ifd);
|
||||
return (n);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
|
||||
{
|
||||
size_t av, left, datalen;
|
||||
|
||||
av = ibuf->r.wpos;
|
||||
|
||||
if (IMSG_HEADER_SIZE > av)
|
||||
return (0);
|
||||
|
||||
memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
|
||||
if (imsg->hdr.len < IMSG_HEADER_SIZE ||
|
||||
imsg->hdr.len > MAX_IMSGSIZE) {
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
if (imsg->hdr.len > av)
|
||||
return (0);
|
||||
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
|
||||
if (datalen == 0)
|
||||
imsg->data = NULL;
|
||||
else if ((imsg->data = malloc(datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (imsg->hdr.flags & IMSGF_HASFD)
|
||||
imsg->fd = imsg_get_fd(ibuf);
|
||||
else
|
||||
imsg->fd = -1;
|
||||
|
||||
memcpy(imsg->data, ibuf->r.rptr, datalen);
|
||||
|
||||
if (imsg->hdr.len < av) {
|
||||
left = av - imsg->hdr.len;
|
||||
memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
|
||||
ibuf->r.wpos = left;
|
||||
} else
|
||||
ibuf->r.wpos = 0;
|
||||
|
||||
return (datalen + IMSG_HEADER_SIZE);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
|
||||
pid_t pid, int fd, const void *data, u_int16_t datalen)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
|
||||
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (imsg_add(wbuf, data, datalen) == -1)
|
||||
return (-1);
|
||||
|
||||
wbuf->fd = fd;
|
||||
|
||||
imsg_close(ibuf, wbuf);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
|
||||
pid_t pid, int fd, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
int i, datalen = 0;
|
||||
|
||||
for (i = 0; i < iovcnt; i++)
|
||||
datalen += iov[i].iov_len;
|
||||
|
||||
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < iovcnt; i++)
|
||||
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
|
||||
return (-1);
|
||||
|
||||
wbuf->fd = fd;
|
||||
|
||||
imsg_close(ibuf, wbuf);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
struct ibuf *
|
||||
imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
|
||||
pid_t pid, u_int16_t datalen)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
struct imsg_hdr hdr;
|
||||
|
||||
datalen += IMSG_HEADER_SIZE;
|
||||
if (datalen > MAX_IMSGSIZE) {
|
||||
errno = ERANGE;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
hdr.type = type;
|
||||
hdr.flags = 0;
|
||||
hdr.peerid = peerid;
|
||||
if ((hdr.pid = pid) == 0)
|
||||
hdr.pid = ibuf->pid;
|
||||
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
|
||||
return (NULL);
|
||||
|
||||
return (wbuf);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen)
|
||||
{
|
||||
if (datalen)
|
||||
if (ibuf_add(msg, data, datalen) == -1) {
|
||||
ibuf_free(msg);
|
||||
return (-1);
|
||||
}
|
||||
return (datalen);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
|
||||
{
|
||||
struct imsg_hdr *hdr;
|
||||
|
||||
hdr = (struct imsg_hdr *)msg->buf;
|
||||
|
||||
hdr->flags &= ~IMSGF_HASFD;
|
||||
if (msg->fd != -1)
|
||||
hdr->flags |= IMSGF_HASFD;
|
||||
|
||||
hdr->len = (u_int16_t)msg->wpos;
|
||||
|
||||
ibuf_close(&ibuf->w, msg);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_free(struct imsg *imsg)
|
||||
{
|
||||
free(imsg->data);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_get_fd(struct imsgbuf *ibuf)
|
||||
{
|
||||
int fd;
|
||||
struct imsg_fd *ifd;
|
||||
|
||||
if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
|
||||
return (-1);
|
||||
|
||||
fd = ifd->fd;
|
||||
TAILQ_REMOVE(&ibuf->fds, ifd, entry);
|
||||
free(ifd);
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_flush(struct imsgbuf *ibuf)
|
||||
{
|
||||
while (ibuf->w.queued)
|
||||
if (msgbuf_write(&ibuf->w) <= 0)
|
||||
return (-1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_clear(struct imsgbuf *ibuf)
|
||||
{
|
||||
int fd;
|
||||
|
||||
msgbuf_clear(&ibuf->w);
|
||||
while ((fd = imsg_get_fd(ibuf)) != -1)
|
||||
close(fd);
|
||||
}
|
114
lib/libopenbsd/imsg.h
Normal file
114
lib/libopenbsd/imsg.h
Normal file
@ -0,0 +1,114 @@
|
||||
/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
|
||||
* Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _IMSG_H_
|
||||
#define _IMSG_H_
|
||||
|
||||
#define IBUF_READ_SIZE 65535
|
||||
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
|
||||
#define MAX_IMSGSIZE 16384
|
||||
|
||||
struct ibuf {
|
||||
TAILQ_ENTRY(ibuf) entry;
|
||||
u_char *buf;
|
||||
size_t size;
|
||||
size_t max;
|
||||
size_t wpos;
|
||||
size_t rpos;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct msgbuf {
|
||||
TAILQ_HEAD(, ibuf) bufs;
|
||||
u_int32_t queued;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct ibuf_read {
|
||||
u_char buf[IBUF_READ_SIZE];
|
||||
u_char *rptr;
|
||||
size_t wpos;
|
||||
};
|
||||
|
||||
struct imsg_fd {
|
||||
TAILQ_ENTRY(imsg_fd) entry;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct imsgbuf {
|
||||
TAILQ_HEAD(, imsg_fd) fds;
|
||||
struct ibuf_read r;
|
||||
struct msgbuf w;
|
||||
int fd;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
#define IMSGF_HASFD 1
|
||||
|
||||
struct imsg_hdr {
|
||||
u_int32_t type;
|
||||
u_int16_t len;
|
||||
u_int16_t flags;
|
||||
u_int32_t peerid;
|
||||
u_int32_t pid;
|
||||
};
|
||||
|
||||
struct imsg {
|
||||
struct imsg_hdr hdr;
|
||||
int fd;
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
||||
/* buffer.c */
|
||||
struct ibuf *ibuf_open(size_t);
|
||||
struct ibuf *ibuf_dynamic(size_t, size_t);
|
||||
int ibuf_add(struct ibuf *, const void *, size_t);
|
||||
void *ibuf_reserve(struct ibuf *, size_t);
|
||||
void *ibuf_seek(struct ibuf *, size_t, size_t);
|
||||
size_t ibuf_size(struct ibuf *);
|
||||
size_t ibuf_left(struct ibuf *);
|
||||
void ibuf_close(struct msgbuf *, struct ibuf *);
|
||||
int ibuf_write(struct msgbuf *);
|
||||
void ibuf_free(struct ibuf *);
|
||||
void msgbuf_init(struct msgbuf *);
|
||||
void msgbuf_clear(struct msgbuf *);
|
||||
int msgbuf_write(struct msgbuf *);
|
||||
void msgbuf_drain(struct msgbuf *, size_t);
|
||||
|
||||
/* imsg.c */
|
||||
void imsg_init(struct imsgbuf *, int);
|
||||
ssize_t imsg_read(struct imsgbuf *);
|
||||
ssize_t imsg_get(struct imsgbuf *, struct imsg *);
|
||||
int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
|
||||
int, const void *, u_int16_t);
|
||||
int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
|
||||
int, const struct iovec *, int);
|
||||
struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
|
||||
u_int16_t);
|
||||
int imsg_add(struct ibuf *, const void *, u_int16_t);
|
||||
void imsg_close(struct imsgbuf *, struct ibuf *);
|
||||
void imsg_free(struct imsg *);
|
||||
int imsg_flush(struct imsgbuf *);
|
||||
void imsg_clear(struct imsgbuf *);
|
||||
|
||||
#endif
|
549
lib/libopenbsd/imsg_init.3
Normal file
549
lib/libopenbsd/imsg_init.3
Normal file
@ -0,0 +1,549 @@
|
||||
.\" $OpenBSD: imsg_init.3,v 1.13 2015/07/11 16:23:59 deraadt Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2010 Nicholas Marriott <nicm@openbsd.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF MIND, 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd $Mdocdate: July 11 2015 $
|
||||
.Dt IMSG_INIT 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm imsg_init ,
|
||||
.Nm imsg_read ,
|
||||
.Nm imsg_get ,
|
||||
.Nm imsg_compose ,
|
||||
.Nm imsg_composev ,
|
||||
.Nm imsg_create ,
|
||||
.Nm imsg_add ,
|
||||
.Nm imsg_close ,
|
||||
.Nm imsg_free ,
|
||||
.Nm imsg_flush ,
|
||||
.Nm imsg_clear ,
|
||||
.Nm ibuf_open ,
|
||||
.Nm ibuf_dynamic ,
|
||||
.Nm ibuf_add ,
|
||||
.Nm ibuf_reserve ,
|
||||
.Nm ibuf_seek ,
|
||||
.Nm ibuf_size ,
|
||||
.Nm ibuf_left ,
|
||||
.Nm ibuf_close ,
|
||||
.Nm ibuf_write ,
|
||||
.Nm ibuf_free ,
|
||||
.Nm msgbuf_init ,
|
||||
.Nm msgbuf_clear ,
|
||||
.Nm msgbuf_write ,
|
||||
.Nm msgbuf_drain
|
||||
.Nd IPC messaging functions
|
||||
.Sh SYNOPSIS
|
||||
.In sys/types.h
|
||||
.In sys/queue.h
|
||||
.In sys/uio.h
|
||||
.In imsg.h
|
||||
.Ft void
|
||||
.Fn imsg_init "struct imsgbuf *ibuf" "int fd"
|
||||
.Ft ssize_t
|
||||
.Fn imsg_read "struct imsgbuf *ibuf"
|
||||
.Ft ssize_t
|
||||
.Fn imsg_get "struct imsgbuf *ibuf" "struct imsg *imsg"
|
||||
.Ft int
|
||||
.Fn imsg_compose "struct imsgbuf *ibuf" "u_int32_t type" "uint32_t peerid" \
|
||||
"pid_t pid" "int fd" "const void *data" "u_int16_t datalen"
|
||||
.Ft int
|
||||
.Fn imsg_composev "struct imsgbuf *ibuf" "u_int32_t type" "u_int32_t peerid" \
|
||||
"pid_t pid" "int fd" "const struct iovec *iov" "int iovcnt"
|
||||
.Ft "struct ibuf *"
|
||||
.Fn imsg_create "struct imsgbuf *ibuf" "u_int32_t type" "u_int32_t peerid" \
|
||||
"pid_t pid" "u_int16_t datalen"
|
||||
.Ft int
|
||||
.Fn imsg_add "struct ibuf *buf" "const void *data" "u_int16_t datalen"
|
||||
.Ft void
|
||||
.Fn imsg_close "struct imsgbuf *ibuf" "struct ibuf *msg"
|
||||
.Ft void
|
||||
.Fn imsg_free "struct imsg *imsg"
|
||||
.Ft int
|
||||
.Fn imsg_flush "struct imsgbuf *ibuf"
|
||||
.Ft void
|
||||
.Fn imsg_clear "struct imsgbuf *ibuf"
|
||||
.Ft "struct ibuf *"
|
||||
.Fn ibuf_open "size_t len"
|
||||
.Ft "struct ibuf *"
|
||||
.Fn ibuf_dynamic "size_t len" "size_t max"
|
||||
.Ft int
|
||||
.Fn ibuf_add "struct ibuf *buf" "const void *data" "size_t len"
|
||||
.Ft "void *"
|
||||
.Fn ibuf_reserve "struct ibuf *buf" "size_t len"
|
||||
.Ft "void *"
|
||||
.Fn ibuf_seek "struct ibuf *buf" "size_t pos" "size_t len"
|
||||
.Ft size_t
|
||||
.Fn ibuf_size "struct ibuf *buf"
|
||||
.Ft size_t
|
||||
.Fn ibuf_left "struct ibuf *buf"
|
||||
.Ft void
|
||||
.Fn ibuf_close "struct msgbuf *msgbuf" "struct ibuf *buf"
|
||||
.Ft int
|
||||
.Fn ibuf_write "struct msgbuf *msgbuf"
|
||||
.Ft void
|
||||
.Fn ibuf_free "struct ibuf *buf"
|
||||
.Ft void
|
||||
.Fn msgbuf_init "struct msgbuf *msgbuf"
|
||||
.Ft void
|
||||
.Fn msgbuf_clear "struct msgbuf *msgbuf"
|
||||
.Ft int
|
||||
.Fn msgbuf_write "struct msgbuf *msgbuf"
|
||||
.Ft void
|
||||
.Fn msgbuf_drain "struct msgbuf *msgbuf" "size_t n"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm imsg
|
||||
functions provide a simple mechanism for communication between processes
|
||||
using sockets.
|
||||
Each transmitted message is guaranteed to be presented to the receiving program
|
||||
whole.
|
||||
They are commonly used in privilege separated processes, where processes with
|
||||
different rights are required to cooperate.
|
||||
.Pp
|
||||
A program using these functions should be linked with
|
||||
.Em -lutil .
|
||||
.Pp
|
||||
The basic
|
||||
.Nm
|
||||
structure is the
|
||||
.Em imsgbuf ,
|
||||
which wraps a file descriptor and represents one side of a channel on which
|
||||
messages are sent and received:
|
||||
.Bd -literal -offset indent
|
||||
struct imsgbuf {
|
||||
TAILQ_HEAD(, imsg_fd) fds;
|
||||
struct ibuf_read r;
|
||||
struct msgbuf w;
|
||||
int fd;
|
||||
pid_t pid;
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
.Fn imsg_init
|
||||
is a routine which initializes
|
||||
.Fa ibuf
|
||||
as one side of a channel associated with
|
||||
.Fa fd .
|
||||
The file descriptor is used to send and receive messages,
|
||||
but is not closed by any of the imsg functions.
|
||||
An imsgbuf is initialized with the
|
||||
.Em w
|
||||
member as the output buffer queue,
|
||||
.Em fd
|
||||
with the file descriptor passed to
|
||||
.Fn imsg_init
|
||||
and the other members for internal use only.
|
||||
.Pp
|
||||
The
|
||||
.Fn imsg_clear
|
||||
function frees any data allocated as part of an imsgbuf.
|
||||
.Pp
|
||||
.Fn imsg_create ,
|
||||
.Fn imsg_add
|
||||
and
|
||||
.Fn imsg_close
|
||||
are generic construction routines for messages that are to be sent using an
|
||||
imsgbuf.
|
||||
.Pp
|
||||
.Fn imsg_create
|
||||
creates a new message with header specified by
|
||||
.Fa type ,
|
||||
.Fa peerid
|
||||
and
|
||||
.Fa pid .
|
||||
A
|
||||
.Fa pid
|
||||
of zero uses the process ID returned by
|
||||
.Xr getpid 2
|
||||
when
|
||||
.Fa ibuf
|
||||
was initialized.
|
||||
In addition to this common imsg header,
|
||||
.Fa datalen
|
||||
bytes of space may be reserved for attaching to this imsg.
|
||||
This space is populated using
|
||||
.Fn imsg_add .
|
||||
Additionally, the file descriptor
|
||||
.Fa fd
|
||||
may be passed over the socket to the other process.
|
||||
If
|
||||
.Fa fd
|
||||
is given, it is closed in the sending program after the message is sent.
|
||||
A value of \-1 indicates no file descriptor should be passed.
|
||||
.Fn imsg_create
|
||||
returns a pointer to a new message if it succeeds, NULL otherwise.
|
||||
.Pp
|
||||
.Fn imsg_add
|
||||
appends to
|
||||
.Fa imsg
|
||||
.Fa len
|
||||
bytes of ancillary data pointed to by
|
||||
.Fa buf .
|
||||
It returns
|
||||
.Fa len
|
||||
if it succeeds, \-1 otherwise.
|
||||
.Pp
|
||||
.Fn imsg_close
|
||||
completes creation of
|
||||
.Fa imsg
|
||||
by adding it to
|
||||
.Fa imsgbuf
|
||||
output buffer.
|
||||
.Pp
|
||||
.Fn imsg_compose
|
||||
is a routine which is used to quickly create and queue an imsg.
|
||||
It takes the same parameters as the
|
||||
.Fn imsg_create ,
|
||||
.Fn imsg_add
|
||||
and
|
||||
.Fn imsg_close
|
||||
routines,
|
||||
except that only one ancillary data buffer can be provided.
|
||||
This routine returns 1 if it succeeds, \-1 otherwise.
|
||||
.Pp
|
||||
.Fn imsg_composev
|
||||
is similar to
|
||||
.Fn imsg_compose .
|
||||
It takes the same parameters, except that the ancillary data buffer is specified
|
||||
by
|
||||
.Fa iovec .
|
||||
.Pp
|
||||
.Fn imsg_flush
|
||||
is a function which calls
|
||||
.Fn msgbuf_write
|
||||
in a loop until all imsgs in the output buffer are sent.
|
||||
It returns 0 if it succeeds, \-1 otherwise.
|
||||
.Pp
|
||||
The
|
||||
.Fn imsg_read
|
||||
routine reads pending data with
|
||||
.Xr recvmsg 2
|
||||
and queues it as individual messages on
|
||||
.Fa imsgbuf .
|
||||
It returns the number of bytes read on success, or \-1 on error.
|
||||
A return value of \-1 from
|
||||
.Fn imsg_read
|
||||
invalidates
|
||||
.Fa imsgbuf ,
|
||||
and renders it suitable only for passing to
|
||||
.Fn imsg_clear .
|
||||
.Pp
|
||||
.Fn imsg_get
|
||||
fills in an individual imsg pending on
|
||||
.Fa imsgbuf
|
||||
into the structure pointed to by
|
||||
.Fa imsg .
|
||||
It returns the total size of the message, 0 if no messages are ready, or \-1
|
||||
for an error.
|
||||
Received messages are returned as a
|
||||
.Em struct imsg ,
|
||||
which must be freed by
|
||||
.Fn imsg_free
|
||||
when no longer required.
|
||||
.Em struct imsg
|
||||
has this form:
|
||||
.Bd -literal -offset indent
|
||||
struct imsg {
|
||||
struct imsg_hdr hdr;
|
||||
int fd;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct imsg_hdr {
|
||||
u_int32_t type;
|
||||
u_int16_t len;
|
||||
u_int16_t flags;
|
||||
u_int32_t peerid;
|
||||
u_int32_t pid;
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The header members are:
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It type
|
||||
A integer identifier, typically used to express the meaning of the message.
|
||||
.It len
|
||||
The total length of the imsg, including the header and any ancillary data
|
||||
transmitted with the message (pointed to by the
|
||||
.Em data
|
||||
member of the message itself).
|
||||
.It flags
|
||||
Flags used internally by the imsg functions: should not be used by application
|
||||
programs.
|
||||
.It peerid, pid
|
||||
32-bit values specified on message creation and free for any use by the
|
||||
caller, normally used to identify the message sender.
|
||||
.El
|
||||
.Pp
|
||||
In addition,
|
||||
.Em struct imsg
|
||||
has the following:
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It fd
|
||||
The file descriptor specified when the message was created and passed using the
|
||||
socket control message API, or \-1 if no file descriptor was sent.
|
||||
.It data
|
||||
A pointer to the ancillary data transmitted with the imsg.
|
||||
.El
|
||||
.Pp
|
||||
The IMSG_HEADER_SIZE define is the size of the imsg message header, which
|
||||
may be subtracted from the
|
||||
.Fa len
|
||||
member of
|
||||
.Em struct imsg_hdr
|
||||
to obtain the length of any additional data passed with the message.
|
||||
.Pp
|
||||
MAX_IMSGSIZE is defined as the maximum size of a single imsg, currently
|
||||
16384 bytes.
|
||||
.Sh BUFFERS
|
||||
The imsg API defines functions to manipulate buffers, used internally and during
|
||||
construction of imsgs with
|
||||
.Fn imsg_create .
|
||||
A
|
||||
.Em struct ibuf
|
||||
is a single buffer and a
|
||||
.Em struct msgbuf
|
||||
a queue of output buffers for transmission:
|
||||
.Bd -literal -offset indent
|
||||
struct ibuf {
|
||||
TAILQ_ENTRY(ibuf) entry;
|
||||
u_char *buf;
|
||||
size_t size;
|
||||
size_t max;
|
||||
size_t wpos;
|
||||
size_t rpos;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct msgbuf {
|
||||
TAILQ_HEAD(, ibuf) bufs;
|
||||
u_int32_t queued;
|
||||
int fd;
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Fn ibuf_open
|
||||
function allocates a fixed-length buffer.
|
||||
The buffer may not be resized and may contain a maximum of
|
||||
.Fa len
|
||||
bytes.
|
||||
On success
|
||||
.Fn ibuf_open
|
||||
returns a pointer to the buffer; on failure it returns NULL.
|
||||
.Pp
|
||||
.Fn ibuf_dynamic
|
||||
allocates a resizeable buffer of initial length
|
||||
.Fa len
|
||||
and maximum size
|
||||
.Fa max .
|
||||
Buffers allocated with
|
||||
.Fn ibuf_dynamic
|
||||
are automatically grown if necessary when data is added.
|
||||
.Pp
|
||||
.Fn ibuf_add
|
||||
is a routine which appends a block of data to
|
||||
.Fa buf .
|
||||
0 is returned on success and \-1 on failure.
|
||||
.Pp
|
||||
.Fn ibuf_reserve
|
||||
is used to reserve
|
||||
.Fa len
|
||||
bytes in
|
||||
.Fa buf .
|
||||
A pointer to the start of the reserved space is returned, or NULL on error.
|
||||
.Pp
|
||||
.Fn ibuf_seek
|
||||
is a function which returns a pointer to the part of the buffer at offset
|
||||
.Fa pos
|
||||
and of extent
|
||||
.Fa len .
|
||||
NULL is returned if the requested range is outside the part of the buffer
|
||||
in use.
|
||||
.Pp
|
||||
.Fn ibuf_size
|
||||
and
|
||||
.Fn ibuf_left
|
||||
are functions which return the total bytes used and available in
|
||||
.Fa buf
|
||||
respectively.
|
||||
.Pp
|
||||
.Fn ibuf_close
|
||||
appends
|
||||
.Fa buf
|
||||
to
|
||||
.Fa msgbuf
|
||||
ready to be sent.
|
||||
.Pp
|
||||
The
|
||||
.Fn ibuf_write
|
||||
routine transmits as many pending buffers as possible from
|
||||
.Fn msgbuf
|
||||
using
|
||||
.Xr writev 2 .
|
||||
It returns 1 if it succeeds, \-1 on error and 0 when no buffers were
|
||||
pending or an EOF condition on the socket is detected.
|
||||
Temporary resource shortages are returned with errno
|
||||
.Er EAGAIN
|
||||
and require the application to retry again in the future.
|
||||
.Pp
|
||||
.Fn ibuf_free
|
||||
frees
|
||||
.Fa buf
|
||||
and any associated storage.
|
||||
.Pp
|
||||
The
|
||||
.Fn msgbuf_init
|
||||
function initializes
|
||||
.Fa msgbuf
|
||||
so that buffers may be appended to it.
|
||||
The
|
||||
.Em fd
|
||||
member should also be set directly before
|
||||
.Fn msgbuf_write
|
||||
is used.
|
||||
.Pp
|
||||
.Fn msgbuf_clear
|
||||
empties a msgbuf, removing and discarding any queued buffers.
|
||||
.Pp
|
||||
The
|
||||
.Fn msgbuf_write
|
||||
routine calls
|
||||
.Xr sendmsg 2
|
||||
to transmit buffers queued in
|
||||
.Fa msgbuf .
|
||||
It returns 1 if it succeeds, \-1 on error, and 0 when the queue was empty
|
||||
or an EOF condition on the socket is detected.
|
||||
Temporary resource shortages are returned with errno
|
||||
.Er EAGAIN
|
||||
and require the application to retry again in the future.
|
||||
.Pp
|
||||
.Fn msgbuf_drain
|
||||
discards data from buffers queued in
|
||||
.Fa msgbuf
|
||||
until
|
||||
.Fa n
|
||||
bytes have been removed or
|
||||
.Fa msgbuf
|
||||
is empty.
|
||||
.Sh EXAMPLES
|
||||
In a typical program, a channel between two processes is created with
|
||||
.Xr socketpair 2 ,
|
||||
and an
|
||||
.Em imsgbuf
|
||||
created around one file descriptor in each process:
|
||||
.Bd -literal -offset indent
|
||||
struct imsgbuf parent_ibuf, child_ibuf;
|
||||
int imsg_fds[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
|
||||
err(1, "socketpair");
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
err(1, "fork");
|
||||
case 0:
|
||||
/* child */
|
||||
close(imsg_fds[0]);
|
||||
imsg_init(&child_ibuf, imsg_fds[1]);
|
||||
exit(child_main(&child_ibuf));
|
||||
}
|
||||
|
||||
/* parent */
|
||||
close(imsg_fds[1]);
|
||||
imsg_init(&parent_ibuf, imsg_fds[0]);
|
||||
exit(parent_main(&parent_ibuf));
|
||||
.Ed
|
||||
.Pp
|
||||
Messages may then be composed and queued on the
|
||||
.Em imsgbuf ,
|
||||
for example using the
|
||||
.Fn imsg_compose
|
||||
function:
|
||||
.Bd -literal -offset indent
|
||||
enum imsg_type {
|
||||
IMSG_A_MESSAGE,
|
||||
IMSG_MESSAGE2
|
||||
};
|
||||
|
||||
int
|
||||
child_main(struct imsgbuf *ibuf)
|
||||
{
|
||||
int idata;
|
||||
...
|
||||
idata = 42;
|
||||
imsg_compose(ibuf, IMSG_A_MESSAGE,
|
||||
0, 0, -1, &idata, sizeof idata);
|
||||
...
|
||||
}
|
||||
.Ed
|
||||
.Pp
|
||||
A mechanism such as
|
||||
.Xr poll 2
|
||||
or the
|
||||
.Xr event 3
|
||||
library is used to monitor the socket file descriptor.
|
||||
When the socket is ready for writing, queued messages are transmitted with
|
||||
.Fn msgbuf_write :
|
||||
.Bd -literal -offset indent
|
||||
if (msgbuf_write(&ibuf-\*(Gtw) \*(Lt= 0 && errno != EAGAIN) {
|
||||
/* handle write failure */
|
||||
}
|
||||
.Ed
|
||||
.Pp
|
||||
And when ready for reading, messages are first received using
|
||||
.Fn imsg_read
|
||||
and then extracted with
|
||||
.Fn imsg_get :
|
||||
.Bd -literal -offset indent
|
||||
void
|
||||
dispatch_imsg(struct imsgbuf *ibuf)
|
||||
{
|
||||
struct imsg imsg;
|
||||
ssize_t n, datalen;
|
||||
int idata;
|
||||
|
||||
if ((n = imsg_read(ibuf)) == -1 || n == 0) {
|
||||
/* handle socket error */
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if ((n = imsg_get(ibuf, &imsg)) == -1) {
|
||||
/* handle read error */
|
||||
}
|
||||
if (n == 0) /* no more messages */
|
||||
return;
|
||||
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
|
||||
|
||||
switch (imsg.hdr.type) {
|
||||
case IMSG_A_MESSAGE:
|
||||
if (datalen \*(Lt sizeof idata) {
|
||||
/* handle corrupt message */
|
||||
}
|
||||
memcpy(&idata, imsg.data, sizeof idata);
|
||||
/* handle message received */
|
||||
break;
|
||||
...
|
||||
}
|
||||
|
||||
imsg_free(&imsg);
|
||||
}
|
||||
}
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr socketpair 2 ,
|
||||
.Xr unix 4
|
Loading…
Reference in New Issue
Block a user