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