/*************************************************

child.cpp

introduces the child class. Provides functions for handling
child processes.

Author: Ryan Findley


History:
who	when		what
--------------------------------
ryan	5/10/99		started

***************************************************/

//#define TEST

//-------------------- include files --------------------------
#include <string.h>	
#include <signal.h>
#include <stdio.h>
#include <sys/sched.h>
#include <sys/kernel.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <stdlib.h>

#include "DML_child.h"

//-------------------- private definitions --------------------

//-------------------- module code ----------------------------

/************************************************************
	child()
	default constructor
	Creates a blank child instance, to be filled later.

	child(char *name)
	useful constructor
	Creates a child process, the executable "name".
*************************************************************/
DML_child::DML_child()	
{
	//walk the child list and insert this child at the end
	DML_child *lastChild = childList;

	if( lastChild == NULL )	//there is no last child: this is the first on list
	{
		prev = NULL;
		signal( SIGCHLD, childDeathHandler );	//handle child deaths
		childList = this;
	}
	else
	{
		while( lastChild->next != NULL )
			lastChild = lastChild->next;
		prev = lastChild;
		prev->next = this;
	}
	
	next = NULL;
	
	//init some data to meaningful defaults	
    pid = -1;	
    proxy = -1;
    priority = -1;
    verbosity = QUIET;
	localArgv = NULL;

	static int childNum = 1;
	sprintf( name, "blank%3d", childNum++ );		//give the child a default name
}

/************************************
	opens the child with NO arguments.
************************************/
DML_child::DML_child(const char * theName)
{
	//walk the child list and insert this child at the end
	DML_child *lastChild = childList;

	if( lastChild == NULL )	//there is no last child: this is the first on list
	{
		prev = NULL;
		signal( SIGCHLD, childDeathHandler );	//handle child deaths
		childList = this;
	}
	else
	{
		while( lastChild->next != NULL )
			lastChild = lastChild->next;
		prev = lastChild;
		prev->next = this;
	}
	
	next = NULL;
	
	//init some data to meaningful defaults	
    pid = -1;	
    proxy = -1;
    priority = -1;
    verbosity = QUIET;
	localArgv = NULL;

	open(theName);
    attachProxy();	//just 'cause it's useful
}

DML_child::DML_child(const char * theName, int waitForResponse)
{
	//walk the child list and insert this child at the end
	DML_child *lastChild = childList;

	if( lastChild == NULL )	//there is no last child: this is the first on list
	{
		prev = NULL;
		signal( SIGCHLD, childDeathHandler );	//handle child deaths
		childList = this;
	}
	else
	{
		while( lastChild->next != NULL )
			lastChild = lastChild->next;
		prev = lastChild;
		prev->next = this;
	}
	
	next = NULL;
	
	//init some data to meaningful defaults	
    pid = -1;	
    proxy = -1;
    priority = -1;
    verbosity = QUIET;
	localArgv = NULL;

	
	open(theName);
    attachProxy();	//just 'cause it's useful
	
    if(waitForResponse == WAIT_RESPONSE)
		sendInfoMsg();
	
}


/*******************************************************************************
	opens the child: specify the arguments, the wait for respons, and the verbosity.
*************************************************************************************/
DML_child::DML_child(const char * theName, const char *theArgv[], int waitForResponse, int theVerbosity)
{
	//walk the child list and insert this child at the end
	DML_child *lastChild = childList;

	if( lastChild == NULL )	//there is no last child: this is the first on list
	{
		prev = NULL;
		signal( SIGCHLD, childDeathHandler );	//handle child deaths
		childList = this;
	}
	else
	{
		while( lastChild->next != NULL )
			lastChild = lastChild->next;
		prev = lastChild;
		prev->next = this;
	}
	
	next = NULL;
	
	//init some data to meaningful defaults	
    pid = -1;	
    proxy = -1;
    priority = -1;
	localArgv = NULL;

	
    verbosity = theVerbosity;
    if(verbosity == LOUD)
    {
		printf("*** creating child: %s... ",theName);
		fflush(stdout);
    }
	theArgv[0] = theName;
	setArgs(theArgv);
    open(theName);
    attachProxy();	//just 'cause it's useful
	
    if(waitForResponse == WAIT_RESPONSE)
		sendInfoMsg();
	
    if(pid != -1 && verbosity == LOUD)
		printf("...success!\n");
    else if(verbosity == LOUD)
		printf("...failure!\n");
}
    
DML_child::~DML_child()
{
    signal(SIGCHLD,SIG_IGN);					//ignore child death handler for a sec
	if(verbosity == LOUD)
	    printf("closing child: %s.  \n",name);
	if(pid != -1)
		kill();
		
    pid = -1;
    strcpy(name,"<defunct>");

    //update the child listing
	if( prev )
		prev->next = next;
	else if( childList == this )
		childList = next;
	if( next )
		next->prev = prev;

    signal(SIGCHLD,childDeathHandler);
}

/****************************************************************
	setArgs( const char * theArgv[])
	sets the arguments to the new process.
	
	open(char * theName)
	opens up the process (spawns)

	sendInfoMsg()
	sends the default info message, containing info.

	int sendMsg(int theMsg)
	sends an integer message and returns the integer response.
	it's so common, I figured I'd include it as a member.
*****************************************************************/
DML_child::setArgs(char const *theArgv[])
{
	localArgv = theArgv;
}

DML_child::open(const char * theName)
{
    pid_t theChildPid;

	if( localArgv == NULL )
	{
		const char *argv[] = { {theName}, NULL };
		setArgs(argv);
	}
    theChildPid = spawnvp(P_NOWAIT, theName, localArgv);
    if (theChildPid == -1)
    {
    	if(verbosity == LOUD)
	    printf("New process error: %s  \n",theName);
    	pid = -1;
    }
    else
    {
		strncpy(name,theName,MAX_CHILD_NAME_LENGTH);
		if(strlen(theName) > MAX_CHILD_NAME_LENGTH && verbosity == LOUD)
			printf("The child name selected, %s, was too long.\n Truncated to %s.\n",theName,name);
		pid = theChildPid;
    }
}

DML_child::sendInfoMsg()
{
    int status;
    msg.proxy = proxy;
    msg.verbosity = verbosity;

    status = Send(pid, &msg, &rmsg, sizeof(childInfoStruct), sizeof(childInfoStruct) );
    if (status == -1 && verbosity == LOUD) //process doesn't exist, or interrupted with a signal
    	printf("error sending message to child: %s  \n",name);
    priority = rmsg.priority;
}


/******************************************************************
	int beQuiet() and int beLoud()
	changes the verbosity, returns the previous verbosity.
*******************************************************************/
int DML_child::beQuiet()
{
    int oldVerbosity;
    oldVerbosity = verbosity;
    verbosity = QUIET;
    return(oldVerbosity);
}

int DML_child::beLoud()
{
    int oldVerbosity;
    oldVerbosity = verbosity;
    verbosity = LOUD;
    return(oldVerbosity);
}

/******************************************************************
	attachProxy();
	attaches a proxy to the child. the InfoMsg must be sent AFTER attaching a proxy.
	if failure in any way, proxy will be -1
	
	kick();
	triggers the child's proxy. The proxy must exist. returns -1 on error, 0 on success
*******************************************************************/
DML_child::attachProxy()
{
    pid_t theProxy;
    proxy = qnx_proxy_attach(pid,0,0,-1);
    if (proxy == -1 && verbosity == LOUD)
       	printf("Error attaching proxy to child:  %s  \n",name);
}    
    
int DML_child::kick()
{
    int status;
    
    if (pid != -1)
		status = Trigger(proxy);
    else
		return(-1);
	
    return(status);
}

/*************************************
	print
	
	prints a formatted statement about the child.
	
**************************************/
DML_child & DML_child::print()
{
    printf("\nInformation on child process \"%s\"\n",name);
    printf("-----------------------------------------\n");
    printf("process ID#: %d\n",pid);
    if(verbosity == LOUD)
	printf("verbosity: LOUD\n");
    else if(verbosity == QUIET)
	printf("verbosity: QUIET\n");
    printf("attached proxy ID: %d\n",proxy);
    printf("child priority: %d\n",priority);
	
    return(*this);
}

/************************************
	signal handler
	
	checks which child died. the children ignore SIGUSR1
*************************************/
static void DML_child::childDeathHandler( int )
{
	signal( SIGCHLD, SIG_IGN);
	
	DML_child *	theChild = DML_child::childList;

	while( theChild != NULL )
    {
		if( theChild->pid > 0 && 									//only want to check CHILD instances that have a PID (i.e. they're running)
			getprio( theChild->pid ) == -1  )						//getprio returns -1 if process doesn't exist
		{															//so remove it from the list
			waitpid(theChild->pid, NULL, 0);						//wait on it so it doesn't hang around as a ZOMBIE
	    	theChild->pid = -1;
	    	theChild->proxy = -1;
			if( theChild->verbosity == LOUD )						//if the child is verbose,
	    		printf("%s terminated itself.\n",theChild->name);	//print a message
	    	strcpy(theChild->name,"<defunct>");						//delete the name
	    	if( theChild->prev )
				theChild->prev->next = theChild->next;				//change the flanking children 
			if( theChild->next )									//next and prev to skip this one.
				theChild->next->prev = theChild->prev;
		}

		theChild = theChild->next;
    }
	
	signal( SIGCHLD, DML_child::childDeathHandler );
}
			
	



/************************************
	static members
	these members can be invoked any time,
	regardless of the creation of DML_child objects. (i think)
	These members operate on ALL of the DML_child objects
	currently instantiated in the entire program.
************************************/
//init the static data members

//the head of the child list
DML_child * DML_child::childList = NULL;


static DML_child::killChildren()
{
   	signal(SIGCHLD,SIG_IGN);		//we KNOW the kids are getting killed

	DML_child * theChild = childList;
	DML_child * nextChild;
	
	while( theChild )
	{
		nextChild = theChild->next;
		theChild->kill();
		theChild = nextChild;
	}
}

static DML_child::printChildren()
{
    if(childList == NULL)
		printf("no children exist.\n");
    else 
    {
		DML_child * theChild = childList;
		while( theChild )
		{
			theChild->print();
			theChild = theChild->next;
		}
	}
}










#ifdef TEST
//test harness

#include <conio.h>
void endMaster(int sigCount);


void main(void)
{

	DML_child foo, bar, foobar, wakawaka;
	DML_child child("child");
	DML_child child2("child");
	DML_child child3("child");
		
	signal(SIGTERM,endMaster);

	child.sendInfoMsg();
	child2.sendInfoMsg();
	child3.sendInfoMsg();
	
	fflush(stdin);
	getch();
	getch();
	
	DML_child::printChildren();
	getch();
		
}	

void endMaster(int sigCount)
{
    DML_child::killChildren();
	exit(1);
}



#endif //TEST
