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

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 NOT_TEST

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

#include "DML_child.hpp"
//-------------------- private definitions --------------------


//-------------------- private prototypes ---------------------

//-------------------- 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()	
{
	char dummy[32];
	
	if(nChildren > MAX_N_CHILD)
	{
		printf("Error! Number of children limted to 256. Exiting program.\n");
		kill(getpid(),SIGTERM);
	}
	
	if(nChildren == 0)
	{
		signal(SIGCHLD,childDeathHandler);
		signal(SIGUSR1,SIG_IGN);
	}
	
    id = getpid();		//since this "child process" is just a data struct its id is the master's
    proxy = -1;
    priority = -1;
    verbosity = LOUD;
    flags = P_NOWAIT;
	
    strcpy(name, strcat("blank",itoa(nChildren,dummy,10)));

	childNum = nChildren;
	nChildren++;
	theChildren[childNum] = this;
}



DML_child::DML_child(char * theName, int theVerbosity, int waitForResponse)
{
	if(nChildren > MAX_N_CHILD)
	{
		printf("Error! Number of children limited to 256. Exiting program.\n");
		kill(getpid(),SIGTERM);
	}
	
	if(nChildren == 0)
	{
		signal(SIGCHLD,childDeathHandler);
    	signal(SIGUSR1,SIG_IGN);
	}
	
	id = -1;
    proxy = -1;
    priority = -1;
    flags = P_NOWAIT;
	verbosity = theVerbosity;
	if(verbosity == LOUD)
	{
		printf("*** creating child: %s... ",theName);
		fflush(stdout);
	}
    open(theName);
    attachProxy();	//just 'cause it's useful
	
	if(waitForResponse == WAIT_RESPONSE)
		sendInfoMsg();
	
	if(id != -1 && verbosity == LOUD)
		printf("...success!\n");
	else if(verbosity == LOUD)
		printf("...failure!\n");
	
	//save your place in the nChildren array
	childNum = nChildren;
	nChildren++;
	theChildren[childNum] = this;
	
	
}
    
DML_child::~DML_child()
{
	signal(SIGCHLD,SIG_IGN);
	if(id != -1)
	{
		if(verbosity == LOUD)
			printf("closing child: %s.  \n",name);
    	kill(id,SIGTERM);
    }
	id = -1;
    strcpy(name,"<defunct>");
	
	//update the child listing
	nChildren--;
	for(int i=childNum; i<nChildren; i++)
	{
		theChildren[i] = theChildren[i+1];
		theChildren[i]->childNum--;
	}
	signal(SIGCHLD,childDeathHandler);
	
	
}

/****************************************************************
	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::open(char * theName)
{
    pid_t theChildPid;
    
    theChildPid = spawnlp(flags, theName, theName, NULL);
    if (theChildPid == -1)
	{
    	if(verbosity == LOUD)
			printf("New process error: %s  \n",name);
    	id = -1;
    }
	
	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);
    id = theChildPid;

}

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

    status = Send(id, &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 DML_child::sendMsg(int theMsg)	//returns -1 on error: -1 can still be valid receive data, so watch out
{
    int theRmsg;
    int status;
    
    status = Send(id, &theMsg, &theRmsg, sizeof(int), sizeof(int) );
    if (status == -1) //process doesn't exist, or interrupted with a signal
    {
		if(verbosity == LOUD)
	    	printf("error sending message to child: %s  \n",name);
    	return(-1);
    }
    return(theRmsg);
}

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

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

/******************************************************************
	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(id,0,0,-1);
    if (proxy == -1 && verbosity == LOUD)
       	printf("Error attaching proxy to child:  %s  \n",name);
}    
    
int DML_child::kick()
{
    int status;
    
    if (id != -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("Child list #:  %d\n",childNum);
	printf("process ID#: %d\n",id);
	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 sig_count)
{
	for(int i=0; i<DML_child::nChildren; i++)
	{
		if( kill(DML_child::theChildren[i]->id,SIGUSR1) == -1)
		{
			DML_child::theChildren[i]->id = -1;
			DML_child::theChildren[i]->proxy = -1;
			printf("child %s terminated itself.\n",DML_child::theChildren[i]->name);
			strcpy(DML_child::theChildren[i]->name,"");
			i--;							//must repeat with new child i
			DML_child::nChildren--;			//now there's 1 fewer child
			for(int j=(i+1);j<DML_child::nChildren;j++)	//so move everyone down 1
			{
				DML_child::theChildren[j] = DML_child::theChildren[j+1];
				DML_child::theChildren[j]->childNum--;	//it's now 1 lower on the list
			}
			
		}
	}
}
			
	



/************************************
	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 number of children the process currently has open
int DML_child::nChildren = 0;

//an array of pointers to children.
DML_child ** DML_child::theChildren = new DML_child*[MAX_N_CHILD];


static DML_child::killChildren()
{
	int n = nChildren;
	signal(SIGCHLD,SIG_IGN);		//we KNOW the kids are getting killed
	for(int i=0;i<n;i++)
		theChildren[0]->~DML_child(); //~DML_child() updates the child list

	delete[] theChildren;
}

static DML_child::printChildren()
{
	if(nChildren == 0)
		printf("no children exist.\n");
	else 
	{
		printf("number of children: %d\n",nChildren);
		for(int i=0; i<nChildren; i++)
			theChildren[i]->print();
	}
}

#ifdef TEST
//test harness

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


void main(void)
{

	DML_child foo, bar;
	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();

		
}	

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



#endif //TEST