refill.c revision 285303
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>
16SM_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
31static 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	/* calulate 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
117static int
118sm_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
140int
141sm_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
263err:
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
292int
293sm_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