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