1N/A/*
1N/A * Copyright (c) 2000-2002, 2004-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 * $Id: local.h,v 1.58 2006/12/19 19:44:23 ca Exp $
1N/A */
1N/A
1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A
1N/A/*
1N/A** Information local to this implementation of stdio,
1N/A** in particular, macros and private variables.
1N/A*/
1N/A
1N/A#include <sm/time.h>
1N/A#if !SM_CONF_MEMCHR
1N/A# include <memory.h>
1N/A#endif /* !SM_CONF_MEMCHR */
1N/A#include <sm/heap.h>
1N/A
1N/Aint sm_flush __P((SM_FILE_T *, int *));
1N/ASM_FILE_T *smfp __P((void));
1N/Aint sm_refill __P((SM_FILE_T *, int));
1N/Avoid sm_init __P((void));
1N/Avoid sm_cleanup __P((void));
1N/Avoid sm_makebuf __P((SM_FILE_T *));
1N/Aint sm_whatbuf __P((SM_FILE_T *, size_t *, int *));
1N/Aint sm_fwalk __P((int (*)(SM_FILE_T *, int *), int *));
1N/Aint sm_wsetup __P((SM_FILE_T *));
1N/Aint sm_flags __P((int));
1N/ASM_FILE_T *sm_fp __P((const SM_FILE_T *, const int, SM_FILE_T *));
1N/Aint sm_vprintf __P((int, char const *, va_list));
1N/A
1N/A/* std io functions */
1N/Assize_t sm_stdread __P((SM_FILE_T *, char *, size_t));
1N/Assize_t sm_stdwrite __P((SM_FILE_T *, char const *, size_t));
1N/Aoff_t sm_stdseek __P((SM_FILE_T *, off_t, int));
1N/Aint sm_stdclose __P((SM_FILE_T *));
1N/Aint sm_stdopen __P((SM_FILE_T *, const void *, int, const void *));
1N/Aint sm_stdfdopen __P((SM_FILE_T *, const void *, int, const void *));
1N/Aint sm_stdsetinfo __P((SM_FILE_T *, int , void *));
1N/Aint sm_stdgetinfo __P((SM_FILE_T *, int , void *));
1N/A
1N/A/* stdio io functions */
1N/Assize_t sm_stdioread __P((SM_FILE_T *, char *, size_t));
1N/Assize_t sm_stdiowrite __P((SM_FILE_T *, char const *, size_t));
1N/Aoff_t sm_stdioseek __P((SM_FILE_T *, off_t, int));
1N/Aint sm_stdioclose __P((SM_FILE_T *));
1N/Aint sm_stdioopen __P((SM_FILE_T *, const void *, int, const void *));
1N/Aint sm_stdiosetinfo __P((SM_FILE_T *, int , void *));
1N/Aint sm_stdiogetinfo __P((SM_FILE_T *, int , void *));
1N/A
1N/A/* string io functions */
1N/Assize_t sm_strread __P((SM_FILE_T *, char *, size_t));
1N/Assize_t sm_strwrite __P((SM_FILE_T *, char const *, size_t));
1N/Aoff_t sm_strseek __P((SM_FILE_T *, off_t, int));
1N/Aint sm_strclose __P((SM_FILE_T *));
1N/Aint sm_stropen __P((SM_FILE_T *, const void *, int, const void *));
1N/Aint sm_strsetinfo __P((SM_FILE_T *, int , void *));
1N/Aint sm_strgetinfo __P((SM_FILE_T *, int , void *));
1N/A
1N/A/* syslog io functions */
1N/Assize_t sm_syslogread __P((SM_FILE_T *, char *, size_t));
1N/Assize_t sm_syslogwrite __P((SM_FILE_T *, char const *, size_t));
1N/Aoff_t sm_syslogseek __P((SM_FILE_T *, off_t, int));
1N/Aint sm_syslogclose __P((SM_FILE_T *));
1N/Aint sm_syslogopen __P((SM_FILE_T *, const void *, int, const void *));
1N/Aint sm_syslogsetinfo __P((SM_FILE_T *, int , void *));
1N/Aint sm_sysloggetinfo __P((SM_FILE_T *, int , void *));
1N/A
1N/Aextern bool Sm_IO_DidInit;
1N/A
1N/A/* Return true iff the given SM_FILE_T cannot be written now. */
1N/A#define cantwrite(fp) \
1N/A ((((fp)->f_flags & SMWR) == 0 || (fp)->f_bf.smb_base == NULL) && \
1N/A sm_wsetup(fp))
1N/A
1N/A/*
1N/A** Test whether the given stdio file has an active ungetc buffer;
1N/A** release such a buffer, without restoring ordinary unread data.
1N/A*/
1N/A
1N/A#define HASUB(fp) ((fp)->f_ub.smb_base != NULL)
1N/A#define FREEUB(fp) \
1N/A{ \
1N/A if ((fp)->f_ub.smb_base != (fp)->f_ubuf) \
1N/A sm_free((char *)(fp)->f_ub.smb_base); \
1N/A (fp)->f_ub.smb_base = NULL; \
1N/A}
1N/A
1N/Aextern const char SmFileMagic[];
1N/A
1N/A#define SM_ALIGN(p) (((unsigned long)(p) + SM_ALIGN_BITS) & ~SM_ALIGN_BITS)
1N/A
1N/A#define sm_io_flockfile(fp) ((void) 0)
1N/A#define sm_io_funlockfile(fp) ((void) 0)
1N/A
1N/Aint sm_flags __P((int));
1N/A
1N/A#ifndef FDSET_CAST
1N/A# define FDSET_CAST /* empty cast for fd_set arg to select */
1N/A#endif
1N/A
1N/A/*
1N/A** SM_CONVERT_TIME -- convert the API timeout flag for select() usage.
1N/A**
1N/A** This takes a 'fp' (a file type pointer) and obtains the "raw"
1N/A** file descriptor (fd) if possible. The 'fd' is needed to possibly
1N/A** switch the mode of the file (blocking/non-blocking) to match
1N/A** the type of timeout. If timeout is SM_TIME_FOREVER then the
1N/A** timeout using select won't be needed and the file is best placed
1N/A** in blocking mode. If there is to be a finite timeout then the file
1N/A** is best placed in non-blocking mode. Then, if not enough can be
1N/A** written, select() can be used to test when something can be written
1N/A** yet still timeout if the wait is too long.
1N/A** If the mode is already in the correct state we don't change it.
1N/A** Iff (yes "iff") the 'fd' is "-1" in value then the mode change
1N/A** will not happen. This situation arises when a late-binding-to-disk
1N/A** file type is in use. An example of this is the sendmail buffered
1N/A** file type (in sendmail/bf.c).
1N/A**
1N/A** Parameters
1N/A** fp -- the file pointer the timeout is for
1N/A** fd -- to become the file descriptor value from 'fp'
1N/A** val -- the timeout value to be converted
1N/A** time -- a struct timeval holding the converted value
1N/A**
1N/A** Returns
1N/A** nothing, this is flow-through code
1N/A**
1N/A** Side Effects:
1N/A** May or may not change the mode of a currently open file.
1N/A** The file mode may be changed to O_NONBLOCK or ~O_NONBLOCK
1N/A** (meaning block). This is done to best match the type of
1N/A** timeout and for (possible) use with select().
1N/A*/
1N/A
1N/A# define SM_CONVERT_TIME(fp, fd, val, time) { \
1N/A if (((fd) = sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)) == -1) \
1N/A { \
1N/A /* can't get an fd, likely internal 'fake' fp */ \
1N/A errno = 0; \
1N/A } \
1N/A if ((val) == SM_TIME_DEFAULT) \
1N/A (val) = (fp)->f_timeout; \
1N/A if ((val) == SM_TIME_IMMEDIATE || (val) == SM_TIME_FOREVER) \
1N/A { \
1N/A (time)->tv_sec = 0; \
1N/A (time)->tv_usec = 0; \
1N/A } \
1N/A else \
1N/A { \
1N/A (time)->tv_sec = (val) / 1000; \
1N/A (time)->tv_usec = ((val) - ((time)->tv_sec * 1000)) * 1000; \
1N/A } \
1N/A if ((val) == SM_TIME_FOREVER) \
1N/A { \
1N/A if ((fp)->f_timeoutstate == SM_TIME_NONBLOCK && (fd) != -1) \
1N/A { \
1N/A int ret; \
1N/A ret = fcntl((fd), F_GETFL, 0); \
1N/A if (ret == -1 || fcntl((fd), F_SETFL, \
1N/A ret & ~O_NONBLOCK) == -1) \
1N/A { \
1N/A /* errno should be set */ \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A (fp)->f_timeoutstate = SM_TIME_BLOCK; \
1N/A if ((fp)->f_modefp != NULL) \
1N/A (fp)->f_modefp->f_timeoutstate = SM_TIME_BLOCK; \
1N/A } \
1N/A } \
1N/A else { \
1N/A if ((fp)->f_timeoutstate == SM_TIME_BLOCK && (fd) != -1) \
1N/A { \
1N/A int ret; \
1N/A ret = fcntl((fd), F_GETFL, 0); \
1N/A if (ret == -1 || fcntl((fd), F_SETFL, \
1N/A ret | O_NONBLOCK) == -1) \
1N/A { \
1N/A /* errno should be set */ \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A (fp)->f_timeoutstate = SM_TIME_NONBLOCK; \
1N/A if ((fp)->f_modefp != NULL) \
1N/A (fp)->f_modefp->f_timeoutstate = SM_TIME_NONBLOCK; \
1N/A } \
1N/A } \
1N/A}
1N/A
1N/A/*
1N/A** SM_IO_WR_TIMEOUT -- setup the timeout for the write
1N/A**
1N/A** This #define uses a select() to wait for the 'fd' to become writable.
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 fvwrite.c).
1N/A**
1N/A** Parameters
1N/A** fd -- a file descriptor for doing select() with
1N/A** timeout -- the original user set value.
1N/A**
1N/A** Returns
1N/A** nothing, this is flow through code
1N/A**
1N/A** Side Effects:
1N/A** adjusts 'timeout' for time used
1N/A*/
1N/A
1N/A#define SM_IO_WR_TIMEOUT(fp, fd, to) { \
1N/A struct timeval sm_io_to_before, sm_io_to_after, sm_io_to_diff; \
1N/A struct timeval sm_io_to; \
1N/A int sm_io_to_sel; \
1N/A fd_set sm_io_to_mask, sm_io_x_mask; \
1N/A errno = 0; \
1N/A if ((to) == SM_TIME_DEFAULT) \
1N/A (to) = (fp)->f_timeout; \
1N/A if ((to) == SM_TIME_IMMEDIATE) \
1N/A { \
1N/A errno = EAGAIN; \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A else if ((to) == SM_TIME_FOREVER) \
1N/A { \
1N/A errno = EINVAL; \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A else \
1N/A { \
1N/A sm_io_to.tv_sec = (to) / 1000; \
1N/A sm_io_to.tv_usec = ((to) - (sm_io_to.tv_sec * 1000)) * 1000; \
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 sm_io_to_sel = select((fd) + 1, NULL, &sm_io_to_mask, \
1N/A &sm_io_x_mask, &sm_io_to); \
1N/A } while (sm_io_to_sel < 0 && errno == EINTR); \
1N/A if (sm_io_to_sel < 0) \
1N/A { \
1N/A /* something went wrong, errno set */ \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A else if (sm_io_to_sel == 0) \
1N/A { \
1N/A /* timeout */ \
1N/A errno = EAGAIN; \
1N/A return SM_IO_EOF; \
1N/A } \
1N/A /* else loop again */ \
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 (to) -= (sm_io_to_diff.tv_sec * 1000); \
1N/A (to) -= (sm_io_to_diff.tv_usec / 1000); \
1N/A if ((to) < 0) \
1N/A (to) = 0; \
1N/A}
1N/A
1N/A/*
1N/A** If there is no 'fd' just error (we can't timeout). If the timeout
1N/A** is SM_TIME_FOREVER then there is no need to do a timeout with
1N/A** select since this will be a real error. If the error is not
1N/A** EAGAIN/EWOULDBLOCK (from a nonblocking) then it's a real error.
1N/A** Specify the condition here as macro so it can be used in several places.
1N/A*/
1N/A
1N/A#define IS_IO_ERROR(fd, ret, to) \
1N/A ((fd) < 0 || \
1N/A ((ret) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || \
1N/A (to) == SM_TIME_FOREVER)
1N/A