xref: /freebsd/contrib/sendmail/libsm/refill.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
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.50 2002/09/09 21:50:10 gshapiro 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 	(sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL,		\
80 			   &sm_io_x_mask, (to));			\
81 	if ((sel_ret) < 0)						\
82 	{								\
83 		/* something went wrong, errno set */			\
84 		fp->f_r = 0;						\
85 		fp->f_flags |= SMERR;					\
86 		return SM_IO_EOF;					\
87 	}								\
88 	else if ((sel_ret) == 0)					\
89 	{								\
90 		/* timeout */						\
91 		errno = EAGAIN;						\
92 		return SM_IO_EOF;					\
93 	}								\
94 	/* calulate wall-clock time used */				\
95 	if (gettimeofday(&sm_io_to_after, NULL) < 0)			\
96 		return SM_IO_EOF;					\
97 	timersub(&sm_io_to_before, &sm_io_to_after, &sm_io_to_diff);	\
98 	timersub((to), &sm_io_to_diff, (to));				\
99 }
100 
101 /*
102 **  SM_LFLUSH -- flush a file if it is line buffered and writable
103 **
104 **	Parameters:
105 **		fp -- file pointer to flush
106 **		timeout -- original timeout value (in milliseconds)
107 **
108 **	Returns:
109 **		Failure: returns SM_IO_EOF and sets errno
110 **		Success: returns 0
111 */
112 
113 static int
114 sm_lflush(fp, timeout)
115 	SM_FILE_T *fp;
116 	int *timeout;
117 {
118 
119 	if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
120 		return sm_flush(fp, timeout);
121 	return 0;
122 }
123 
124 /*
125 **  SM_REFILL -- refill a buffer
126 **
127 **	Parameters:
128 **		fp -- file pointer for buffer refill
129 **		timeout -- time to complete filling the buffer in milliseconds
130 **
131 **	Returns:
132 **		Success: returns 0
133 **		Failure: returns SM_IO_EOF
134 */
135 
136 int
137 sm_refill(fp, timeout)
138 	register SM_FILE_T *fp;
139 	int timeout;
140 {
141 	int ret, r;
142 	struct timeval to;
143 	int fd;
144 
145 	if (timeout == SM_TIME_DEFAULT)
146 		timeout = fp->f_timeout;
147 	if (timeout == SM_TIME_IMMEDIATE)
148 	{
149 		/*
150 		**  Filling the buffer will take time and we are wanted to
151 		**  return immediately. And we're not EOF or ERR really.
152 		**  So... the failure is we couldn't do it in time.
153 		*/
154 
155 		errno = EAGAIN;
156 		fp->f_r = 0; /* just to be sure */
157 		return 0;
158 	}
159 
160 	/* make sure stdio is set up */
161 	if (!Sm_IO_DidInit)
162 		sm_init();
163 
164 	fp->f_r = 0;		/* largely a convenience for callers */
165 
166 	if (fp->f_flags & SMFEOF)
167 		return SM_IO_EOF;
168 
169 	SM_CONVERT_TIME(fp, fd, timeout, &to);
170 
171 	/* if not already reading, have to be reading and writing */
172 	if ((fp->f_flags & SMRD) == 0)
173 	{
174 		if ((fp->f_flags & SMRW) == 0)
175 		{
176 			errno = EBADF;
177 			fp->f_flags |= SMERR;
178 			return SM_IO_EOF;
179 		}
180 
181 		/* switch to reading */
182 		if (fp->f_flags & SMWR)
183 		{
184 			if (sm_flush(fp, &timeout))
185 				return SM_IO_EOF;
186 			fp->f_flags &= ~SMWR;
187 			fp->f_w = 0;
188 			fp->f_lbfsize = 0;
189 		}
190 		fp->f_flags |= SMRD;
191 	}
192 	else
193 	{
194 		/*
195 		**  We were reading.  If there is an ungetc buffer,
196 		**  we must have been reading from that.  Drop it,
197 		**  restoring the previous buffer (if any).  If there
198 		**  is anything in that buffer, return.
199 		*/
200 
201 		if (HASUB(fp))
202 		{
203 			FREEUB(fp);
204 			if ((fp->f_r = fp->f_ur) != 0)
205 			{
206 				fp->f_p = fp->f_up;
207 
208 				/* revert blocking state */
209 				return 0;
210 			}
211 		}
212 	}
213 
214 	if (fp->f_bf.smb_base == NULL)
215 		sm_makebuf(fp);
216 
217 	/*
218 	**  Before reading from a line buffered or unbuffered file,
219 	**  flush all line buffered output files, per the ANSI C standard.
220 	*/
221 
222 	if (fp->f_flags & (SMLBF|SMNBF))
223 		(void) sm_fwalk(sm_lflush, &timeout);
224 
225 	/*
226 	**  If this file is linked to another, and we are going to hang
227 	**  on the read, flush the linked file before continuing.
228 	*/
229 
230 	if (fp->f_flushfp != NULL &&
231 	    (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
232 		sm_flush(fp->f_flushfp, &timeout);
233 
234 	fp->f_p = fp->f_bf.smb_base;
235 
236 	/*
237 	**  The do-while loop stops trying to read when something is read
238 	**  or it appears that the timeout has expired before finding
239 	**  something available to be read (via select()).
240 	*/
241 
242 	ret = 0;
243 	do
244 	{
245 		errno = 0; /* needed to ensure EOF correctly found */
246 		r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
247 		if (r <= 0)
248 		{
249 			if (r == 0 && errno == 0)
250 				break; /* EOF found */
251 			if (IS_IO_ERROR(fd, r, timeout))
252 				goto err; /* errno set */
253 
254 			/* read would block */
255 			SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
256 		}
257 	} while (r <= 0 && ret > 0);
258 
259 err:
260 	if (r <= 0)
261 	{
262 		if (r == 0)
263 			fp->f_flags |= SMFEOF;
264 		else
265 			fp->f_flags |= SMERR;
266 		fp->f_r = 0;
267 		return SM_IO_EOF;
268 	}
269 	fp->f_r = r;
270 	return 0;
271 }
272 
273 /*
274 **  SM_RGET -- refills buffer and returns first character
275 **
276 **  Handle sm_getc() when the buffer ran out:
277 **  Refill, then return the first character in the newly-filled buffer.
278 **
279 **	Parameters:
280 **		fp -- file pointer to work on
281 **		timeout -- time to complete refill
282 **
283 **	Returns:
284 **		Success: first character in refilled buffer as an int
285 **		Failure: SM_IO_EOF
286 */
287 
288 int
289 sm_rget(fp, timeout)
290 	register SM_FILE_T *fp;
291 	int timeout;
292 {
293 	if (sm_refill(fp, timeout) == 0)
294 	{
295 		fp->f_r--;
296 		return *fp->f_p++;
297 	}
298 	return SM_IO_EOF;
299 }
300