xref: /titanic_51/usr/src/lib/libast/common/sfio/sfmode.c (revision 8c95159466b445c16660b550063d080ca9868cb5)
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
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
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
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
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
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
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
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
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