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> 16 SM_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 27 extern int sm_io_fclose __P((SM_FILE_T *)); 28 29 static 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 */ 51 static void 52 openalrm(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 */ 77 static void 78 reopenalrm(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 104 SM_FILE_T * 105 sm_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 180 SM_FILE_T * 181 sm_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 218 SM_FILE_T * 219 sm_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 330 SM_FILE_T * 331 sm_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 362 void 363 sm_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