1 | /* |
---|
2 | * Copyright (c) 2000-2001 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.2 2006-03-23 21:02:45 zacheiss Exp $") |
---|
17 | #include <stdlib.h> |
---|
18 | #include <unistd.h> |
---|
19 | #include <errno.h> |
---|
20 | #include <setjmp.h> |
---|
21 | #include <signal.h> |
---|
22 | #include <sys/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 |
---|
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 |
---|
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 |
---|
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 | } |
---|