NAME
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
SYNOPSIS
#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);
DESCRIPTION
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.
BUFFERS
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. If
buf is a NULL pointer, no action occurs.
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.
EXAMPLES
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 && errno != EAGAIN) || 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); } }