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 #include <sm/gen.h>
16 SM_RCSID("@(#)$Id: fopen.c,v 1.62 2005/06/14 23:07:20 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
openalrm(sig)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
reopenalrm(sig)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 dependant)
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 *
sm_io_open(type,timeout,info,flags,rpool)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 /* 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
182 SM_FILE_T *
sm_io_dup(fp)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 -- 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
220 SM_FILE_T *
sm_io_reopen(type,timeout,info,flags,rpool,fp)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 /* 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
332 SM_FILE_T *
sm_io_autoflush(fp,fp2)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
sm_io_automode(fp1,fp2)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