1N/A/*
1N/A * Copyright (c) 2000-2001, 2005-2006 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A * Copyright (c) 1990, 1993
1N/A * The Regents of the University of California. All rights reserved.
1N/A *
1N/A * This code is derived from software contributed to Berkeley by
1N/A * Chris Torek.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A */
1N/A
1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A
1N/A#include <sm/gen.h>
1N/ASM_RCSID("@(#)$Id: refill.c,v 1.53 2006/02/28 18:48:25 ca Exp $")
1N/A#include <stdlib.h>
1N/A#include <unistd.h>
1N/A#include <errno.h>
1N/A#include <setjmp.h>
1N/A#include <signal.h>
1N/A#include <sm/time.h>
1N/A#include <fcntl.h>
1N/A#include <string.h>
1N/A#include <sm/io.h>
1N/A#include <sm/conf.h>
1N/A#include <sm/assert.h>
1N/A#include "local.h"
1N/A
1N/Astatic int sm_lflush __P((SM_FILE_T *, int *));
1N/A
1N/A/*
1N/A** SM_IO_RD_TIMEOUT -- measured timeout for reads
1N/A**
1N/A** This #define uses a select() to wait for the 'fd' to become readable.
1N/A** The select() can be active for up to 'To' time. The select() may not
1N/A** use all of the the 'To' time. Hence, the amount of "wall-clock" time is
1N/A** measured to decide how much to subtract from 'To' to update it. On some
1N/A** BSD-based/like systems the timeout for a select() is updated for the
1N/A** amount of time used. On many/most systems this does not happen. Therefore
1N/A** the updating of 'To' must be done ourselves; a copy of 'To' is passed
1N/A** since a BSD-like system will have updated it and we don't want to
1N/A** double the time used!
1N/A** Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the
1N/A** sendmail buffered file type in sendmail/bf.c; see use below).
1N/A**
1N/A** Parameters
1N/A** fp -- the file pointer for the active file
1N/A** fd -- raw file descriptor (from 'fp') to use for select()
1N/A** to -- struct timeval of the timeout
1N/A** timeout -- the original timeout value
1N/A** sel_ret -- the return value from the select()
1N/A**
1N/A** Returns:
1N/A** nothing, flow through code
1N/A*/
1N/A
1N/A#define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret) \
1N/A{ \
1N/A struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \
1N/A fd_set sm_io_to_mask, sm_io_x_mask; \
1N/A errno = 0; \
1N/A if (timeout == SM_TIME_IMMEDIATE) \
1N/A { \
1N/A errno = EAGAIN; \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \
1N/A { \
1N/A errno = EINVAL; \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A FD_ZERO(&sm_io_to_mask); \
1N/A FD_SET((fd), &sm_io_to_mask); \
1N/A FD_ZERO(&sm_io_x_mask); \
1N/A FD_SET((fd), &sm_io_x_mask); \
1N/A if (gettimeofday(&sm_io_to_before, NULL) < 0) \
1N/A return SM_IO_EOF; \
1N/A do \
1N/A { \
1N/A (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL, \
1N/A &sm_io_x_mask, (to)); \
1N/A } while ((sel_ret) < 0 && errno == EINTR); \
1N/A if ((sel_ret) < 0) \
1N/A { \
1N/A /* something went wrong, errno set */ \
1N/A fp->f_r = 0; \
1N/A fp->f_flags |= SMERR; \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A else if ((sel_ret) == 0) \
1N/A { \
1N/A /* timeout */ \
1N/A errno = EAGAIN; \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A /* calulate wall-clock time used */ \
1N/A if (gettimeofday(&sm_io_to_after, NULL) < 0) \
1N/A return SM_IO_EOF; \
1N/A timersub(&sm_io_to_after, &sm_io_to_before, &sm_io_to_diff); \
1N/A timersub((to), &sm_io_to_diff, (to)); \
1N/A}
1N/A
1N/A/*
1N/A** SM_LFLUSH -- flush a file if it is line buffered and writable
1N/A**
1N/A** Parameters:
1N/A** fp -- file pointer to flush
1N/A** timeout -- original timeout value (in milliseconds)
1N/A**
1N/A** Returns:
1N/A** Failure: returns SM_IO_EOF and sets errno
1N/A** Success: returns 0
1N/A*/
1N/A
1N/Astatic int
1N/Asm_lflush(fp, timeout)
1N/A SM_FILE_T *fp;
1N/A int *timeout;
1N/A{
1N/A
1N/A if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR))
1N/A return sm_flush(fp, timeout);
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** SM_REFILL -- refill a buffer
1N/A**
1N/A** Parameters:
1N/A** fp -- file pointer for buffer refill
1N/A** timeout -- time to complete filling the buffer in milliseconds
1N/A**
1N/A** Returns:
1N/A** Success: returns 0
1N/A** Failure: returns SM_IO_EOF
1N/A*/
1N/A
1N/Aint
1N/Asm_refill(fp, timeout)
1N/A register SM_FILE_T *fp;
1N/A int timeout;
1N/A{
1N/A int ret, r;
1N/A struct timeval to;
1N/A int fd;
1N/A
1N/A if (timeout == SM_TIME_DEFAULT)
1N/A timeout = fp->f_timeout;
1N/A if (timeout == SM_TIME_IMMEDIATE)
1N/A {
1N/A /*
1N/A ** Filling the buffer will take time and we are wanted to
1N/A ** return immediately. And we're not EOF or ERR really.
1N/A ** So... the failure is we couldn't do it in time.
1N/A */
1N/A
1N/A errno = EAGAIN;
1N/A fp->f_r = 0; /* just to be sure */
1N/A return 0;
1N/A }
1N/A
1N/A /* make sure stdio is set up */
1N/A if (!Sm_IO_DidInit)
1N/A sm_init();
1N/A
1N/A fp->f_r = 0; /* largely a convenience for callers */
1N/A
1N/A if (fp->f_flags & SMFEOF)
1N/A return SM_IO_EOF;
1N/A
1N/A SM_CONVERT_TIME(fp, fd, timeout, &to);
1N/A
1N/A /* if not already reading, have to be reading and writing */
1N/A if ((fp->f_flags & SMRD) == 0)
1N/A {
1N/A if ((fp->f_flags & SMRW) == 0)
1N/A {
1N/A errno = EBADF;
1N/A fp->f_flags |= SMERR;
1N/A return SM_IO_EOF;
1N/A }
1N/A
1N/A /* switch to reading */
1N/A if (fp->f_flags & SMWR)
1N/A {
1N/A if (sm_flush(fp, &timeout))
1N/A return SM_IO_EOF;
1N/A fp->f_flags &= ~SMWR;
1N/A fp->f_w = 0;
1N/A fp->f_lbfsize = 0;
1N/A }
1N/A fp->f_flags |= SMRD;
1N/A }
1N/A else
1N/A {
1N/A /*
1N/A ** We were reading. If there is an ungetc buffer,
1N/A ** we must have been reading from that. Drop it,
1N/A ** restoring the previous buffer (if any). If there
1N/A ** is anything in that buffer, return.
1N/A */
1N/A
1N/A if (HASUB(fp))
1N/A {
1N/A FREEUB(fp);
1N/A if ((fp->f_r = fp->f_ur) != 0)
1N/A {
1N/A fp->f_p = fp->f_up;
1N/A
1N/A /* revert blocking state */
1N/A return 0;
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (fp->f_bf.smb_base == NULL)
1N/A sm_makebuf(fp);
1N/A
1N/A /*
1N/A ** Before reading from a line buffered or unbuffered file,
1N/A ** flush all line buffered output files, per the ANSI C standard.
1N/A */
1N/A
1N/A if (fp->f_flags & (SMLBF|SMNBF))
1N/A (void) sm_fwalk(sm_lflush, &timeout);
1N/A
1N/A /*
1N/A ** If this file is linked to another, and we are going to hang
1N/A ** on the read, flush the linked file before continuing.
1N/A */
1N/A
1N/A if (fp->f_flushfp != NULL &&
1N/A (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0)
1N/A sm_flush(fp->f_flushfp, &timeout);
1N/A
1N/A fp->f_p = fp->f_bf.smb_base;
1N/A
1N/A /*
1N/A ** The do-while loop stops trying to read when something is read
1N/A ** or it appears that the timeout has expired before finding
1N/A ** something available to be read (via select()).
1N/A */
1N/A
1N/A ret = 0;
1N/A do
1N/A {
1N/A errno = 0; /* needed to ensure EOF correctly found */
1N/A r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size);
1N/A if (r <= 0)
1N/A {
1N/A if (r == 0 && errno == 0)
1N/A break; /* EOF found */
1N/A if (IS_IO_ERROR(fd, r, timeout))
1N/A goto err; /* errno set */
1N/A
1N/A /* read would block */
1N/A SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret);
1N/A }
1N/A } while (r <= 0 && ret > 0);
1N/A
1N/Aerr:
1N/A if (r <= 0)
1N/A {
1N/A if (r == 0)
1N/A fp->f_flags |= SMFEOF;
1N/A else
1N/A fp->f_flags |= SMERR;
1N/A fp->f_r = 0;
1N/A return SM_IO_EOF;
1N/A }
1N/A fp->f_r = r;
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A** SM_RGET -- refills buffer and returns first character
1N/A**
1N/A** Handle sm_getc() when the buffer ran out:
1N/A** Refill, then return the first character in the newly-filled buffer.
1N/A**
1N/A** Parameters:
1N/A** fp -- file pointer to work on
1N/A** timeout -- time to complete refill
1N/A**
1N/A** Returns:
1N/A** Success: first character in refilled buffer as an int
1N/A** Failure: SM_IO_EOF
1N/A*/
1N/A
1N/Aint
1N/Asm_rget(fp, timeout)
1N/A register SM_FILE_T *fp;
1N/A int timeout;
1N/A{
1N/A if (sm_refill(fp, timeout) == 0)
1N/A {
1N/A fp->f_r--;
1N/A return *fp->f_p++;
1N/A }
1N/A return SM_IO_EOF;
1N/A}