xref: /freebsd/contrib/sendmail/libsm/fopen.c (revision 42c159fe388a3765f69860c84183700af37aca8a)
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>
16 SM_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 
27 extern int      sm_io_fclose __P((SM_FILE_T *));
28 
29 static 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 */
51 static void
52 openalrm(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 */
77 static void
78 reopenalrm(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 
104 SM_FILE_T *
105 sm_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 
180 SM_FILE_T *
181 sm_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 
218 SM_FILE_T *
219 sm_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 
330 SM_FILE_T *
331 sm_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 
362 void
363 sm_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