xref: /freebsd/contrib/sendmail/libsm/fopen.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /*
2  * Copyright (c) 2000-2002, 2004 Proofpoint, 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.63 2013-11-22 20:51:42 ca Exp $")
17 #include <errno.h>
18 #include <setjmp.h>
19 #include <sm/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 static void	openalrm __P((int));
28 static void	reopenalrm __P((int));
29 extern int      sm_io_fclose __P((SM_FILE_T *));
30 
31 static jmp_buf OpenTimeOut, ReopenTimeOut;
32 
33 /*
34 **  OPENALRM -- handler when timeout activated for sm_io_open()
35 **
36 **  Returns flow of control to where setjmp(OpenTimeOut) was set.
37 **
38 **	Parameters:
39 **		sig -- unused
40 **
41 **	Returns:
42 **		does not return
43 **
44 **	Side Effects:
45 **		returns flow of control to setjmp(OpenTimeOut).
46 **
47 **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
48 **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
49 **		DOING.
50 */
51 
52 /* ARGSUSED0 */
53 static void
54 openalrm(sig)
55 	int sig;
56 {
57 	longjmp(OpenTimeOut, 1);
58 }
59 /*
60 **  REOPENALRM -- handler when timeout activated for sm_io_reopen()
61 **
62 **  Returns flow of control to where setjmp(ReopenTimeOut) was set.
63 **
64 **	Parameters:
65 **		sig -- unused
66 **
67 **	Returns:
68 **		does not return
69 **
70 **	Side Effects:
71 **		returns flow of control to setjmp(ReopenTimeOut).
72 **
73 **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
74 **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
75 **		DOING.
76 */
77 
78 /* ARGSUSED0 */
79 static void
80 reopenalrm(sig)
81 	int sig;
82 {
83 	longjmp(ReopenTimeOut, 1);
84 }
85 
86 /*
87 **  SM_IO_OPEN -- open a file of a specific type
88 **
89 **	Parameters:
90 **		type -- type of file to open
91 **		timeout -- time to complete the open
92 **		info -- info describing what is to be opened (type dependent)
93 **		flags -- user selected flags
94 **		rpool -- pointer to rpool to be used for this open
95 **
96 **	Returns:
97 **		Raises exception on heap exhaustion.
98 **		Aborts if type is invalid.
99 **		Returns NULL and sets errno
100 **			- when the type specific open fails
101 **			- when open vector errors
102 **			- when flags not set or invalid
103 **		Success returns a file pointer to the opened file type.
104 */
105 
106 SM_FILE_T *
107 sm_io_open(type, timeout, info, flags, rpool)
108 	const SM_FILE_T *type;
109 	int SM_NONVOLATILE timeout;	/* this is not the file type timeout */
110 	const void *info;
111 	int flags;
112 	const void *rpool;
113 {
114 	register SM_FILE_T *fp;
115 	int ioflags;
116 	SM_EVENT *evt = NULL;
117 
118 	ioflags = sm_flags(flags);
119 
120 	if (ioflags == 0)
121 	{
122 		/* must give some indication/intent */
123 		errno = EINVAL;
124 		return NULL;
125 	}
126 
127 	if (timeout == SM_TIME_DEFAULT)
128 		timeout = SM_TIME_FOREVER;
129 	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
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 
182 SM_FILE_T *
183 sm_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 -- information 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 
220 SM_FILE_T *
221 sm_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
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 
332 SM_FILE_T *
333 sm_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 
364 void
365 sm_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