xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/nbbio.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
1 /*	$NetBSD: nbbio.c,v 1.3 2020/03/18 19:05:21 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	nbbio 3
6 /* SUMMARY
7 /*	non-blocking buffered I/O
8 /* SYNOPSIS
9 /*	#include <nbbio.h>
10 /*
11 /*	NBBIO	*nbbio_create(fd, bufsize, label, action, context)
12 /*	int	fd;
13 /*	ssize_t	bufsize;
14 /*	const char *label;
15 /*	void	(*action)(int event, void *context);
16 /*	char	*context;
17 /*
18 /*	void	nbbio_free(np)
19 /*	NBBIO	*np;
20 /*
21 /*	void	nbbio_enable_read(np, timeout)
22 /*	NBBIO	*np;
23 /*	int	timeout;
24 /*
25 /*	void	nbbio_enable_write(np, timeout)
26 /*	NBBIO	*np;
27 /*	int	timeout;
28 /*
29 /*	void	nbbio_disable_readwrite(np)
30 /*	NBBIO	*np;
31 /*
32 /*	void	nbbio_slumber(np, timeout)
33 /*	NBBIO	*np;
34 /*	int	timeout;
35 /*
36 /*	int	NBBIO_ACTIVE_FLAGS(np)
37 /*	NBBIO	*np;
38 /*
39 /*	int	NBBIO_ERROR_FLAGS(np)
40 /*	NBBIO	*np;
41 /*
42 /*	const ssize_t NBBIO_BUFSIZE(np)
43 /*	NBBIO	*np;
44 /*
45 /*	ssize_t	NBBIO_READ_PEND(np)
46 /*	NBBIO	*np;
47 /*
48 /*	char	*NBBIO_READ_BUF(np)
49 /*	NBBIO	*np;
50 /*
51 /*	const ssize_t NBBIO_WRITE_PEND(np)
52 /*	NBBIO	*np;
53 /*
54 /*	char	*NBBIO_WRITE_BUF(np)
55 /*	NBBIO	*np;
56 /* DESCRIPTION
57 /*	This module implements low-level support for event-driven
58 /*	I/O on a full-duplex stream. Read/write events are handled
59 /*	by pseudothreads that run under control by the events(5)
60 /*	module.  After each I/O operation, the application is
61 /*	notified via a call-back routine.
62 /*
63 /*	It is up to the call-back routine to turn on/off read/write
64 /*	events as appropriate.  It is an error to leave read events
65 /*	enabled for a buffer that is full, or to leave write events
66 /*	enabled for a buffer that is empty.
67 /*
68 /*	nbbio_create() creates a pair of buffers of the named size
69 /*	for the named stream. The label specifies the purpose of
70 /*	the stream, and is used for diagnostic messages.  The
71 /*	nbbio(3) event handler invokes the application call-back
72 /*	routine with the current event type (EVENT_READ etc.) and
73 /*	with the application-specified context.
74 /*
75 /*	nbbio_free() terminates any pseudothreads associated with
76 /*	the named buffer pair, closes the stream, and destroys the
77 /*	buffer pair.
78 /*
79 /*	nbbio_enable_read() enables a read pseudothread (if one
80 /*	does not already exist) for the named buffer pair, and
81 /*	(re)starts the buffer pair's timer. It is an error to enable
82 /*	a read pseudothread while the read buffer is full, or while
83 /*	a write pseudothread is still enabled.
84 /*
85 /*	nbbio_enable_write() enables a write pseudothread (if one
86 /*	does not already exist) for the named buffer pair, and
87 /*	(re)starts the buffer pair's timer. It is an error to enable
88 /*	a write pseudothread while the write buffer is empty, or
89 /*	while a read pseudothread is still enabled.
90 /*
91 /*	nbbio_disable_readwrite() disables any read/write pseudothreads
92 /*	for the named buffer pair, including timeouts. To ensure
93 /*	buffer liveness, use nbbio_slumber() instead of
94 /*	nbbio_disable_readwrite().  It is no error to call this
95 /*	function while no read/write pseudothread is enabled.
96 /*
97 /*	nbbio_slumber() disables any read/write pseudothreads for
98 /*	the named buffer pair, but keeps the timer active to ensure
99 /*	buffer liveness. It is no error to call this function while
100 /*	no read/write pseudothread is enabled.
101 /*
102 /*	NBBIO_ERROR_FLAGS() returns the error flags for the named buffer
103 /*	pair: zero or more of NBBIO_FLAG_EOF (read EOF), NBBIO_FLAG_ERROR
104 /*	(read/write error) or NBBIO_FLAG_TIMEOUT (time limit
105 /*	exceeded).
106 /*
107 /*	NBBIO_ACTIVE_FLAGS() returns the pseudothread flags for the
108 /*	named buffer pair: NBBIO_FLAG_READ (read pseudothread is
109 /*	active), NBBIO_FLAG_WRITE (write pseudothread is active),
110 /*	or zero (no pseudothread is active).
111 /*
112 /*	NBBIO_WRITE_PEND() and NBBIO_WRITE_BUF() evaluate to the
113 /*	number of to-be-written bytes and the write buffer for the
114 /*	named buffer pair. NBBIO_WRITE_PEND() must be updated by
115 /*	the application code that fills the write buffer; no more
116 /*	than NBBIO_BUFSIZE() bytes may be filled.
117 /*
118 /*	NBBIO_READ_PEND() and NBBIO_READ_BUF() evaluate to the
119 /*	number of unread bytes and the read buffer for the named
120 /*	buffer pair. NBBIO_READ_PEND() and NBBIO_READ_BUF() must
121 /*	be updated by the application code that drains the read
122 /*	buffer.
123 /* SEE ALSO
124 /*	events(3) event manager
125 /* DIAGNOSTICS
126 /*	Panic: interface violation.
127 /*
128 /*	Fatal: out of memory.
129 /* LICENSE
130 /* .ad
131 /* .fi
132 /*	The Secure Mailer license must be distributed with this software.
133 /* AUTHOR(S)
134 /*	Wietse Venema
135 /*	IBM T.J. Watson Research
136 /*	P.O. Box 704
137 /*	Yorktown Heights, NY 10598, USA
138 /*
139 /*	Wietse Venema
140 /*	Google, Inc.
141 /*	111 8th Avenue
142 /*	New York, NY 10011, USA
143 /*--*/
144 
145  /*
146   * System library.
147   */
148 #include <sys_defs.h>
149 #include <unistd.h>
150 #include <errno.h>
151 #include <string.h>			/* memmove() */
152 
153  /*
154   * Utility library.
155   */
156 #include <mymalloc.h>
157 #include <msg.h>
158 #include <events.h>
159 #include <nbbio.h>
160 
161 /* nbbio_event - non-blocking event handler */
162 
nbbio_event(int event,void * context)163 static void nbbio_event(int event, void *context)
164 {
165     const char *myname = "nbbio_event";
166     NBBIO  *np = (NBBIO *) context;
167     ssize_t count;
168 
169     switch (event) {
170 
171 	/*
172 	 * Read data into the read buffer. Leave it up to the application to
173 	 * drain the buffer until it is empty.
174 	 */
175     case EVENT_READ:
176 	if (np->read_pend == np->bufsize)
177 	    msg_panic("%s: socket fd=%d: read buffer is full",
178 		      myname, np->fd);
179 	if (np->read_pend < 0 || np->read_pend > np->bufsize)
180 	    msg_panic("%s: socket fd=%d: bad pending read count %ld",
181 		      myname, np->fd, (long) np->read_pend);
182 	count = read(np->fd, np->read_buf + np->read_pend,
183 		     np->bufsize - np->read_pend);
184 	if (count > 0) {
185 	    np->read_pend += count;
186 	    if (msg_verbose)
187 		msg_info("%s: read %ld on %s fd=%d",
188 			 myname, (long) count, np->label, np->fd);
189 	} else if (count == 0) {
190 	    np->flags |= NBBIO_FLAG_EOF;
191 	    if (msg_verbose)
192 		msg_info("%s: read EOF on %s fd=%d",
193 			 myname, np->label, np->fd);
194 	} else {
195 	    if (errno == EAGAIN)
196 		msg_warn("%s: read() returns EAGAIN on readable descriptor",
197 			 myname);
198 	    np->flags |= NBBIO_FLAG_ERROR;
199 	    if (msg_verbose)
200 		msg_info("%s: read %s fd=%d: %m", myname, np->label, np->fd);
201 	}
202 	break;
203 
204 	/*
205 	 * Drain data from the output buffer.  Notify the application
206 	 * whenever some bytes are written.
207 	 *
208 	 * XXX Enforce a total time limit to ensure liveness when a hostile
209 	 * receiver sets a very small TCP window size.
210 	 */
211     case EVENT_WRITE:
212 	if (np->write_pend == 0)
213 	    msg_panic("%s: socket fd=%d: empty write buffer", myname, np->fd);
214 	if (np->write_pend < 0 || np->write_pend > np->bufsize)
215 	    msg_panic("%s: socket fd=%d: bad pending write count %ld",
216 		      myname, np->fd, (long) np->write_pend);
217 	count = write(np->fd, np->write_buf, np->write_pend);
218 	if (count > 0) {
219 	    np->write_pend -= count;
220 	    if (np->write_pend > 0)
221 		memmove(np->write_buf, np->write_buf + count, np->write_pend);
222 	} else {
223 	    if (errno == EAGAIN)
224 		msg_warn("%s: write() returns EAGAIN on writable descriptor",
225 			 myname);
226 	    np->flags |= NBBIO_FLAG_ERROR;
227 	    if (msg_verbose)
228 		msg_info("%s: write %s fd=%d: %m", myname, np->label, np->fd);
229 	}
230 	break;
231 
232 	/*
233 	 * Something bad happened.
234 	 */
235     case EVENT_XCPT:
236 	np->flags |= NBBIO_FLAG_ERROR;
237 	if (msg_verbose)
238 	    msg_info("%s: error on %s fd=%d: %m", myname, np->label, np->fd);
239 	break;
240 
241 	/*
242 	 * Something good didn't happen.
243 	 */
244     case EVENT_TIME:
245 	np->flags |= NBBIO_FLAG_TIMEOUT;
246 	if (msg_verbose)
247 	    msg_info("%s: %s timeout on %s fd=%d",
248 		     myname, NBBIO_OP_NAME(np), np->label, np->fd);
249 	break;
250 
251     default:
252 	msg_panic("%s: unknown event %d", myname, event);
253     }
254 
255     /*
256      * Application notification. The application will check for any error
257      * flags, copy application data from or to our buffer pair, and decide
258      * what I/O happens next.
259      */
260     np->action(event, np->context);
261 }
262 
263 /* nbbio_enable_read - enable reading from socket into buffer */
264 
nbbio_enable_read(NBBIO * np,int timeout)265 void    nbbio_enable_read(NBBIO *np, int timeout)
266 {
267     const char *myname = "nbbio_enable_read";
268 
269     /*
270      * Sanity checks.
271      */
272     if (np->flags & (NBBIO_MASK_ACTIVE & ~NBBIO_FLAG_READ))
273 	msg_panic("%s: socket fd=%d is enabled for %s",
274 		  myname, np->fd, NBBIO_OP_NAME(np));
275     if (timeout <= 0)
276 	msg_panic("%s: socket fd=%d: bad timeout %d",
277 		  myname, np->fd, timeout);
278     if (np->read_pend >= np->bufsize)
279 	msg_panic("%s: socket fd=%d: read buffer is full",
280 		  myname, np->fd);
281 
282     /*
283      * Enable events.
284      */
285     if ((np->flags & NBBIO_FLAG_READ) == 0) {
286 	event_enable_read(np->fd, nbbio_event, (void *) np);
287 	np->flags |= NBBIO_FLAG_READ;
288     }
289     event_request_timer(nbbio_event, (void *) np, timeout);
290 }
291 
292 /* nbbio_enable_write - enable writing from buffer to socket */
293 
nbbio_enable_write(NBBIO * np,int timeout)294 void    nbbio_enable_write(NBBIO *np, int timeout)
295 {
296     const char *myname = "nbbio_enable_write";
297 
298     /*
299      * Sanity checks.
300      */
301     if (np->flags & (NBBIO_MASK_ACTIVE & ~NBBIO_FLAG_WRITE))
302 	msg_panic("%s: socket fd=%d is enabled for %s",
303 		  myname, np->fd, NBBIO_OP_NAME(np));
304     if (timeout <= 0)
305 	msg_panic("%s: socket fd=%d: bad timeout %d",
306 		  myname, np->fd, timeout);
307     if (np->write_pend <= 0)
308 	msg_panic("%s: socket fd=%d: empty write buffer",
309 		  myname, np->fd);
310 
311     /*
312      * Enable events.
313      */
314     if ((np->flags & NBBIO_FLAG_WRITE) == 0) {
315 	event_enable_write(np->fd, nbbio_event, (void *) np);
316 	np->flags |= NBBIO_FLAG_WRITE;
317     }
318     event_request_timer(nbbio_event, (void *) np, timeout);
319 }
320 
321 /* nbbio_disable_readwrite - disable read/write/timer events */
322 
nbbio_disable_readwrite(NBBIO * np)323 void    nbbio_disable_readwrite(NBBIO *np)
324 {
325     np->flags &= ~NBBIO_MASK_ACTIVE;
326     event_disable_readwrite(np->fd);
327     event_cancel_timer(nbbio_event, (void *) np);
328 }
329 
330 /* nbbio_slumber - disable read/write events, keep timer */
331 
nbbio_slumber(NBBIO * np,int timeout)332 void    nbbio_slumber(NBBIO *np, int timeout)
333 {
334     np->flags &= ~NBBIO_MASK_ACTIVE;
335     event_disable_readwrite(np->fd);
336     event_request_timer(nbbio_event, (void *) np, timeout);
337 }
338 
339 /* nbbio_create - create socket buffer */
340 
nbbio_create(int fd,ssize_t bufsize,const char * label,NBBIO_ACTION action,void * context)341 NBBIO  *nbbio_create(int fd, ssize_t bufsize, const char *label,
342 		             NBBIO_ACTION action, void *context)
343 {
344     NBBIO  *np;
345 
346     /*
347      * Sanity checks.
348      */
349     if (fd < 0)
350 	msg_panic("nbbio_create: bad file descriptor: %d", fd);
351     if (bufsize <= 0)
352 	msg_panic("nbbio_create: bad buffer size: %ld", (long) bufsize);
353 
354     /*
355      * Create a new buffer pair.
356      */
357     np = (NBBIO *) mymalloc(sizeof(*np));
358     np->fd = fd;
359     np->bufsize = bufsize;
360     np->label = mystrdup(label);
361     np->action = action;
362     np->context = context;
363     np->flags = 0;
364 
365     np->read_buf = mymalloc(bufsize);
366     np->read_pend = 0;
367 
368     np->write_buf = mymalloc(bufsize);
369     np->write_pend = 0;
370 
371     return (np);
372 }
373 
374 /* nbbio_free - destroy socket buffer */
375 
nbbio_free(NBBIO * np)376 void    nbbio_free(NBBIO *np)
377 {
378     nbbio_disable_readwrite(np);
379     (void) close(np->fd);
380     myfree(np->label);
381     myfree(np->read_buf);
382     myfree(np->write_buf);
383     myfree((void *) np);
384 }
385