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