imsg_init
, imsg_read
,
imsg_get
, imsg_compose
,
imsg_composev
, imsg_create
,
imsg_add
, imsg_close
,
imsg_free
, imsg_flush
,
imsg_clear
, ibuf_open
,
ibuf_dynamic
, ibuf_add
,
ibuf_reserve
, ibuf_seek
,
ibuf_size
, ibuf_left
,
ibuf_close
, ibuf_write
,
ibuf_free
, msgbuf_init
,
msgbuf_clear
, msgbuf_write
,
msgbuf_drain
—
IPC messaging functions
OpenBSD Utilities Library (libopenbsd, -lopenbsd)
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/uio.h>
#include <imsg.h>
void
imsg_init
(struct
imsgbuf *ibuf, int
fd);
ssize_t
imsg_read
(struct
imsgbuf *ibuf);
ssize_t
imsg_get
(struct
imsgbuf *ibuf, struct
imsg *imsg);
int
imsg_compose
(struct
imsgbuf *ibuf, u_int32_t
type, uint32_t
peerid, pid_t pid,
int fd,
const void *data,
u_int16_t datalen);
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 *
imsg_create
(struct
imsgbuf *ibuf, u_int32_t
type, u_int32_t
peerid, pid_t pid,
u_int16_t datalen);
int
imsg_add
(struct
ibuf *buf, const void
*data, u_int16_t
datalen);
void
imsg_close
(struct
imsgbuf *ibuf, struct
ibuf *msg);
void
imsg_free
(struct
imsg *imsg);
int
imsg_flush
(struct
imsgbuf *ibuf);
void
imsg_clear
(struct
imsgbuf *ibuf);
struct ibuf *
ibuf_open
(size_t
len);
struct ibuf *
ibuf_dynamic
(size_t
len, size_t
max);
int
ibuf_add
(struct
ibuf *buf, const void
*data, size_t
len);
void *
ibuf_reserve
(struct
ibuf *buf, size_t
len);
void *
ibuf_seek
(struct
ibuf *buf, size_t
pos, size_t
len);
size_t
ibuf_size
(struct
ibuf *buf);
size_t
ibuf_left
(struct
ibuf *buf);
void
ibuf_close
(struct
msgbuf *msgbuf, struct
ibuf *buf);
int
ibuf_write
(struct
msgbuf *msgbuf);
void
ibuf_free
(struct
ibuf *buf);
void
msgbuf_init
(struct
msgbuf *msgbuf);
void
msgbuf_clear
(struct
msgbuf *msgbuf);
int
msgbuf_write
(struct
msgbuf *msgbuf);
void
msgbuf_drain
(struct
msgbuf *msgbuf, size_t
n);
The 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.
A program using these functions should be linked with
-lutil.
The basic imsg_init
structure is the
imsgbuf, which wraps a file descriptor and represents one
side of a channel on which messages are sent and received:
struct imsgbuf {
TAILQ_HEAD(, imsg_fd) fds;
struct ibuf_read r;
struct msgbuf w;
int fd;
pid_t pid;
};
imsg_init
() is a routine which initializes
ibuf as one side of a channel associated with
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 w member as the output buffer queue,
fd with the file descriptor passed to
imsg_init
() and the other members for internal use
only.
The imsg_clear
() function frees any data
allocated as part of an imsgbuf.
imsg_create
(),
imsg_add
() and imsg_close
()
are generic construction routines for messages that are to be sent using an
imsgbuf.
imsg_create
() creates a new message with
header specified by type, peerid
and pid. A pid of zero uses the
process ID returned by
getpid(2)
when ibuf was initialized. In addition to this common
imsg header, datalen bytes of space may be reserved
for attaching to this imsg. This space is populated using
imsg_add
(). Additionally, the file descriptor
fd may be passed over the socket to the other process.
If 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. imsg_create
() returns a pointer to a new
message if it succeeds, NULL otherwise.
imsg_add
() appends to
imsg len bytes of ancillary data
pointed to by buf. It returns
len if it succeeds, -1 otherwise.
imsg_close
() completes creation of
imsg by adding it to imsgbuf
output buffer.
imsg_compose
() is a routine which is used
to quickly create and queue an imsg. It takes the same parameters as the
imsg_create
(), imsg_add
()
and imsg_close
() routines, except that only one
ancillary data buffer can be provided. This routine returns 1 if it
succeeds, -1 otherwise.
imsg_composev
() is similar to
imsg_compose
(). It takes the same parameters, except
that the ancillary data buffer is specified by
iovec.
imsg_flush
() is a function which calls
msgbuf_write
() in a loop until all imsgs in the
output buffer are sent. It returns 0 if it succeeds, -1 otherwise.
The imsg_read
() routine reads pending data
with
recvmsg(2)
and queues it as individual messages on imsgbuf. It
returns the number of bytes read on success, or -1 on error. A return value
of -1 from imsg_read
() invalidates
imsgbuf, and renders it suitable only for passing to
imsg_clear
().
imsg_get
() fills in an individual imsg
pending on imsgbuf into the structure pointed to by
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 struct imsg, which must be freed by
imsg_free
() when no longer required.
struct imsg has this form:
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;
};
The header members are:
- type
- A integer identifier, typically used to express the meaning of the
message.
- len
- The total length of the imsg, including the header and any ancillary data
transmitted with the message (pointed to by the data
member of the message itself).
- flags
- Flags used internally by the imsg functions: should not be used by
application programs.
- peerid, pid
- 32-bit values specified on message creation and free for any use by the
caller, normally used to identify the message sender.
In addition, struct imsg has the following:
- 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.
- data
- A pointer to the ancillary data transmitted with the imsg.
The IMSG_HEADER_SIZE define is the size of the imsg message
header, which may be subtracted from the len member of
struct imsg_hdr to obtain the length of any additional
data passed with the message.
MAX_IMSGSIZE is defined as the maximum size of a single imsg,
currently 16384 bytes.
The imsg API defines functions to manipulate buffers, used internally and during
construction of imsgs with imsg_create
(). A
struct ibuf is a single buffer and a struct
msgbuf a queue of output buffers for transmission:
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;
};
The ibuf_open
() function allocates a
fixed-length buffer. The buffer may not be resized and may contain a maximum
of len bytes. On success
ibuf_open
() returns a pointer to the buffer; on
failure it returns NULL.
ibuf_dynamic
() allocates a resizeable
buffer of initial length len and maximum size
max. Buffers allocated with
ibuf_dynamic
() are automatically grown if necessary
when data is added.
ibuf_add
() is a routine which appends a
block of data to buf. 0 is returned on success and -1
on failure.
ibuf_reserve
() is used to reserve
len bytes in buf. A pointer to
the start of the reserved space is returned, or NULL on error.
ibuf_seek
() is a function which returns a
pointer to the part of the buffer at offset pos and of
extent len. NULL is returned if the requested range is
outside the part of the buffer in use.
ibuf_size
() and
ibuf_left
() are functions which return the total
bytes used and available in buf respectively.
ibuf_close
() appends
buf to msgbuf ready to be
sent.
The ibuf_write
() routine transmits as many
pending buffers as possible from msgbuf
() using
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 EAGAIN
and require the
application to retry again in the future.
ibuf_free
() frees
buf and any associated storage.
The msgbuf_init
() function initializes
msgbuf so that buffers may be appended to it. The
fd member should also be set directly before
msgbuf_write
() is used.
msgbuf_clear
() empties a msgbuf, removing
and discarding any queued buffers.
The msgbuf_write
() routine calls
sendmsg(2)
to transmit buffers queued in 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 EAGAIN
and require the application to retry
again in the future.
msgbuf_drain
() discards data from buffers
queued in msgbuf until n bytes
have been removed or msgbuf is empty.
In a typical program, a channel between two processes is created with
socketpair(2),
and an imsgbuf created around one file descriptor in each
process:
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));
Messages may then be composed and queued on the
imsgbuf, for example using the
imsg_compose
() function:
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);
...
}
A mechanism such as
poll(2)
or the
event(3)
library is used to monitor the socket file descriptor. When the socket is
ready for writing, queued messages are transmitted with
msgbuf_write
():
if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) {
/* handle write failure */
}
And when ready for reading, messages are first received using
imsg_read
() and then extracted with
imsg_get
():
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 < sizeof idata) {
/* handle corrupt message */
}
memcpy(&idata, imsg.data, sizeof idata);
/* handle message received */
break;
...
}
imsg_free(&imsg);
}
}