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