xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/nbbio.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: nbbio.c,v 1.1.1.1 2011/03/02 19:32:44 tron 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, char *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 for the
80 /*	named buffer pair.  It is an error to enable a read
81 /*	pseudothread while the read buffer is full, or while a read
82 /*	or write pseudothread is still enabled.
83 /*
84 /*	nbbio_enable_write() enables a write pseudothread for the
85 /*	named buffer pair.  It is an error to enable a write
86 /*	pseudothread while the write buffer is empty, or while a
87 /*	read or write pseudothread is still enabled.
88 /*
89 /*	nbbio_disable_readwrite() disables any read/write pseudothreads
90 /*	for the named buffer pair, including timeouts. To ensure
91 /*	buffer liveness, use nbbio_slumber() instead of
92 /*	nbbio_disable_readwrite().  It is no error to call this
93 /*	function while no read/write pseudothread is enabled.
94 /*
95 /*	nbbio_slumber() disables any read/write pseudothreads for
96 /*	the named buffer pair, but keeps the timer active to ensure
97 /*	buffer liveness. It is no error to call this function while
98 /*	no read/write pseudothread is enabled.
99 /*
100 /*	NBBIO_ERROR_FLAGS() returns the error flags for the named buffer
101 /*	pair: zero or more of NBBIO_FLAG_EOF (read EOF), NBBIO_FLAG_ERROR
102 /*	(read/write error) or NBBIO_FLAG_TIMEOUT (time limit
103 /*	exceeded).
104 /*
105 /*	NBBIO_ACTIVE_FLAGS() returns the pseudothread flags for the
106 /*	named buffer pair: NBBIO_FLAG_READ (read pseudothread is
107 /*	active), NBBIO_FLAG_WRITE (write pseudothread is active),
108 /*	or zero (no pseudothread is active).
109 /*
110 /*	NBBIO_WRITE_PEND() and NBBIO_WRITE_BUF() evaluate to the
111 /*	number of to-be-written bytes and the write buffer for the
112 /*	named buffer pair. NBBIO_WRITE_PEND() must be updated by
113 /*	the application code that fills the write buffer; no more
114 /*	than NBBIO_BUFSIZE() bytes may be filled.
115 /*
116 /*	NBBIO_READ_PEND() and NBBIO_READ_BUF() evaluate to the
117 /*	number of unread bytes and the read buffer for the named
118 /*	buffer pair. NBBIO_READ_PEND() and NBBIO_READ_BUF() must
119 /*	be updated by the application code that drains the read
120 /*	buffer.
121 /* SEE ALSO
122 /*	events(3) event manager
123 /* DIAGNOSTICS
124 /*	Panic: interface violation.
125 /*
126 /*	Fatal: out of memory.
127 /* LICENSE
128 /* .ad
129 /* .fi
130 /*	The Secure Mailer license must be distributed with this software.
131 /* AUTHOR(S)
132 /*	Wietse Venema
133 /*	IBM T.J. Watson Research
134 /*	P.O. Box 704
135 /*	Yorktown Heights, NY 10598, USA
136 /*--*/
137 
138  /*
139   * System library.
140   */
141 #include <sys_defs.h>
142 #include <unistd.h>
143 #include <errno.h>
144 #include <string.h>			/* memmove() */
145 
146  /*
147   * Utility library.
148   */
149 #include <mymalloc.h>
150 #include <msg.h>
151 #include <events.h>
152 #include <nbbio.h>
153 
154 /* nbbio_event - non-blocking event handler */
155 
156 static void nbbio_event(int event, char *context)
157 {
158     const char *myname = "nbbio_event";
159     NBBIO  *np = (NBBIO *) context;
160     ssize_t count;
161 
162     switch (event) {
163 
164 	/*
165 	 * Read data into the read buffer. Leave it up to the application to
166 	 * drain the buffer until it is empty.
167 	 */
168     case EVENT_READ:
169 	if (np->read_pend == np->bufsize)
170 	    msg_panic("%s: socket fd=%d: read buffer is full",
171 		      myname, np->fd);
172 	if (np->read_pend < 0 || np->read_pend > np->bufsize)
173 	    msg_panic("%s: socket fd=%d: bad pending read count %ld",
174 		      myname, np->fd, (long) np->read_pend);
175 	count = read(np->fd, np->read_buf + np->read_pend,
176 		     np->bufsize - np->read_pend);
177 	if (count > 0) {
178 	    np->read_pend += count;
179 	    if (msg_verbose)
180 		msg_info("%s: read %ld on %s fd=%d",
181 			 myname, (long) count, np->label, np->fd);
182 	} else if (count == 0) {
183 	    np->flags |= NBBIO_FLAG_EOF;
184 	    if (msg_verbose)
185 		msg_info("%s: read EOF on %s fd=%d",
186 			 myname, np->label, np->fd);
187 	} else {
188 	    if (errno == EAGAIN)
189 		msg_warn("%s: read() returns EAGAIN on readable descriptor",
190 			 myname);
191 	    np->flags |= NBBIO_FLAG_ERROR;
192 	    if (msg_verbose)
193 		msg_info("%s: read %s fd=%d: %m", myname, np->label, np->fd);
194 	}
195 	break;
196 
197 	/*
198 	 * Drain data from the output buffer.  Notify the application
199 	 * whenever some bytes are written.
200 	 *
201 	 * XXX Enforce a total time limit to ensure liveness when a hostile
202 	 * receiver sets a very small TCP window size.
203 	 */
204     case EVENT_WRITE:
205 	if (np->write_pend == 0)
206 	    msg_panic("%s: socket fd=%d: empty write buffer", myname, np->fd);
207 	if (np->write_pend < 0 || np->write_pend > np->bufsize)
208 	    msg_panic("%s: socket fd=%d: bad pending write count %ld",
209 		      myname, np->fd, (long) np->write_pend);
210 	count = write(np->fd, np->write_buf, np->write_pend);
211 	if (count > 0) {
212 	    np->write_pend -= count;
213 	    if (np->write_pend > 0)
214 		memmove(np->write_buf, np->write_buf + count, np->write_pend);
215 	} else {
216 	    if (errno == EAGAIN)
217 		msg_warn("%s: write() returns EAGAIN on writable descriptor",
218 			 myname);
219 	    np->flags |= NBBIO_FLAG_ERROR;
220 	    if (msg_verbose)
221 		msg_info("%s: write %s fd=%d: %m", myname, np->label, np->fd);
222 	}
223 	break;
224 
225 	/*
226 	 * Something bad happened.
227 	 */
228     case EVENT_XCPT:
229 	np->flags |= NBBIO_FLAG_ERROR;
230 	if (msg_verbose)
231 	    msg_info("%s: error on %s fd=%d: %m", myname, np->label, np->fd);
232 	break;
233 
234 	/*
235 	 * Something good didn't happen.
236 	 */
237     case EVENT_TIME:
238 	np->flags |= NBBIO_FLAG_TIMEOUT;
239 	if (msg_verbose)
240 	    msg_info("%s: %s timeout on %s fd=%d",
241 		     myname, NBBIO_OP_NAME(np), np->label, np->fd);
242 	break;
243 
244     default:
245 	msg_panic("%s: unknown event %d", myname, event);
246     }
247 
248     /*
249      * Application notification. The application will check for any error
250      * flags, copy application data from or to our buffer pair, and decide
251      * what I/O happens next.
252      */
253     np->action(event, np->context);
254 }
255 
256 /* nbbio_enable_read - enable reading from socket into buffer */
257 
258 void    nbbio_enable_read(NBBIO *np, int timeout)
259 {
260     const char *myname = "nbbio_enable_read";
261 
262     /*
263      * Sanity checks.
264      */
265     if (np->flags & NBBIO_MASK_ACTIVE)
266 	msg_panic("%s: socket fd=%d is enabled for %s",
267 		  myname, np->fd, NBBIO_OP_NAME(np));
268     if (timeout <= 0)
269 	msg_panic("%s: socket fd=%d: bad timeout %d",
270 		  myname, np->fd, timeout);
271     if (np->read_pend >= np->bufsize)
272 	msg_panic("%s: socket fd=%d: read buffer is full",
273 		  myname, np->fd);
274 
275     /*
276      * Enable events.
277      */
278     event_enable_read(np->fd, nbbio_event, (char *) np);
279     event_request_timer(nbbio_event, (char *) np, timeout);
280     np->flags |= NBBIO_FLAG_READ;
281 }
282 
283 /* nbbio_enable_write - enable writing from buffer to socket */
284 
285 void    nbbio_enable_write(NBBIO *np, int timeout)
286 {
287     const char *myname = "nbbio_enable_write";
288 
289     /*
290      * Sanity checks.
291      */
292     if (np->flags & NBBIO_MASK_ACTIVE)
293 	msg_panic("%s: socket fd=%d is enabled for %s",
294 		  myname, np->fd, NBBIO_OP_NAME(np));
295     if (timeout <= 0)
296 	msg_panic("%s: socket fd=%d bad timeout %d",
297 		  myname, np->fd, timeout);
298     if (np->write_pend <= 0)
299 	msg_panic("%s: socket fd=%d: empty write buffer",
300 		  myname, np->fd);
301 
302     /*
303      * Enable events.
304      */
305     event_enable_write(np->fd, nbbio_event, (char *) np);
306     event_request_timer(nbbio_event, (char *) np, timeout);
307     np->flags |= NBBIO_FLAG_WRITE;
308 }
309 
310 /* nbbio_disable_readwrite - disable read/write/timer events */
311 
312 void    nbbio_disable_readwrite(NBBIO *np)
313 {
314     np->flags &= ~NBBIO_MASK_ACTIVE;
315     event_disable_readwrite(np->fd);
316     event_cancel_timer(nbbio_event, (char *) np);
317 }
318 
319 /* nbbio_slumber - disable read/write events, keep timer */
320 
321 void    nbbio_slumber(NBBIO *np, int timeout)
322 {
323     np->flags &= ~NBBIO_MASK_ACTIVE;
324     event_disable_readwrite(np->fd);
325     event_request_timer(nbbio_event, (char *) np, timeout);
326 }
327 
328 /* nbbio_create - create socket buffer */
329 
330 NBBIO  *nbbio_create(int fd, ssize_t bufsize, const char *label,
331 		             NBBIO_ACTION action, char *context)
332 {
333     NBBIO  *np;
334 
335     /*
336      * Sanity checks.
337      */
338     if (fd < 0)
339 	msg_panic("nbbio_create: bad file descriptor: %d", fd);
340     if (bufsize <= 0)
341 	msg_panic("nbbio_create: bad buffer size: %ld", (long) bufsize);
342 
343     /*
344      * Create a new buffer pair.
345      */
346     np = (NBBIO *) mymalloc(sizeof(*np));
347     np->fd = fd;
348     np->bufsize = bufsize;
349     np->label = mystrdup(label);
350     np->action = action;
351     np->context = context;
352     np->flags = 0;
353 
354     np->read_buf = mymalloc(bufsize);
355     np->read_pend = 0;
356 
357     np->write_buf = mymalloc(bufsize);
358     np->write_pend = 0;
359 
360     return (np);
361 }
362 
363 /* nbbio_free - destroy socket buffer */
364 
365 void    nbbio_free(NBBIO *np)
366 {
367     nbbio_disable_readwrite(np);
368     (void) close(np->fd);
369     myfree(np->label);
370     myfree(np->read_buf);
371     myfree(np->write_buf);
372     myfree((char *) np);
373 }
374