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