17c478bd9Sstevel@tonic-gate /*
2*445f2479Sjbeck * Copyright (c) 2000-2001, 2005-2006 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate * All rights reserved.
47c478bd9Sstevel@tonic-gate * Copyright (c) 1990, 1993
57c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved.
67c478bd9Sstevel@tonic-gate *
77c478bd9Sstevel@tonic-gate * This code is derived from software contributed to Berkeley by
87c478bd9Sstevel@tonic-gate * Chris Torek.
97c478bd9Sstevel@tonic-gate *
107c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set
117c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of
127c478bd9Sstevel@tonic-gate * the sendmail distribution.
137c478bd9Sstevel@tonic-gate */
147c478bd9Sstevel@tonic-gate
157c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
167c478bd9Sstevel@tonic-gate
177c478bd9Sstevel@tonic-gate #include <sm/gen.h>
18*445f2479Sjbeck SM_RCSID("@(#)$Id: refill.c,v 1.53 2006/02/28 18:48:25 ca Exp $")
197c478bd9Sstevel@tonic-gate #include <stdlib.h>
207c478bd9Sstevel@tonic-gate #include <unistd.h>
217c478bd9Sstevel@tonic-gate #include <errno.h>
227c478bd9Sstevel@tonic-gate #include <setjmp.h>
237c478bd9Sstevel@tonic-gate #include <signal.h>
2449218d4fSjbeck #include <sm/time.h>
257c478bd9Sstevel@tonic-gate #include <fcntl.h>
267c478bd9Sstevel@tonic-gate #include <string.h>
277c478bd9Sstevel@tonic-gate #include <sm/io.h>
287c478bd9Sstevel@tonic-gate #include <sm/conf.h>
297c478bd9Sstevel@tonic-gate #include <sm/assert.h>
307c478bd9Sstevel@tonic-gate #include "local.h"
317c478bd9Sstevel@tonic-gate
327c478bd9Sstevel@tonic-gate static int sm_lflush __P((SM_FILE_T *, int *));
337c478bd9Sstevel@tonic-gate
347c478bd9Sstevel@tonic-gate /*
357c478bd9Sstevel@tonic-gate ** SM_IO_RD_TIMEOUT -- measured timeout for reads
367c478bd9Sstevel@tonic-gate **
377c478bd9Sstevel@tonic-gate ** This #define uses a select() to wait for the 'fd' to become readable.
387c478bd9Sstevel@tonic-gate ** The select() can be active for up to 'To' time. The select() may not
397c478bd9Sstevel@tonic-gate ** use all of the the 'To' time. Hence, the amount of "wall-clock" time is
407c478bd9Sstevel@tonic-gate ** measured to decide how much to subtract from 'To' to update it. On some
417c478bd9Sstevel@tonic-gate ** BSD-based/like systems the timeout for a select() is updated for the
427c478bd9Sstevel@tonic-gate ** amount of time used. On many/most systems this does not happen. Therefore
437c478bd9Sstevel@tonic-gate ** the updating of 'To' must be done ourselves; a copy of 'To' is passed
447c478bd9Sstevel@tonic-gate ** since a BSD-like system will have updated it and we don't want to
457c478bd9Sstevel@tonic-gate ** double the time used!
467c478bd9Sstevel@tonic-gate ** Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
477c478bd9Sstevel@tonic-gate ** sendmail buffered file type in sendmail/bf.c; see use below).
487c478bd9Sstevel@tonic-gate **
497c478bd9Sstevel@tonic-gate ** Parameters
507c478bd9Sstevel@tonic-gate ** fp -- the file pointer for the active file
517c478bd9Sstevel@tonic-gate ** fd -- raw file descriptor (from 'fp') to use for select()
527c478bd9Sstevel@tonic-gate ** to -- struct timeval of the timeout
537c478bd9Sstevel@tonic-gate ** timeout -- the original timeout value
547c478bd9Sstevel@tonic-gate ** sel_ret -- the return value from the select()
557c478bd9Sstevel@tonic-gate **
567c478bd9Sstevel@tonic-gate ** Returns:
577c478bd9Sstevel@tonic-gate ** nothing, flow through code
587c478bd9Sstevel@tonic-gate */
597c478bd9Sstevel@tonic-gate
607c478bd9Sstevel@tonic-gate #define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret) \
617c478bd9Sstevel@tonic-gate { \
627c478bd9Sstevel@tonic-gate struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \
637c478bd9Sstevel@tonic-gate fd_set sm_io_to_mask, sm_io_x_mask; \
647c478bd9Sstevel@tonic-gate errno = 0; \
657c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_IMMEDIATE) \
667c478bd9Sstevel@tonic-gate { \
677c478bd9Sstevel@tonic-gate errno = EAGAIN; \
687c478bd9Sstevel@tonic-gate return SM_IO_EOF; \
697c478bd9Sstevel@tonic-gate } \
707c478bd9Sstevel@tonic-gate if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \
717c478bd9Sstevel@tonic-gate { \
727c478bd9Sstevel@tonic-gate errno = EINVAL; \
737c478bd9Sstevel@tonic-gate return SM_IO_EOF; \
747c478bd9Sstevel@tonic-gate } \
757c478bd9Sstevel@tonic-gate FD_ZERO(&sm_io_to_mask); \
767c478bd9Sstevel@tonic-gate FD_SET((fd), &sm_io_to_mask); \
777c478bd9Sstevel@tonic-gate FD_ZERO(&sm_io_x_mask); \
787c478bd9Sstevel@tonic-gate FD_SET((fd), &sm_io_x_mask); \
797c478bd9Sstevel@tonic-gate if (gettimeofday(&sm_io_to_before, NULL) < 0) \
807c478bd9Sstevel@tonic-gate return SM_IO_EOF; \
81*445f2479Sjbeck do \
82*445f2479Sjbeck { \
837c478bd9Sstevel@tonic-gate (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL, \
847c478bd9Sstevel@tonic-gate &sm_io_x_mask, (to)); \
85*445f2479Sjbeck } while ((sel_ret) < 0 && errno == EINTR); \
867c478bd9Sstevel@tonic-gate if ((sel_ret) < 0) \
877c478bd9Sstevel@tonic-gate { \
887c478bd9Sstevel@tonic-gate /* something went wrong, errno set */ \
897c478bd9Sstevel@tonic-gate fp->f_r = 0; \
907c478bd9Sstevel@tonic-gate fp->f_flags |= SMERR; \
917c478bd9Sstevel@tonic-gate return SM_IO_EOF; \
927c478bd9Sstevel@tonic-gate } \
937c478bd9Sstevel@tonic-gate else if ((sel_ret) == 0) \
947c478bd9Sstevel@tonic-gate { \
957c478bd9Sstevel@tonic-gate /* timeout */ \
967c478bd9Sstevel@tonic-gate errno = EAGAIN; \
977c478bd9Sstevel@tonic-gate return SM_IO_EOF; \
987c478bd9Sstevel@tonic-gate } \
997c478bd9Sstevel@tonic-gate /* calulate wall-clock time used */ \
1007c478bd9Sstevel@tonic-gate if (gettimeofday(&sm_io_to_after, NULL) < 0) \
1017c478bd9Sstevel@tonic-gate return SM_IO_EOF; \
102*445f2479Sjbeck timersub(&sm_io_to_after, &sm_io_to_before, &sm_io_to_diff); \
1037c478bd9Sstevel@tonic-gate timersub((to), &sm_io_to_diff, (to)); \
1047c478bd9Sstevel@tonic-gate }
1057c478bd9Sstevel@tonic-gate
1067c478bd9Sstevel@tonic-gate /*
1077c478bd9Sstevel@tonic-gate ** SM_LFLUSH -- flush a file if it is line buffered and writable
1087c478bd9Sstevel@tonic-gate **
1097c478bd9Sstevel@tonic-gate ** Parameters:
1107c478bd9Sstevel@tonic-gate ** fp -- file pointer to flush
1117c478bd9Sstevel@tonic-gate ** timeout -- original timeout value (in milliseconds)
1127c478bd9Sstevel@tonic-gate **
1137c478bd9Sstevel@tonic-gate ** Returns:
1147c478bd9Sstevel@tonic-gate ** Failure: returns SM_IO_EOF and sets errno
1157c478bd9Sstevel@tonic-gate ** Success: returns 0
1167c478bd9Sstevel@tonic-gate */
1177c478bd9Sstevel@tonic-gate
1187c478bd9Sstevel@tonic-gate static int
sm_lflush(fp,timeout)1197c478bd9Sstevel@tonic-gate sm_lflush(fp, timeout)
1207c478bd9Sstevel@tonic-gate SM_FILE_T *fp;
1217c478bd9Sstevel@tonic-gate int *timeout;
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate
1247c478bd9Sstevel@tonic-gate if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
1257c478bd9Sstevel@tonic-gate return sm_flush(fp, timeout);
1267c478bd9Sstevel@tonic-gate return 0;
1277c478bd9Sstevel@tonic-gate }
1287c478bd9Sstevel@tonic-gate
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate ** SM_REFILL -- refill a buffer
1317c478bd9Sstevel@tonic-gate **
1327c478bd9Sstevel@tonic-gate ** Parameters:
1337c478bd9Sstevel@tonic-gate ** fp -- file pointer for buffer refill
1347c478bd9Sstevel@tonic-gate ** timeout -- time to complete filling the buffer in milliseconds
1357c478bd9Sstevel@tonic-gate **
1367c478bd9Sstevel@tonic-gate ** Returns:
1377c478bd9Sstevel@tonic-gate ** Success: returns 0
1387c478bd9Sstevel@tonic-gate ** Failure: returns SM_IO_EOF
1397c478bd9Sstevel@tonic-gate */
1407c478bd9Sstevel@tonic-gate
1417c478bd9Sstevel@tonic-gate int
sm_refill(fp,timeout)1427c478bd9Sstevel@tonic-gate sm_refill(fp, timeout)
1437c478bd9Sstevel@tonic-gate register SM_FILE_T *fp;
1447c478bd9Sstevel@tonic-gate int timeout;
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate int ret, r;
1477c478bd9Sstevel@tonic-gate struct timeval to;
1487c478bd9Sstevel@tonic-gate int fd;
1497c478bd9Sstevel@tonic-gate
1507c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_DEFAULT)
1517c478bd9Sstevel@tonic-gate timeout = fp->f_timeout;
1527c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_IMMEDIATE)
1537c478bd9Sstevel@tonic-gate {
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate ** Filling the buffer will take time and we are wanted to
1567c478bd9Sstevel@tonic-gate ** return immediately. And we're not EOF or ERR really.
1577c478bd9Sstevel@tonic-gate ** So... the failure is we couldn't do it in time.
1587c478bd9Sstevel@tonic-gate */
1597c478bd9Sstevel@tonic-gate
1607c478bd9Sstevel@tonic-gate errno = EAGAIN;
1617c478bd9Sstevel@tonic-gate fp->f_r = 0; /* just to be sure */
1627c478bd9Sstevel@tonic-gate return 0;
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate
1657c478bd9Sstevel@tonic-gate /* make sure stdio is set up */
1667c478bd9Sstevel@tonic-gate if (!Sm_IO_DidInit)
1677c478bd9Sstevel@tonic-gate sm_init();
1687c478bd9Sstevel@tonic-gate
1697c478bd9Sstevel@tonic-gate fp->f_r = 0; /* largely a convenience for callers */
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate if (fp->f_flags & SMFEOF)
1727c478bd9Sstevel@tonic-gate return SM_IO_EOF;
1737c478bd9Sstevel@tonic-gate
1747c478bd9Sstevel@tonic-gate SM_CONVERT_TIME(fp, fd, timeout, &to);
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate /* if not already reading, have to be reading and writing */
1777c478bd9Sstevel@tonic-gate if ((fp->f_flags & SMRD) == 0)
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate if ((fp->f_flags & SMRW) == 0)
1807c478bd9Sstevel@tonic-gate {
1817c478bd9Sstevel@tonic-gate errno = EBADF;
1827c478bd9Sstevel@tonic-gate fp->f_flags |= SMERR;
1837c478bd9Sstevel@tonic-gate return SM_IO_EOF;
1847c478bd9Sstevel@tonic-gate }
1857c478bd9Sstevel@tonic-gate
1867c478bd9Sstevel@tonic-gate /* switch to reading */
1877c478bd9Sstevel@tonic-gate if (fp->f_flags & SMWR)
1887c478bd9Sstevel@tonic-gate {
1897c478bd9Sstevel@tonic-gate if (sm_flush(fp, &timeout))
1907c478bd9Sstevel@tonic-gate return SM_IO_EOF;
1917c478bd9Sstevel@tonic-gate fp->f_flags &= ~SMWR;
1927c478bd9Sstevel@tonic-gate fp->f_w = 0;
1937c478bd9Sstevel@tonic-gate fp->f_lbfsize = 0;
1947c478bd9Sstevel@tonic-gate }
1957c478bd9Sstevel@tonic-gate fp->f_flags |= SMRD;
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate else
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate /*
2007c478bd9Sstevel@tonic-gate ** We were reading. If there is an ungetc buffer,
2017c478bd9Sstevel@tonic-gate ** we must have been reading from that. Drop it,
2027c478bd9Sstevel@tonic-gate ** restoring the previous buffer (if any). If there
2037c478bd9Sstevel@tonic-gate ** is anything in that buffer, return.
2047c478bd9Sstevel@tonic-gate */
2057c478bd9Sstevel@tonic-gate
2067c478bd9Sstevel@tonic-gate if (HASUB(fp))
2077c478bd9Sstevel@tonic-gate {
2087c478bd9Sstevel@tonic-gate FREEUB(fp);
2097c478bd9Sstevel@tonic-gate if ((fp->f_r = fp->f_ur) != 0)
2107c478bd9Sstevel@tonic-gate {
2117c478bd9Sstevel@tonic-gate fp->f_p = fp->f_up;
2127c478bd9Sstevel@tonic-gate
2137c478bd9Sstevel@tonic-gate /* revert blocking state */
2147c478bd9Sstevel@tonic-gate return 0;
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate }
2177c478bd9Sstevel@tonic-gate }
2187c478bd9Sstevel@tonic-gate
2197c478bd9Sstevel@tonic-gate if (fp->f_bf.smb_base == NULL)
2207c478bd9Sstevel@tonic-gate sm_makebuf(fp);
2217c478bd9Sstevel@tonic-gate
2227c478bd9Sstevel@tonic-gate /*
2237c478bd9Sstevel@tonic-gate ** Before reading from a line buffered or unbuffered file,
2247c478bd9Sstevel@tonic-gate ** flush all line buffered output files, per the ANSI C standard.
2257c478bd9Sstevel@tonic-gate */
2267c478bd9Sstevel@tonic-gate
2277c478bd9Sstevel@tonic-gate if (fp->f_flags & (SMLBF|SMNBF))
2287c478bd9Sstevel@tonic-gate (void) sm_fwalk(sm_lflush, &timeout);
2297c478bd9Sstevel@tonic-gate
2307c478bd9Sstevel@tonic-gate /*
2317c478bd9Sstevel@tonic-gate ** If this file is linked to another, and we are going to hang
2327c478bd9Sstevel@tonic-gate ** on the read, flush the linked file before continuing.
2337c478bd9Sstevel@tonic-gate */
2347c478bd9Sstevel@tonic-gate
2357c478bd9Sstevel@tonic-gate if (fp->f_flushfp != NULL &&
2367c478bd9Sstevel@tonic-gate (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
2377c478bd9Sstevel@tonic-gate sm_flush(fp->f_flushfp, &timeout);
2387c478bd9Sstevel@tonic-gate
2397c478bd9Sstevel@tonic-gate fp->f_p = fp->f_bf.smb_base;
2407c478bd9Sstevel@tonic-gate
2417c478bd9Sstevel@tonic-gate /*
2427c478bd9Sstevel@tonic-gate ** The do-while loop stops trying to read when something is read
2437c478bd9Sstevel@tonic-gate ** or it appears that the timeout has expired before finding
2447c478bd9Sstevel@tonic-gate ** something available to be read (via select()).
2457c478bd9Sstevel@tonic-gate */
2467c478bd9Sstevel@tonic-gate
2477c478bd9Sstevel@tonic-gate ret = 0;
2487c478bd9Sstevel@tonic-gate do
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate errno = 0; /* needed to ensure EOF correctly found */
2517c478bd9Sstevel@tonic-gate r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
2527c478bd9Sstevel@tonic-gate if (r <= 0)
2537c478bd9Sstevel@tonic-gate {
2547c478bd9Sstevel@tonic-gate if (r == 0 && errno == 0)
2557c478bd9Sstevel@tonic-gate break; /* EOF found */
2567c478bd9Sstevel@tonic-gate if (IS_IO_ERROR(fd, r, timeout))
2577c478bd9Sstevel@tonic-gate goto err; /* errno set */
2587c478bd9Sstevel@tonic-gate
2597c478bd9Sstevel@tonic-gate /* read would block */
2607c478bd9Sstevel@tonic-gate SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
2617c478bd9Sstevel@tonic-gate }
2627c478bd9Sstevel@tonic-gate } while (r <= 0 && ret > 0);
2637c478bd9Sstevel@tonic-gate
2647c478bd9Sstevel@tonic-gate err:
2657c478bd9Sstevel@tonic-gate if (r <= 0)
2667c478bd9Sstevel@tonic-gate {
2677c478bd9Sstevel@tonic-gate if (r == 0)
2687c478bd9Sstevel@tonic-gate fp->f_flags |= SMFEOF;
2697c478bd9Sstevel@tonic-gate else
2707c478bd9Sstevel@tonic-gate fp->f_flags |= SMERR;
2717c478bd9Sstevel@tonic-gate fp->f_r = 0;
2727c478bd9Sstevel@tonic-gate return SM_IO_EOF;
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate fp->f_r = r;
2757c478bd9Sstevel@tonic-gate return 0;
2767c478bd9Sstevel@tonic-gate }
2777c478bd9Sstevel@tonic-gate
2787c478bd9Sstevel@tonic-gate /*
2797c478bd9Sstevel@tonic-gate ** SM_RGET -- refills buffer and returns first character
2807c478bd9Sstevel@tonic-gate **
2817c478bd9Sstevel@tonic-gate ** Handle sm_getc() when the buffer ran out:
2827c478bd9Sstevel@tonic-gate ** Refill, then return the first character in the newly-filled buffer.
2837c478bd9Sstevel@tonic-gate **
2847c478bd9Sstevel@tonic-gate ** Parameters:
2857c478bd9Sstevel@tonic-gate ** fp -- file pointer to work on
2867c478bd9Sstevel@tonic-gate ** timeout -- time to complete refill
2877c478bd9Sstevel@tonic-gate **
2887c478bd9Sstevel@tonic-gate ** Returns:
2897c478bd9Sstevel@tonic-gate ** Success: first character in refilled buffer as an int
2907c478bd9Sstevel@tonic-gate ** Failure: SM_IO_EOF
2917c478bd9Sstevel@tonic-gate */
2927c478bd9Sstevel@tonic-gate
2937c478bd9Sstevel@tonic-gate int
sm_rget(fp,timeout)2947c478bd9Sstevel@tonic-gate sm_rget(fp, timeout)
2957c478bd9Sstevel@tonic-gate register SM_FILE_T *fp;
2967c478bd9Sstevel@tonic-gate int timeout;
2977c478bd9Sstevel@tonic-gate {
2987c478bd9Sstevel@tonic-gate if (sm_refill(fp, timeout) == 0)
2997c478bd9Sstevel@tonic-gate {
3007c478bd9Sstevel@tonic-gate fp->f_r--;
3017c478bd9Sstevel@tonic-gate return *fp->f_p++;
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate return SM_IO_EOF;
3047c478bd9Sstevel@tonic-gate }
305