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
openalrm(sig)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
reopenalrm(sig)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 *
sm_io_open(type,timeout,info,flags,rpool)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 *
sm_io_dup(fp)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 *
sm_io_reopen(type,timeout,info,flags,rpool,fp)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 *
sm_io_autoflush(fp,fp2)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
sm_io_automode(fp1,fp2)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