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