/*==============================================================================

FICHIER     : [tampon.c]

DATE        : 2005/12/0006 21:16:44

CREATEUR    : [Linux!jef]

COMMENTAIRE :
		Released under GPL license, see gnu.org
================================================================================

==============================================================================*/
#if 0 /* Not used anymore */
#define __USE_LARGEFILE64
#define _LARGEFILE64_SOURCE
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#define __USE_GNU
#include <pthread.h>

char ** environ;

#define	SH_PATH	"/bin/sh"
#define	SH_NAME	"sh"

#include "tampon.h"
#include "globals.h"

#define SIMUL 0

#define CTEST(x)		/***/
#define CTEST2(x)		/***/

#define CLOSE(x)		if( x != -1 ) { close( x ); x = -1; }

#define NB_BUFFERS		4000
static TBuffer * FreeBuffer = NULL;
static pthread_mutex_t FreeMutex;
static sem_t FreeSem;

static int NbFreed = 0;
static int NbAlloc = 0;


static int ErrorCondition;

/*@$#[tampon.c] static proto. AutoProtoSigV1.1. date: 105/12/14 23:32:36 */
#include "proto.h"
#ifdef __cplusplus
extern "C" {
#endif
static int BufferInit PROTO((void));
static void BufferDestroy PROTO((void));
static TBuffer *BufferAlloc PROTO((void));
static void BufferFree PROTO((TBuffer *b));
static TStream *TamponAddStream PROTO((Tampon *tampon, char *command));
static TFifo *AddFifo PROTO((TStream *stream, char *oFile));
static int OpenFifoRead PROTO((char *fifoName));
static int OpenFifoWrite PROTO((char *fifoName));
static void SetCloExec PROTO((int fd));
static void *WriterRoutine PROTO((void *arg));
static void *ThreadRoutine PROTO((void *arg));
static int SetupStream PROTO((Tampon *tampon, TStream *stream, TStream *parent));
static int WriteBuffer PROTO((TFifo *fifo, char *buffer, int lg));
static int MngStream PROTO((Tampon *tampon, TStream *stream));
static int SetupStage PROTO((Tampon *tampon, TStage *stage));
static int MngStage PROTO((Tampon *tampon, TStage *stage));
static int StreamDestroyProc PROTO((TStream *stream));
static int StreamDestroyThreads PROTO((TStream *stream));
static int StreamDestroyResources PROTO((TStream *stream));
static int StreamDestroyAlloc PROTO((TStream *stream));
#ifdef __cplusplus
}
#endif
/*@$% end of AutoProtoSigV1.1 (Dont remove this line) [-I ../include]*/

/*------------------------------------------------------------------------------
	BUFFERINIT-
Linux!jef 2005/12/13 22:07:54
------------------------------------------------------------------------------*/

static int BufferInit()
{
	int i;
	pthread_mutexattr_t attr;

	FreeBuffer = NULL;
	NbFreed = NbAlloc = 0;
	for( i = 0; i < NB_BUFFERS; i++ ) {
		TBuffer * b =(TBuffer *)calloc( 1, sizeof(*b));

		b->next = FreeBuffer;
		FreeBuffer = b;
		NbFreed++;
	}
	sem_init( &FreeSem, 0, NB_BUFFERS );
	pthread_mutexattr_init( &attr );
	pthread_mutex_init( &FreeMutex, &attr );

	return( 0 );
}
/*------------------------------------------------------------------------------
	BUFFERDESTROY-
Linux!jef 2005/12/13 22:09:14
------------------------------------------------------------------------------*/

static void BufferDestroy()
{
	TBuffer * b = FreeBuffer;
	int cnt = 0;

	while( b ) {
		TBuffer * n = b->next;

		cnt++;
		free( b );
		b = n;
	}
	FreeBuffer = NULL;
	DBG( 't',fprintf(stderr,"%s: %d buffer destroyed.\n", __FUNCTION__, cnt ););
}
/*------------------------------------------------------------------------------
	BUFFERALLOC-
Linux!jef 2005/12/13 22:10:47
------------------------------------------------------------------------------*/

static TBuffer * BufferAlloc()
{
	TBuffer * b;

	sem_wait( &FreeSem );
	pthread_mutex_lock( &FreeMutex );
	b = FreeBuffer;
	FreeBuffer = b->next;
	b->next = NULL;
	NbAlloc++;
	pthread_mutex_unlock( &FreeMutex );
	return( b );
}
/*------------------------------------------------------------------------------
	BUFFERFREE-
Linux!jef 2005/12/13 22:16:41
------------------------------------------------------------------------------*/

static void BufferFree( b )
TBuffer * b;
{
	pthread_mutex_lock( &FreeMutex );
	b->next = FreeBuffer;
	FreeBuffer = b;
	NbFreed++;
	pthread_mutex_unlock( &FreeMutex );
	sem_post( &FreeSem );
}

/*------------------------------------------------------------------------------
	TAMPONADDSTREAM-
Linux!jef 2005/12/06 21:19:21
------------------------------------------------------------------------------*/

static TStream * TamponAddStream( tampon, command )
Tampon * tampon;
char * command;
{
	TStream * stream =(TStream *)calloc( 1, sizeof(*stream) );

	stream->command = strdup( command );
	stream->pid = 0;

	AddFifo( stream, NULL );

	if( tampon ) {
		stream->next = tampon->stream->next;
		tampon->stream->next = stream;
	}
	return( stream );
}

/*------------------------------------------------------------------------------
	CREATETAMPON-
Linux!jef 2005/12/06 21:17:54
------------------------------------------------------------------------------*/

Tampon * CreateTampon( command )
char * command;
{
	Tampon * new =(Tampon *)calloc( 1, sizeof( *new ) );

	new->stream = TamponAddStream( NULL, command );

	return( new );
}


/*------------------------------------------------------------------------------
	ADDFIFO-
Linux!jef 2005/12/07 22:51:27
------------------------------------------------------------------------------*/

static TFifo * AddFifo( stream, oFile )
TStream * stream;
char * oFile;
{
	TFifo * fifo = (TFifo * )calloc( 1, sizeof(*fifo));

	if( oFile )	fifo->oFifo = strdup( oFile );
	fifo->fifoFdR = fifo->fifoFdW = -1;

	fifo->next = stream->oFifos;
	stream->oFifos = fifo;
	return( fifo );
}

/*------------------------------------------------------------------------------
	STREAMADDSTREAM-
Linux!jef 2005/12/06 21:22:17
------------------------------------------------------------------------------*/

TStream * StreamAddBrother( stream, command, cb, oFile )
TStream * stream;
char * command;
TCbArray * cb;
char * oFile;
{
	TStream * new =(TStream *)calloc( 1, sizeof(*new) );
	TFifo * f;

	if( cb )
		new->cb = *cb;
	else
		new->command = strdup( command );
	new->pid = 0;
	AddFifo( new, oFile );
/* Add a fifo to the father WARNING must be a callback */
	f = AddFifo( stream->parent, NULL );
	f->reader = new;

/* Link stream */
	new->parent = stream->parent;
	stream->next = new;

	return( new );
}

/*------------------------------------------------------------------------------
	STREAMADDSTREAM-
Linux!jef 2005/12/06 21:22:17
------------------------------------------------------------------------------*/

TStream * StreamAddSon( parent, command, cb, oFile )
TStream * parent;
char * command;
TCbArray * cb;
char * oFile;
{
	TStream * new =(TStream *)calloc( 1, sizeof(*new) );

	if( cb )
		new->cb = *cb;
	else
		new->command = strdup( command );
	new->pid = 0;
	AddFifo( new, oFile );
	parent->oFifos->reader = new;
	new->parent = parent;
	parent->up = new;

	return( new );
}
/*------------------------------------------------------------------------------
	SETSTREAMENDCB-
Linux!jef 2006/01/17 21:37:36
------------------------------------------------------------------------------*/

void SetStreamEndCb( stream, endCb, arg )
TStream * stream;
void (* endCb)();
void * arg;
{
	stream->endCallBack = endCb;
	stream->arg = arg;
}

/*------------------------------------------------------------------------------
	TAMPONADDSTAGE-
Linux!jef 2005/12/13 20:57:01
------------------------------------------------------------------------------*/

int TamponAddStage( tampon, sysCmd )
Tampon * tampon;
char * sysCmd;
{
	TStage * new =(TStage * )calloc( 1, sizeof( *new ));

	new->command = strdup( sysCmd );
	new->pid = 0;

	if( !tampon->stages ) {
		tampon->stages = new;
	}
	else {
		TStage * s = tampon->stages;

		while( s->next )	s = s->next;
		s->next = new;
	}
	return( 0 );
}

/*------------------------------------------------------------------------------
	OPENFIFOREAD-
Linux!jef 2005/12/07 23:10:51
------------------------------------------------------------------------------*/

static int OpenFifoRead( fifoName )
char * fifoName;
{
	int handle;
	int flags;

	if( *fifoName == '|' )	fifoName++;
	handle = open( fifoName, O_RDONLY | O_NDELAY | O_LARGEFILE );
	if( handle < 0 )	return( -1 );
	flags = fcntl( handle, F_GETFL, 0 );
	fcntl( handle, F_SETFL, flags & ~O_NDELAY );

	return( handle );
}

/*------------------------------------------------------------------------------
	OPENFIFOWRITE-
Linux!jef 2005/12/07 23:10:51
------------------------------------------------------------------------------*/

static int OpenFifoWrite( fifoName )
char * fifoName;
{
	int handle;
	int flags;

	if( *fifoName == '|' )	fifoName++;
	handle = open( fifoName, O_RDWR | O_NDELAY | O_LARGEFILE );
	if( handle < 0 )	return( -1 );
	flags = fcntl( handle, F_GETFL, 0 );
	fcntl( handle, F_SETFL, flags & ~O_NDELAY );

	return( handle );
}
/*------------------------------------------------------------------------------
	SETCLOEXEC-
Linux!jef 2005/12/07 23:58:12
------------------------------------------------------------------------------*/

static void SetCloExec( fd )
int fd;
{
	fcntl( fd, F_SETFD, FD_CLOEXEC);
}

/*------------------------------------------------------------------------------
	WRITERROUTINE-
Linux!jef 2005/12/13 22:31:44
------------------------------------------------------------------------------*/

static void * WriterRoutine( arg )
void * arg;
{
	TFifo * fifo =(TFifo *)arg;

	DBG( 't',fprintf(stderr,"WriterRoutine: start fifo %p thread: %p file: %s\n", fifo, (void *)fifo->thread, fifo->oFifo ););
	while( 1 ) {
		sem_wait( &fifo->wSem );

		pthread_testcancel();
#if 0
		if( ErrorCondition )	break;
#endif
		if( fifo->head ) {
			TBuffer bb;
			TBuffer * b;

			pthread_mutex_lock( &fifo->aMutex );
			b = fifo->head;
			bb = *b;
// fprintf(stderr,"%p: Pop head\n", fifo);
			fifo->head = b->next;
			if( !fifo->head ) {
				fifo->tail = NULL;
// fprintf(stderr,"%p: Pop head == tail\n", fifo);
			}
			pthread_mutex_unlock( &fifo->aMutex );
			BufferFree( b );

			if( WriteBuffer( fifo, bb.data, bb.lg ) < 0 ) {
				DBG( 't',fprintf(stderr,"WriterRoutine: write error %d exiting.\n", errno ););
				break;
			}
		}
		else {
			DBG( 't',fprintf(stderr,"thread:%p BUG:: nothing to pop !\n", (void *)fifo->thread););
		}
	}
	CLOSE( fifo->fifoFdW );
	fifo->thread = 0;
	return( (void *)0 );
}

/*------------------------------------------------------------------------------
	THREADROUTINE-
Linux!jef 2005/12/09 20:33:46
------------------------------------------------------------------------------*/

static void * ThreadRoutine( arg )
void * arg;
{
	TStream * stream = (TStream *)arg;
	int i;
	TFifo * fifo, * f;
	char buffer[RSIZE];
	int lg;
	int nOut = 0;
	int res = 0;

	f = stream->oFifos;
	while( f ) {
		nOut++;
		f = f->next;
	}
	DBG( 't',fprintf(stderr,"ThreadRoutine: start stream %p thread: %p nOut: %d pid: %d\n", stream,(void *)stream->thread, nOut,getpid()););

	if( nOut > 1 ) {
		f = stream->oFifos;
		while( f ) {
			pthread_mutexattr_t attr;

			sem_init( &f->wSem, 0, 0 );

			pthread_mutexattr_init( &attr );
			pthread_mutex_init( &f->aMutex, &attr );

			pthread_create( &f->thread, NULL, WriterRoutine, (void *)f );
			f = f->next;
		}
	}

	fifo = stream->parent->oFifos;
	while( fifo->fifoFdR != -1 ) {
		pthread_testcancel();
#if 0
		if( ErrorCondition ) {
			DBG( 't',fprintf(stderr,"ThreadRoutine: %p ErrorCondition exit\n", stream ););
			res = -1;
			goto out;
		}
#endif
		CTEST2(fprintf(stderr,"stream %p before read\n", stream ););
		lg = read( fifo->fifoFdR, buffer, sizeof(buffer));
		CTEST2(fprintf(stderr,"stream: %p read %d bytes errno: %d\n", stream, lg, errno ););
		if( lg < 0 ) {
			DBG( 't',fprintf(stderr,"ThreadRoutine: %p read exit errno: %d\n", stream, errno ););
			res = -1;
			goto out;
		}
		if( lg == 0 ) { /* input stream close, close any output */
			DBG( 't',fprintf(stderr,"input of callback end, shutting down in and out: errno %d\n", errno););
			CLOSE( fifo->fifoFdR );
/*
			CLOSE( fifo->fifoFdW );
			fifo = stream->oFifos;
			while( fifo ) {
				CLOSE( fifo->fifoFdR );
				CLOSE( fifo->fifoFdW );
				fifo = fifo->next;
			}
*/
			break;
		}
		if( lg > 0 ) {
			for( i = 0; i < stream->cb.nCb; i++ ) {
				(* stream->cb.callBack[i] )( buffer, &lg );
			}
			f = stream->oFifos;
			if( nOut > 1 ) {
				while( f ) {
					TBuffer * b = BufferAlloc();

					memcpy( b->data, buffer, lg );
					b->lg = lg;
					pthread_mutex_lock( &f->aMutex );
					if( !f->head ) {
// fprintf(stderr,"%p: Push head\n", f);
						f->head = f->tail = b;
					}
					else {
// fprintf(stderr,"%p: Push tail\n", f );
						f->tail->next = b;
						f->tail = b;
					}
					pthread_mutex_unlock( &f->aMutex );
					sem_post( &f->wSem );
					f = f->next;
				}
			}
			else {
				if( WriteBuffer( f, buffer, lg ) < 0 ) {
					DBG( 't',fprintf(stderr,"ThreadRoutine: write error %d exiting.\n", errno ););
					res = -1;
					goto out;
				}
			}
		}
	}
out:;
	DBG( 't',fprintf(stderr,"Thread end: stream %p thread: %p res: %d.\n", stream, (void *)stream->thread, res ););
/* kill any writer thread */
	while( 1 ) {
		int found;

		f = stream->oFifos;
		found = 0;
		while( f ) {
			if( f->thread ) {
				DBG( 't',fprintf(stderr,"Thread end: cancel fifo thread: %p.\n", (void *)f->thread ););
/* drain the fifo: */
				if( !f->head ) {
					pthread_cancel( f->thread );
					pthread_join( f->thread, NULL );
					DBG( 't',fprintf(stderr,"Thread end: closing (%s).\n", f->oFifo ););
					CLOSE( f->fifoFdR );
					CLOSE( f->fifoFdW );
					f->thread = 0;
				}
				else
					found++;
			}
			f = f->next;
		}
		if( !found )	break;
		usleep( 100 * 1000 );
	}
	if( nOut == 1 ) {
		f = stream->oFifos;
		CLOSE( f->fifoFdW );
	}
	stream->thread = 0;
	DBG( 't',fprintf(stderr,"ThreadRoutine: stream %p exit.\n", stream ););
	return( (void *)(long)res );
}

/*------------------------------------------------------------------------------
	SETUPSTREAM-
Linux!jef 2005/12/06 21:35:23
------------------------------------------------------------------------------*/

static int SetupStream( tampon, stream, parent )
Tampon * tampon;
TStream * stream;
TStream * parent;
{
	static int cpt = 0;
	char oFifo[256];
	pid_t pid;
	TFifo * fifo;
	TFifo * fifoRead;

/* Create any output fifos and files */
	fifo = stream->oFifos;
	while( fifo ) {
		if( !fifo->oFifo ) {
			sprintf( oFifo, "/tmp/_t%d.%d.fifo", getpid(), cpt++ );
			unlink( oFifo );
			if( mknod( oFifo, S_IFIFO | 0666, 0 ) ) {
				DBG( 't',fprintf(stderr,"SetupStream: fifo (%s) creation failed errno: %d\n", oFifo, errno ););
				return( -1 );
			}
			fifo->oFifo = strdup( oFifo );
			fifo->rmF = 1;
			DBG( 't',fprintf(stderr,"SetupStream: %p creating fifo %s\n", stream, fifo->oFifo ););
		}
		else {
			if( *fifo->oFifo == '|' ) {
				DBG( 't',fprintf(stderr,"SetupStream: %p creating fifo %s\n", stream, fifo->oFifo +1 ););
				unlink( fifo->oFifo+1 );
				if( mknod( fifo->oFifo+1, S_IFIFO | 0666, 0 ) ) {
					DBG( 't',fprintf(stderr,"SetupStream: fifo (%s) creation failed errno: %d\n", fifo->oFifo+1, errno ););
					return( -1 );
				}
			}
			else {
				if( !strcmp(fifo->oFifo, "/dev/null")) {
					DBG( 't',fprintf(stderr,"SetupStream: %p null out %s\n", stream, fifo->oFifo ););
				}
				else {
					DBG( 't',fprintf(stderr,"SetupStream: %p creating file %s\n", stream, fifo->oFifo ););
					unlink( fifo->oFifo );
					close( open( fifo->oFifo, O_WRONLY | O_CREAT | O_TRUNC, 0666 ) );
				}
			}
		}
		fifo = fifo->next;
	}
/* Search parent input fifo */
	if( parent ) {
		fifoRead = parent->oFifos;

		while( fifoRead ) {
			if( fifoRead->reader == stream )	break;
			fifoRead = fifoRead->next;
		}
		if( !fifoRead ) {
			DBG( 't',fprintf(stderr, "fifo parent not found !\n"););
			return( -1 );
		}
	}
	else
		fifoRead = NULL;

/* A callback stream read in parent fifo en write in any sons fifos, so no need to have his hown fifo */
	if( IsCB(stream) ) {
		if( !parent ) {
			DBG( 't',fprintf(stderr,"SetupStream: callback an no parent !\n"););
			return( -1 );
		}
		if( !fifoRead ) {
			DBG( 't',fprintf(stderr,"SetupStream: callback need a parent read fifo !\n"););
			return( -1 );
		}
		fifoRead->fifoFdR = OpenFifoRead( fifoRead->oFifo );
		if( fifoRead->fifoFdR < 0 ) {
			DBG( 't',fprintf(stderr,"SetupStream: OpenFifoRead(%s) failed errno: %d !\n", fifoRead->oFifo, errno););
			return( -1 );
		}
		SetCloExec( fifoRead->fifoFdR );

		DBG( 't',fprintf(stderr,"SetupStream: %p(callback) parent: %p\n", stream, parent ););
		DBG( 't',fprintf(stderr,"Input=%s\n", fifoRead->oFifo ););
		DBG( 't',fprintf(stderr,"Output="););
		fifo = stream->oFifos;
		while( fifo ) {
			fifo->fifoFdW = OpenFifoWrite( fifo->oFifo );
			if( fifo->fifoFdW < 0 ) {
				DBG( 't',fprintf(stderr,"Opening (%s) failed errno: %d!\n", fifo->oFifo, errno ););
				return( -1 );
			}
			SetCloExec( fifo->fifoFdW );
			DBG( 't',fprintf(stderr,"%s,", fifo->oFifo ););
			fifo = fifo->next;
		}
		pthread_create( &stream->thread, NULL, ThreadRoutine, (void *)stream );
		tampon->nThread++;
		DBG( 't',fprintf(stderr,"\n"););
		goto doNext;
	}

	DBG( 't',fprintf(stderr,"SetupStream: %p(%s) parent: %p\n", stream, stream->command, parent ););
#if !SIMUL
	DBG( 't',fprintf(stderr,"Input=%s Output=%s\n", fifoRead ? fifoRead->oFifo : "<none>", stream->oFifos->oFifo ););
	pid = fork();
	if( pid < 0 )	return( -1 );
	if( !pid ) {
		int i;
		int handle;
		char * new_argv[4];

		for( i = 3; i < 100; i++ )	close( i );
		if( fifoRead ) {
			handle = OpenFifoRead( fifoRead->oFifo );
			dup2( handle, 0 );
			close( handle );
		}
		else {
			close( 0 );
		}
/* Set output fifo */
		handle = OpenFifoWrite( stream->oFifos->oFifo );
		dup2( handle, 1 );
		close( handle );
		if( !IsLevelSet( 'e' ) ) {
			handle = open( "/dev/null", O_WRONLY );
			dup2( handle, 2 );
			close( handle );
		}
		new_argv[0] = SH_NAME;
		new_argv[1] = "-c";
		new_argv[2] = stream->command;
		new_argv[3] = NULL;
		(void) execve(	SH_PATH,
				(char * const *) new_argv,
				(char * const *)environ);
		/* Die if it failed.  */
		_exit(127);
	}
	stream->pid = pid;
	tampon->nProc++;
#else
	DBG( 't',fprintf(stderr,"Input=%s Output=%s\n", parent ? parent->oFifo : "<none>", stream->oFifo ););
#endif
doNext:;
	if( stream->up ) {
		if( SetupStream( tampon, stream->up, stream ) < 0 )	return( -1 );
	}
	if( stream->next ) {
		if( SetupStream( tampon, stream->next, parent ) < 0 )	return( -1 );
	}
	return( 0 );
}
/*------------------------------------------------------------------------------
	WRITEBUFFER-
Linux!jef 2005/12/07 01:10:29
------------------------------------------------------------------------------*/

static int WriteBuffer( fifo, buffer, lg )
TFifo * fifo;
char * buffer;
int lg;
{
	while( lg ) {
		int len = write( fifo->fifoFdW, buffer, lg );

		if( len < 0 )	return( -1 );
		if( len > 0 ) {
			buffer += len;
			lg -= len;
		}
	}
	return( 0 );
}

/*------------------------------------------------------------------------------
	MNGSTREAM-
Linux!jef 2005/12/07 01:05:57
------------------------------------------------------------------------------*/

static int MngStream( tampon, stream )
Tampon * tampon;
TStream * stream;
{
	if( IsCB(stream) ) {
		if( !stream->threadKilledF ) {
			if( !stream->thread ) {
				tampon->nThread--;
				stream->threadKilledF = 1;
				DBG( 't',fprintf(stderr,"MngStream: stream %p thread died remain: %d thread\n", stream, tampon->nThread ););
			}
		}
	}
	else {
		if( stream->pid ) {
			int status;
			int pid;

			pid = waitpid( stream->pid, &status, WNOHANG );
			if( pid == stream->pid ) { /* stream is dead */
				if( stream->endCallBack )	(* stream->endCallBack)( stream, stream->arg );
				stream->pid = 0;
				tampon->nProc--;
				DBG( 't',fprintf(stderr,"MngStream: stream %p died pid %d remain: %d proc\n", stream, pid, tampon->nProc ););
			}
		}
	}
	if( stream->next ) {
		if( MngStream( tampon, stream->next ) < 0 )	return( -1 );
	}
	if( stream->up ) {
		if( MngStream( tampon, stream->up ) < 0 )	return( -1 );
	}
	return( 0 );
}

/*------------------------------------------------------------------------------
	SETUPSTAGE-
Linux!jef 2005/12/13 21:02:46
------------------------------------------------------------------------------*/

static int SetupStage( tampon, stage )
Tampon * tampon;
TStage * stage;
{
	pid_t pid;

	if( !stage )	return( 0 );

	DBG( 't',fprintf(stderr,"SetupStage: launching %s\n", stage->command ););
	pid = fork();
	if( pid < 0 )	return( -1 );

	if( !pid ) {
		int i;
		int handle;
		char * new_argv[4];

		for( i = 3; i < 100; i++ )	close( i );
		if( !IsLevelSet( 'e' ) ) {
			handle = open( "/dev/null", O_WRONLY );
			dup2( handle, 2 );
			close( handle );
		}
		new_argv[0] = SH_NAME;
		new_argv[1] = "-c";
		new_argv[2] = stage->command;
		new_argv[3] = NULL;
		(void) execve(	SH_PATH,
				(char * const *) new_argv,
				(char * const *)environ);
		/* Die if it failed.  */
		_exit(127);
	}
	stage->pid = pid;
	tampon->nProc++;

	return( SetupStage( tampon, stage->next ) );
}
/*------------------------------------------------------------------------------
	MNGSTAGE-
Linux!jef 2005/12/13 21:08:26
------------------------------------------------------------------------------*/

static int MngStage( tampon, stage )
Tampon * tampon;
TStage * stage;
{
	if( !stage )	return( 0 );

	if( stage->pid ) {
		int status;
		int pid;

		pid = waitpid( stage->pid, &status, WNOHANG );
		if( pid == stage->pid ) { /* stage is dead */
			stage->pid = 0;
			tampon->nProc--;
			DBG( 't',fprintf(stderr,"Stage died pid %d remain: %d proc\n", pid, tampon->nProc ););
		}
	}
	return( MngStage( tampon, stage->next ) );
}

/*------------------------------------------------------------------------------
	TAMPONRUN-
Linux!jef 2005/12/06 21:33:55
------------------------------------------------------------------------------*/

int TamponRun( tampon, uiCB )
Tampon * tampon;
int (* uiCB)();
{
	ErrorCondition = 0;
	if( BufferInit() < 0 ) {
		DBG( 't',fprintf(stderr,"TamponRun: BufferInit failed !\n"););
		return( -1 );
	}
	if( SetupStream( tampon, tampon->stream, NULL ) < 0 ) {
		DBG( 't',fprintf(stderr,"TamponRun: SetupStream failed !\n"););
		return( -1 );
	}
	if( SetupStage( tampon, tampon->stages ) < 0 ) {
		DBG( 't',fprintf(stderr,"TamponRun: SetupStage failed !\n"););
		return( -1 );
	}

#if !SIMUL
	while( 1 ) {
		if( MngStream( tampon, tampon->stream ) < 0 )	break;
		if( MngStage( tampon, tampon->stages ) < 0 )	break;
		if( !tampon->nProc && !tampon->nThread )	break;
		if( uiCB && (* uiCB)( tampon ) < 0 ) {
			DBG( 't',fprintf(stderr,"TamponRun: UI Callback exit !\n"););
			return( -1 );
		}
		usleep( 100 * 1000 );
	}
#endif
	DBG( 't',fprintf(stderr,"TamponRun: exiting !\n"););
	return( 0 );
}

/*------------------------------------------------------------------------------
	STREAMDESTROYPROC-
Linux!jef 2005/12/06 21:55:40
------------------------------------------------------------------------------*/

static int StreamDestroyProc( stream )
TStream * stream;
{
	if( stream->up )	StreamDestroyProc( stream->up );
	if( stream->next )	StreamDestroyProc( stream->next );
	if( stream->pid ) {
		int status;

		DBG( 't',fprintf(stderr,"StreamDestroyProc: killproc %d\n", stream->pid ););
		kill( stream->pid, SIGTERM );
		waitpid( stream->pid, &status, 0 );
		stream->pid = 0;
	}
	return( 0 );
}

/*------------------------------------------------------------------------------
	STREAMDESTROYTHREADS-
Linux!jef 2005/12/06 21:55:40
------------------------------------------------------------------------------*/

static int StreamDestroyThreads( stream )
TStream * stream;
{
	TFifo * fifo;

	if( stream->up )	StreamDestroyThreads( stream->up );
	if( stream->next )	StreamDestroyThreads( stream->next );

	if( stream->thread ) {
		DBG( 't',fprintf(stderr,"StreamDestroyThreads: killthread %p\n", (void *)stream->thread ););
		pthread_cancel( stream->thread );
		pthread_join( stream->thread, NULL );
		DBG( 't',fprintf(stderr,"StreamDestroyThreads: killthread %p destroyed\n", (void *)stream->thread ););
		stream->thread = 0;
	}

	fifo = stream->oFifos;
	while( fifo ) {
		if( fifo->thread ) {
			DBG( 't',fprintf(stderr,"StreamDestroyThreads: killfifothread %p\n", (void *)fifo->thread ););
			pthread_cancel( fifo->thread );
			pthread_join( fifo->thread, NULL );
			DBG( 't',fprintf(stderr,"StreamDestroyThreads: killfifothread %p destroyed\n", (void *)fifo->thread ););
			fifo->thread = 0;
		}
		fifo = fifo->next;
	}
	return( 0 );
}

/*------------------------------------------------------------------------------
	STREAMDESTROYRESOURCES-
Linux!jef 2005/12/06 21:55:40
------------------------------------------------------------------------------*/

static int StreamDestroyResources( stream )
TStream * stream;
{
	TFifo * fifo;

	if( stream->up )	StreamDestroyResources( stream->up );
	if( stream->next )	StreamDestroyResources( stream->next );

	fifo = stream->oFifos;
	while( fifo ) {
		CLOSE( fifo->fifoFdR );
		CLOSE( fifo->fifoFdW );
		if( fifo->rmF )	unlink( fifo->oFifo );
		fifo = fifo->next;
	}
	return( 0 );
}

/*------------------------------------------------------------------------------
	STREAMDESTROYALLOC-
Linux!jef 2005/12/06 21:55:40
------------------------------------------------------------------------------*/

static int StreamDestroyAlloc( stream )
TStream * stream;
{
	TFifo * fifo;

	if( stream->up )	StreamDestroyAlloc( stream->up );
	if( stream->next )	StreamDestroyAlloc( stream->next );

	fifo = stream->oFifos;
	while( fifo ) {
		TFifo * n;

		if( fifo->oFifo ) {
			free( fifo->oFifo );
			fifo->oFifo = NULL;
		}
		n = fifo->next;
		free( fifo );
		fifo = n;
	}
	if( stream->command ) {
		free( stream->command );
		stream->command = NULL;
	}
	free( stream );
	return( 0 );
}

/*------------------------------------------------------------------------------
	TAMPONDESTROY-
Linux!jef 2005/12/06 21:55:01
------------------------------------------------------------------------------*/

int TamponDestroy( tampon )
Tampon * tampon;
{
	TStage * s;

/* Kill the stream head */
	if( tampon->stream->pid ) {
		int status;

		DBG( 't',fprintf(stderr,"TamponDestroy: killprochead %d\n", tampon->stream->pid ););
		kill( tampon->stream->pid, SIGTERM );
		waitpid( tampon->stream->pid, &status, 0 );
		tampon->stream->pid = 0;
	}
/* Gracefull shutdown for threads */
	ErrorCondition = 1;
	while( tampon->nThread > 0 ) {
		MngStream( tampon, tampon->stream );
		CTEST2(fprintf(stderr,"TamponDestroy: remain: %d thread\n", tampon->nThread ););
		usleep( 10 * 1000 );
	}
	StreamDestroyProc( tampon->stream );

	StreamDestroyResources( tampon->stream );

	StreamDestroyAlloc( tampon->stream );

	s = tampon->stages;

	while( s ) {
		TStage * n = s->next;

		if( s->pid ) {
			int status;

			DBG( 't',fprintf(stderr,"TamponDestroy: killproc %d\n", s->pid ););
			kill( s->pid, SIGTERM );
			waitpid( s->pid, &status, 0 );
			s->pid = 0;
		}
		free( s->command );
		free( s );
		s = n;
	}
	free( tampon );
	BufferDestroy();
	return( 0 );
}
#endif
