Merge from vendor branch SENDMAIL:
[dragonfly.git] / contrib / sendmail-8.13.7 / libsm / fopen.c
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
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 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 *
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 *
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 *
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 *
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 }