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