root/usr/src/cmd/sendmail/libsm/fclose.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: fclose.c,v 1.44 2005/06/14 23:07:20 ca Exp $")
#include <errno.h>
#include <stdlib.h>
#include <sm/time.h>
#include <setjmp.h>
#include <sm/io.h>
#include <sm/assert.h>
#include <sm/heap.h>
#include <sm/signal.h>
#include <sm/conf.h>
#include <sm/clock.h>
#include "local.h"

static void     closealrm __P((int));
static jmp_buf CloseTimeOut;

/*
**  CLOSEALRM -- handler when timeout activated for sm_io_close()
**
**      Returns flow of control to where setjmp(CloseTimeOut) was set.
**
**      Parameters:
**              sig -- unused
**
**      Returns:
**              does not return
**
**      Side Effects:
**              returns flow of control to setjmp(CloseTimeOut).
**
**      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
closealrm(sig)
        int sig;
{
        longjmp(CloseTimeOut, 1);
}

/*
**  SM_IO_CLOSE -- close a file handle/pointer
**
**      Parameters:
**              fp -- file pointer to be closed
**              timeout -- maximum time allowed to perform the close (millisecs)
**
**      Returns:
**              0 on success
**              -1 on failure and sets errno
**
**      Side Effects:
**              file pointer 'fp' will no longer be valid.
*/

int
sm_io_close(fp, timeout)
        register SM_FILE_T *fp;
        int SM_NONVOLATILE timeout;
{
        register int SM_NONVOLATILE r;
        SM_EVENT *evt = NULL;

        if (fp == NULL)
        {
                errno = EBADF;
                return SM_IO_EOF;
        }

        SM_REQUIRE_ISA(fp, SmFileMagic);

        /* XXX this won't be reached if above macro is active */
        if (fp->sm_magic == NULL)
        {
                /* not open! */
                errno = EBADF;
                return SM_IO_EOF;
        }
        if (fp->f_close == NULL)
        {
                /* no close function! */
                errno = ENODEV;
                return SM_IO_EOF;
        }
        if (fp->f_dup_cnt > 0)
        {
                /* decrement file pointer open count */
                fp->f_dup_cnt--;
                return 0;
        }

        /*  Okay, this is where we set the timeout.  */
        if (timeout == SM_TIME_DEFAULT)
                timeout = fp->f_timeout;
        if (timeout == SM_TIME_IMMEDIATE)
        {
                errno = EAGAIN;
                return -1;
        }

        /* No more duplicates of file pointer. Flush buffer and close */
        r = fp->f_flags & SMWR ? sm_flush(fp, (int *) &timeout) : 0;

        /* sm_flush() has updated to.it_value for the time it's used */
        if (timeout != SM_TIME_FOREVER)
        {
                if (setjmp(CloseTimeOut) != 0)
                {
                        errno = EAGAIN;
                        return SM_IO_EOF;
                }
                evt = sm_seteventm(timeout, closealrm, 0);
        }
        if ((*fp->f_close)(fp) < 0)
                r = SM_IO_EOF;

        /*  We're back. So undo our timeout and handler */
        if (evt != NULL)
                sm_clrevent(evt);
        if (fp->f_flags & SMMBF)
        {
                sm_free((char *)fp->f_bf.smb_base);
                fp->f_bf.smb_base = NULL;
        }
        if (HASUB(fp))
                FREEUB(fp);
        fp->f_flags = 0;        /* clear flags */
        fp->sm_magic = NULL;    /* Release this SM_FILE_T for reuse. */
        fp->f_r = fp->f_w = 0;  /* Mess up if reaccessed. */
        return r;
}