jcs's openbsd hax
openbsd
1.\" $OpenBSD: imsg_init.3,v 1.46 2025/06/13 18:34:00 schwarze Exp $
2.\"
3.\" Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
4.\" Copyright (c) 2010 Nicholas Marriott <nicm@openbsd.org>
5.\"
6.\" Permission to use, copy, modify, and distribute this software for any
7.\" purpose with or without fee is hereby granted, provided that the above
8.\" copyright notice and this permission notice appear in all copies.
9.\"
10.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17.\"
18.Dd $Mdocdate: June 13 2025 $
19.Dt IMSG_ADD 3
20.Os
21.Sh NAME
22.Nm imsg_add ,
23.Nm imsg_close ,
24.Nm imsg_compose ,
25.Nm imsg_compose_ibuf ,
26.Nm imsg_composev ,
27.Nm imsg_create ,
28.Nm imsg_forward ,
29.Nm imsg_free ,
30.Nm imsg_get_buf ,
31.Nm imsg_get_data ,
32.Nm imsg_get_fd ,
33.Nm imsg_get_ibuf ,
34.Nm imsg_get_id ,
35.Nm imsg_get_len ,
36.Nm imsg_get_pid ,
37.Nm imsg_get_strbuf ,
38.Nm imsg_get_type ,
39.Nm imsg_ibufq_pop ,
40.Nm imsg_ibufq_push ,
41.Nm imsg_set_maxsize ,
42.Nm imsgbuf_allow_fdpass ,
43.Nm imsgbuf_clear ,
44.Nm imsgbuf_flush ,
45.Nm imsgbuf_get ,
46.Nm imsgbuf_init ,
47.Nm imsgbuf_queuelen ,
48.Nm imsgbuf_read ,
49.Nm imsgbuf_set_maxsize ,
50.Nm imsgbuf_write
51.Nd IPC messaging functions
52.Sh SYNOPSIS
53.Lb libutil
54.In imsg.h
55.Fd #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
56.Fd #define MAX_IMSGSIZE 16384
57.Ft int
58.Fn imsg_add "struct ibuf *msg" "const void *data" "size_t datalen"
59.Ft void
60.Fn imsg_close "struct imsgbuf *imsgbuf" "struct ibuf *msg"
61.Ft int
62.Fn imsg_compose "struct imsgbuf *imsgbuf" "uint32_t type" "uint32_t id" \
63 "pid_t pid" "int fd" "const void *data" "size_t datalen"
64.Ft int
65.Fn imsg_compose_ibuf "struct imsgbuf *imsgbuf" "uint32_t type" \
66 "uint32_t id" "pid_t pid" "struct ibuf *buf"
67.Ft struct ibuf *
68.Fn imsg_create "struct imsgbuf *imsgbuf" "uint32_t type" "uint32_t id" \
69 "pid_t pid" "size_t datalen"
70.Ft int
71.Fn imsg_forward "struct imsgbuf *imsgbuf" "struct imsg *msg"
72.Ft void
73.Fn imsg_free "struct imsg *imsg"
74.Ft int
75.Fn imsg_get_buf "struct imsg *imsg" "void *data" "size_t len"
76.Ft int
77.Fn imsg_get_data "struct imsg *imsg" "void *data" "size_t len"
78.Ft int
79.Fn imsg_get_fd "struct imsg *imsg"
80.Ft int
81.Fn imsg_get_ibuf "struct imsg *imsg" "struct ibuf *ibuf"
82.Ft uint32_t
83.Fn imsg_get_id "struct imsg *imsg"
84.Ft size_t
85.Fn imsg_get_len "struct imsg *imsg"
86.Ft pid_t
87.Fn imsg_get_pid "struct imsg *imsg"
88.Ft int
89.Fn imsg_get_strbuf "struct imsg *imsg" "char *str" "size_t len"
90.Ft uint32_t
91.Fn imsg_get_type "struct imsg *imsg"
92.Ft int
93.Fn imsg_ibufq_pop "struct ibufqueue *bufq" "struct imsg *imsg"
94.Ft void
95.Fn imsg_ibufq_push "struct ibufqueue *bufq" "struct imsg *imsg"
96.Ft int
97.Fn imsg_set_maxsize "struct ibuf *msg" "size_t max"
98.Ft void
99.Fn imsgbuf_allow_fdpass "struct imsgbuf *imsgbuf"
100.Ft void
101.Fn imsgbuf_clear "struct imsgbuf *imsgbuf"
102.Ft int
103.Fn imsgbuf_flush "struct imsgbuf *imsgbuf"
104.Ft int
105.Fn imsgbuf_get "struct imsgbuf *imsgbuf" "struct imsg *imsg"
106.Ft int
107.Fn imsgbuf_init "struct imsgbuf *imsgbuf" "int fd"
108.Ft uint32_t
109.Fn imsgbuf_queuelen "struct imsgbuf *imsgbuf"
110.Ft int
111.Fn imsgbuf_read "struct imsgbuf *imsgbuf"
112.Ft int
113.Fn imsgbuf_set_maxsize "struct imsgbuf *imsgbuf" "uint32_t max"
114.Ft int
115.Fn imsgbuf_write "struct imsgbuf *imsgbuf"
116.In sys/uio.h
117.Ft int
118.Fn imsg_composev "struct imsgbuf *imsgbuf" "uint32_t type" "uint32_t id" \
119 "pid_t pid" "int fd" "const struct iovec *iov" "int iovcnt"
120.Sh DESCRIPTION
121The
122.Nm imsg
123functions provide a simple mechanism for communication between local processes
124using sockets.
125Each transmitted message is guaranteed to be presented to the receiving program
126whole.
127They are commonly used in privilege separated processes, where processes with
128different rights are required to cooperate.
129.Pp
130.Fn imsgbuf_init
131initializes
132.Fa imsgbuf
133as one side of a channel associated with
134.Fa fd .
135The file descriptor is used to send and receive messages,
136but is not closed by any of the imsg functions.
137It returns 0 if successful and -1 on failure.
138.Pp
139.Fn imsgbuf_allow_fdpass
140enables file descriptor passing in both directions for this
141.Fa imsgbuf .
142.Pp
143.Fn imsgbuf_set_maxsize
144changes the default maximum payload size
145to
146.Fa max .
147It returns 0 if successful and -1 on failure.
148.Pp
149The
150.Fn imsgbuf_clear
151function frees any data allocated as part of an imsgbuf.
152This function does not close the file descriptor used for communication.
153.Pp
154The
155.Fn imsgbuf_read
156routine reads pending data with
157.Xr recvmsg 2
158and queues it as individual messages on
159.Fa imsgbuf .
160It returns 1 on success, 0 if the connection is closed, or \-1 on error
161and the global variable
162.Va errno
163is set to indicate the error.
164The errors
165.Er EINTR
166and
167.Er EAGAIN
168are treated as follows.
169.Er EINTR
170will automatically retry the read operation while the other errors are
171ignored with a 1 return.
172.Pp
173.Fn imsgbuf_write
174writes out queued messages.
175It returns 0 if it succeeds, -1 on error and the global variable
176.Va errno
177is set to indicate the error.
178The errors
179.Er EINTR ,
180.Er EAGAIN ,
181and
182.Er ENOBUFS
183are treated as follows.
184.Er EINTR
185will automatically retry the write operation while the other errors are
186ignored with a 0 return.
187.Pp
188.Fn imsgbuf_flush
189calls
190.Fn imsgbuf_write
191in a loop until all imsgs in the output buffer are sent.
192It returns 0 if it succeeds, \-1 otherwise and the global variable
193.Va errno
194is set to indicate the error.
195.Fn imsgbuf_flush
196should not be called on non-blocking sockets since it will busy loop if the
197socket is not available.
198.Pp
199.Fn imsgbuf_get
200fills in an individual imsg pending on
201.Fa imsgbuf
202into the structure pointed to by
203.Fa imsg .
204It returns 1 on success, 0 if no messages are ready, or \-1 for an error.
205Received messages are returned as a
206.Em struct imsg ,
207which must be freed by
208.Fn imsg_free
209when no longer required.
210.Pp
211.Fn imsgbuf_queuelen
212returns the number of messages ready to be sent.
213This function returns 0 if no messages are pending for transmission.
214.Pp
215.Fn imsg_create ,
216.Fn imsg_add
217and
218.Fn imsg_close
219are generic construction routines for messages that are to be sent using an
220imsgbuf.
221.Pp
222.Fn imsg_create
223creates a new message with header specified by
224.Fa type ,
225.Fa id
226and
227.Fa pid .
228A
229.Fa pid
230of zero uses the process ID returned by
231.Xr getpid 2
232when
233.Fa imsgbuf
234was initialized.
235In addition to this common imsg header,
236.Fa datalen
237bytes of space may be reserved for attaching to this imsg.
238This space is populated using
239.Fn imsg_add .
240.Fn imsg_create
241returns a pointer to a new message if it succeeds, NULL otherwise.
242.Pp
243.Fn imsg_add
244appends to
245.Fa msg
246.Fa datalen
247bytes of ancillary data pointed to by
248.Fa data .
249It returns
250.Fa datalen
251if it succeeds, otherwise
252.Fa msg
253is freed and \-1 is returned.
254.Pp
255.Fn imsg_set_maxsize
256reduces the maximum payload of
257.Fa msg
258to
259.Fa max .
260The routine returns 0 if it succeeds, \-1 otherwise.
261.Pp
262.Fn imsg_close
263completes creation of
264.Fa msg
265by adding it to
266.Fa imsgbuf
267output buffer.
268.Pp
269.Fn imsg_compose
270is used to quickly create and queue an imsg.
271It takes the same parameters as the
272.Fn imsg_create ,
273.Fn imsg_add
274and
275.Fn imsg_close
276routines,
277except that only one ancillary data buffer can be provided.
278Additionally, the file descriptor
279.Fa fd
280may be passed over the socket to the other process.
281If
282.Fa fd
283is given, it is closed in the sending program after the message is sent.
284A value of \-1 indicates no file descriptor should be passed.
285This routine returns 1 if it succeeds, \-1 otherwise.
286.Pp
287.Fn imsg_composev
288is similar to
289.Fn imsg_compose .
290It takes the same parameters, except that the ancillary data buffer is specified
291by
292.Fa iovec .
293.Pp
294.Fn imsg_compose_ibuf
295is similar to
296.Fn imsg_compose .
297It takes the same parameters, except that the ancillary data buffer is specified
298by an ibuf
299.Fa buf .
300This routine returns 1 if it succeeds, \-1 otherwise.
301In either case the buffer
302.Fa buf
303is consumed by the function.
304.Pp
305.Fn imsg_forward
306forwards a just received
307.Fa msg
308unaltered on
309.Fa imsgbuf .
310File descriptors are not forwarded by this function.
311It is possible to call
312.Fn imsg_forward
313more than once per message.
314.Pp
315The accessors
316.Fn imsg_get_type ,
317.Fn imsg_get_pid ,
318.Fn imsg_get_id ,
319and
320.Fn imsg_get_len ,
321return the
322.Fa type ,
323.Fa pid ,
324.Fa id ,
325and payload length used in
326.Fn imsg_create
327to build the
328.Fa imsg .
329If there is no payload
330.Fn imsg_get_len
331returns 0.
332.Pp
333.Fn imsg_get_fd
334returns the file descriptor and passes the responsibility to track the
335descriptor back to the program.
336Unclaimed file descriptors are closed by
337.Fn imsg_free .
338.Pp
339.Fn imsg_get_data
340and
341.Fn imsg_get_ibuf
342are used to extract the payload of an
343.Fa imsg .
344.Fn imsg_get_data
345can be used if the structure of the payload is known and can be extracted
346in one go.
3470 is returned on success and \-1 on failure.
348.Fn imsg_get_ibuf
349initializes the passed
350.Fa ibuf
351to hold the payload which can be read using
352.Xr ibuf_get 3 .
353The
354.Fa ibuf
355remains valid until
356.Fn imsg_free
357is called and there is no need to call
358.Fn ibuf_free
359on this stack based buffer.
360The function returns 0 on success, \-1 otherwise.
361.Pp
362.Fn imsg_get_buf
363and
364.Fn imsg_get_strbuf
365read
366.Fa len
367bytes from the
368.Fa imsg
369and copy them into
370.Fa buf
371or
372.Fa str ,
373respectively.
374.Fn imsg_get_strbuf
375ensures that
376.Fa str
377is NUL-terminated.
378The functions return 0 on success, \-1 otherwise.
379.Pp
380.Fn imsg_set_maxsize
381reduces the maximum payload of
382.Fa msg
383to
384.Fa max .
385The routine returns 0 if it succeeds, \-1 otherwise.
386.Pp
387.Fn imsg_ibufq_pop
388and
389.Fn imsg_ibufq_push
390allow to requeue an imsg onto the ibufqueue
391.Fa bufq .
392.Fn imsg_ibufq_pop
393returns 1 on success, 0 if no messages are ready, or \-1 for an error.
394See
395.Xr ibufq_new 3 for more info.
396.Pp
397MAX_IMSGSIZE is defined as the maximum size of a single imsg, currently
39816384 bytes.
399.Sh EXAMPLES
400In a typical program, a channel between two processes is created with
401.Xr socketpair 2 ,
402and an
403.Em imsgbuf
404created around one file descriptor in each process:
405.Bd -literal -offset indent
406struct imsgbuf parent_ibuf, child_ibuf;
407int imsg_fds[2];
408
409if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
410 err(1, "socketpair");
411
412switch (fork()) {
413case -1:
414 err(1, "fork");
415case 0:
416 /* child */
417 close(imsg_fds[0]);
418 if (imsgbuf_init(&child_ibuf, imsg_fds[1]) == -1)
419 err(1, NULL);
420 exit(child_main(&child_ibuf));
421}
422
423/* parent */
424close(imsg_fds[1]);
425if (imsgbuf_init(&parent_ibuf, imsg_fds[0]) == -1)
426 err(1, NULL);
427exit(parent_main(&parent_ibuf));
428.Ed
429.Pp
430Messages may then be composed and queued on the
431.Em imsgbuf ,
432for example using the
433.Fn imsg_compose
434function:
435.Bd -literal -offset indent
436enum imsg_type {
437 IMSG_A_MESSAGE,
438 IMSG_MESSAGE2
439};
440
441int
442child_main(struct imsgbuf *imsgbuf)
443{
444 int idata;
445 ...
446 idata = 42;
447 imsg_compose(imsgbuf, IMSG_A_MESSAGE,
448 0, 0, -1, &idata, sizeof idata);
449 ...
450}
451.Ed
452.Pp
453A mechanism such as
454.Xr poll 2
455or the
456.Xr event 3
457library is used to monitor the socket file descriptor.
458When the socket is ready for writing, queued messages are transmitted with
459.Fn imsgbuf_write :
460.Bd -literal -offset indent
461 if (imsgbuf_write(imsgbuf) == -1) {
462 if (errno == EPIPE)
463 /* handle closed connection */
464 else
465 /* handle write failure */
466 }
467.Ed
468.Pp
469And when ready for reading, messages are first received using
470.Fn imsgbuf_read
471and then extracted with
472.Fn imsgbuf_get :
473.Bd -literal -offset indent
474void
475dispatch_imsg(struct imsgbuf *imsgbuf)
476{
477 struct imsg imsg;
478 int n, idata;
479
480 switch (imsgbuf_read(imsgbuf)) {
481 case -1:
482 /* handle read error */
483 break;
484 case 0:
485 /* handle closed connection */
486 break;
487 }
488
489 for (;;) {
490 if ((n = imsgbuf_get(imsgbuf, &imsg)) == -1) {
491 /* handle read error */
492 break;
493 }
494 if (n == 0) /* no more messages */
495 return;
496
497 switch (imsg_get_type(&imsg)) {
498 case IMSG_A_MESSAGE:
499 if (imsg_get_data(&imsg, &idata,
500 sizeof(idata)) == -1) {
501 /* handle corrupt message */
502 }
503 /* handle message received */
504 break;
505 ...
506 }
507
508 imsg_free(&imsg);
509 }
510}
511.Ed
512.Sh SEE ALSO
513.Xr socketpair 2 ,
514.Xr ibuf_add 3 ,
515.Xr unix 4