fopen.c revision 261194
1230557Sjimharris/*
2230557Sjimharris * Copyright (c) 2000-2002, 2004 Proofpoint, Inc. and its suppliers.
3230557Sjimharris *      All rights reserved.
4230557Sjimharris * Copyright (c) 1990, 1993
5230557Sjimharris *	The Regents of the University of California.  All rights reserved.
6230557Sjimharris *
7230557Sjimharris * This code is derived from software contributed to Berkeley by
8230557Sjimharris * Chris Torek.
9230557Sjimharris *
10230557Sjimharris * By using this file, you agree to the terms and conditions set
11230557Sjimharris * forth in the LICENSE file which can be found at the top level of
12230557Sjimharris * the sendmail distribution.
13230557Sjimharris */
14230557Sjimharris
15230557Sjimharris#include <sm/gen.h>
16230557SjimharrisSM_RCSID("@(#)$Id: fopen.c,v 1.63 2013/11/22 20:51:42 ca Exp $")
17230557Sjimharris#include <errno.h>
18230557Sjimharris#include <setjmp.h>
19230557Sjimharris#include <sm/time.h>
20230557Sjimharris#include <sm/heap.h>
21230557Sjimharris#include <sm/signal.h>
22230557Sjimharris#include <sm/assert.h>
23230557Sjimharris#include <sm/io.h>
24230557Sjimharris#include <sm/clock.h>
25230557Sjimharris#include "local.h"
26230557Sjimharris
27230557Sjimharrisstatic void	openalrm __P((int));
28230557Sjimharrisstatic void	reopenalrm __P((int));
29230557Sjimharrisextern int      sm_io_fclose __P((SM_FILE_T *));
30230557Sjimharris
31230557Sjimharrisstatic jmp_buf OpenTimeOut, ReopenTimeOut;
32230557Sjimharris
33230557Sjimharris/*
34230557Sjimharris**  OPENALRM -- handler when timeout activated for sm_io_open()
35230557Sjimharris**
36230557Sjimharris**  Returns flow of control to where setjmp(OpenTimeOut) was set.
37230557Sjimharris**
38230557Sjimharris**	Parameters:
39230557Sjimharris**		sig -- unused
40230557Sjimharris**
41230557Sjimharris**	Returns:
42230557Sjimharris**		does not return
43230557Sjimharris**
44230557Sjimharris**	Side Effects:
45230557Sjimharris**		returns flow of control to setjmp(OpenTimeOut).
46230557Sjimharris**
47230557Sjimharris**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
48230557Sjimharris**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
49230557Sjimharris**		DOING.
50230557Sjimharris*/
51230557Sjimharris
52230557Sjimharris/* ARGSUSED0 */
53230557Sjimharrisstatic void
54230557Sjimharrisopenalrm(sig)
55230557Sjimharris	int sig;
56230557Sjimharris{
57230557Sjimharris	longjmp(OpenTimeOut, 1);
58230557Sjimharris}
59230557Sjimharris/*
60230557Sjimharris**  REOPENALRM -- handler when timeout activated for sm_io_reopen()
61230557Sjimharris**
62230557Sjimharris**  Returns flow of control to where setjmp(ReopenTimeOut) was set.
63230557Sjimharris**
64230557Sjimharris**	Parameters:
65230557Sjimharris**		sig -- unused
66230557Sjimharris**
67230557Sjimharris**	Returns:
68230557Sjimharris**		does not return
69230557Sjimharris**
70230557Sjimharris**	Side Effects:
71230557Sjimharris**		returns flow of control to setjmp(ReopenTimeOut).
72230557Sjimharris**
73230557Sjimharris**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
74230557Sjimharris**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
75230557Sjimharris**		DOING.
76230557Sjimharris*/
77230557Sjimharris
78230557Sjimharris/* ARGSUSED0 */
79230557Sjimharrisstatic void
80230557Sjimharrisreopenalrm(sig)
81230557Sjimharris	int sig;
82230557Sjimharris{
83230557Sjimharris	longjmp(ReopenTimeOut, 1);
84230557Sjimharris}
85230557Sjimharris
86230557Sjimharris/*
87230557Sjimharris**  SM_IO_OPEN -- open a file of a specific type
88230557Sjimharris**
89230557Sjimharris**	Parameters:
90230557Sjimharris**		type -- type of file to open
91230557Sjimharris**		timeout -- time to complete the open
92230557Sjimharris**		info -- info describing what is to be opened (type dependant)
93230557Sjimharris**		flags -- user selected flags
94230557Sjimharris**		rpool -- pointer to rpool to be used for this open
95230557Sjimharris**
96230557Sjimharris**	Returns:
97230557Sjimharris**		Raises exception on heap exhaustion.
98230557Sjimharris**		Aborts if type is invalid.
99230557Sjimharris**		Returns NULL and sets errno
100230557Sjimharris**			- when the type specific open fails
101230557Sjimharris**			- when open vector errors
102230557Sjimharris**			- when flags not set or invalid
103230557Sjimharris**		Success returns a file pointer to the opened file type.
104230557Sjimharris*/
105230557Sjimharris
106230557SjimharrisSM_FILE_T *
107230557Sjimharrissm_io_open(type, timeout, info, flags, rpool)
108230557Sjimharris	const SM_FILE_T *type;
109230557Sjimharris	int SM_NONVOLATILE timeout;	/* this is not the file type timeout */
110230557Sjimharris	const void *info;
111230557Sjimharris	int flags;
112230557Sjimharris	const void *rpool;
113230557Sjimharris{
114230557Sjimharris	register SM_FILE_T *fp;
115230557Sjimharris	int ioflags;
116230557Sjimharris	SM_EVENT *evt = NULL;
117230557Sjimharris
118230557Sjimharris	ioflags = sm_flags(flags);
119230557Sjimharris
120230557Sjimharris	if (ioflags == 0)
121230557Sjimharris	{
122230557Sjimharris		/* must give some indication/intent */
123230557Sjimharris		errno = EINVAL;
124230557Sjimharris		return NULL;
125230557Sjimharris	}
126230557Sjimharris
127230557Sjimharris	if (timeout == SM_TIME_DEFAULT)
128230557Sjimharris		timeout = SM_TIME_FOREVER;
129230557Sjimharris	if (timeout == SM_TIME_IMMEDIATE)
130	{
131		errno = EAGAIN;
132		return NULL;
133	}
134
135	fp = sm_fp(type, ioflags, NULL);
136
137	/*  Okay, this is where we set the timeout.  */
138	if (timeout != SM_TIME_FOREVER)
139	{
140		if (setjmp(OpenTimeOut) != 0)
141		{
142			errno = EAGAIN;
143			return NULL;
144		}
145		evt = sm_seteventm(timeout, openalrm, 0);
146	}
147
148	if ((*fp->f_open)(fp, info, flags, rpool) < 0)
149	{
150		fp->f_flags = 0;	/* release */
151		fp->sm_magic = NULL;	/* release */
152		return NULL;
153	}
154
155	/*  We're back. So undo our timeout and handler */
156	if (evt != NULL)
157		sm_clrevent(evt);
158
159#if SM_RPOOL
160	if (rpool != NULL)
161		sm_rpool_attach_x(rpool, sm_io_fclose, fp);
162#endif /* SM_RPOOL */
163
164	return fp;
165}
166/*
167**  SM_IO_DUP -- duplicate a file pointer
168**
169**	Parameters:
170**		fp -- file pointer to duplicate
171**
172**	Returns:
173**		Success - the duplicated file pointer
174**		Failure - NULL (was an invalid file pointer or too many open)
175**
176**	Increments the duplicate counter (dup_cnt) for the open file pointer.
177**	The counter counts the number of duplicates. When the duplicate
178**	counter is 0 (zero) then the file pointer is the only one left
179**	(no duplicates, it is the only one).
180*/
181
182SM_FILE_T *
183sm_io_dup(fp)
184	SM_FILE_T *fp;
185{
186
187	SM_REQUIRE_ISA(fp, SmFileMagic);
188	if (fp->sm_magic != SmFileMagic)
189	{
190		errno = EBADF;
191		return NULL;
192	}
193	if (fp->f_dup_cnt >= INT_MAX - 1)
194	{
195		/* Can't let f_dup_cnt wrap! */
196		errno = EMFILE;
197		return NULL;
198	}
199	fp->f_dup_cnt++;
200	return fp;
201}
202/*
203**  SM_IO_REOPEN -- open a new file using the old file pointer
204**
205**	Parameters:
206**		type -- file type to be opened
207**		timeout -- time to complete the reopen
208**		info -- infomation about what is to be "re-opened" (type dep.)
209**		flags -- user flags to map to internal flags
210**		rpool -- rpool file to be associated with
211**		fp -- the file pointer to reuse
212**
213**	Returns:
214**		Raises an exception on heap exhaustion.
215**		Aborts if type is invalid.
216**		Failure: returns NULL
217**		Success: returns "reopened" file pointer
218*/
219
220SM_FILE_T *
221sm_io_reopen(type, timeout, info, flags, rpool, fp)
222	const SM_FILE_T *type;
223	int SM_NONVOLATILE timeout;
224	const void *info;
225	int flags;
226	const void *rpool;
227	SM_FILE_T *fp;
228{
229	int ioflags, ret;
230	SM_FILE_T *fp2;
231	SM_EVENT *evt = NULL;
232
233	if ((ioflags = sm_flags(flags)) == 0)
234	{
235		(void) sm_io_close(fp, timeout);
236		return NULL;
237	}
238
239	if (!Sm_IO_DidInit)
240		sm_init();
241
242	if (timeout == SM_TIME_DEFAULT)
243		timeout = SM_TIME_FOREVER;
244	if (timeout == SM_TIME_IMMEDIATE)
245	{
246		/*
247		**  Filling the buffer will take time and we are wanted to
248		**  return immediately. So...
249		*/
250
251		errno = EAGAIN;
252		return NULL;
253	}
254	/*  Okay, this is where we set the timeout.  */
255	if (timeout != SM_TIME_FOREVER)
256	{
257		if (setjmp(ReopenTimeOut) != 0)
258		{
259			errno = EAGAIN;
260			return NULL;
261		}
262
263		evt = sm_seteventm(timeout, reopenalrm, 0);
264	}
265
266	/*
267	**  There are actually programs that depend on being able to "reopen"
268	**  descriptors that weren't originally open.  Keep this from breaking.
269	**  Remember whether the stream was open to begin with, and which file
270	**  descriptor (if any) was associated with it.  If it was attached to
271	**  a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin)
272	**  should work.  This is unnecessary if it was not a Unix file.
273	*/
274
275	if (fp != NULL)
276	{
277		if (fp->sm_magic != SmFileMagic)
278			fp->f_flags = SMFEOF;	/* hold on to it */
279		else
280		{
281			/* flush the stream; ANSI doesn't require this. */
282			(void) sm_io_flush(fp, SM_TIME_FOREVER);
283			(void) sm_io_close(fp, SM_TIME_FOREVER);
284		}
285	}
286
287	fp2 = sm_fp(type, ioflags, fp);
288	ret = (*fp2->f_open)(fp2, info, flags, rpool);
289
290	/*  We're back. So undo our timeout and handler */
291	if (evt != NULL)
292		sm_clrevent(evt);
293
294	if (ret < 0)
295	{
296		fp2->f_flags = 0;	/* release */
297		fp2->sm_magic = NULL;	/* release */
298		return NULL;
299	}
300
301	/*
302	**  We're not preserving this logic (below) for sm_io because it is now
303	**  abstracted at least one "layer" away. By closing and reopening
304	**  the 1st fd used should be the just released one (when Unix
305	**  behavior followed). Old comment::
306	**  If reopening something that was open before on a real file, try
307	**  to maintain the descriptor.  Various C library routines (perror)
308	**  assume stderr is always fd STDERR_FILENO, even if being reopen'd.
309	*/
310
311#if SM_RPOOL
312	if (rpool != NULL)
313		sm_rpool_attach_x(rpool, sm_io_close, fp2);
314#endif /* SM_RPOOL */
315
316	return fp2;
317}
318/*
319**  SM_IO_AUTOFLUSH -- link another file to this for auto-flushing
320**
321**	When a read occurs on fp, fp2 will be flushed iff there is no
322**	data waiting on fp.
323**
324**	Parameters:
325**		fp -- the file opened for reading.
326**		fp2 -- the file opened for writing.
327**
328**	Returns:
329**		The old flush file pointer.
330*/
331
332SM_FILE_T *
333sm_io_autoflush(fp, fp2)
334	SM_FILE_T *fp;
335	SM_FILE_T *fp2;
336{
337	SM_FILE_T *savefp;
338
339	SM_REQUIRE_ISA(fp, SmFileMagic);
340	if (fp2 != NULL)
341		SM_REQUIRE_ISA(fp2, SmFileMagic);
342
343	savefp = fp->f_flushfp;
344	fp->f_flushfp = fp2;
345	return savefp;
346}
347/*
348**  SM_IO_AUTOMODE -- link another file to this for auto-moding
349**
350**	When the mode (blocking or non-blocking) changes for fp1 then
351**	update fp2's mode at the same time. This is to be used when
352**	a system dup() has generated a second file descriptor for
353**	another sm_io_open() by file descriptor. The modes have been
354**	linked in the system and this formalizes it for sm_io internally.
355**
356**	Parameters:
357**		fp1 -- the first file
358**		fp2 -- the second file
359**
360**	Returns:
361**		nothing
362*/
363
364void
365sm_io_automode(fp1, fp2)
366	SM_FILE_T *fp1;
367	SM_FILE_T *fp2;
368{
369	SM_REQUIRE_ISA(fp1, SmFileMagic);
370	SM_REQUIRE_ISA(fp2, SmFileMagic);
371
372	fp1->f_modefp = fp2;
373	fp2->f_modefp = fp1;
374}
375