fopen.c revision 98121
1/*
2 * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 * Copyright (c) 1990, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek.
9 *
10 * By using this file, you agree to the terms and conditions set
11 * forth in the LICENSE file which can be found at the top level of
12 * the sendmail distribution.
13 */
14
15#include <sm/gen.h>
16SM_RCSID("@(#)$Id: fopen.c,v 1.60 2002/01/07 21:41:35 ca Exp $")
17#include <errno.h>
18#include <setjmp.h>
19#include <sys/time.h>
20#include <sm/heap.h>
21#include <sm/signal.h>
22#include <sm/assert.h>
23#include <sm/io.h>
24#include <sm/clock.h>
25#include "local.h"
26
27extern int      sm_io_fclose __P((SM_FILE_T *));
28
29static jmp_buf OpenTimeOut, ReopenTimeOut;
30
31/*
32**  OPENALRM -- handler when timeout activated for sm_io_open()
33**
34**  Returns flow of control to where setjmp(OpenTimeOut) was set.
35**
36**	Parameters:
37**		sig -- unused
38**
39**	Returns:
40**		does not return
41**
42**	Side Effects:
43**		returns flow of control to setjmp(OpenTimeOut).
44**
45**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
46**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
47**		DOING.
48*/
49
50/* ARGSUSED0 */
51static void
52openalrm(sig)
53	int sig;
54{
55	longjmp(OpenTimeOut, 1);
56}
57/*
58**  REOPENALRM -- handler when timeout activated for sm_io_reopen()
59**
60**  Returns flow of control to where setjmp(ReopenTimeOut) was set.
61**
62**	Parameters:
63**		sig -- unused
64**
65**	Returns:
66**		does not return
67**
68**	Side Effects:
69**		returns flow of control to setjmp(ReopenTimeOut).
70**
71**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
72**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
73**		DOING.
74*/
75
76/* ARGSUSED0 */
77static void
78reopenalrm(sig)
79	int sig;
80{
81	longjmp(ReopenTimeOut, 1);
82}
83
84/*
85**  SM_IO_OPEN -- open a file of a specific type
86**
87**	Parameters:
88**		type -- type of file to open
89**		timeout -- time to complete the open
90**		info -- info describing what is to be opened (type dependant)
91**		flags -- user selected flags
92**		rpool -- pointer to rpool to be used for this open
93**
94**	Returns:
95**		Raises exception on heap exhaustion.
96**		Aborts if type is invalid.
97**		Returns NULL and sets errno
98**			- when the type specific open fails
99**			- when open vector errors
100**			- when flags not set or invalid
101**		Success returns a file pointer to the opened file type.
102*/
103
104SM_FILE_T *
105sm_io_open(type, timeout, info, flags, rpool)
106	const SM_FILE_T *type;
107	int SM_NONVOLATILE timeout;	/* this is not the file type timeout */
108	const void *info;
109	int flags;
110	const void *rpool;
111{
112	register SM_FILE_T *fp;
113	int ioflags;
114	SM_EVENT *evt = NULL;
115
116	ioflags = sm_flags(flags);
117
118	if (ioflags == 0)
119	{
120		/* must give some indication/intent */
121		errno = EINVAL;
122		return NULL;
123	}
124
125	if (timeout == SM_TIME_DEFAULT)
126		timeout = SM_TIME_FOREVER;
127	if (timeout == SM_TIME_IMMEDIATE)
128	{
129		errno = EAGAIN;
130		return NULL;
131	}
132
133	fp = sm_fp(type, ioflags, NULL);
134
135	/*  Okay, this is where we set the timeout.  */
136	if (timeout != SM_TIME_FOREVER)
137	{
138		if (setjmp(OpenTimeOut) != 0)
139		{
140			errno = EAGAIN;
141			return NULL;
142		}
143		evt = sm_seteventm(timeout, openalrm, 0);
144	}
145
146	if ((*fp->f_open)(fp, info, flags, rpool) < 0)
147	{
148		fp->f_flags = 0;	/* release */
149		fp->sm_magic = NULL;	/* release */
150		return NULL;
151	}
152
153	/*  We're back. So undo our timeout and handler */
154	if (evt != NULL)
155		sm_clrevent(evt);
156
157#if SM_RPOOL
158	if (rpool != NULL)
159		sm_rpool_attach_x(rpool, sm_io_fclose, fp);
160#endif /* SM_RPOOL */
161
162	return fp;
163}
164/*
165**  SM_IO_DUP -- duplicate a file pointer
166**
167**	Parameters:
168**		fp -- file pointer to duplicate
169**
170**	Returns:
171**		Success - the duplicated file pointer
172**		Failure - NULL (was an invalid file pointer or too many open)
173**
174**	Increments the duplicate counter (dup_cnt) for the open file pointer.
175**	The counter counts the number of duplicates. When the duplicate
176**	counter is 0 (zero) then the file pointer is the only one left
177**	(no duplicates, it is the only one).
178*/
179
180SM_FILE_T *
181sm_io_dup(fp)
182	SM_FILE_T *fp;
183{
184
185	SM_REQUIRE_ISA(fp, SmFileMagic);
186	if (fp->sm_magic != SmFileMagic)
187	{
188		errno = EBADF;
189		return NULL;
190	}
191	if (fp->f_dup_cnt >= INT_MAX - 1)
192	{
193		/* Can't let f_dup_cnt wrap! */
194		errno = EMFILE;
195		return NULL;
196	}
197	fp->f_dup_cnt++;
198	return fp;
199}
200/*
201**  SM_IO_REOPEN -- open a new file using the old file pointer
202**
203**	Parameters:
204**		type -- file type to be opened
205**		timeout -- time to complete the reopen
206**		info -- infomation about what is to be "re-opened" (type dep.)
207**		flags -- user flags to map to internal flags
208**		rpool -- rpool file to be associated with
209**		fp -- the file pointer to reuse
210**
211**	Returns:
212**		Raises an exception on heap exhaustion.
213**		Aborts if type is invalid.
214**		Failure: returns NULL
215**		Success: returns "reopened" file pointer
216*/
217
218SM_FILE_T *
219sm_io_reopen(type, timeout, info, flags, rpool, fp)
220	const SM_FILE_T *type;
221	int SM_NONVOLATILE timeout;
222	const void *info;
223	int flags;
224	const void *rpool;
225	SM_FILE_T *fp;
226{
227	int ioflags, ret;
228	SM_FILE_T *fp2;
229	SM_EVENT *evt = NULL;
230
231	if ((ioflags = sm_flags(flags)) == 0)
232	{
233		(void) sm_io_close(fp, timeout);
234		return NULL;
235	}
236
237	if (!Sm_IO_DidInit)
238		sm_init();
239
240	if (timeout == SM_TIME_DEFAULT)
241		timeout = SM_TIME_FOREVER;
242	if (timeout == SM_TIME_IMMEDIATE)
243	{
244		/*
245		**  Filling the buffer will take time and we are wanted to
246		**  return immediately. So...
247		*/
248
249		errno = EAGAIN;
250		return NULL;
251	}
252	/*  Okay, this is where we set the timeout.  */
253	if (timeout != SM_TIME_FOREVER)
254	{
255		if (setjmp(ReopenTimeOut) != 0)
256		{
257			errno = EAGAIN;
258			return NULL;
259		}
260
261		evt = sm_seteventm(timeout, reopenalrm, 0);
262	}
263
264	/*
265	**  There are actually programs that depend on being able to "reopen"
266	**  descriptors that weren't originally open.  Keep this from breaking.
267	**  Remember whether the stream was open to begin with, and which file
268	**  descriptor (if any) was associated with it.  If it was attached to
269	**  a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin)
270	**  should work.  This is unnecessary if it was not a Unix file.
271	*/
272
273	if (fp != NULL)
274	{
275		if (fp->sm_magic != SmFileMagic)
276			fp->f_flags = SMFEOF;	/* hold on to it */
277		else
278		{
279			/* flush the stream; ANSI doesn't require this. */
280			(void) sm_io_flush(fp, SM_TIME_FOREVER);
281			(void) sm_io_close(fp, SM_TIME_FOREVER);
282		}
283	}
284
285	fp2 = sm_fp(type, ioflags, fp);
286	ret = (*fp2->f_open)(fp2, info, flags, rpool);
287
288	/*  We're back. So undo our timeout and handler */
289	if (evt != NULL)
290		sm_clrevent(evt);
291
292	if (ret < 0)
293	{
294		fp2->f_flags = 0;	/* release */
295		fp2->sm_magic = NULL;	/* release */
296		return NULL;
297	}
298
299	/*
300	**  We're not preserving this logic (below) for sm_io because it is now
301	**  abstracted at least one "layer" away. By closing and reopening
302	**  the 1st fd used should be the just released one (when Unix
303	**  behavior followed). Old comment::
304	**  If reopening something that was open before on a real file, try
305	**  to maintain the descriptor.  Various C library routines (perror)
306	**  assume stderr is always fd STDERR_FILENO, even if being reopen'd.
307	*/
308
309#if SM_RPOOL
310	if (rpool != NULL)
311		sm_rpool_attach_x(rpool, sm_io_close, fp2);
312#endif /* SM_RPOOL */
313
314	return fp2;
315}
316/*
317**  SM_IO_AUTOFLUSH -- link another file to this for auto-flushing
318**
319**	When a read occurs on fp, fp2 will be flushed iff there is no
320**	data waiting on fp.
321**
322**	Parameters:
323**		fp -- the file opened for reading.
324**		fp2 -- the file opened for writing.
325**
326**	Returns:
327**		The old flush file pointer.
328*/
329
330SM_FILE_T *
331sm_io_autoflush(fp, fp2)
332	SM_FILE_T *fp;
333	SM_FILE_T *fp2;
334{
335	SM_FILE_T *savefp;
336
337	SM_REQUIRE_ISA(fp, SmFileMagic);
338	if (fp2 != NULL)
339		SM_REQUIRE_ISA(fp2, SmFileMagic);
340
341	savefp = fp->f_flushfp;
342	fp->f_flushfp = fp2;
343	return savefp;
344}
345/*
346**  SM_IO_AUTOMODE -- link another file to this for auto-moding
347**
348**	When the mode (blocking or non-blocking) changes for fp1 then
349**	update fp2's mode at the same time. This is to be used when
350**	a system dup() has generated a second file descriptor for
351**	another sm_io_open() by file descriptor. The modes have been
352**	linked in the system and this formalizes it for sm_io internally.
353**
354**	Parameters:
355**		fp1 -- the first file
356**		fp2 -- the second file
357**
358**	Returns:
359**		nothing
360*/
361
362void
363sm_io_automode(fp1, fp2)
364	SM_FILE_T *fp1;
365	SM_FILE_T *fp2;
366{
367	SM_REQUIRE_ISA(fp1, SmFileMagic);
368	SM_REQUIRE_ISA(fp2, SmFileMagic);
369
370	fp1->f_modefp = fp2;
371	fp2->f_modefp = fp1;
372}
373