1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. 3*7c478bd9Sstevel@tonic-gate * All rights reserved. 4*7c478bd9Sstevel@tonic-gate * Copyright (c) 1990, 1993 5*7c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 6*7c478bd9Sstevel@tonic-gate * 7*7c478bd9Sstevel@tonic-gate * This code is derived from software contributed to Berkeley by 8*7c478bd9Sstevel@tonic-gate * Chris Torek. 9*7c478bd9Sstevel@tonic-gate * 10*7c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 11*7c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 12*7c478bd9Sstevel@tonic-gate * the sendmail distribution. 13*7c478bd9Sstevel@tonic-gate */ 14*7c478bd9Sstevel@tonic-gate 15*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 16*7c478bd9Sstevel@tonic-gate 17*7c478bd9Sstevel@tonic-gate #include <sm/gen.h> 18*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: refill.c,v 1.49.2.1 2002/09/09 21:38:08 gshapiro Exp $") 19*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 20*7c478bd9Sstevel@tonic-gate #include <unistd.h> 21*7c478bd9Sstevel@tonic-gate #include <errno.h> 22*7c478bd9Sstevel@tonic-gate #include <setjmp.h> 23*7c478bd9Sstevel@tonic-gate #include <signal.h> 24*7c478bd9Sstevel@tonic-gate #include <sys/time.h> 25*7c478bd9Sstevel@tonic-gate #include <fcntl.h> 26*7c478bd9Sstevel@tonic-gate #include <string.h> 27*7c478bd9Sstevel@tonic-gate #include <sm/io.h> 28*7c478bd9Sstevel@tonic-gate #include <sm/conf.h> 29*7c478bd9Sstevel@tonic-gate #include <sm/assert.h> 30*7c478bd9Sstevel@tonic-gate #include "local.h" 31*7c478bd9Sstevel@tonic-gate 32*7c478bd9Sstevel@tonic-gate static int sm_lflush __P((SM_FILE_T *, int *)); 33*7c478bd9Sstevel@tonic-gate 34*7c478bd9Sstevel@tonic-gate /* 35*7c478bd9Sstevel@tonic-gate ** SM_IO_RD_TIMEOUT -- measured timeout for reads 36*7c478bd9Sstevel@tonic-gate ** 37*7c478bd9Sstevel@tonic-gate ** This #define uses a select() to wait for the 'fd' to become readable. 38*7c478bd9Sstevel@tonic-gate ** The select() can be active for up to 'To' time. The select() may not 39*7c478bd9Sstevel@tonic-gate ** use all of the the 'To' time. Hence, the amount of "wall-clock" time is 40*7c478bd9Sstevel@tonic-gate ** measured to decide how much to subtract from 'To' to update it. On some 41*7c478bd9Sstevel@tonic-gate ** BSD-based/like systems the timeout for a select() is updated for the 42*7c478bd9Sstevel@tonic-gate ** amount of time used. On many/most systems this does not happen. Therefore 43*7c478bd9Sstevel@tonic-gate ** the updating of 'To' must be done ourselves; a copy of 'To' is passed 44*7c478bd9Sstevel@tonic-gate ** since a BSD-like system will have updated it and we don't want to 45*7c478bd9Sstevel@tonic-gate ** double the time used! 46*7c478bd9Sstevel@tonic-gate ** Note: if a valid 'fd' doesn't exist yet, don't use this (e.g. the 47*7c478bd9Sstevel@tonic-gate ** sendmail buffered file type in sendmail/bf.c; see use below). 48*7c478bd9Sstevel@tonic-gate ** 49*7c478bd9Sstevel@tonic-gate ** Parameters 50*7c478bd9Sstevel@tonic-gate ** fp -- the file pointer for the active file 51*7c478bd9Sstevel@tonic-gate ** fd -- raw file descriptor (from 'fp') to use for select() 52*7c478bd9Sstevel@tonic-gate ** to -- struct timeval of the timeout 53*7c478bd9Sstevel@tonic-gate ** timeout -- the original timeout value 54*7c478bd9Sstevel@tonic-gate ** sel_ret -- the return value from the select() 55*7c478bd9Sstevel@tonic-gate ** 56*7c478bd9Sstevel@tonic-gate ** Returns: 57*7c478bd9Sstevel@tonic-gate ** nothing, flow through code 58*7c478bd9Sstevel@tonic-gate */ 59*7c478bd9Sstevel@tonic-gate 60*7c478bd9Sstevel@tonic-gate #define SM_IO_RD_TIMEOUT(fp, fd, to, timeout, sel_ret) \ 61*7c478bd9Sstevel@tonic-gate { \ 62*7c478bd9Sstevel@tonic-gate struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \ 63*7c478bd9Sstevel@tonic-gate fd_set sm_io_to_mask, sm_io_x_mask; \ 64*7c478bd9Sstevel@tonic-gate errno = 0; \ 65*7c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_IMMEDIATE) \ 66*7c478bd9Sstevel@tonic-gate { \ 67*7c478bd9Sstevel@tonic-gate errno = EAGAIN; \ 68*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; \ 69*7c478bd9Sstevel@tonic-gate } \ 70*7c478bd9Sstevel@tonic-gate if (FD_SETSIZE > 0 && (fd) >= FD_SETSIZE) \ 71*7c478bd9Sstevel@tonic-gate { \ 72*7c478bd9Sstevel@tonic-gate errno = EINVAL; \ 73*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; \ 74*7c478bd9Sstevel@tonic-gate } \ 75*7c478bd9Sstevel@tonic-gate FD_ZERO(&sm_io_to_mask); \ 76*7c478bd9Sstevel@tonic-gate FD_SET((fd), &sm_io_to_mask); \ 77*7c478bd9Sstevel@tonic-gate FD_ZERO(&sm_io_x_mask); \ 78*7c478bd9Sstevel@tonic-gate FD_SET((fd), &sm_io_x_mask); \ 79*7c478bd9Sstevel@tonic-gate if (gettimeofday(&sm_io_to_before, NULL) < 0) \ 80*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; \ 81*7c478bd9Sstevel@tonic-gate (sel_ret) = select((fd) + 1, &sm_io_to_mask, NULL, \ 82*7c478bd9Sstevel@tonic-gate &sm_io_x_mask, (to)); \ 83*7c478bd9Sstevel@tonic-gate if ((sel_ret) < 0) \ 84*7c478bd9Sstevel@tonic-gate { \ 85*7c478bd9Sstevel@tonic-gate /* something went wrong, errno set */ \ 86*7c478bd9Sstevel@tonic-gate fp->f_r = 0; \ 87*7c478bd9Sstevel@tonic-gate fp->f_flags |= SMERR; \ 88*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; \ 89*7c478bd9Sstevel@tonic-gate } \ 90*7c478bd9Sstevel@tonic-gate else if ((sel_ret) == 0) \ 91*7c478bd9Sstevel@tonic-gate { \ 92*7c478bd9Sstevel@tonic-gate /* timeout */ \ 93*7c478bd9Sstevel@tonic-gate errno = EAGAIN; \ 94*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; \ 95*7c478bd9Sstevel@tonic-gate } \ 96*7c478bd9Sstevel@tonic-gate /* calulate wall-clock time used */ \ 97*7c478bd9Sstevel@tonic-gate if (gettimeofday(&sm_io_to_after, NULL) < 0) \ 98*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; \ 99*7c478bd9Sstevel@tonic-gate timersub(&sm_io_to_before, &sm_io_to_after, &sm_io_to_diff); \ 100*7c478bd9Sstevel@tonic-gate timersub((to), &sm_io_to_diff, (to)); \ 101*7c478bd9Sstevel@tonic-gate } 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate /* 104*7c478bd9Sstevel@tonic-gate ** SM_LFLUSH -- flush a file if it is line buffered and writable 105*7c478bd9Sstevel@tonic-gate ** 106*7c478bd9Sstevel@tonic-gate ** Parameters: 107*7c478bd9Sstevel@tonic-gate ** fp -- file pointer to flush 108*7c478bd9Sstevel@tonic-gate ** timeout -- original timeout value (in milliseconds) 109*7c478bd9Sstevel@tonic-gate ** 110*7c478bd9Sstevel@tonic-gate ** Returns: 111*7c478bd9Sstevel@tonic-gate ** Failure: returns SM_IO_EOF and sets errno 112*7c478bd9Sstevel@tonic-gate ** Success: returns 0 113*7c478bd9Sstevel@tonic-gate */ 114*7c478bd9Sstevel@tonic-gate 115*7c478bd9Sstevel@tonic-gate static int 116*7c478bd9Sstevel@tonic-gate sm_lflush(fp, timeout) 117*7c478bd9Sstevel@tonic-gate SM_FILE_T *fp; 118*7c478bd9Sstevel@tonic-gate int *timeout; 119*7c478bd9Sstevel@tonic-gate { 120*7c478bd9Sstevel@tonic-gate 121*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & (SMLBF|SMWR)) == (SMLBF|SMWR)) 122*7c478bd9Sstevel@tonic-gate return sm_flush(fp, timeout); 123*7c478bd9Sstevel@tonic-gate return 0; 124*7c478bd9Sstevel@tonic-gate } 125*7c478bd9Sstevel@tonic-gate 126*7c478bd9Sstevel@tonic-gate /* 127*7c478bd9Sstevel@tonic-gate ** SM_REFILL -- refill a buffer 128*7c478bd9Sstevel@tonic-gate ** 129*7c478bd9Sstevel@tonic-gate ** Parameters: 130*7c478bd9Sstevel@tonic-gate ** fp -- file pointer for buffer refill 131*7c478bd9Sstevel@tonic-gate ** timeout -- time to complete filling the buffer in milliseconds 132*7c478bd9Sstevel@tonic-gate ** 133*7c478bd9Sstevel@tonic-gate ** Returns: 134*7c478bd9Sstevel@tonic-gate ** Success: returns 0 135*7c478bd9Sstevel@tonic-gate ** Failure: returns SM_IO_EOF 136*7c478bd9Sstevel@tonic-gate */ 137*7c478bd9Sstevel@tonic-gate 138*7c478bd9Sstevel@tonic-gate int 139*7c478bd9Sstevel@tonic-gate sm_refill(fp, timeout) 140*7c478bd9Sstevel@tonic-gate register SM_FILE_T *fp; 141*7c478bd9Sstevel@tonic-gate int timeout; 142*7c478bd9Sstevel@tonic-gate { 143*7c478bd9Sstevel@tonic-gate int ret, r; 144*7c478bd9Sstevel@tonic-gate struct timeval to; 145*7c478bd9Sstevel@tonic-gate int fd; 146*7c478bd9Sstevel@tonic-gate 147*7c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_DEFAULT) 148*7c478bd9Sstevel@tonic-gate timeout = fp->f_timeout; 149*7c478bd9Sstevel@tonic-gate if (timeout == SM_TIME_IMMEDIATE) 150*7c478bd9Sstevel@tonic-gate { 151*7c478bd9Sstevel@tonic-gate /* 152*7c478bd9Sstevel@tonic-gate ** Filling the buffer will take time and we are wanted to 153*7c478bd9Sstevel@tonic-gate ** return immediately. And we're not EOF or ERR really. 154*7c478bd9Sstevel@tonic-gate ** So... the failure is we couldn't do it in time. 155*7c478bd9Sstevel@tonic-gate */ 156*7c478bd9Sstevel@tonic-gate 157*7c478bd9Sstevel@tonic-gate errno = EAGAIN; 158*7c478bd9Sstevel@tonic-gate fp->f_r = 0; /* just to be sure */ 159*7c478bd9Sstevel@tonic-gate return 0; 160*7c478bd9Sstevel@tonic-gate } 161*7c478bd9Sstevel@tonic-gate 162*7c478bd9Sstevel@tonic-gate /* make sure stdio is set up */ 163*7c478bd9Sstevel@tonic-gate if (!Sm_IO_DidInit) 164*7c478bd9Sstevel@tonic-gate sm_init(); 165*7c478bd9Sstevel@tonic-gate 166*7c478bd9Sstevel@tonic-gate fp->f_r = 0; /* largely a convenience for callers */ 167*7c478bd9Sstevel@tonic-gate 168*7c478bd9Sstevel@tonic-gate if (fp->f_flags & SMFEOF) 169*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; 170*7c478bd9Sstevel@tonic-gate 171*7c478bd9Sstevel@tonic-gate SM_CONVERT_TIME(fp, fd, timeout, &to); 172*7c478bd9Sstevel@tonic-gate 173*7c478bd9Sstevel@tonic-gate /* if not already reading, have to be reading and writing */ 174*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & SMRD) == 0) 175*7c478bd9Sstevel@tonic-gate { 176*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & SMRW) == 0) 177*7c478bd9Sstevel@tonic-gate { 178*7c478bd9Sstevel@tonic-gate errno = EBADF; 179*7c478bd9Sstevel@tonic-gate fp->f_flags |= SMERR; 180*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; 181*7c478bd9Sstevel@tonic-gate } 182*7c478bd9Sstevel@tonic-gate 183*7c478bd9Sstevel@tonic-gate /* switch to reading */ 184*7c478bd9Sstevel@tonic-gate if (fp->f_flags & SMWR) 185*7c478bd9Sstevel@tonic-gate { 186*7c478bd9Sstevel@tonic-gate if (sm_flush(fp, &timeout)) 187*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; 188*7c478bd9Sstevel@tonic-gate fp->f_flags &= ~SMWR; 189*7c478bd9Sstevel@tonic-gate fp->f_w = 0; 190*7c478bd9Sstevel@tonic-gate fp->f_lbfsize = 0; 191*7c478bd9Sstevel@tonic-gate } 192*7c478bd9Sstevel@tonic-gate fp->f_flags |= SMRD; 193*7c478bd9Sstevel@tonic-gate } 194*7c478bd9Sstevel@tonic-gate else 195*7c478bd9Sstevel@tonic-gate { 196*7c478bd9Sstevel@tonic-gate /* 197*7c478bd9Sstevel@tonic-gate ** We were reading. If there is an ungetc buffer, 198*7c478bd9Sstevel@tonic-gate ** we must have been reading from that. Drop it, 199*7c478bd9Sstevel@tonic-gate ** restoring the previous buffer (if any). If there 200*7c478bd9Sstevel@tonic-gate ** is anything in that buffer, return. 201*7c478bd9Sstevel@tonic-gate */ 202*7c478bd9Sstevel@tonic-gate 203*7c478bd9Sstevel@tonic-gate if (HASUB(fp)) 204*7c478bd9Sstevel@tonic-gate { 205*7c478bd9Sstevel@tonic-gate FREEUB(fp); 206*7c478bd9Sstevel@tonic-gate if ((fp->f_r = fp->f_ur) != 0) 207*7c478bd9Sstevel@tonic-gate { 208*7c478bd9Sstevel@tonic-gate fp->f_p = fp->f_up; 209*7c478bd9Sstevel@tonic-gate 210*7c478bd9Sstevel@tonic-gate /* revert blocking state */ 211*7c478bd9Sstevel@tonic-gate return 0; 212*7c478bd9Sstevel@tonic-gate } 213*7c478bd9Sstevel@tonic-gate } 214*7c478bd9Sstevel@tonic-gate } 215*7c478bd9Sstevel@tonic-gate 216*7c478bd9Sstevel@tonic-gate if (fp->f_bf.smb_base == NULL) 217*7c478bd9Sstevel@tonic-gate sm_makebuf(fp); 218*7c478bd9Sstevel@tonic-gate 219*7c478bd9Sstevel@tonic-gate /* 220*7c478bd9Sstevel@tonic-gate ** Before reading from a line buffered or unbuffered file, 221*7c478bd9Sstevel@tonic-gate ** flush all line buffered output files, per the ANSI C standard. 222*7c478bd9Sstevel@tonic-gate */ 223*7c478bd9Sstevel@tonic-gate 224*7c478bd9Sstevel@tonic-gate if (fp->f_flags & (SMLBF|SMNBF)) 225*7c478bd9Sstevel@tonic-gate (void) sm_fwalk(sm_lflush, &timeout); 226*7c478bd9Sstevel@tonic-gate 227*7c478bd9Sstevel@tonic-gate /* 228*7c478bd9Sstevel@tonic-gate ** If this file is linked to another, and we are going to hang 229*7c478bd9Sstevel@tonic-gate ** on the read, flush the linked file before continuing. 230*7c478bd9Sstevel@tonic-gate */ 231*7c478bd9Sstevel@tonic-gate 232*7c478bd9Sstevel@tonic-gate if (fp->f_flushfp != NULL && 233*7c478bd9Sstevel@tonic-gate (*fp->f_getinfo)(fp, SM_IO_IS_READABLE, NULL) <= 0) 234*7c478bd9Sstevel@tonic-gate sm_flush(fp->f_flushfp, &timeout); 235*7c478bd9Sstevel@tonic-gate 236*7c478bd9Sstevel@tonic-gate fp->f_p = fp->f_bf.smb_base; 237*7c478bd9Sstevel@tonic-gate 238*7c478bd9Sstevel@tonic-gate /* 239*7c478bd9Sstevel@tonic-gate ** The do-while loop stops trying to read when something is read 240*7c478bd9Sstevel@tonic-gate ** or it appears that the timeout has expired before finding 241*7c478bd9Sstevel@tonic-gate ** something available to be read (via select()). 242*7c478bd9Sstevel@tonic-gate */ 243*7c478bd9Sstevel@tonic-gate 244*7c478bd9Sstevel@tonic-gate ret = 0; 245*7c478bd9Sstevel@tonic-gate do 246*7c478bd9Sstevel@tonic-gate { 247*7c478bd9Sstevel@tonic-gate errno = 0; /* needed to ensure EOF correctly found */ 248*7c478bd9Sstevel@tonic-gate r = (*fp->f_read)(fp, (char *)fp->f_p, fp->f_bf.smb_size); 249*7c478bd9Sstevel@tonic-gate if (r <= 0) 250*7c478bd9Sstevel@tonic-gate { 251*7c478bd9Sstevel@tonic-gate if (r == 0 && errno == 0) 252*7c478bd9Sstevel@tonic-gate break; /* EOF found */ 253*7c478bd9Sstevel@tonic-gate if (IS_IO_ERROR(fd, r, timeout)) 254*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 255*7c478bd9Sstevel@tonic-gate 256*7c478bd9Sstevel@tonic-gate /* read would block */ 257*7c478bd9Sstevel@tonic-gate SM_IO_RD_TIMEOUT(fp, fd, &to, timeout, ret); 258*7c478bd9Sstevel@tonic-gate } 259*7c478bd9Sstevel@tonic-gate } while (r <= 0 && ret > 0); 260*7c478bd9Sstevel@tonic-gate 261*7c478bd9Sstevel@tonic-gate err: 262*7c478bd9Sstevel@tonic-gate if (r <= 0) 263*7c478bd9Sstevel@tonic-gate { 264*7c478bd9Sstevel@tonic-gate if (r == 0) 265*7c478bd9Sstevel@tonic-gate fp->f_flags |= SMFEOF; 266*7c478bd9Sstevel@tonic-gate else 267*7c478bd9Sstevel@tonic-gate fp->f_flags |= SMERR; 268*7c478bd9Sstevel@tonic-gate fp->f_r = 0; 269*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; 270*7c478bd9Sstevel@tonic-gate } 271*7c478bd9Sstevel@tonic-gate fp->f_r = r; 272*7c478bd9Sstevel@tonic-gate return 0; 273*7c478bd9Sstevel@tonic-gate } 274*7c478bd9Sstevel@tonic-gate 275*7c478bd9Sstevel@tonic-gate /* 276*7c478bd9Sstevel@tonic-gate ** SM_RGET -- refills buffer and returns first character 277*7c478bd9Sstevel@tonic-gate ** 278*7c478bd9Sstevel@tonic-gate ** Handle sm_getc() when the buffer ran out: 279*7c478bd9Sstevel@tonic-gate ** Refill, then return the first character in the newly-filled buffer. 280*7c478bd9Sstevel@tonic-gate ** 281*7c478bd9Sstevel@tonic-gate ** Parameters: 282*7c478bd9Sstevel@tonic-gate ** fp -- file pointer to work on 283*7c478bd9Sstevel@tonic-gate ** timeout -- time to complete refill 284*7c478bd9Sstevel@tonic-gate ** 285*7c478bd9Sstevel@tonic-gate ** Returns: 286*7c478bd9Sstevel@tonic-gate ** Success: first character in refilled buffer as an int 287*7c478bd9Sstevel@tonic-gate ** Failure: SM_IO_EOF 288*7c478bd9Sstevel@tonic-gate */ 289*7c478bd9Sstevel@tonic-gate 290*7c478bd9Sstevel@tonic-gate int 291*7c478bd9Sstevel@tonic-gate sm_rget(fp, timeout) 292*7c478bd9Sstevel@tonic-gate register SM_FILE_T *fp; 293*7c478bd9Sstevel@tonic-gate int timeout; 294*7c478bd9Sstevel@tonic-gate { 295*7c478bd9Sstevel@tonic-gate if (sm_refill(fp, timeout) == 0) 296*7c478bd9Sstevel@tonic-gate { 297*7c478bd9Sstevel@tonic-gate fp->f_r--; 298*7c478bd9Sstevel@tonic-gate return *fp->f_p++; 299*7c478bd9Sstevel@tonic-gate } 300*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; 301*7c478bd9Sstevel@tonic-gate } 302