/* jobs.c
   get some idea of the jobs spawned by Xgopher */

     /*---------------------------------------------------------------*/
     /* Xgopher        version 1.3     08 April 1993                  */
     /*                version 1.2     20 November 1992               */
     /*                version 1.1     20 April 1992                  */
     /*                version 1.0     04 March 1992                  */
     /* X window system client for the University of Minnesota        */
     /*                                Internet Gopher System.        */
     /* Allan Tuchman, University of Illinois at Urbana-Champaign     */
     /*                Computing and Communications Services Office   */
     /* Copyright 1992, 1993 by                                       */
     /*           the Board of Trustees of the University of Illinois */
     /* Permission is granted to freely copy and redistribute this    */
     /* software with the copyright notice intact.                    */
     /*---------------------------------------------------------------*/


#include <stdio.h>

#include "conf.h"
#include "gopher.h"
#include "osdep.h"
#include "jobs.h"

#include <sys/wait.h>
#include <sys/time.h>
#ifndef ISCX
#include <sys/resource.h>
#endif

static jobList *jobs = (jobList *) NULL;


/* addJob
   add the given pid to the internal job data structures */

void
addJob(t, pid)
char		t;
PID_TYPE	pid;
{
	jobList	*j=(jobList *) malloc(sizeof(jobList));

	j->jobType = t;
	j->pid	   = pid;
	j->next    = jobs;

	jobs = j;

}


/* removeJob
   remove the given pid from the internal job data structures */

BOOLEAN
removeJob(pid)
PID_TYPE	pid;
{
	jobList *j, *jp;

	jp = NULL;
	for (j=jobs; (j != (jobList *) NULL  &&  j->pid != pid); j=j->next){
		jp = j;
	}

	if (j != NULL) {		/* pid in list */
#ifdef DEBUG
		fprintf (stderr, "Remove process %d (type %c)\n",
				j->pid, j->jobType);
#endif	/* DEBUG */
		if (jp == NULL) {	/* first element */
			jobs = j->next;
		} else {
			jp->next = j->next;
		}
		free(j);
		return TRUE;
	} else {
		return FALSE;
	}
}


/* findJobPID
   return the item type of the child with the supplied pid */

char	
findJobPID(pid)
PID_TYPE	pid;
{
	jobList *j;

	for (j=jobs; (j != (jobList *) NULL  &&  j->pid != pid); j=j->next){
	}

	return (j == NULL) ? NO_TYPE : j->jobType;
}


/* findJobType
   return the pid of the first child that matches the supplied type */

PID_TYPE
findJobType(t)
char		t;
{
	jobList *j;

	for (j=jobs; (j != (jobList *) NULL  &&  j->jobType != t); j=j->next){
	}

	return (j == NULL ? NO_JOB: j->pid);
}


/* killItemProcess
   terminate a specific child process */

void
killItemProcess(pid)
PID_TYPE	pid;
{
#ifdef DEBUG
	fprintf (stderr, "killing process %d\n", pid);
#endif	/* DEBUG */
	kill(pid, KILL_SIGNAL);
	waitOnChildren();
}


/* killAllItemType
   terminate all items that match the supplied type */

void
killAllItemType(t)
char		t;
{
	jobList *j;

#ifdef DEBUG
	listJobs();
	fprintf (stderr, "killing processes for item type %c\n", t);
#endif	/*  DEBUG */

	for (j=jobs; j != (jobList *) NULL; j=j->next){
		if (j->jobType == t) {
			killItemProcess(j->pid);
		}
	}

}


/* killAllItemProcesses
   terminate all child processes. */

void
killAllItemProcesses()
{
	jobList *j;

	for (j=jobs; j != (jobList *) NULL; j=j->next){
		killItemProcess(j->pid);
	}
}


/* waitForJob
   block until a specified job is completed */

void
waitForJob(waitPID)
PID_TYPE	waitPID;
{
	PID_TYPE	donePID;
#if    	!defined(SYSV) && !defined(__convex__) && !defined(__bsdi__) || defined(sgi) && !defined(linux)
	union wait	status;
#else
	int		status;
#endif	/* !defined(SYSV) */

	do {
		donePID = wait(&status);
#ifdef		DEBUG
		fprintf (stderr, "Process %d is done\n", donePID);
#endif		/*  DEBUG */
		if (donePID != (PID_TYPE) 0) {
			removeJob(donePID);
		}

	} while (donePID != waitPID);
}


/* childIsGone
   handle signal noting the termination of a child process */

void
childIsGone()
{
	waitOnChildren();
}


/* waitOnChildren
   handle request or signal noting the termination of a child process */

void
waitOnChildren()
{
	PID_TYPE	donePID;

#if    	!defined(SYSV) && !defined(__convex__) && !defined(__bsdi__) || defined(sgi) && !defined(linux)
	union wait	status;
#else
	int		status;
#endif	/* !defined(SYSV) */

#if	!defined(macII) && !defined(CRAY) && !defined(SVR4) && !defined(SYSV)
#define WAIT3
	struct rusage	rusage;
#endif	/* MacII, CRAY, SVR4, SYSV */

	
	do {

#if		defined(macII)
		donePID = wait3(&status, WNOHANG, NULL);
#else		/* not macII */
#  if		!defined(WAIT3)
		donePID = waitpid(-1, &status, WNOHANG);
#  else		/* use wait3 call for others */
		donePID = wait3(&status, WNOHANG, &rusage);
#  endif	/* other */
#endif		/* */

		
#ifdef DEBUG
		fprintf (stderr, "Process %d is done\n", donePID);
#endif	/*  DEBUG */
		if (donePID != (PID_TYPE) 0) {
			removeJob(donePID);
		}

	} while (donePID > (PID_TYPE) 0);
}


#ifdef DEBUG
void
listJobs()
{
	jobList *j;

	fprintf (stderr, "Active processes started by Xgopher\n");

	for (j=jobs; j != (jobList *) NULL; j=j->next){
		fprintf (stderr, "    pid: %6d    Xgopher item type \'%c\'\n",
				j->pid, j->jobType);
	}

	fprintf (stderr, "End of active process list\n");
}
#endif
