375 lines
8.3 KiB
C
375 lines
8.3 KiB
C
/*
|
|
* Copyright (c) 2000-2002, 2004 Sendmail, Inc. and its suppliers.
|
|
* All rights reserved.
|
|
* Copyright (c) 1990, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Chris Torek.
|
|
*
|
|
* By using this file, you agree to the terms and conditions set
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
* the sendmail distribution.
|
|
*/
|
|
|
|
#include <sm/gen.h>
|
|
SM_RCSID("@(#)$Id: fopen.c,v 1.62 2005/06/14 23:07:20 ca Exp $")
|
|
#include <errno.h>
|
|
#include <setjmp.h>
|
|
#include <sm/time.h>
|
|
#include <sm/heap.h>
|
|
#include <sm/signal.h>
|
|
#include <sm/assert.h>
|
|
#include <sm/io.h>
|
|
#include <sm/clock.h>
|
|
#include "local.h"
|
|
|
|
static void openalrm __P((int));
|
|
static void reopenalrm __P((int));
|
|
extern int sm_io_fclose __P((SM_FILE_T *));
|
|
|
|
static jmp_buf OpenTimeOut, ReopenTimeOut;
|
|
|
|
/*
|
|
** OPENALRM -- handler when timeout activated for sm_io_open()
|
|
**
|
|
** Returns flow of control to where setjmp(OpenTimeOut) was set.
|
|
**
|
|
** Parameters:
|
|
** sig -- unused
|
|
**
|
|
** Returns:
|
|
** does not return
|
|
**
|
|
** Side Effects:
|
|
** returns flow of control to setjmp(OpenTimeOut).
|
|
**
|
|
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
|
|
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
|
|
** DOING.
|
|
*/
|
|
|
|
/* ARGSUSED0 */
|
|
static void
|
|
openalrm(sig)
|
|
int sig;
|
|
{
|
|
longjmp(OpenTimeOut, 1);
|
|
}
|
|
/*
|
|
** REOPENALRM -- handler when timeout activated for sm_io_reopen()
|
|
**
|
|
** Returns flow of control to where setjmp(ReopenTimeOut) was set.
|
|
**
|
|
** Parameters:
|
|
** sig -- unused
|
|
**
|
|
** Returns:
|
|
** does not return
|
|
**
|
|
** Side Effects:
|
|
** returns flow of control to setjmp(ReopenTimeOut).
|
|
**
|
|
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
|
|
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
|
|
** DOING.
|
|
*/
|
|
|
|
/* ARGSUSED0 */
|
|
static void
|
|
reopenalrm(sig)
|
|
int sig;
|
|
{
|
|
longjmp(ReopenTimeOut, 1);
|
|
}
|
|
|
|
/*
|
|
** SM_IO_OPEN -- open a file of a specific type
|
|
**
|
|
** Parameters:
|
|
** type -- type of file to open
|
|
** timeout -- time to complete the open
|
|
** info -- info describing what is to be opened (type dependant)
|
|
** flags -- user selected flags
|
|
** rpool -- pointer to rpool to be used for this open
|
|
**
|
|
** Returns:
|
|
** Raises exception on heap exhaustion.
|
|
** Aborts if type is invalid.
|
|
** Returns NULL and sets errno
|
|
** - when the type specific open fails
|
|
** - when open vector errors
|
|
** - when flags not set or invalid
|
|
** Success returns a file pointer to the opened file type.
|
|
*/
|
|
|
|
SM_FILE_T *
|
|
sm_io_open(type, timeout, info, flags, rpool)
|
|
const SM_FILE_T *type;
|
|
int SM_NONVOLATILE timeout; /* this is not the file type timeout */
|
|
const void *info;
|
|
int flags;
|
|
const void *rpool;
|
|
{
|
|
register SM_FILE_T *fp;
|
|
int ioflags;
|
|
SM_EVENT *evt = NULL;
|
|
|
|
ioflags = sm_flags(flags);
|
|
|
|
if (ioflags == 0)
|
|
{
|
|
/* must give some indication/intent */
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
if (timeout == SM_TIME_DEFAULT)
|
|
timeout = SM_TIME_FOREVER;
|
|
if (timeout == SM_TIME_IMMEDIATE)
|
|
{
|
|
errno = EAGAIN;
|
|
return NULL;
|
|
}
|
|
|
|
fp = sm_fp(type, ioflags, NULL);
|
|
|
|
/* Okay, this is where we set the timeout. */
|
|
if (timeout != SM_TIME_FOREVER)
|
|
{
|
|
if (setjmp(OpenTimeOut) != 0)
|
|
{
|
|
errno = EAGAIN;
|
|
return NULL;
|
|
}
|
|
evt = sm_seteventm(timeout, openalrm, 0);
|
|
}
|
|
|
|
if ((*fp->f_open)(fp, info, flags, rpool) < 0)
|
|
{
|
|
fp->f_flags = 0; /* release */
|
|
fp->sm_magic = NULL; /* release */
|
|
return NULL;
|
|
}
|
|
|
|
/* We're back. So undo our timeout and handler */
|
|
if (evt != NULL)
|
|
sm_clrevent(evt);
|
|
|
|
#if SM_RPOOL
|
|
if (rpool != NULL)
|
|
sm_rpool_attach_x(rpool, sm_io_fclose, fp);
|
|
#endif /* SM_RPOOL */
|
|
|
|
return fp;
|
|
}
|
|
/*
|
|
** SM_IO_DUP -- duplicate a file pointer
|
|
**
|
|
** Parameters:
|
|
** fp -- file pointer to duplicate
|
|
**
|
|
** Returns:
|
|
** Success - the duplicated file pointer
|
|
** Failure - NULL (was an invalid file pointer or too many open)
|
|
**
|
|
** Increments the duplicate counter (dup_cnt) for the open file pointer.
|
|
** The counter counts the number of duplicates. When the duplicate
|
|
** counter is 0 (zero) then the file pointer is the only one left
|
|
** (no duplicates, it is the only one).
|
|
*/
|
|
|
|
SM_FILE_T *
|
|
sm_io_dup(fp)
|
|
SM_FILE_T *fp;
|
|
{
|
|
|
|
SM_REQUIRE_ISA(fp, SmFileMagic);
|
|
if (fp->sm_magic != SmFileMagic)
|
|
{
|
|
errno = EBADF;
|
|
return NULL;
|
|
}
|
|
if (fp->f_dup_cnt >= INT_MAX - 1)
|
|
{
|
|
/* Can't let f_dup_cnt wrap! */
|
|
errno = EMFILE;
|
|
return NULL;
|
|
}
|
|
fp->f_dup_cnt++;
|
|
return fp;
|
|
}
|
|
/*
|
|
** SM_IO_REOPEN -- open a new file using the old file pointer
|
|
**
|
|
** Parameters:
|
|
** type -- file type to be opened
|
|
** timeout -- time to complete the reopen
|
|
** info -- infomation about what is to be "re-opened" (type dep.)
|
|
** flags -- user flags to map to internal flags
|
|
** rpool -- rpool file to be associated with
|
|
** fp -- the file pointer to reuse
|
|
**
|
|
** Returns:
|
|
** Raises an exception on heap exhaustion.
|
|
** Aborts if type is invalid.
|
|
** Failure: returns NULL
|
|
** Success: returns "reopened" file pointer
|
|
*/
|
|
|
|
SM_FILE_T *
|
|
sm_io_reopen(type, timeout, info, flags, rpool, fp)
|
|
const SM_FILE_T *type;
|
|
int SM_NONVOLATILE timeout;
|
|
const void *info;
|
|
int flags;
|
|
const void *rpool;
|
|
SM_FILE_T *fp;
|
|
{
|
|
int ioflags, ret;
|
|
SM_FILE_T *fp2;
|
|
SM_EVENT *evt = NULL;
|
|
|
|
if ((ioflags = sm_flags(flags)) == 0)
|
|
{
|
|
(void) sm_io_close(fp, timeout);
|
|
return NULL;
|
|
}
|
|
|
|
if (!Sm_IO_DidInit)
|
|
sm_init();
|
|
|
|
if (timeout == SM_TIME_DEFAULT)
|
|
timeout = SM_TIME_FOREVER;
|
|
if (timeout == SM_TIME_IMMEDIATE)
|
|
{
|
|
/*
|
|
** Filling the buffer will take time and we are wanted to
|
|
** return immediately. So...
|
|
*/
|
|
|
|
errno = EAGAIN;
|
|
return NULL;
|
|
}
|
|
/* Okay, this is where we set the timeout. */
|
|
if (timeout != SM_TIME_FOREVER)
|
|
{
|
|
if (setjmp(ReopenTimeOut) != 0)
|
|
{
|
|
errno = EAGAIN;
|
|
return NULL;
|
|
}
|
|
|
|
evt = sm_seteventm(timeout, reopenalrm, 0);
|
|
}
|
|
|
|
/*
|
|
** There are actually programs that depend on being able to "reopen"
|
|
** descriptors that weren't originally open. Keep this from breaking.
|
|
** Remember whether the stream was open to begin with, and which file
|
|
** descriptor (if any) was associated with it. If it was attached to
|
|
** a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin)
|
|
** should work. This is unnecessary if it was not a Unix file.
|
|
*/
|
|
|
|
if (fp != NULL)
|
|
{
|
|
if (fp->sm_magic != SmFileMagic)
|
|
fp->f_flags = SMFEOF; /* hold on to it */
|
|
else
|
|
{
|
|
/* flush the stream; ANSI doesn't require this. */
|
|
(void) sm_io_flush(fp, SM_TIME_FOREVER);
|
|
(void) sm_io_close(fp, SM_TIME_FOREVER);
|
|
}
|
|
}
|
|
|
|
fp2 = sm_fp(type, ioflags, fp);
|
|
ret = (*fp2->f_open)(fp2, info, flags, rpool);
|
|
|
|
/* We're back. So undo our timeout and handler */
|
|
if (evt != NULL)
|
|
sm_clrevent(evt);
|
|
|
|
if (ret < 0)
|
|
{
|
|
fp2->f_flags = 0; /* release */
|
|
fp2->sm_magic = NULL; /* release */
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** We're not preserving this logic (below) for sm_io because it is now
|
|
** abstracted at least one "layer" away. By closing and reopening
|
|
** the 1st fd used should be the just released one (when Unix
|
|
** behavior followed). Old comment::
|
|
** If reopening something that was open before on a real file, try
|
|
** to maintain the descriptor. Various C library routines (perror)
|
|
** assume stderr is always fd STDERR_FILENO, even if being reopen'd.
|
|
*/
|
|
|
|
#if SM_RPOOL
|
|
if (rpool != NULL)
|
|
sm_rpool_attach_x(rpool, sm_io_close, fp2);
|
|
#endif /* SM_RPOOL */
|
|
|
|
return fp2;
|
|
}
|
|
/*
|
|
** SM_IO_AUTOFLUSH -- link another file to this for auto-flushing
|
|
**
|
|
** When a read occurs on fp, fp2 will be flushed iff there is no
|
|
** data waiting on fp.
|
|
**
|
|
** Parameters:
|
|
** fp -- the file opened for reading.
|
|
** fp2 -- the file opened for writing.
|
|
**
|
|
** Returns:
|
|
** The old flush file pointer.
|
|
*/
|
|
|
|
SM_FILE_T *
|
|
sm_io_autoflush(fp, fp2)
|
|
SM_FILE_T *fp;
|
|
SM_FILE_T *fp2;
|
|
{
|
|
SM_FILE_T *savefp;
|
|
|
|
SM_REQUIRE_ISA(fp, SmFileMagic);
|
|
if (fp2 != NULL)
|
|
SM_REQUIRE_ISA(fp2, SmFileMagic);
|
|
|
|
savefp = fp->f_flushfp;
|
|
fp->f_flushfp = fp2;
|
|
return savefp;
|
|
}
|
|
/*
|
|
** SM_IO_AUTOMODE -- link another file to this for auto-moding
|
|
**
|
|
** When the mode (blocking or non-blocking) changes for fp1 then
|
|
** update fp2's mode at the same time. This is to be used when
|
|
** a system dup() has generated a second file descriptor for
|
|
** another sm_io_open() by file descriptor. The modes have been
|
|
** linked in the system and this formalizes it for sm_io internally.
|
|
**
|
|
** Parameters:
|
|
** fp1 -- the first file
|
|
** fp2 -- the second file
|
|
**
|
|
** Returns:
|
|
** nothing
|
|
*/
|
|
|
|
void
|
|
sm_io_automode(fp1, fp2)
|
|
SM_FILE_T *fp1;
|
|
SM_FILE_T *fp2;
|
|
{
|
|
SM_REQUIRE_ISA(fp1, SmFileMagic);
|
|
SM_REQUIRE_ISA(fp2, SmFileMagic);
|
|
|
|
fp1->f_modefp = fp2;
|
|
fp2->f_modefp = fp1;
|
|
}
|