1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #include "sfhdr.h" 23 static char* Version = "\n@(#)$Id: sfio (AT&T Research) 2008-07-17 $\0\n"; 24 25 /* Functions to set a given stream to some desired mode 26 ** 27 ** Written by Kiem-Phong Vo. 28 ** 29 ** Modifications: 30 ** 06/27/1990 (first version) 31 ** 01/06/1991 32 ** 07/08/1991 33 ** 06/18/1992 34 ** 02/02/1993 35 ** 05/25/1993 36 ** 02/07/1994 37 ** 05/21/1996 38 ** 08/01/1997 39 ** 08/01/1998 (extended formatting) 40 ** 09/09/1999 (thread-safe) 41 ** 02/01/2001 (adaptive buffering) 42 ** 05/31/2002 (multi-byte handling in sfvprintf/vscanf) 43 ** 09/06/2002 (SF_IOINTR flag) 44 ** 11/15/2002 (%#c for sfvprintf) 45 ** 05/31/2003 (sfsetbuf(f,f,align_size) to set alignment for data) 46 ** (%I1d is fixed to handle "signed char" correctly) 47 ** 01/01/2004 Porting issues to various platforms resolved. 48 ** 06/01/2008 Allowing notify() at entering/exiting thread-safe routines. 49 */ 50 51 /* the below is for protecting the application from SIGPIPE */ 52 #if _PACKAGE_ast 53 #include <sig.h> 54 #include <wait.h> 55 #define Sfsignal_f Sig_handler_t 56 #else 57 #include <signal.h> 58 typedef void(* Sfsignal_f)_ARG_((int)); 59 #endif 60 static int _Sfsigp = 0; /* # of streams needing SIGPIPE protection */ 61 62 /* done at exiting time */ 63 #if __STD_C 64 static void _sfcleanup(void) 65 #else 66 static void _sfcleanup() 67 #endif 68 { 69 reg Sfpool_t* p; 70 reg Sfio_t* f; 71 reg int n; 72 reg int pool; 73 74 f = (Sfio_t*)Version; /* shut compiler warning */ 75 76 /* set this so that no more buffering is allowed for write streams */ 77 _Sfexiting = 1001; 78 79 sfsync(NIL(Sfio_t*)); 80 81 for(p = &_Sfpool; p; p = p->next) 82 { for(n = 0; n < p->n_sf; ++n) 83 { if(!(f = p->sf[n]) || SFFROZEN(f) ) 84 continue; 85 86 SFLOCK(f,0); 87 SFMTXLOCK(f); 88 89 /* let application know that we are leaving */ 90 (void)SFRAISE(f, SF_ATEXIT, NIL(Void_t*)); 91 92 if(f->flags&SF_STRING) 93 continue; 94 95 /* from now on, write streams are unbuffered */ 96 pool = f->mode&SF_POOL; 97 f->mode &= ~SF_POOL; 98 if((f->flags&SF_WRITE) && !(f->mode&SF_WRITE)) 99 (void)_sfmode(f,SF_WRITE,1); 100 if(((f->bits&SF_MMAP) && f->data) || 101 ((f->mode&SF_WRITE) && f->next == f->data) ) 102 (void)SFSETBUF(f,NIL(Void_t*),0); 103 f->mode |= pool; 104 105 SFMTXUNLOCK(f); 106 SFOPEN(f,0); 107 } 108 } 109 } 110 111 /* put into discrete pool */ 112 #if __STD_C 113 int _sfsetpool(Sfio_t* f) 114 #else 115 int _sfsetpool(f) 116 Sfio_t* f; 117 #endif 118 { 119 reg Sfpool_t* p; 120 reg Sfio_t** array; 121 reg int n, rv; 122 123 if(!_Sfcleanup) 124 { _Sfcleanup = _sfcleanup; 125 (void)atexit(_sfcleanup); 126 } 127 128 if(!(p = f->pool) ) 129 p = f->pool = &_Sfpool; 130 131 POOLMTXENTER(p); 132 133 rv = -1; 134 135 if(p->n_sf >= p->s_sf) 136 { if(p->s_sf == 0) /* initialize pool array */ 137 { p->s_sf = sizeof(p->array)/sizeof(p->array[0]); 138 p->sf = p->array; 139 } 140 else /* allocate a larger array */ 141 { n = (p->sf != p->array ? p->s_sf : (p->s_sf/4 + 1)*4) + 4; 142 if(!(array = (Sfio_t**)malloc(n*sizeof(Sfio_t*))) ) 143 goto done; 144 145 /* move old array to new one */ 146 memcpy((Void_t*)array,(Void_t*)p->sf,p->n_sf*sizeof(Sfio_t*)); 147 if(p->sf != p->array) 148 free((Void_t*)p->sf); 149 150 p->sf = array; 151 p->s_sf = n; 152 } 153 } 154 155 /* always add at end of array because if this was done during some sort 156 of walk thru all streams, we'll want the new stream to be seen. 157 */ 158 p->sf[p->n_sf++] = f; 159 rv = 0; 160 161 done: 162 POOLMTXRETURN(p, rv); 163 } 164 165 /* create an auxiliary buffer for sfgetr/sfreserve/sfputr */ 166 #if __STD_C 167 Sfrsrv_t* _sfrsrv(reg Sfio_t* f, reg ssize_t size) 168 #else 169 Sfrsrv_t* _sfrsrv(f,size) 170 reg Sfio_t* f; 171 reg ssize_t size; 172 #endif 173 { 174 Sfrsrv_t *rsrv, *rs; 175 176 /* make buffer if nothing yet */ 177 size = ((size + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN; 178 if(!(rsrv = f->rsrv) || size > rsrv->size) 179 { if(!(rs = (Sfrsrv_t*)malloc(size+sizeof(Sfrsrv_t)))) 180 size = -1; 181 else 182 { if(rsrv) 183 { if(rsrv->slen > 0) 184 memcpy(rs,rsrv,sizeof(Sfrsrv_t)+rsrv->slen); 185 free(rsrv); 186 } 187 f->rsrv = rsrv = rs; 188 rsrv->size = size; 189 rsrv->slen = 0; 190 } 191 } 192 193 if(rsrv && size > 0) 194 rsrv->slen = 0; 195 196 return size >= 0 ? rsrv : NIL(Sfrsrv_t*); 197 } 198 199 #ifdef SIGPIPE 200 #if __STD_C 201 static void ignoresig(int sig) 202 #else 203 static void ignoresig(sig) 204 int sig; 205 #endif 206 { 207 signal(sig, ignoresig); 208 } 209 #endif 210 211 #if __STD_C 212 int _sfpopen(reg Sfio_t* f, int fd, int pid, int stdio) 213 #else 214 int _sfpopen(f, fd, pid, stdio) 215 reg Sfio_t* f; 216 int fd; 217 int pid; 218 int stdio; /* stdio popen() does not reset SIGPIPE handler */ 219 #endif 220 { 221 reg Sfproc_t* p; 222 223 if(f->proc) 224 return 0; 225 226 if(!(p = f->proc = (Sfproc_t*)malloc(sizeof(Sfproc_t))) ) 227 return -1; 228 229 p->pid = pid; 230 p->size = p->ndata = 0; 231 p->rdata = NIL(uchar*); 232 p->file = fd; 233 p->sigp = (!stdio && pid >= 0 && (f->flags&SF_WRITE)) ? 1 : 0; 234 235 #ifdef SIGPIPE /* protect from broken pipe signal */ 236 if(p->sigp) 237 { Sfsignal_f handler; 238 239 (void)vtmtxlock(_Sfmutex); 240 if((handler = signal(SIGPIPE, ignoresig)) != SIG_DFL && 241 handler != ignoresig) 242 signal(SIGPIPE, handler); /* honor user handler */ 243 _Sfsigp += 1; 244 (void)vtmtxunlock(_Sfmutex); 245 } 246 #endif 247 248 return 0; 249 } 250 251 #if __STD_C 252 int _sfpclose(reg Sfio_t* f) 253 #else 254 int _sfpclose(f) 255 reg Sfio_t* f; /* stream to close */ 256 #endif 257 { 258 Sfproc_t* p; 259 int pid, status; 260 261 if(!(p = f->proc)) 262 return -1; 263 f->proc = NIL(Sfproc_t*); 264 265 if(p->rdata) 266 free(p->rdata); 267 268 if(p->pid < 0) 269 status = 0; 270 else 271 { /* close the associated stream */ 272 if(p->file >= 0) 273 CLOSE(p->file); 274 275 /* wait for process termination */ 276 #if _PACKAGE_ast 277 sigcritical(SIG_REG_EXEC|SIG_REG_PROC); 278 #endif 279 while ((pid = waitpid(p->pid,&status,0)) == -1 && errno == EINTR) 280 ; 281 if(pid == -1) 282 status = -1; 283 #if _PACKAGE_ast 284 sigcritical(0); 285 #endif 286 287 #ifdef SIGPIPE 288 (void)vtmtxlock(_Sfmutex); 289 if(p->sigp && (_Sfsigp -= 1) <= 0) 290 { Sfsignal_f handler; 291 if((handler = signal(SIGPIPE,SIG_DFL)) != SIG_DFL && 292 handler != ignoresig) 293 signal(SIGPIPE,handler); /* honor user handler */ 294 _Sfsigp = 0; 295 } 296 (void)vtmtxunlock(_Sfmutex); 297 #endif 298 } 299 300 free(p); 301 return status; 302 } 303 304 #if __STD_C 305 static int _sfpmode(Sfio_t* f, int type) 306 #else 307 static int _sfpmode(f,type) 308 Sfio_t* f; 309 int type; 310 #endif 311 { 312 Sfproc_t* p; 313 314 if(!(p = f->proc) ) 315 return -1; 316 317 if(type == SF_WRITE) 318 { /* save unread data */ 319 p->ndata = f->endb-f->next; 320 if(p->ndata > p->size) 321 { if(p->rdata) 322 free((char*)p->rdata); 323 if((p->rdata = (uchar*)malloc(p->ndata)) ) 324 p->size = p->ndata; 325 else 326 { p->size = 0; 327 return -1; 328 } 329 } 330 if(p->ndata > 0) 331 memcpy((Void_t*)p->rdata,(Void_t*)f->next,p->ndata); 332 f->endb = f->data; 333 } 334 else 335 { /* restore read data */ 336 if(p->ndata > f->size) /* may lose data!!! */ 337 p->ndata = f->size; 338 if(p->ndata > 0) 339 { memcpy((Void_t*)f->data,(Void_t*)p->rdata,p->ndata); 340 f->endb = f->data+p->ndata; 341 p->ndata = 0; 342 } 343 } 344 345 /* switch file descriptor */ 346 if(p->pid >= 0) 347 { type = f->file; 348 f->file = p->file; 349 p->file = type; 350 } 351 352 return 0; 353 } 354 355 #if __STD_C 356 int _sfmode(reg Sfio_t* f, reg int wanted, reg int local) 357 #else 358 int _sfmode(f, wanted, local) 359 reg Sfio_t* f; /* change r/w mode and sync file pointer for this stream */ 360 reg int wanted; /* desired mode */ 361 reg int local; /* a local call */ 362 #endif 363 { 364 reg int n; 365 Sfoff_t addr; 366 reg int rv = 0; 367 368 SFONCE(); /* initialize mutexes */ 369 370 if(wanted&SF_SYNCED) /* for (SF_SYNCED|SF_READ) stream, just junk data */ 371 { wanted &= ~SF_SYNCED; 372 if((f->mode&(SF_SYNCED|SF_READ)) == (SF_SYNCED|SF_READ) ) 373 { f->next = f->endb = f->endr = f->data; 374 f->mode &= ~SF_SYNCED; 375 } 376 } 377 378 if((!local && SFFROZEN(f)) || (!(f->flags&SF_STRING) && f->file < 0)) 379 { if(local || !f->disc || !f->disc->exceptf) 380 { local = 1; 381 goto err_notify; 382 } 383 384 for(;;) 385 { if((rv = (*f->disc->exceptf)(f,SF_LOCKED,0,f->disc)) < 0) 386 return rv; 387 if((!local && SFFROZEN(f)) || 388 (!(f->flags&SF_STRING) && f->file < 0) ) 389 { if(rv == 0) 390 { local = 1; 391 goto err_notify; 392 } 393 else continue; 394 } 395 else break; 396 } 397 } 398 399 if(f->mode&SF_GETR) 400 { f->mode &= ~SF_GETR; 401 #ifdef MAP_TYPE 402 if((f->bits&SF_MMAP) && (f->tiny[0] += 1) >= (4*SF_NMAP) ) 403 { /* turn off mmap to avoid page faulting */ 404 sfsetbuf(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND); 405 f->tiny[0] = 0; 406 } 407 else 408 #endif 409 if(f->getr) 410 { f->next[-1] = f->getr; 411 f->getr = 0; 412 } 413 } 414 415 if(f->mode&SF_STDIO) /* synchronizing with stdio pointers */ 416 (*_Sfstdsync)(f); 417 418 if(f->disc == _Sfudisc && wanted == SF_WRITE && 419 sfclose((*_Sfstack)(f,NIL(Sfio_t*))) < 0 ) 420 { local = 1; 421 goto err_notify; 422 } 423 424 if(f->mode&SF_POOL) 425 { /* move to head of pool */ 426 if(f == f->pool->sf[0] || (*_Sfpmove)(f,0) < 0 ) 427 { local = 1; 428 goto err_notify; 429 } 430 f->mode &= ~SF_POOL; 431 } 432 433 SFLOCK(f,local); 434 435 /* buffer initialization */ 436 wanted &= SF_RDWR; 437 if(f->mode&SF_INIT) 438 { 439 if(!f->pool && _sfsetpool(f) < 0) 440 { rv = -1; 441 goto done; 442 } 443 444 if(wanted == 0) 445 goto done; 446 447 if(wanted != (int)(f->mode&SF_RDWR) && !(f->flags&wanted) ) 448 goto err_notify; 449 450 if((f->flags&SF_STRING) && f->size >= 0 && f->data) 451 { f->mode &= ~SF_INIT; 452 f->extent = ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ? 453 f->size : 0; 454 f->here = 0; 455 f->endb = f->data + f->size; 456 f->next = f->endr = f->endw = f->data; 457 if(f->mode&SF_READ) 458 f->endr = f->endb; 459 else f->endw = f->endb; 460 } 461 else 462 { n = f->flags; 463 (void)SFSETBUF(f,f->data,f->size); 464 f->flags |= (n&SF_MALLOC); 465 } 466 } 467 468 if(wanted == (int)SFMODE(f,1)) 469 goto done; 470 471 switch(SFMODE(f,1)) 472 { 473 case SF_WRITE: /* switching to SF_READ */ 474 if(wanted == 0 || wanted == SF_WRITE) 475 break; 476 if(!(f->flags&SF_READ) ) 477 goto err_notify; 478 else if(f->flags&SF_STRING) 479 { SFSTRSIZE(f); 480 f->endb = f->data+f->extent; 481 f->mode = SF_READ; 482 break; 483 } 484 485 /* reset buffer */ 486 if(f->next > f->data && SFFLSBUF(f,-1) < 0) 487 goto err_notify; 488 489 if(f->size == 0) 490 { /* unbuffered */ 491 f->data = f->tiny; 492 f->size = sizeof(f->tiny); 493 } 494 f->next = f->endr = f->endw = f->endb = f->data; 495 f->mode = SF_READ|SF_LOCK; 496 497 /* restore saved read data for coprocess */ 498 if(f->proc && _sfpmode(f,wanted) < 0) 499 goto err_notify; 500 501 break; 502 503 case (SF_READ|SF_SYNCED): /* a previously sync-ed read stream */ 504 if(wanted != SF_WRITE) 505 { /* just reset the pointers */ 506 f->mode = SF_READ|SF_LOCK; 507 508 /* see if must go with new physical location */ 509 if((f->flags&(SF_SHARE|SF_PUBLIC)) == (SF_SHARE|SF_PUBLIC) && 510 (addr = SFSK(f,0,SEEK_CUR,f->disc)) != f->here) 511 { 512 #ifdef MAP_TYPE 513 if((f->bits&SF_MMAP) && f->data) 514 { SFMUNMAP(f,f->data,f->endb-f->data); 515 f->data = NIL(uchar*); 516 } 517 #endif 518 f->endb = f->endr = f->endw = f->next = f->data; 519 f->here = addr; 520 } 521 else 522 { addr = f->here + (f->endb - f->next); 523 if(SFSK(f,addr,SEEK_SET,f->disc) < 0) 524 goto err_notify; 525 f->here = addr; 526 } 527 528 break; 529 } 530 /* fall thru */ 531 532 case SF_READ: /* switching to SF_WRITE */ 533 if(wanted != SF_WRITE) 534 break; 535 else if(!(f->flags&SF_WRITE)) 536 goto err_notify; 537 else if(f->flags&SF_STRING) 538 { f->endb = f->data+f->size; 539 f->mode = SF_WRITE|SF_LOCK; 540 break; 541 } 542 543 /* save unread data before switching mode */ 544 if(f->proc && _sfpmode(f,wanted) < 0) 545 goto err_notify; 546 547 /* reset buffer and seek pointer */ 548 if(!(f->mode&SF_SYNCED) ) 549 { n = f->endb - f->next; 550 if(f->extent >= 0 && (n > 0 || (f->data && (f->bits&SF_MMAP))) ) 551 { /* reset file pointer */ 552 addr = f->here - n; 553 if(SFSK(f,addr,SEEK_SET,f->disc) < 0) 554 goto err_notify; 555 f->here = addr; 556 } 557 } 558 559 f->mode = SF_WRITE|SF_LOCK; 560 #ifdef MAP_TYPE 561 if(f->bits&SF_MMAP) 562 { if(f->data) 563 SFMUNMAP(f,f->data,f->endb-f->data); 564 (void)SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND); 565 } 566 #endif 567 if(f->data == f->tiny) 568 { f->endb = f->data = f->next = NIL(uchar*); 569 f->size = 0; 570 } 571 else f->endb = (f->next = f->data) + f->size; 572 573 break; 574 575 default: /* unknown case */ 576 err_notify: 577 if((wanted &= SF_RDWR) == 0 && (wanted = f->flags&SF_RDWR) == SF_RDWR) 578 wanted = SF_READ; 579 580 /* set errno for operations that access wrong stream type */ 581 if(wanted != (f->mode&SF_RDWR) && f->file >= 0) 582 errno = EBADF; 583 584 if(_Sfnotify) /* notify application of the error */ 585 (*_Sfnotify)(f, wanted, (void*)((long)f->file)); 586 587 rv = -1; 588 break; 589 } 590 591 done: 592 SFOPEN(f,local); 593 return rv; 594 } 595