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