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