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 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 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 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