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.61 2004/08/03 20:17:38 ca Exp $") 19 #include <errno.h> 20 #include <setjmp.h> 21 #include <sys/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 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 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 * 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 * 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 * 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 * 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 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