/* this module intended to handle child process clean up through callbacks*/
/* written for version 2.5 */

#include <stdio.h>

#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>

#include "list.h"

List childProcessList;

typedef struct {
	pid_t pid;
	void (*callback)();
	void *callBackData;
	} ProcessHandle;


void InitChildProcessor()
{
	childProcessList = ListCreate();
}


/* Add a child process handler.  Callback is made when child dies */
/* callback is of the form callback(callBackData,pid); */
void AddChildProcessHandler(pid_t pid,void (*callback)(), void *callBackData)
{
ProcessHandle *p;

	if (!(p = (ProcessHandle *) malloc(sizeof(ProcessHandle)))) {
		fprintf(stderr,"Out of Memory\n");
		return;
		}
	p->pid = pid;
	p->callback = callback;
	p->callBackData = callBackData;

	ListAddEntry(childProcessList,p);
}



static ProcessHandle *SearchForChildRecordByPID(pid_t pid)
{
ProcessHandle *p;

	p = (ProcessHandle *) ListHead(childProcessList);
	while(p) {
		if (p->pid == pid) {
			return(p);
			}
		p = (ProcessHandle *) ListNext(childProcessList);
		}

	return(NULL);

}

/* terminate the children... 
   you may want to remove SIGCHLD signal handler before calling this routine
*/
void KillAllChildren()
{
ProcessHandle *p;

	/* first, be nice and send SIGHUP */
	p = (ProcessHandle *) ListHead(childProcessList);
	while(p) {
		kill(p->pid,SIGHUP);
		p = (ProcessHandle *) ListNext(childProcessList);
		}

	/* hack and slash */
	p = (ProcessHandle *) ListHead(childProcessList);
	while(p) {
		kill(p->pid,SIGKILL);
		p = (ProcessHandle *) ListNext(childProcessList);
		}
}


/* callback routine for SIGCHLD signal handler */
void ChildTerminated()
{
pid_t pid;
ProcessHandle *p;

#ifdef SOLARIS
	pid = waitpid((pid_t)(-1),NULL,WNOHANG);
	signal(SIGCHLD, (void (*)())ChildTerminated); /*Solaris resets the signal on a catch*/
#else
	pid = wait3(NULL,WNOHANG,NULL);
#endif

	p = SearchForChildRecordByPID(pid);
	if (!p) {
		/* un registered child process */
		return;
		}

	(p->callback)(p->callBackData,p->pid);

	ListDeleteEntry(childProcessList,p);
	free(p);

	return;
}

