190792Sgshapiro/*
2261363Sgshapiro * Copyright (c) 2000-2002, 2004 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *      All rights reserved.
490792Sgshapiro * Copyright (c) 1990, 1993
590792Sgshapiro *	The Regents of the University of California.  All rights reserved.
690792Sgshapiro *
790792Sgshapiro * This code is derived from software contributed to Berkeley by
890792Sgshapiro * Chris Torek.
990792Sgshapiro *
1090792Sgshapiro * By using this file, you agree to the terms and conditions set
1190792Sgshapiro * forth in the LICENSE file which can be found at the top level of
1290792Sgshapiro * the sendmail distribution.
1390792Sgshapiro */
1490792Sgshapiro
1590792Sgshapiro#include <sm/gen.h>
16266692SgshapiroSM_RCSID("@(#)$Id: fopen.c,v 1.63 2013-11-22 20:51:42 ca Exp $")
1790792Sgshapiro#include <errno.h>
1890792Sgshapiro#include <setjmp.h>
19157001Sgshapiro#include <sm/time.h>
2090792Sgshapiro#include <sm/heap.h>
2190792Sgshapiro#include <sm/signal.h>
2290792Sgshapiro#include <sm/assert.h>
2390792Sgshapiro#include <sm/io.h>
2490792Sgshapiro#include <sm/clock.h>
2590792Sgshapiro#include "local.h"
2690792Sgshapiro
27141858Sgshapirostatic void	openalrm __P((int));
28141858Sgshapirostatic void	reopenalrm __P((int));
2990792Sgshapiroextern int      sm_io_fclose __P((SM_FILE_T *));
3090792Sgshapiro
3190792Sgshapirostatic jmp_buf OpenTimeOut, ReopenTimeOut;
3290792Sgshapiro
3390792Sgshapiro/*
3490792Sgshapiro**  OPENALRM -- handler when timeout activated for sm_io_open()
3590792Sgshapiro**
3690792Sgshapiro**  Returns flow of control to where setjmp(OpenTimeOut) was set.
3790792Sgshapiro**
3890792Sgshapiro**	Parameters:
3990792Sgshapiro**		sig -- unused
4090792Sgshapiro**
4190792Sgshapiro**	Returns:
4290792Sgshapiro**		does not return
4390792Sgshapiro**
4490792Sgshapiro**	Side Effects:
4590792Sgshapiro**		returns flow of control to setjmp(OpenTimeOut).
4690792Sgshapiro**
4790792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
4890792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4990792Sgshapiro**		DOING.
5090792Sgshapiro*/
5190792Sgshapiro
5290792Sgshapiro/* ARGSUSED0 */
5390792Sgshapirostatic void
5490792Sgshapiroopenalrm(sig)
5590792Sgshapiro	int sig;
5690792Sgshapiro{
5790792Sgshapiro	longjmp(OpenTimeOut, 1);
5890792Sgshapiro}
5990792Sgshapiro/*
6090792Sgshapiro**  REOPENALRM -- handler when timeout activated for sm_io_reopen()
6190792Sgshapiro**
6290792Sgshapiro**  Returns flow of control to where setjmp(ReopenTimeOut) was set.
6390792Sgshapiro**
6490792Sgshapiro**	Parameters:
6590792Sgshapiro**		sig -- unused
6690792Sgshapiro**
6790792Sgshapiro**	Returns:
6890792Sgshapiro**		does not return
6990792Sgshapiro**
7090792Sgshapiro**	Side Effects:
7190792Sgshapiro**		returns flow of control to setjmp(ReopenTimeOut).
7290792Sgshapiro**
7390792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
7490792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
7590792Sgshapiro**		DOING.
7690792Sgshapiro*/
7790792Sgshapiro
7890792Sgshapiro/* ARGSUSED0 */
7990792Sgshapirostatic void
8090792Sgshapiroreopenalrm(sig)
8190792Sgshapiro	int sig;
8290792Sgshapiro{
8390792Sgshapiro	longjmp(ReopenTimeOut, 1);
8490792Sgshapiro}
8590792Sgshapiro
8690792Sgshapiro/*
8790792Sgshapiro**  SM_IO_OPEN -- open a file of a specific type
8890792Sgshapiro**
8990792Sgshapiro**	Parameters:
9090792Sgshapiro**		type -- type of file to open
9190792Sgshapiro**		timeout -- time to complete the open
9290792Sgshapiro**		info -- info describing what is to be opened (type dependant)
9390792Sgshapiro**		flags -- user selected flags
9490792Sgshapiro**		rpool -- pointer to rpool to be used for this open
9590792Sgshapiro**
9690792Sgshapiro**	Returns:
9790792Sgshapiro**		Raises exception on heap exhaustion.
9890792Sgshapiro**		Aborts if type is invalid.
9990792Sgshapiro**		Returns NULL and sets errno
10090792Sgshapiro**			- when the type specific open fails
10190792Sgshapiro**			- when open vector errors
10290792Sgshapiro**			- when flags not set or invalid
10390792Sgshapiro**		Success returns a file pointer to the opened file type.
10490792Sgshapiro*/
10590792Sgshapiro
10690792SgshapiroSM_FILE_T *
10790792Sgshapirosm_io_open(type, timeout, info, flags, rpool)
10890792Sgshapiro	const SM_FILE_T *type;
10990792Sgshapiro	int SM_NONVOLATILE timeout;	/* this is not the file type timeout */
11090792Sgshapiro	const void *info;
11190792Sgshapiro	int flags;
11290792Sgshapiro	const void *rpool;
11390792Sgshapiro{
11490792Sgshapiro	register SM_FILE_T *fp;
11590792Sgshapiro	int ioflags;
11690792Sgshapiro	SM_EVENT *evt = NULL;
11790792Sgshapiro
11890792Sgshapiro	ioflags = sm_flags(flags);
11990792Sgshapiro
12090792Sgshapiro	if (ioflags == 0)
12190792Sgshapiro	{
12290792Sgshapiro		/* must give some indication/intent */
12390792Sgshapiro		errno = EINVAL;
12490792Sgshapiro		return NULL;
12590792Sgshapiro	}
12690792Sgshapiro
12790792Sgshapiro	if (timeout == SM_TIME_DEFAULT)
12890792Sgshapiro		timeout = SM_TIME_FOREVER;
12990792Sgshapiro	if (timeout == SM_TIME_IMMEDIATE)
13090792Sgshapiro	{
13190792Sgshapiro		errno = EAGAIN;
13290792Sgshapiro		return NULL;
13390792Sgshapiro	}
13490792Sgshapiro
13590792Sgshapiro	fp = sm_fp(type, ioflags, NULL);
13690792Sgshapiro
13790792Sgshapiro	/*  Okay, this is where we set the timeout.  */
13890792Sgshapiro	if (timeout != SM_TIME_FOREVER)
13990792Sgshapiro	{
14090792Sgshapiro		if (setjmp(OpenTimeOut) != 0)
14190792Sgshapiro		{
14290792Sgshapiro			errno = EAGAIN;
14390792Sgshapiro			return NULL;
14490792Sgshapiro		}
14590792Sgshapiro		evt = sm_seteventm(timeout, openalrm, 0);
14690792Sgshapiro	}
14790792Sgshapiro
14890792Sgshapiro	if ((*fp->f_open)(fp, info, flags, rpool) < 0)
14990792Sgshapiro	{
15090792Sgshapiro		fp->f_flags = 0;	/* release */
15190792Sgshapiro		fp->sm_magic = NULL;	/* release */
15290792Sgshapiro		return NULL;
15390792Sgshapiro	}
15490792Sgshapiro
15590792Sgshapiro	/*  We're back. So undo our timeout and handler */
15690792Sgshapiro	if (evt != NULL)
15790792Sgshapiro		sm_clrevent(evt);
15890792Sgshapiro
15990792Sgshapiro#if SM_RPOOL
16090792Sgshapiro	if (rpool != NULL)
16190792Sgshapiro		sm_rpool_attach_x(rpool, sm_io_fclose, fp);
16290792Sgshapiro#endif /* SM_RPOOL */
16390792Sgshapiro
16490792Sgshapiro	return fp;
16590792Sgshapiro}
16690792Sgshapiro/*
16790792Sgshapiro**  SM_IO_DUP -- duplicate a file pointer
16890792Sgshapiro**
16990792Sgshapiro**	Parameters:
17090792Sgshapiro**		fp -- file pointer to duplicate
17190792Sgshapiro**
17290792Sgshapiro**	Returns:
17390792Sgshapiro**		Success - the duplicated file pointer
17490792Sgshapiro**		Failure - NULL (was an invalid file pointer or too many open)
17590792Sgshapiro**
17690792Sgshapiro**	Increments the duplicate counter (dup_cnt) for the open file pointer.
17790792Sgshapiro**	The counter counts the number of duplicates. When the duplicate
17890792Sgshapiro**	counter is 0 (zero) then the file pointer is the only one left
17990792Sgshapiro**	(no duplicates, it is the only one).
18090792Sgshapiro*/
18190792Sgshapiro
18290792SgshapiroSM_FILE_T *
18390792Sgshapirosm_io_dup(fp)
18490792Sgshapiro	SM_FILE_T *fp;
18590792Sgshapiro{
18690792Sgshapiro
18790792Sgshapiro	SM_REQUIRE_ISA(fp, SmFileMagic);
18890792Sgshapiro	if (fp->sm_magic != SmFileMagic)
18990792Sgshapiro	{
19090792Sgshapiro		errno = EBADF;
19190792Sgshapiro		return NULL;
19290792Sgshapiro	}
19390792Sgshapiro	if (fp->f_dup_cnt >= INT_MAX - 1)
19490792Sgshapiro	{
19590792Sgshapiro		/* Can't let f_dup_cnt wrap! */
19690792Sgshapiro		errno = EMFILE;
19790792Sgshapiro		return NULL;
19890792Sgshapiro	}
19990792Sgshapiro	fp->f_dup_cnt++;
20090792Sgshapiro	return fp;
20190792Sgshapiro}
20290792Sgshapiro/*
20390792Sgshapiro**  SM_IO_REOPEN -- open a new file using the old file pointer
20490792Sgshapiro**
20590792Sgshapiro**	Parameters:
20690792Sgshapiro**		type -- file type to be opened
20790792Sgshapiro**		timeout -- time to complete the reopen
20890792Sgshapiro**		info -- infomation about what is to be "re-opened" (type dep.)
20990792Sgshapiro**		flags -- user flags to map to internal flags
21090792Sgshapiro**		rpool -- rpool file to be associated with
21190792Sgshapiro**		fp -- the file pointer to reuse
21290792Sgshapiro**
21390792Sgshapiro**	Returns:
21490792Sgshapiro**		Raises an exception on heap exhaustion.
21590792Sgshapiro**		Aborts if type is invalid.
21690792Sgshapiro**		Failure: returns NULL
21790792Sgshapiro**		Success: returns "reopened" file pointer
21890792Sgshapiro*/
21990792Sgshapiro
22090792SgshapiroSM_FILE_T *
22190792Sgshapirosm_io_reopen(type, timeout, info, flags, rpool, fp)
22290792Sgshapiro	const SM_FILE_T *type;
22390792Sgshapiro	int SM_NONVOLATILE timeout;
22490792Sgshapiro	const void *info;
22590792Sgshapiro	int flags;
22690792Sgshapiro	const void *rpool;
22790792Sgshapiro	SM_FILE_T *fp;
22890792Sgshapiro{
22990792Sgshapiro	int ioflags, ret;
23090792Sgshapiro	SM_FILE_T *fp2;
23190792Sgshapiro	SM_EVENT *evt = NULL;
23290792Sgshapiro
23390792Sgshapiro	if ((ioflags = sm_flags(flags)) == 0)
23490792Sgshapiro	{
23590792Sgshapiro		(void) sm_io_close(fp, timeout);
23690792Sgshapiro		return NULL;
23790792Sgshapiro	}
23890792Sgshapiro
23990792Sgshapiro	if (!Sm_IO_DidInit)
24090792Sgshapiro		sm_init();
24190792Sgshapiro
24290792Sgshapiro	if (timeout == SM_TIME_DEFAULT)
24390792Sgshapiro		timeout = SM_TIME_FOREVER;
24490792Sgshapiro	if (timeout == SM_TIME_IMMEDIATE)
24590792Sgshapiro	{
24690792Sgshapiro		/*
24790792Sgshapiro		**  Filling the buffer will take time and we are wanted to
24890792Sgshapiro		**  return immediately. So...
24990792Sgshapiro		*/
25090792Sgshapiro
25190792Sgshapiro		errno = EAGAIN;
25290792Sgshapiro		return NULL;
25390792Sgshapiro	}
25490792Sgshapiro	/*  Okay, this is where we set the timeout.  */
25590792Sgshapiro	if (timeout != SM_TIME_FOREVER)
25690792Sgshapiro	{
25790792Sgshapiro		if (setjmp(ReopenTimeOut) != 0)
25890792Sgshapiro		{
25990792Sgshapiro			errno = EAGAIN;
26090792Sgshapiro			return NULL;
26190792Sgshapiro		}
26290792Sgshapiro
26390792Sgshapiro		evt = sm_seteventm(timeout, reopenalrm, 0);
26490792Sgshapiro	}
26590792Sgshapiro
26690792Sgshapiro	/*
26790792Sgshapiro	**  There are actually programs that depend on being able to "reopen"
26890792Sgshapiro	**  descriptors that weren't originally open.  Keep this from breaking.
26990792Sgshapiro	**  Remember whether the stream was open to begin with, and which file
27090792Sgshapiro	**  descriptor (if any) was associated with it.  If it was attached to
27190792Sgshapiro	**  a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin)
27290792Sgshapiro	**  should work.  This is unnecessary if it was not a Unix file.
27390792Sgshapiro	*/
27490792Sgshapiro
27590792Sgshapiro	if (fp != NULL)
27690792Sgshapiro	{
27790792Sgshapiro		if (fp->sm_magic != SmFileMagic)
27890792Sgshapiro			fp->f_flags = SMFEOF;	/* hold on to it */
27990792Sgshapiro		else
28090792Sgshapiro		{
28190792Sgshapiro			/* flush the stream; ANSI doesn't require this. */
28290792Sgshapiro			(void) sm_io_flush(fp, SM_TIME_FOREVER);
28390792Sgshapiro			(void) sm_io_close(fp, SM_TIME_FOREVER);
28490792Sgshapiro		}
28590792Sgshapiro	}
28690792Sgshapiro
28790792Sgshapiro	fp2 = sm_fp(type, ioflags, fp);
28890792Sgshapiro	ret = (*fp2->f_open)(fp2, info, flags, rpool);
28990792Sgshapiro
29090792Sgshapiro	/*  We're back. So undo our timeout and handler */
29190792Sgshapiro	if (evt != NULL)
29290792Sgshapiro		sm_clrevent(evt);
29390792Sgshapiro
29490792Sgshapiro	if (ret < 0)
29590792Sgshapiro	{
29690792Sgshapiro		fp2->f_flags = 0;	/* release */
29790792Sgshapiro		fp2->sm_magic = NULL;	/* release */
29890792Sgshapiro		return NULL;
29990792Sgshapiro	}
30090792Sgshapiro
30190792Sgshapiro	/*
30290792Sgshapiro	**  We're not preserving this logic (below) for sm_io because it is now
30390792Sgshapiro	**  abstracted at least one "layer" away. By closing and reopening
30490792Sgshapiro	**  the 1st fd used should be the just released one (when Unix
30590792Sgshapiro	**  behavior followed). Old comment::
30690792Sgshapiro	**  If reopening something that was open before on a real file, try
30790792Sgshapiro	**  to maintain the descriptor.  Various C library routines (perror)
30890792Sgshapiro	**  assume stderr is always fd STDERR_FILENO, even if being reopen'd.
30990792Sgshapiro	*/
31090792Sgshapiro
31190792Sgshapiro#if SM_RPOOL
31290792Sgshapiro	if (rpool != NULL)
31390792Sgshapiro		sm_rpool_attach_x(rpool, sm_io_close, fp2);
31490792Sgshapiro#endif /* SM_RPOOL */
31590792Sgshapiro
31690792Sgshapiro	return fp2;
31790792Sgshapiro}
31890792Sgshapiro/*
31990792Sgshapiro**  SM_IO_AUTOFLUSH -- link another file to this for auto-flushing
32090792Sgshapiro**
32190792Sgshapiro**	When a read occurs on fp, fp2 will be flushed iff there is no
32290792Sgshapiro**	data waiting on fp.
32390792Sgshapiro**
32490792Sgshapiro**	Parameters:
32590792Sgshapiro**		fp -- the file opened for reading.
32690792Sgshapiro**		fp2 -- the file opened for writing.
32790792Sgshapiro**
32890792Sgshapiro**	Returns:
32990792Sgshapiro**		The old flush file pointer.
33090792Sgshapiro*/
33190792Sgshapiro
33290792SgshapiroSM_FILE_T *
33390792Sgshapirosm_io_autoflush(fp, fp2)
33490792Sgshapiro	SM_FILE_T *fp;
33590792Sgshapiro	SM_FILE_T *fp2;
33690792Sgshapiro{
33790792Sgshapiro	SM_FILE_T *savefp;
33890792Sgshapiro
33990792Sgshapiro	SM_REQUIRE_ISA(fp, SmFileMagic);
34090792Sgshapiro	if (fp2 != NULL)
34190792Sgshapiro		SM_REQUIRE_ISA(fp2, SmFileMagic);
34290792Sgshapiro
34390792Sgshapiro	savefp = fp->f_flushfp;
34490792Sgshapiro	fp->f_flushfp = fp2;
34590792Sgshapiro	return savefp;
34690792Sgshapiro}
34790792Sgshapiro/*
34890792Sgshapiro**  SM_IO_AUTOMODE -- link another file to this for auto-moding
34990792Sgshapiro**
35090792Sgshapiro**	When the mode (blocking or non-blocking) changes for fp1 then
35190792Sgshapiro**	update fp2's mode at the same time. This is to be used when
35290792Sgshapiro**	a system dup() has generated a second file descriptor for
35390792Sgshapiro**	another sm_io_open() by file descriptor. The modes have been
35490792Sgshapiro**	linked in the system and this formalizes it for sm_io internally.
35590792Sgshapiro**
35690792Sgshapiro**	Parameters:
35790792Sgshapiro**		fp1 -- the first file
35890792Sgshapiro**		fp2 -- the second file
35990792Sgshapiro**
36090792Sgshapiro**	Returns:
36190792Sgshapiro**		nothing
36290792Sgshapiro*/
36390792Sgshapiro
36490792Sgshapirovoid
36590792Sgshapirosm_io_automode(fp1, fp2)
36690792Sgshapiro	SM_FILE_T *fp1;
36790792Sgshapiro	SM_FILE_T *fp2;
36890792Sgshapiro{
36990792Sgshapiro	SM_REQUIRE_ISA(fp1, SmFileMagic);
37090792Sgshapiro	SM_REQUIRE_ISA(fp2, SmFileMagic);
37190792Sgshapiro
37290792Sgshapiro	fp1->f_modefp = fp2;
37390792Sgshapiro	fp2->f_modefp = fp1;
37490792Sgshapiro}
375