fopen.c revision 98121
1/* 2 * Copyright (c) 2000-2002 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> 16SM_RCSID("@(#)$Id: fopen.c,v 1.60 2002/01/07 21:41:35 ca Exp $") 17#include <errno.h> 18#include <setjmp.h> 19#include <sys/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 27extern int sm_io_fclose __P((SM_FILE_T *)); 28 29static jmp_buf OpenTimeOut, ReopenTimeOut; 30 31/* 32** OPENALRM -- handler when timeout activated for sm_io_open() 33** 34** Returns flow of control to where setjmp(OpenTimeOut) was set. 35** 36** Parameters: 37** sig -- unused 38** 39** Returns: 40** does not return 41** 42** Side Effects: 43** returns flow of control to setjmp(OpenTimeOut). 44** 45** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 46** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 47** DOING. 48*/ 49 50/* ARGSUSED0 */ 51static void 52openalrm(sig) 53 int sig; 54{ 55 longjmp(OpenTimeOut, 1); 56} 57/* 58** REOPENALRM -- handler when timeout activated for sm_io_reopen() 59** 60** Returns flow of control to where setjmp(ReopenTimeOut) was set. 61** 62** Parameters: 63** sig -- unused 64** 65** Returns: 66** does not return 67** 68** Side Effects: 69** returns flow of control to setjmp(ReopenTimeOut). 70** 71** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 72** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 73** DOING. 74*/ 75 76/* ARGSUSED0 */ 77static void 78reopenalrm(sig) 79 int sig; 80{ 81 longjmp(ReopenTimeOut, 1); 82} 83 84/* 85** SM_IO_OPEN -- open a file of a specific type 86** 87** Parameters: 88** type -- type of file to open 89** timeout -- time to complete the open 90** info -- info describing what is to be opened (type dependant) 91** flags -- user selected flags 92** rpool -- pointer to rpool to be used for this open 93** 94** Returns: 95** Raises exception on heap exhaustion. 96** Aborts if type is invalid. 97** Returns NULL and sets errno 98** - when the type specific open fails 99** - when open vector errors 100** - when flags not set or invalid 101** Success returns a file pointer to the opened file type. 102*/ 103 104SM_FILE_T * 105sm_io_open(type, timeout, info, flags, rpool) 106 const SM_FILE_T *type; 107 int SM_NONVOLATILE timeout; /* this is not the file type timeout */ 108 const void *info; 109 int flags; 110 const void *rpool; 111{ 112 register SM_FILE_T *fp; 113 int ioflags; 114 SM_EVENT *evt = NULL; 115 116 ioflags = sm_flags(flags); 117 118 if (ioflags == 0) 119 { 120 /* must give some indication/intent */ 121 errno = EINVAL; 122 return NULL; 123 } 124 125 if (timeout == SM_TIME_DEFAULT) 126 timeout = SM_TIME_FOREVER; 127 if (timeout == SM_TIME_IMMEDIATE) 128 { 129 errno = EAGAIN; 130 return NULL; 131 } 132 133 fp = sm_fp(type, ioflags, NULL); 134 135 /* Okay, this is where we set the timeout. */ 136 if (timeout != SM_TIME_FOREVER) 137 { 138 if (setjmp(OpenTimeOut) != 0) 139 { 140 errno = EAGAIN; 141 return NULL; 142 } 143 evt = sm_seteventm(timeout, openalrm, 0); 144 } 145 146 if ((*fp->f_open)(fp, info, flags, rpool) < 0) 147 { 148 fp->f_flags = 0; /* release */ 149 fp->sm_magic = NULL; /* release */ 150 return NULL; 151 } 152 153 /* We're back. So undo our timeout and handler */ 154 if (evt != NULL) 155 sm_clrevent(evt); 156 157#if SM_RPOOL 158 if (rpool != NULL) 159 sm_rpool_attach_x(rpool, sm_io_fclose, fp); 160#endif /* SM_RPOOL */ 161 162 return fp; 163} 164/* 165** SM_IO_DUP -- duplicate a file pointer 166** 167** Parameters: 168** fp -- file pointer to duplicate 169** 170** Returns: 171** Success - the duplicated file pointer 172** Failure - NULL (was an invalid file pointer or too many open) 173** 174** Increments the duplicate counter (dup_cnt) for the open file pointer. 175** The counter counts the number of duplicates. When the duplicate 176** counter is 0 (zero) then the file pointer is the only one left 177** (no duplicates, it is the only one). 178*/ 179 180SM_FILE_T * 181sm_io_dup(fp) 182 SM_FILE_T *fp; 183{ 184 185 SM_REQUIRE_ISA(fp, SmFileMagic); 186 if (fp->sm_magic != SmFileMagic) 187 { 188 errno = EBADF; 189 return NULL; 190 } 191 if (fp->f_dup_cnt >= INT_MAX - 1) 192 { 193 /* Can't let f_dup_cnt wrap! */ 194 errno = EMFILE; 195 return NULL; 196 } 197 fp->f_dup_cnt++; 198 return fp; 199} 200/* 201** SM_IO_REOPEN -- open a new file using the old file pointer 202** 203** Parameters: 204** type -- file type to be opened 205** timeout -- time to complete the reopen 206** info -- infomation about what is to be "re-opened" (type dep.) 207** flags -- user flags to map to internal flags 208** rpool -- rpool file to be associated with 209** fp -- the file pointer to reuse 210** 211** Returns: 212** Raises an exception on heap exhaustion. 213** Aborts if type is invalid. 214** Failure: returns NULL 215** Success: returns "reopened" file pointer 216*/ 217 218SM_FILE_T * 219sm_io_reopen(type, timeout, info, flags, rpool, fp) 220 const SM_FILE_T *type; 221 int SM_NONVOLATILE timeout; 222 const void *info; 223 int flags; 224 const void *rpool; 225 SM_FILE_T *fp; 226{ 227 int ioflags, ret; 228 SM_FILE_T *fp2; 229 SM_EVENT *evt = NULL; 230 231 if ((ioflags = sm_flags(flags)) == 0) 232 { 233 (void) sm_io_close(fp, timeout); 234 return NULL; 235 } 236 237 if (!Sm_IO_DidInit) 238 sm_init(); 239 240 if (timeout == SM_TIME_DEFAULT) 241 timeout = SM_TIME_FOREVER; 242 if (timeout == SM_TIME_IMMEDIATE) 243 { 244 /* 245 ** Filling the buffer will take time and we are wanted to 246 ** return immediately. So... 247 */ 248 249 errno = EAGAIN; 250 return NULL; 251 } 252 /* Okay, this is where we set the timeout. */ 253 if (timeout != SM_TIME_FOREVER) 254 { 255 if (setjmp(ReopenTimeOut) != 0) 256 { 257 errno = EAGAIN; 258 return NULL; 259 } 260 261 evt = sm_seteventm(timeout, reopenalrm, 0); 262 } 263 264 /* 265 ** There are actually programs that depend on being able to "reopen" 266 ** descriptors that weren't originally open. Keep this from breaking. 267 ** Remember whether the stream was open to begin with, and which file 268 ** descriptor (if any) was associated with it. If it was attached to 269 ** a descriptor, defer closing it; reopen("/dev/stdin", "r", stdin) 270 ** should work. This is unnecessary if it was not a Unix file. 271 */ 272 273 if (fp != NULL) 274 { 275 if (fp->sm_magic != SmFileMagic) 276 fp->f_flags = SMFEOF; /* hold on to it */ 277 else 278 { 279 /* flush the stream; ANSI doesn't require this. */ 280 (void) sm_io_flush(fp, SM_TIME_FOREVER); 281 (void) sm_io_close(fp, SM_TIME_FOREVER); 282 } 283 } 284 285 fp2 = sm_fp(type, ioflags, fp); 286 ret = (*fp2->f_open)(fp2, info, flags, rpool); 287 288 /* We're back. So undo our timeout and handler */ 289 if (evt != NULL) 290 sm_clrevent(evt); 291 292 if (ret < 0) 293 { 294 fp2->f_flags = 0; /* release */ 295 fp2->sm_magic = NULL; /* release */ 296 return NULL; 297 } 298 299 /* 300 ** We're not preserving this logic (below) for sm_io because it is now 301 ** abstracted at least one "layer" away. By closing and reopening 302 ** the 1st fd used should be the just released one (when Unix 303 ** behavior followed). Old comment:: 304 ** If reopening something that was open before on a real file, try 305 ** to maintain the descriptor. Various C library routines (perror) 306 ** assume stderr is always fd STDERR_FILENO, even if being reopen'd. 307 */ 308 309#if SM_RPOOL 310 if (rpool != NULL) 311 sm_rpool_attach_x(rpool, sm_io_close, fp2); 312#endif /* SM_RPOOL */ 313 314 return fp2; 315} 316/* 317** SM_IO_AUTOFLUSH -- link another file to this for auto-flushing 318** 319** When a read occurs on fp, fp2 will be flushed iff there is no 320** data waiting on fp. 321** 322** Parameters: 323** fp -- the file opened for reading. 324** fp2 -- the file opened for writing. 325** 326** Returns: 327** The old flush file pointer. 328*/ 329 330SM_FILE_T * 331sm_io_autoflush(fp, fp2) 332 SM_FILE_T *fp; 333 SM_FILE_T *fp2; 334{ 335 SM_FILE_T *savefp; 336 337 SM_REQUIRE_ISA(fp, SmFileMagic); 338 if (fp2 != NULL) 339 SM_REQUIRE_ISA(fp2, SmFileMagic); 340 341 savefp = fp->f_flushfp; 342 fp->f_flushfp = fp2; 343 return savefp; 344} 345/* 346** SM_IO_AUTOMODE -- link another file to this for auto-moding 347** 348** When the mode (blocking or non-blocking) changes for fp1 then 349** update fp2's mode at the same time. This is to be used when 350** a system dup() has generated a second file descriptor for 351** another sm_io_open() by file descriptor. The modes have been 352** linked in the system and this formalizes it for sm_io internally. 353** 354** Parameters: 355** fp1 -- the first file 356** fp2 -- the second file 357** 358** Returns: 359** nothing 360*/ 361 362void 363sm_io_automode(fp1, fp2) 364 SM_FILE_T *fp1; 365 SM_FILE_T *fp2; 366{ 367 SM_REQUIRE_ISA(fp1, SmFileMagic); 368 SM_REQUIRE_ISA(fp2, SmFileMagic); 369 370 fp1->f_modefp = fp2; 371 fp2->f_modefp = fp1; 372} 373