xref: /illumos-gate/usr/src/cmd/sendmail/libsm/fopen.c (revision 49218d4f)
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>
18 SM_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 
29 static void	openalrm __P((int));
30 static void	reopenalrm __P((int));
31 extern int      sm_io_fclose __P((SM_FILE_T *));
32 
33 static 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 */
55 static void
56 openalrm(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 */
81 static void
82 reopenalrm(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 
108 SM_FILE_T *
109 sm_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 
184 SM_FILE_T *
185 sm_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 
222 SM_FILE_T *
223 sm_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 
334 SM_FILE_T *
335 sm_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 
366 void
367 sm_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