xref: /illumos-gate/usr/src/contrib/ast/src/lib/libast/sfio/sfmode.c (revision b30d193948be5a7794d7ae3ba0ed9c2f72c88e0f)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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->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
_sfsetpool(Sfio_t * f)115 int _sfsetpool(Sfio_t* f)
116 #else
117 int _sfsetpool(f)
118 Sfio_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 
163 done:
164 	POOLMTXRETURN(p, rv);
165 }
166 
167 /* create an auxiliary buffer for sfgetr/sfreserve/sfputr */
168 #if __STD_C
_sfrsrv(reg Sfio_t * f,reg ssize_t size)169 Sfrsrv_t* _sfrsrv(reg Sfio_t* f, reg ssize_t size)
170 #else
171 Sfrsrv_t* _sfrsrv(f,size)
172 reg Sfio_t*	f;
173 reg 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
ignoresig(int sig)203 static void ignoresig(int sig)
204 #else
205 static void ignoresig(sig)
206 int sig;
207 #endif
208 {
209 	signal(sig, ignoresig);
210 }
211 #endif
212 
213 #if __STD_C
_sfpopen(reg Sfio_t * f,int fd,int pid,int stdio)214 int _sfpopen(reg Sfio_t* f, int fd, int pid, int stdio)
215 #else
216 int _sfpopen(f, fd, pid, stdio)
217 reg Sfio_t*	f;
218 int		fd;
219 int		pid;
220 int		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
_sfpclose(reg Sfio_t * f)254 int _sfpclose(reg Sfio_t* f)
255 #else
256 int _sfpclose(f)
257 reg 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 		status = -1;
282 		while ((pid = waitpid(p->pid,&status,0)) == -1 && errno == EINTR)
283 			;
284 #if _PACKAGE_ast
285 		status = status == -1 ?
286 			 EXIT_QUIT :
287 			 WIFSIGNALED(status) ?
288 			 EXIT_TERM(WTERMSIG(status)) :
289 			 EXIT_CODE(WEXITSTATUS(status));
290 		sigcritical(0);
291 #endif
292 
293 #ifdef SIGPIPE
294 		(void)vtmtxlock(_Sfmutex);
295 		if(p->sigp && (_Sfsigp -= 1) <= 0)
296 		{	Sfsignal_f	handler;
297 			if((handler = signal(SIGPIPE,SIG_DFL)) != SIG_DFL &&
298 			   handler != ignoresig)
299 				signal(SIGPIPE,handler); /* honor user handler */
300 			_Sfsigp = 0;
301 		}
302 		(void)vtmtxunlock(_Sfmutex);
303 #endif
304 	}
305 
306 	free(p);
307 	return status;
308 }
309 
310 #if __STD_C
_sfpmode(Sfio_t * f,int type)311 static int _sfpmode(Sfio_t* f, int type)
312 #else
313 static int _sfpmode(f,type)
314 Sfio_t*	f;
315 int	type;
316 #endif
317 {
318 	Sfproc_t*	p;
319 
320 	if(!(p = f->proc) )
321 		return -1;
322 
323 	if(type == SF_WRITE)
324 	{	/* save unread data */
325 		p->ndata = f->endb-f->next;
326 		if(p->ndata > p->size)
327 		{	if(p->rdata)
328 				free((char*)p->rdata);
329 			if((p->rdata = (uchar*)malloc(p->ndata)) )
330 				p->size = p->ndata;
331 			else
332 			{	p->size = 0;
333 				return -1;
334 			}
335 		}
336 		if(p->ndata > 0)
337 			memcpy((Void_t*)p->rdata,(Void_t*)f->next,p->ndata);
338 		f->endb = f->data;
339 	}
340 	else
341 	{	/* restore read data */
342 		if(p->ndata > f->size)	/* may lose data!!! */
343 			p->ndata = f->size;
344 		if(p->ndata > 0)
345 		{	memcpy((Void_t*)f->data,(Void_t*)p->rdata,p->ndata);
346 			f->endb = f->data+p->ndata;
347 			p->ndata = 0;
348 		}
349 	}
350 
351 	/* switch file descriptor */
352 	if(p->pid >= 0)
353 	{	type = f->file;
354 		f->file = p->file;
355 		p->file = type;
356 	}
357 
358 	return 0;
359 }
360 
361 #if __STD_C
_sfmode(reg Sfio_t * f,reg int wanted,reg int local)362 int _sfmode(reg Sfio_t* f, reg int wanted, reg int local)
363 #else
364 int _sfmode(f, wanted, local)
365 reg Sfio_t*	f;	/* change r/w mode and sync file pointer for this stream */
366 reg int		wanted;	/* desired mode */
367 reg int		local;	/* a local call */
368 #endif
369 {
370 	reg int	n;
371 	Sfoff_t	addr;
372 	reg int	rv = 0;
373 
374 	SFONCE();	/* initialize mutexes */
375 
376 	if(wanted&SF_SYNCED) /* for (SF_SYNCED|SF_READ) stream, just junk data */
377 	{	wanted &= ~SF_SYNCED;
378 		if((f->mode&(SF_SYNCED|SF_READ)) == (SF_SYNCED|SF_READ) )
379 		{	f->next = f->endb = f->endr = f->data;
380 			f->mode &= ~SF_SYNCED;
381 		}
382 	}
383 
384 	if((!local && SFFROZEN(f)) || (!(f->flags&SF_STRING) && f->file < 0))
385 	{	if(local || !f->disc || !f->disc->exceptf)
386 		{	local = 1;
387 			goto err_notify;
388 		}
389 
390 		for(;;)
391 		{	if((rv = (*f->disc->exceptf)(f,SF_LOCKED,0,f->disc)) < 0)
392 				return rv;
393 			if((!local && SFFROZEN(f)) ||
394 			   (!(f->flags&SF_STRING) && f->file < 0) )
395 			{	if(rv == 0)
396 				{	local = 1;
397 					goto err_notify;
398 				}
399 				else	continue;
400 			}
401 			else	break;
402 		}
403 	}
404 
405 	if(f->mode&SF_GETR)
406 	{	f->mode &= ~SF_GETR;
407 #ifdef MAP_TYPE
408 		if((f->bits&SF_MMAP) && (f->tiny[0] += 1) >= (4*SF_NMAP) )
409 		{	/* turn off mmap to avoid page faulting */
410 			sfsetbuf(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
411 			f->tiny[0] = 0;
412 		}
413 		else
414 #endif
415 		if(f->getr)
416 		{	f->next[-1] = f->getr;
417 			f->getr = 0;
418 		}
419 	}
420 
421 	if(f->mode&SF_STDIO) /* synchronizing with stdio pointers */
422 		(*_Sfstdsync)(f);
423 
424 	if(f->disc == _Sfudisc && wanted == SF_WRITE &&
425 	   sfclose((*_Sfstack)(f,NIL(Sfio_t*))) < 0 )
426 	{	local = 1;
427 		goto err_notify;
428 	}
429 
430 	if(f->mode&SF_POOL)
431 	{	/* move to head of pool */
432 		if(f == f->pool->sf[0] || (*_Sfpmove)(f,0) < 0 )
433 		{	local = 1;
434 			goto err_notify;
435 		}
436 		f->mode &= ~SF_POOL;
437 	}
438 
439 	SFLOCK(f,local);
440 
441 	/* buffer initialization */
442 	wanted &= SF_RDWR;
443 	if(f->mode&SF_INIT)
444 	{
445 		if(!f->pool && _sfsetpool(f) < 0)
446 		{	rv = -1;
447 			goto done;
448 		}
449 
450 		if(wanted == 0)
451 			goto done;
452 
453 		if(wanted != (int)(f->mode&SF_RDWR) && !(f->flags&wanted) )
454 			goto err_notify;
455 
456 		if((f->flags&SF_STRING) && f->size >= 0 && f->data)
457 		{	f->mode &= ~SF_INIT;
458 			f->extent = ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ?
459 					f->size : 0;
460 			f->here = 0;
461 			f->endb = f->data + f->size;
462 			f->next = f->endr = f->endw = f->data;
463 			if(f->mode&SF_READ)
464 				f->endr = f->endb;
465 			else	f->endw = f->endb;
466 		}
467 		else
468 		{	n = f->flags;
469 			(void)SFSETBUF(f,f->data,f->size);
470 			f->flags |= (n&SF_MALLOC);
471 		}
472 	}
473 
474 	if(wanted == (int)SFMODE(f,1))
475 		goto done;
476 
477 	switch(SFMODE(f,1))
478 	{
479 	case SF_WRITE: /* switching to SF_READ */
480 		if(wanted == 0 || wanted == SF_WRITE)
481 			break;
482 		if(!(f->flags&SF_READ) )
483 			goto err_notify;
484 		else if(f->flags&SF_STRING)
485 		{	SFSTRSIZE(f);
486 			f->endb = f->data+f->extent;
487 			f->mode = SF_READ;
488 			break;
489 		}
490 
491 		/* reset buffer */
492 		if(f->next > f->data && SFFLSBUF(f,-1) < 0)
493 			goto err_notify;
494 
495 		if(f->size == 0)
496 		{	/* unbuffered */
497 			f->data = f->tiny;
498 			f->size = sizeof(f->tiny);
499 		}
500 		f->next = f->endr = f->endw = f->endb = f->data;
501 		f->mode = SF_READ|SF_LOCK;
502 
503 		/* restore saved read data for coprocess */
504 		if(f->proc && _sfpmode(f,wanted) < 0)
505 			goto err_notify;
506 
507 		break;
508 
509 	case (SF_READ|SF_SYNCED): /* a previously sync-ed read stream */
510 		if(wanted != SF_WRITE)
511 		{	/* just reset the pointers */
512 			f->mode = SF_READ|SF_LOCK;
513 
514 			/* see if must go with new physical location */
515 			if((f->flags&(SF_SHARE|SF_PUBLIC)) == (SF_SHARE|SF_PUBLIC) &&
516 			   (addr = SFSK(f,0,SEEK_CUR,f->disc)) != f->here)
517 			{
518 #ifdef MAP_TYPE
519 				if((f->bits&SF_MMAP) && f->data)
520 				{	SFMUNMAP(f,f->data,f->endb-f->data);
521 					f->data = NIL(uchar*);
522 				}
523 #endif
524 				f->endb = f->endr = f->endw = f->next = f->data;
525 				f->here = addr;
526 			}
527 			else
528 			{	addr = f->here + (f->endb - f->next);
529 				if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
530 					goto err_notify;
531 				f->here = addr;
532 			}
533 
534 			break;
535 		}
536 		/* fall thru */
537 
538 	case SF_READ: /* switching to SF_WRITE */
539 		if(wanted != SF_WRITE)
540 			break;
541 		else if(!(f->flags&SF_WRITE))
542 			goto err_notify;
543 		else if(f->flags&SF_STRING)
544 		{	f->endb = f->data+f->size;
545 			f->mode = SF_WRITE|SF_LOCK;
546 			break;
547 		}
548 
549 		/* save unread data before switching mode */
550 		if(f->proc && _sfpmode(f,wanted) < 0)
551 			goto err_notify;
552 
553 		/* reset buffer and seek pointer */
554 		if(!(f->mode&SF_SYNCED) )
555 		{	n = f->endb - f->next;
556 			if(f->extent >= 0 && (n > 0 || (f->data && (f->bits&SF_MMAP))) )
557 			{	/* reset file pointer */
558 				addr = f->here - n;
559 				if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
560 					goto err_notify;
561 				f->here = addr;
562 			}
563 		}
564 
565 		f->mode = SF_WRITE|SF_LOCK;
566 #ifdef MAP_TYPE
567 		if(f->bits&SF_MMAP)
568 		{	if(f->data)
569 				SFMUNMAP(f,f->data,f->endb-f->data);
570 			(void)SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
571 		}
572 #endif
573 		if(f->data == f->tiny)
574 		{	f->endb = f->data = f->next = NIL(uchar*);
575 			f->size = 0;
576 		}
577 		else	f->endb = (f->next = f->data) + f->size;
578 
579 		break;
580 
581 	default: /* unknown case */
582 	err_notify:
583 		if((wanted &= SF_RDWR) == 0 && (wanted = f->flags&SF_RDWR) == SF_RDWR)
584 			wanted = SF_READ;
585 
586 		/* set errno for operations that access wrong stream type */
587 		if(wanted != (f->mode&SF_RDWR) && f->file >= 0)
588 			errno = EBADF;
589 
590 		if(_Sfnotify) /* notify application of the error */
591 			(*_Sfnotify)(f, wanted, (void*)((long)f->file));
592 
593 		rv = -1;
594 		break;
595 	}
596 
597 done:
598 	SFOPEN(f,local);
599 	return rv;
600 }
601