1 /* @(#)fifo.c	1.66 15/04/22 Copyright 1989,1997-2015 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)fifo.c	1.66 15/04/22 Copyright 1989,1997-2015 J. Schilling";
6 #endif
7 /*
8  *	A "fifo" that uses shared memory between two processes
9  *
10  *	The actual code is a mixture of borrowed code from star's fifo.c
11  *	and a proposal from Finn Arne Gangstad <finnag@guardian.no>
12  *	who had the idea to use a ring buffer to handle average size chunks.
13  *
14  *	Copyright (c) 1989,1997-2015 J. Schilling
15  */
16 /*
17  * The contents of this file are subject to the terms of the
18  * Common Development and Distribution License, Version 1.0 only
19  * (the "License").  You may not use this file except in compliance
20  * with the License.
21  *
22  * See the file CDDL.Schily.txt in this distribution for details.
23  * A copy of the CDDL is also available via the Internet at
24  * http://www.opensource.org/licenses/cddl1.txt
25  *
26  * When distributing Covered Code, include this CDDL HEADER in each
27  * file and include the License file CDDL.Schily.txt from this distribution.
28  */
29 
30 #ifndef	DEBUG
31 #define	DEBUG
32 #endif
33 /*#define	XDEBUG*/
34 #include <schily/mconfig.h>
35 #if	defined(HAVE_OS_H) && \
36 	defined(HAVE_CLONE_AREA) && defined(HAVE_CREATE_AREA) && \
37 	defined(HAVE_DELETE_AREA)
38 #include <OS.h>
39 #	define	HAVE_BEOS_AREAS	/* BeOS/Zeta/Haiku */
40 #endif
41 #if	!defined(HAVE_SMMAP) && !defined(HAVE_USGSHM) && \
42 	!defined(HAVE_DOSALLOCSHAREDMEM) && !defined(HAVE_BEOS_AREAS)
43 #undef	FIFO			/* We cannot have a FIFO on this platform */
44 #endif
45 #if	!defined(HAVE_FORK)
46 #undef	FIFO			/* We cannot have a FIFO on this platform */
47 #endif
48 #ifdef	FIFO
49 #if !defined(USE_MMAP) && !defined(USE_USGSHM)
50 #define	USE_MMAP
51 #endif
52 #ifndef	HAVE_SMMAP
53 #	undef	USE_MMAP
54 #	define	USE_USGSHM	/* now SYSV shared memory is the default*/
55 #endif
56 #ifdef	USE_MMAP		/* Only want to have one implementation */
57 #	undef	USE_USGSHM	/* mmap() is preferred			*/
58 #endif
59 
60 #ifdef	HAVE_DOSALLOCSHAREDMEM	/* This is for OS/2 */
61 #	undef	USE_MMAP
62 #	undef	USE_USGSHM
63 #	define	USE_OS2SHM
64 #endif
65 
66 #ifdef	HAVE_BEOS_AREAS		/* This is for BeOS/Zeta */
67 #	undef	USE_MMAP
68 #	undef	USE_USGSHM
69 #	undef	USE_OS2SHM
70 #	define	USE_BEOS_AREAS
71 #endif
72 
73 #include <schily/stdio.h>
74 #include <schily/stdlib.h>
75 #include <schily/unistd.h>	/* includes <sys/types.h> */
76 #include <schily/utypes.h>
77 #include <schily/fcntl.h>
78 #if defined(HAVE_SMMAP) && defined(USE_MMAP)
79 #include <schily/mman.h>
80 #endif
81 #include <schily/wait.h>
82 #include <schily/standard.h>
83 #include <schily/errno.h>
84 #include <schily/signal.h>
85 #include <schily/libport.h>
86 #include <schily/schily.h>
87 #include <schily/nlsdefs.h>
88 #include <schily/vfork.h>
89 
90 #include "cdrecord.h"
91 #include "xio.h"
92 
93 #ifdef DEBUG
94 #ifdef XDEBUG
95 FILE	*ef;
96 #define	USDEBUG1	if (debug) {if (s == owner_reader) fprintf(ef, "r"); else fprintf(ef, "w"); fflush(ef); }
97 #define	USDEBUG2	if (debug) {if (s == owner_reader) fprintf(ef, "R"); else fprintf(ef, "W"); fflush(ef); }
98 #else
99 #define	USDEBUG1
100 #define	USDEBUG2
101 #endif
102 #define	EDEBUG(a)	if (debug) error a
103 #else
104 #define	EDEBUG(a)
105 #define	USDEBUG1
106 #define	USDEBUG2
107 #endif
108 
109 #define	palign(x, a)	(((char *)(x)) + ((a) - 1 - (((UIntptr_t)((x)-1))%(a))))
110 
111 typedef enum faio_owner {
112 	owner_none,		/* Unused in real life			    */
113 	owner_writer,		/* owned by process that writes into FIFO   */
114 	owner_faio,		/* Intermediate state when buf still in use */
115 	owner_reader		/* owned by process that reads from FIFO    */
116 } fowner_t;
117 
118 char	*onames[] = {
119 	"none",
120 	"writer",
121 	"faio",
122 	"reader",
123 };
124 
125 typedef struct faio {
126 	int	len;
127 	volatile fowner_t owner;
128 	volatile int users;
129 	short	fd;
130 	short	saved_errno;
131 	char	*bufp;
132 } faio_t;
133 
134 struct faio_stats {
135 	long	puts;
136 	long	gets;
137 	long	empty;
138 	long	full;
139 	long	done;
140 	long	cont_low;
141 	int	users;
142 } *sp;
143 
144 #define	MIN_BUFFERS	3
145 
146 #define	MSECS	1000
147 #define	SECS	(1000*MSECS)
148 
149 /*
150  * Note: WRITER_MAXWAIT & READER_MAXWAIT need to be greater than the SCSI
151  * timeout for commands that write to the media. This is currently 200s
152  * if we are in SAO mode.
153  */
154 /* microsecond delay between each buffer-ready probe by writing process */
155 #define	WRITER_DELAY	(20*MSECS)
156 #define	WRITER_MAXWAIT	(240*SECS)	/* 240 seconds max wait for data */
157 
158 /* microsecond delay between each buffer-ready probe by reading process */
159 #define	READER_DELAY	(80*MSECS)
160 #define	READER_MAXWAIT	(240*SECS)	/* 240 seconds max wait for reader */
161 
162 LOCAL	char	*buf;
163 LOCAL	char	*bufbase;
164 LOCAL	char	*bufend;
165 LOCAL	long	buflen;			/* The size of the FIFO buffer */
166 
167 extern	int	debug;
168 extern	int	lverbose;
169 
170 EXPORT	long	init_fifo	__PR((long));
171 #ifdef	USE_MMAP
172 LOCAL	char	*mkshare	__PR((int size));
173 #endif
174 #ifdef	USE_USGSHM
175 LOCAL	char	*mkshm		__PR((int size));
176 #endif
177 #ifdef	USE_OS2SHM
178 LOCAL	char	*mkos2shm	__PR((int size));
179 #endif
180 #ifdef	USE_BEOS_AREAS
181 LOCAL	char	*mkbeosshm	__PR((int size));
182 LOCAL	void	beosshm_child	__PR((void));
183 #endif
184 
185 EXPORT	BOOL	init_faio	__PR((track_t *trackp, int));
186 EXPORT	BOOL	await_faio	__PR((void));
187 EXPORT	void	kill_faio	__PR((void));
188 EXPORT	int	wait_faio	__PR((void));
189 LOCAL	void	faio_reader	__PR((track_t *trackp));
190 LOCAL	void	faio_read_track	__PR((track_t *trackp));
191 LOCAL	void	faio_wait_on_buffer __PR((faio_t *f, fowner_t s,
192 					unsigned long delay,
193 					unsigned long max_wait));
194 LOCAL	int	faio_read_segment __PR((int fd, faio_t *f, track_t *track, long secno, int len));
195 LOCAL	faio_t	*faio_ref	__PR((int n));
196 EXPORT	int	faio_read_buf	__PR((int f, char *bp, int size));
197 EXPORT	int	faio_get_buf	__PR((int f, char **bpp, int size));
198 EXPORT	void	fifo_stats	__PR((void));
199 EXPORT	int	fifo_percent	__PR((BOOL addone));
200 
201 
202 EXPORT long
init_fifo(fs)203 init_fifo(fs)
204 	long	fs;
205 {
206 	int	pagesize;
207 
208 	if (fs == 0L)
209 		return (fs);
210 
211 #ifdef	_SC_PAGESIZE
212 	pagesize = sysconf(_SC_PAGESIZE);
213 #else
214 	pagesize = getpagesize();
215 #endif
216 	buflen = roundup(fs, pagesize) + pagesize;
217 	EDEBUG(("fs: %ld buflen: %ld\n", fs, buflen));
218 
219 #if	defined(USE_MMAP)
220 	buf = mkshare(buflen);
221 #endif
222 #if	defined(USE_USGSHM)
223 	buf = mkshm(buflen);
224 #endif
225 #if	defined(USE_OS2SHM)
226 	buf = mkos2shm(buflen);
227 #endif
228 #if	defined(USE_BEOS_AREAS)
229 	buf = mkbeosshm(buflen);
230 #endif
231 
232 	bufbase = buf;
233 	bufend = buf + buflen;
234 	EDEBUG(("buf: %p bufend: %p, buflen: %ld\n", buf, bufend, buflen));
235 	buf = palign(buf, pagesize);
236 	buflen -= buf - bufbase;
237 	EDEBUG(("buf: %p bufend: %p, buflen: %ld (align %ld)\n", buf, bufend, buflen, (long)(buf - bufbase)));
238 
239 	/*
240 	 * Dirty the whole buffer. This can die with various signals if
241 	 * we're trying to lock too much memory
242 	 */
243 	fillbytes(buf, buflen, '\0');
244 
245 #ifdef	XDEBUG
246 	if (debug)
247 		ef = fopen("/tmp/ef", "w");
248 #endif
249 	return (buflen-pagesize); /* We use one page for administrative data */
250 }
251 
252 #ifdef	USE_MMAP
253 LOCAL char *
mkshare(size)254 mkshare(size)
255 	int	size;
256 {
257 	int	f;
258 	char	*addr;
259 
260 #ifdef	MAP_ANONYMOUS	/* HP/UX */
261 	f = -1;
262 	addr = mmap(0, mmap_sizeparm(size),
263 			PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, f, 0);
264 #else
265 	if ((f = open("/dev/zero", O_RDWR)) < 0)
266 		comerr(_("Cannot open '/dev/zero'.\n"));
267 	addr = mmap(0, mmap_sizeparm(size),
268 			PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
269 #endif
270 	if (addr == (char *)-1)
271 		comerr(_("Cannot get mmap for %d Bytes on /dev/zero.\n"), size);
272 	if (f >= 0)
273 		close(f);
274 
275 	if (debug) errmsgno(EX_BAD, _("shared memory segment attached at: %p size %d\n"),
276 				(void *)addr, size);
277 
278 	return (addr);
279 }
280 #endif
281 
282 #ifdef	USE_USGSHM
283 #include <schily/ipc.h>
284 #include <schily/shm.h>
285 LOCAL char *
mkshm(size)286 mkshm(size)
287 	int	size;
288 {
289 	int	id;
290 	char	*addr;
291 	/*
292 	 * Unfortunately, a declaration of shmat() is missing in old
293 	 * implementations such as AT&T SVr0 and SunOS.
294 	 * We cannot add this definition here because the return-type
295 	 * changed on newer systems.
296 	 *
297 	 * We will get a warning like this:
298 	 *
299 	 * warning: assignment of pointer from integer lacks a cast
300 	 * or
301 	 * warning: illegal combination of pointer and integer, op =
302 	 */
303 /*	extern	char *shmat();*/
304 
305 	if ((id = shmget(IPC_PRIVATE, size, IPC_CREAT|0600)) == -1)
306 		comerr(_("shmget failed\n"));
307 
308 	if (debug) errmsgno(EX_BAD, _("shared memory segment allocated: %d\n"), id);
309 
310 	if ((addr = shmat(id, (char *)0, 0600)) == (char *)-1)
311 		comerr(_("shmat failed\n"));
312 
313 	if (debug) errmsgno(EX_BAD, _("shared memory segment attached at: %p size %d\n"),
314 				(void *)addr, size);
315 
316 	if (shmctl(id, IPC_RMID, 0) < 0)
317 		comerr(_("shmctl failed to detach shared memory segment\n"));
318 
319 #ifdef	SHM_LOCK
320 	/*
321 	 * Although SHM_LOCK is standard, it seems that all versions of AIX
322 	 * ommit this definition.
323 	 */
324 	if (shmctl(id, SHM_LOCK, 0) < 0)
325 		comerr(_("shmctl failed to lock shared memory segment\n"));
326 #endif
327 
328 	return (addr);
329 }
330 #endif
331 
332 #ifdef	USE_OS2SHM
333 LOCAL char *
mkos2shm(size)334 mkos2shm(size)
335 	int	size;
336 {
337 	char	*addr;
338 
339 	/*
340 	 * The OS/2 implementation of shm (using shm.dll) limits the size of one shared
341 	 * memory segment to 0x3fa000 (aprox. 4MBytes). Using OS/2 native API we have
342 	 * no such restriction so I decided to use it allowing fifos of arbitrary size.
343 	 */
344 	if (DosAllocSharedMem(&addr, NULL, size, 0X100L | 0x1L | 0x2L | 0x10L))
345 		comerr(_("DosAllocSharedMem() failed\n"));
346 
347 	if (debug) errmsgno(EX_BAD, _("shared memory allocated attached at: %p size %d\n"),
348 				(void *)addr, size);
349 
350 	return (addr);
351 }
352 #endif
353 
354 #ifdef	USE_BEOS_AREAS
355 LOCAL	area_id	faio_aid;
356 LOCAL	void	*faio_addr;
357 LOCAL	char	faio_name[32];
358 
359 LOCAL char *
mkbeosshm(size)360 mkbeosshm(size)
361 	int	size;
362 {
363 	snprintf(faio_name, sizeof (faio_name), "cdrecord FIFO %lld",
364 		(Llong)getpid());
365 
366 	faio_aid = create_area(faio_name, &faio_addr,
367 			B_ANY_ADDRESS,
368 			size,
369 			B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
370 	if (faio_addr == NULL) {
371 		comerrno(faio_aid,
372 			_("Cannot get create_area for %d Bytes FIFO.\n"), size);
373 	}
374 	if (debug) errmsgno(EX_BAD, _("shared memory allocated attached at: %p size %d\n"),
375 				(void *)faio_addr, size);
376 	return (faio_addr);
377 }
378 
379 LOCAL void
beosshm_child()380 beosshm_child()
381 {
382 	/*
383 	 * Delete the area created by fork that is copy-on-write.
384 	 */
385 	delete_area(area_for(faio_addr));
386 	/*
387 	 * Clone (share) the original one.
388 	 * The original implementaion used B_ANY_ADDRESS, but newer Haiku
389 	 * versions implement address randomization that prevents us from
390 	 * using the pointer in the child. So we noe use B_EXACT_ADDRESS.
391 	 */
392 	faio_aid = clone_area(faio_name, &faio_addr,
393 			B_EXACT_ADDRESS, B_READ_AREA|B_WRITE_AREA,
394 			faio_aid);
395 	if (bufbase != faio_addr) {
396 		comerrno(EX_BAD, _("Panic FIFO addr.\n"));
397 		/* NOTREACHED */
398 	}
399 }
400 #endif
401 
402 LOCAL	int	faio_buffers;
403 LOCAL	int	faio_buf_size;
404 LOCAL	int	buf_idx = 0;		/* Initialize to fix an Amiga bug   */
405 LOCAL	int	buf_idx_reader = 0;	/* Separate var to allow vfork()    */
406 					/* buf_idx_reader is for the process */
407 					/* that fills the FIFO		    */
408 LOCAL	pid_t	faio_pid;
409 LOCAL	BOOL	faio_didwait;
410 
411 #ifdef AMIGA
412 /*
413  * On Amiga fork will be replaced by the speciall vfork() like call ix_vfork,
414  * which lets the parent asleep. The child process later wakes up the parent
415  * process by calling ix_fork_resume().
416  */
417 #define	fork()		 ix_vfork()
418 #define	__vfork_resume() ix_vfork_resume()
419 
420 #else	/* !AMIGA */
421 #define	__vfork_resume()
422 #endif
423 
424 
425 /*#define	faio_ref(n)	(&((faio_t *)buf)[n])*/
426 
427 
428 EXPORT BOOL
init_faio(trackp,bufsize)429 init_faio(trackp, bufsize)
430 	track_t	*trackp;
431 	int	bufsize;	/* The size of a single transfer buffer */
432 {
433 	int	n;
434 	faio_t	*f;
435 	int	pagesize;
436 	char	*base;
437 
438 	if (buflen == 0L)
439 		return (FALSE);
440 
441 #ifdef	_SC_PAGESIZE
442 	pagesize = sysconf(_SC_PAGESIZE);
443 #else
444 	pagesize = getpagesize();
445 #endif
446 
447 	faio_buf_size = bufsize;
448 	f = (faio_t *)buf;
449 
450 	/*
451 	 * Compute space for buffer headers.
452 	 * Round bufsize up to pagesize to make each FIFO segment
453 	 * properly page aligned.
454 	 */
455 	bufsize = roundup(bufsize, pagesize);
456 	faio_buffers = (buflen - sizeof (*sp)) / bufsize;
457 	EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
458 
459 	/*
460 	 * Reduce buffer space by header space.
461 	 */
462 	n = sizeof (*sp) + faio_buffers * sizeof (struct faio);
463 	n = roundup(n, pagesize);
464 	faio_buffers = (buflen-n) / bufsize;
465 	EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
466 
467 	if (faio_buffers < MIN_BUFFERS) {
468 		errmsgno(EX_BAD,
469 			_("write-buffer too small, minimum is %dk. Disabling.\n"),
470 						MIN_BUFFERS*bufsize/1024);
471 		return (FALSE);
472 	}
473 
474 	if (debug)
475 		printf(_("Using %d buffers of %d bytes.\n"), faio_buffers, faio_buf_size);
476 
477 	f = (faio_t *)buf;
478 	base = buf + roundup(sizeof (*sp) + faio_buffers * sizeof (struct faio),
479 				pagesize);
480 
481 	for (n = 0; n < faio_buffers; n++, f++, base += bufsize) {
482 		/* Give all the buffers to the file reader process */
483 		f->owner = owner_writer;
484 		f->users = 0;
485 		f->bufp = base;
486 		f->fd = -1;
487 	}
488 	sp = (struct faio_stats *)f;	/* point past headers */
489 	sp->gets = sp->puts = sp->done = 0L;
490 	sp->users = 1;
491 
492 	faio_pid = fork();
493 	if (faio_pid < 0)
494 		comerr(_("fork(2) failed"));
495 
496 	if (faio_pid == 0) {
497 		/*
498 		 * child (background) process that fills the FIFO.
499 		 */
500 		raisepri(1);		/* almost max priority */
501 
502 #ifdef USE_OS2SHM
503 		DosGetSharedMem(buf, 3); /* PAG_READ|PAG_WRITE */
504 #endif
505 #ifdef	USE_BEOS_AREAS
506 		beosshm_child();
507 #endif
508 		/* Ignoring SIGALRM cures the SCO usleep() bug */
509 /*		signal(SIGALRM, SIG_IGN);*/
510 		__vfork_resume();	/* Needed on some platforms */
511 		faio_reader(trackp);
512 		/* NOTREACHED */
513 	} else {
514 #ifdef	__needed__
515 		Uint	t;
516 #endif
517 
518 		faio_didwait = FALSE;
519 
520 		/*
521 		 * XXX We used to close all track files in the foreground
522 		 * XXX process. This was not correct before we used "xio"
523 		 * XXX and with "xio" it will start to fail because we need
524 		 * XXX the fd handles for the faio_get_buf() function.
525 		 */
526 #ifdef	__needed__
527 		/* close all file-descriptors that only the child will use */
528 		for (t = 1; t <= trackp->tracks; t++) {
529 			if (trackp[t].xfp != NULL)
530 				xclose(trackp[t].xfp);
531 		}
532 #endif
533 	}
534 
535 	return (TRUE);
536 }
537 
538 EXPORT BOOL
await_faio()539 await_faio()
540 {
541 	int	n;
542 	int	lastfd = -1;
543 	faio_t	*f;
544 
545 	/*
546 	 * Wait until the reader is active and has filled the buffer.
547 	 */
548 	if (lverbose || debug) {
549 		printf(_("Waiting for reader process to fill input buffer ... "));
550 		flush();
551 	}
552 
553 	faio_wait_on_buffer(faio_ref(faio_buffers - 1), owner_reader,
554 			    500*MSECS, 0);
555 
556 	if (lverbose || debug)
557 		printf(_("input buffer ready.\n"));
558 
559 	sp->empty = sp->full = 0L;	/* set correct stat state */
560 	sp->cont_low = faio_buffers;	/* set cont to max value  */
561 
562 	f = faio_ref(0);
563 	for (n = 0; n < faio_buffers; n++, f++) {
564 		if (f->fd != lastfd &&
565 			f->fd == STDIN_FILENO && f->len == 0) {
566 			errmsgno(EX_BAD, _("Premature EOF on stdin.\n"));
567 			kill_faio();
568 			return (FALSE);
569 		}
570 		lastfd = f->fd;
571 	}
572 	return (TRUE);
573 }
574 
575 EXPORT void
kill_faio()576 kill_faio()
577 {
578 	if (faio_pid > 0)
579 		kill(faio_pid, SIGKILL);
580 }
581 
582 EXPORT int
wait_faio()583 wait_faio()
584 {
585 	if (faio_pid > 0 && !faio_didwait)
586 		return (wait(0));
587 	faio_didwait = TRUE;
588 	return (0);
589 }
590 
591 LOCAL void
faio_reader(trackp)592 faio_reader(trackp)
593 	track_t	*trackp;
594 {
595 	/* This function should not return, but _exit. */
596 	Uint	trackno;
597 
598 	if (debug)
599 		printf(_("\nfaio_reader starting\n"));
600 
601 	for (trackno = 0; trackno <= trackp->tracks; trackno++) {
602 		if (trackno == 0 && trackp[0].xfp == NULL)
603 			continue;
604 		if (debug)
605 			printf(_("\nfaio_reader reading track %u\n"), trackno);
606 		faio_read_track(&trackp[trackno]);
607 	}
608 	sp->done++;
609 	if (debug)
610 		printf(_("\nfaio_reader all tracks read, exiting\n"));
611 
612 	/* Prevent hang if buffer is larger than all the tracks combined */
613 	if (sp->gets == 0)
614 		faio_ref(faio_buffers - 1)->owner = owner_reader;
615 
616 #ifdef	USE_OS2SHM
617 	DosFreeMem(buf);
618 	sleep(30000);	/* XXX If calling _exit() here the parent process seems to be blocked */
619 			/* XXX This should be fixed soon */
620 #endif
621 	if (debug)
622 		error(_("\nfaio_reader _exit(0)\n"));
623 	_exit(0);
624 }
625 
626 #ifndef	faio_ref
627 LOCAL faio_t *
faio_ref(n)628 faio_ref(n)
629 	int	n;
630 {
631 	return (&((faio_t *)buf)[n]);
632 }
633 #endif
634 
635 
636 LOCAL void
faio_read_track(trackp)637 faio_read_track(trackp)
638 	track_t *trackp;
639 {
640 	int	fd = -1;
641 	int	bytespt = trackp->secsize * trackp->secspt;
642 	int	secspt = trackp->secspt;
643 	int	l;
644 	long	secno = trackp->trackstart;
645 	tsize_t	tracksize = trackp->tracksize;
646 	tsize_t	bytes_read = (tsize_t)0;
647 	long	bytes_to_read;
648 
649 	if (trackp->xfp != NULL)
650 		fd = xfileno(trackp->xfp);
651 
652 	if (bytespt > faio_buf_size) {
653 		comerrno(EX_BAD,
654 		_("faio_read_track fatal: secsize %d secspt %d, bytespt(%d) > %d !!\n"),
655 			trackp->secsize, trackp->secspt, bytespt,
656 			faio_buf_size);
657 	}
658 
659 	do {
660 		bytes_to_read = bytespt;
661 		if (tracksize > 0) {
662 			if ((tracksize - bytes_read) > bytespt) {
663 				bytes_to_read = bytespt;
664 			} else {
665 				bytes_to_read = tracksize - bytes_read;
666 			}
667 		}
668 		l = faio_read_segment(fd, faio_ref(buf_idx_reader), trackp, secno, bytes_to_read);
669 		if (++buf_idx_reader >= faio_buffers)
670 			buf_idx_reader = 0;
671 		if (l <= 0)
672 			break;
673 		bytes_read += l;
674 		secno += secspt;
675 	} while (tracksize < 0 || bytes_read < tracksize);
676 
677 	if (trackp->xfp != NULL) {
678 		xclose(trackp->xfp);	/* Don't keep files open longer than neccesary */
679 		trackp->xfp = NULL;
680 	}
681 }
682 
683 LOCAL void
684 #ifdef	PROTOTYPES
faio_wait_on_buffer(faio_t * f,fowner_t s,unsigned long delay,unsigned long max_wait)685 faio_wait_on_buffer(faio_t *f, fowner_t s,
686 			unsigned long delay,
687 			unsigned long max_wait)
688 #else
689 faio_wait_on_buffer(f, s, delay, max_wait)
690 	faio_t	*f;
691 	fowner_t s;
692 	unsigned long delay;
693 	unsigned long max_wait;
694 #endif
695 {
696 	unsigned long max_loops;
697 
698 	if (f->owner == s)
699 		return;		/* return immediately if the buffer is ours */
700 
701 	if (s == owner_reader)
702 		sp->empty++;
703 	else
704 		sp->full++;
705 
706 	max_loops = max_wait / delay + 1;
707 
708 	while (max_wait == 0 || max_loops--) {
709 		USDEBUG1;
710 		usleep(delay);
711 		USDEBUG2;
712 
713 		if (f->owner == s)
714 			return;
715 	}
716 	if (debug) {
717 		errmsgno(EX_BAD,
718 		_("%lu microseconds passed waiting for %d current: %d idx: %ld\n"),
719 		max_wait, s, f->owner, (long)(f - faio_ref(0))/sizeof (*f));
720 	}
721 	comerrno(EX_BAD, _("faio_wait_on_buffer for %s timed out.\n"),
722 	(s > owner_reader || s < owner_none) ? "bad_owner" : onames[s-owner_none]);
723 }
724 
725 LOCAL int
faio_read_segment(fd,f,trackp,secno,len)726 faio_read_segment(fd, f, trackp, secno, len)
727 	int	fd;
728 	faio_t	*f;
729 	track_t	*trackp;
730 	long	secno;
731 	int	len;
732 {
733 	int l;
734 
735 	faio_wait_on_buffer(f, owner_writer, WRITER_DELAY, WRITER_MAXWAIT);
736 
737 	f->fd = fd;
738 	l = fill_buf(fd, trackp, secno, f->bufp, len);
739 	f->len = l;
740 	f->saved_errno = geterrno();
741 	f->owner = owner_reader;
742 	f->users = sp->users;
743 
744 	sp->puts++;
745 
746 	return (l);
747 }
748 
749 EXPORT int
faio_read_buf(fd,bp,size)750 faio_read_buf(fd, bp, size)
751 	int fd;
752 	char *bp;
753 	int size;
754 {
755 	char *bufp;
756 
757 	int len = faio_get_buf(fd, &bufp, size);
758 	if (len > 0) {
759 		movebytes(bufp, bp, len);
760 	}
761 	return (len);
762 }
763 
764 EXPORT int
faio_get_buf(fd,bpp,size)765 faio_get_buf(fd, bpp, size)
766 	int fd;
767 	char **bpp;
768 	int size;
769 {
770 	faio_t	*f;
771 	int	len;
772 
773 again:
774 	f = faio_ref(buf_idx);
775 	if (f->owner == owner_faio) {
776 		f->owner = owner_writer;
777 		if (++buf_idx >= faio_buffers)
778 			buf_idx = 0;
779 		f = faio_ref(buf_idx);
780 	}
781 
782 	if ((sp->puts - sp->gets) < sp->cont_low && sp->done == 0) {
783 		EDEBUG(("gets: %ld puts: %ld cont: %ld low: %ld\n", sp->gets, sp->puts, sp->puts - sp->gets, sp->cont_low));
784 		sp->cont_low = sp->puts - sp->gets;
785 	}
786 	faio_wait_on_buffer(f, owner_reader, READER_DELAY, READER_MAXWAIT);
787 	len = f->len;
788 
789 	if (f->fd != fd) {
790 		if (f->len == 0) {
791 			/*
792 			 * If the tracksize for this track was known, and
793 			 * the tracksize is 0 mod bytespt, this happens.
794 			 */
795 			goto again;
796 		}
797 		comerrno(EX_BAD,
798 		_("faio_get_buf fatal: fd=%d, f->fd=%d, f->len=%d f->errno=%d\n"),
799 		fd, f->fd, f->len, f->saved_errno);
800 	}
801 	if (size < len) {
802 		comerrno(EX_BAD,
803 		_("unexpected short read-attempt in faio_get_buf. size = %d, len = %d\n"),
804 		size, len);
805 	}
806 
807 	if (len < 0)
808 		seterrno(f->saved_errno);
809 
810 	sp->gets++;
811 
812 	*bpp = f->bufp;
813 	if (--f->users <= 0)
814 		f->owner = owner_faio;
815 	return (len);
816 }
817 
818 EXPORT void
fifo_stats()819 fifo_stats()
820 {
821 	if (sp == NULL)	/* We might not use a FIFO */
822 		return;
823 
824 	errmsgno(EX_BAD, _("fifo had %ld puts and %ld gets.\n"),
825 		sp->puts, sp->gets);
826 	errmsgno(EX_BAD, _("fifo was %ld times empty and %ld times full, min fill was %ld%%.\n"),
827 		sp->empty, sp->full, (100L*sp->cont_low)/faio_buffers);
828 }
829 
830 EXPORT int
fifo_percent(addone)831 fifo_percent(addone)
832 	BOOL	addone;
833 {
834 	int	percent;
835 
836 	if (sp == NULL)	/* We might not use a FIFO */
837 		return (-1);
838 
839 	if (sp->done)
840 		return (100);
841 	percent = (100*(sp->puts + 1 - sp->gets)/faio_buffers);
842 	if (percent > 100)
843 		return (100);
844 	return (percent);
845 }
846 #else	/* FIFO */
847 
848 #include <schily/standard.h>
849 #include <schily/utypes.h>	/* includes sys/types.h */
850 #include <schily/schily.h>
851 #include <schily/nlsdefs.h>
852 
853 #include "cdrecord.h"
854 
855 EXPORT	long	init_fifo	__PR((long));
856 EXPORT	BOOL	init_faio	__PR((track_t *track, int));
857 EXPORT	BOOL	await_faio	__PR((void));
858 EXPORT	void	kill_faio	__PR((void));
859 EXPORT	int	wait_faio	__PR((void));
860 EXPORT	int	faio_read_buf	__PR((int f, char *bp, int size));
861 EXPORT	int	faio_get_buf	__PR((int f, char **bpp, int size));
862 EXPORT	void	fifo_stats	__PR((void));
863 EXPORT	int	fifo_percent	__PR((BOOL addone));
864 
865 
866 EXPORT long
init_fifo(fs)867 init_fifo(fs)
868 	long	fs;
869 {
870 	errmsgno(EX_BAD, _("Fifo not supported.\n"));
871 	return (0L);
872 }
873 
874 EXPORT BOOL
init_faio(track,bufsize)875 init_faio(track, bufsize)
876 	track_t	*track;
877 	int	bufsize;
878 {
879 	return (FALSE);
880 }
881 
882 EXPORT BOOL
await_faio()883 await_faio()
884 {
885 	return (TRUE);
886 }
887 
888 EXPORT void
kill_faio()889 kill_faio()
890 {
891 }
892 
893 EXPORT int
wait_faio()894 wait_faio()
895 {
896 	return (0);
897 }
898 
899 EXPORT int
faio_read_buf(fd,bp,size)900 faio_read_buf(fd, bp, size)
901 	int fd;
902 	char *bp;
903 	int size;
904 {
905 	return (0);
906 }
907 
908 EXPORT int
faio_get_buf(fd,bpp,size)909 faio_get_buf(fd, bpp, size)
910 	int fd;
911 	char **bpp;
912 	int size;
913 {
914 	return (0);
915 }
916 
917 EXPORT void
fifo_stats()918 fifo_stats()
919 {
920 }
921 
922 EXPORT int
fifo_percent(addone)923 fifo_percent(addone)
924 	BOOL	addone;
925 {
926 	return (-1);
927 }
928 
929 #endif	/* FIFO */
930