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