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 /* calculate 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
sm_lflush(fp,timeout)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
sm_refill(fp,timeout)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
sm_rget(fp,timeout)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 }
304