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>
18SM_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
32static 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
118static int
119sm_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
141int
142sm_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
264err:
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
293int
294sm_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