]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/sendmail/libsm/refill.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / sendmail / libsm / refill.c
1 /*
2  * Copyright (c) 2000-2001, 2005-2006 Proofpoint, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1990, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Chris Torek.
9  *
10  * By using this file, you agree to the terms and conditions set
11  * forth in the LICENSE file which can be found at the top level of
12  * the sendmail distribution.
13  */
14
15 #include <sm/gen.h>
16 SM_RCSID("@(#)$Id: refill.c,v 1.54 2013-11-22 20:51:43 ca Exp $")
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <setjmp.h>
21 #include <signal.h>
22 #include <sm/time.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <sm/io.h>
26 #include <sm/conf.h>
27 #include <sm/assert.h>
28 #include <sm/fdset.h>
29 #include "local.h"
30
31 static int sm_lflush __P((SM_FILE_T *, int *));
32
33 /*
34 **  SM_IO_RD_TIMEOUT -- measured timeout for reads
35 **
36 **  This #define uses a select() to wait for the 'fd' to become readable.
37 **  The select() can be active for up to 'To' time. The select() may not
38 **  use all of the the 'To' time. Hence, the amount of "wall-clock" time is
39 **  measured to decide how much to subtract from 'To' to update it. On some
40 **  BSD-based/like systems the timeout for a select() is updated for the
41 **  amount of time used. On many/most systems this does not happen. Therefore
42 **  the updating of 'To' must be done ourselves; a copy of 'To' is passed
43 **  since a BSD-like system will have updated it and we don't want to
44 **  double the time used!
45 **  Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
46 **  sendmail buffered file type in sendmail/bf.c; see use below).
47 **
48 **      Parameters
49 **              fp -- the file pointer for the active file
50 **              fd -- raw file descriptor (from 'fp') to use for select()
51 **              to -- struct timeval of the timeout
52 **              timeout -- the original timeout value
53 **              sel_ret -- the return value from the select()
54 **
55 **      Returns:
56 **              nothing, flow through code
57 */
58
59 #define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret)                  \
60 {                                                                       \
61         struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff;  \
62         fd_set sm_io_to_mask, sm_io_x_mask;                             \
63         errno = 0;                                                      \
64         if (timeout == SM_TIME_IMMEDIATE)                               \
65         {                                                               \
66                 errno = EAGAIN;                                         \
67                 return SM_IO_EOF;                                       \
68         }                                                               \
69         if (!SM_FD_OK_SELECT(fd))                                       \
70         {                                                               \
71                 errno = EINVAL;                                         \
72                 return SM_IO_EOF;                                       \
73         }                                                               \
74         FD_ZERO(&sm_io_to_mask);                                        \
75         FD_SET((fd), &sm_io_to_mask);                                   \
76         FD_ZERO(&sm_io_x_mask);                                         \
77         FD_SET((fd), &sm_io_x_mask);                                    \
78         if (gettimeofday(&sm_io_to_before, NULL) < 0)                   \
79                 return SM_IO_EOF;                                       \
80         do                                                              \
81         {                                                               \
82                 (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL,      \
83                                 &sm_io_x_mask, (to));                   \
84         } while ((sel_ret) < 0 && errno == EINTR);                      \
85         if ((sel_ret) < 0)                                              \
86         {                                                               \
87                 /* something went wrong, errno set */                   \
88                 fp->f_r = 0;                                            \
89                 fp->f_flags |= SMERR;                                   \
90                 return SM_IO_EOF;                                       \
91         }                                                               \
92         else if ((sel_ret) == 0)                                        \
93         {                                                               \
94                 /* timeout */                                           \
95                 errno = EAGAIN;                                         \
96                 return SM_IO_EOF;                                       \
97         }                                                               \
98         /* calulate wall-clock time used */                             \
99         if (gettimeofday(&sm_io_to_after, NULL) < 0)                    \
100                 return SM_IO_EOF;                                       \
101         timersub(&sm_io_to_after, &sm_io_to_before, &sm_io_to_diff);    \
102         timersub((to), &sm_io_to_diff, (to));                           \
103 }
104
105 /*
106 **  SM_LFLUSH -- flush a file if it is line buffered and writable
107 **
108 **      Parameters:
109 **              fp -- file pointer to flush
110 **              timeout -- original timeout value (in milliseconds)
111 **
112 **      Returns:
113 **              Failure: returns SM_IO_EOF and sets errno
114 **              Success: returns 0
115 */
116
117 static int
118 sm_lflush(fp, timeout)
119         SM_FILE_T *fp;
120         int *timeout;
121 {
122
123         if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
124                 return sm_flush(fp, timeout);
125         return 0;
126 }
127
128 /*
129 **  SM_REFILL -- refill a buffer
130 **
131 **      Parameters:
132 **              fp -- file pointer for buffer refill
133 **              timeout -- time to complete filling the buffer in milliseconds
134 **
135 **      Returns:
136 **              Success: returns 0
137 **              Failure: returns SM_IO_EOF
138 */
139
140 int
141 sm_refill(fp, timeout)
142         register SM_FILE_T *fp;
143         int timeout;
144 {
145         int ret, r;
146         struct timeval to;
147         int fd;
148
149         if (timeout == SM_TIME_DEFAULT)
150                 timeout = fp->f_timeout;
151         if (timeout == SM_TIME_IMMEDIATE)
152         {
153                 /*
154                 **  Filling the buffer will take time and we are wanted to
155                 **  return immediately. And we're not EOF or ERR really.
156                 **  So... the failure is we couldn't do it in time.
157                 */
158
159                 errno = EAGAIN;
160                 fp->f_r = 0; /* just to be sure */
161                 return 0;
162         }
163
164         /* make sure stdio is set up */
165         if (!Sm_IO_DidInit)
166                 sm_init();
167
168         fp->f_r = 0;            /* largely a convenience for callers */
169
170         if (fp->f_flags & SMFEOF)
171                 return SM_IO_EOF;
172
173         SM_CONVERT_TIME(fp, fd, timeout, &to);
174
175         /* if not already reading, have to be reading and writing */
176         if ((fp->f_flags & SMRD) == 0)
177         {
178                 if ((fp->f_flags & SMRW) == 0)
179                 {
180                         errno = EBADF;
181                         fp->f_flags |= SMERR;
182                         return SM_IO_EOF;
183                 }
184
185                 /* switch to reading */
186                 if (fp->f_flags & SMWR)
187                 {
188                         if (sm_flush(fp, &timeout))
189                                 return SM_IO_EOF;
190                         fp->f_flags &= ~SMWR;
191                         fp->f_w = 0;
192                         fp->f_lbfsize = 0;
193                 }
194                 fp->f_flags |= SMRD;
195         }
196         else
197         {
198                 /*
199                 **  We were reading.  If there is an ungetc buffer,
200                 **  we must have been reading from that.  Drop it,
201                 **  restoring the previous buffer (if any).  If there
202                 **  is anything in that buffer, return.
203                 */
204
205                 if (HASUB(fp))
206                 {
207                         FREEUB(fp);
208                         if ((fp->f_r = fp->f_ur) != 0)
209                         {
210                                 fp->f_p = fp->f_up;
211
212                                 /* revert blocking state */
213                                 return 0;
214                         }
215                 }
216         }
217
218         if (fp->f_bf.smb_base == NULL)
219                 sm_makebuf(fp);
220
221         /*
222         **  Before reading from a line buffered or unbuffered file,
223         **  flush all line buffered output files, per the ANSI C standard.
224         */
225
226         if (fp->f_flags & (SMLBF|SMNBF))
227                 (void) sm_fwalk(sm_lflush, &timeout);
228
229         /*
230         **  If this file is linked to another, and we are going to hang
231         **  on the read, flush the linked file before continuing.
232         */
233
234         if (fp->f_flushfp != NULL &&
235             (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
236                 sm_flush(fp->f_flushfp, &timeout);
237
238         fp->f_p = fp->f_bf.smb_base;
239
240         /*
241         **  The do-while loop stops trying to read when something is read
242         **  or it appears that the timeout has expired before finding
243         **  something available to be read (via select()).
244         */
245
246         ret = 0;
247         do
248         {
249                 errno = 0; /* needed to ensure EOF correctly found */
250                 r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
251                 if (r <= 0)
252                 {
253                         if (r == 0 && errno == 0)
254                                 break; /* EOF found */
255                         if (IS_IO_ERROR(fd, r, timeout))
256                                 goto err; /* errno set */
257
258                         /* read would block */
259                         SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
260                 }
261         } while (r <= 0 && ret > 0);
262
263 err:
264         if (r <= 0)
265         {
266                 if (r == 0)
267                         fp->f_flags |= SMFEOF;
268                 else
269                         fp->f_flags |= SMERR;
270                 fp->f_r = 0;
271                 return SM_IO_EOF;
272         }
273         fp->f_r = r;
274         return 0;
275 }
276
277 /*
278 **  SM_RGET -- refills buffer and returns first character
279 **
280 **  Handle sm_getc() when the buffer ran out:
281 **  Refill, then return the first character in the newly-filled buffer.
282 **
283 **      Parameters:
284 **              fp -- file pointer to work on
285 **              timeout -- time to complete refill
286 **
287 **      Returns:
288 **              Success: first character in refilled buffer as an int
289 **              Failure: SM_IO_EOF
290 */
291
292 int
293 sm_rget(fp, timeout)
294         register SM_FILE_T *fp;
295         int timeout;
296 {
297         if (sm_refill(fp, timeout) == 0)
298         {
299                 fp->f_r--;
300                 return *fp->f_p++;
301         }
302         return SM_IO_EOF;
303 }