xref: /freebsd/contrib/sendmail/libsm/refill.c (revision d9a42747950146bf03cda7f6e25d219253f8a57a)
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
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
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
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