/*---------------------------------------------------------------------
 * $Date: 2009-08-18 15:23:20 $		$Revision: 1.4 $
 *---------------------------------------------------------------------
 *             Copyright (c) 1992, Visual Edge Software Ltd.
 *
 * ALL  RIGHTS  RESERVED.  Permission  to  use,  copy,  modify,  and
 * distribute  this  software  and its documentation for any purpose
 * and  without  fee  is  hereby  granted,  provided  that the above
 * copyright  notice  appear  in  all  copies  and  that  both  that
 * copyright  notice and this permission notice appear in supporting
 * documentation,  and that  the name of Visual Edge Software not be
 * used  in advertising  or publicity  pertaining to distribution of
 * the software without specific, written prior permission. The year
 * included in the notice is the year of the creation of the work.
 *---------------------------------------------------------------------
 * Motorola additions to make the subprocess management more amenable to
 * an application doing it's own signal handling and/or child process
 * creation (esp. in the "inocuous" cases of calls to popen(3S), system(3S)
 * or getcwd(3C) are contained in "ifdef SPMD conditionals.

.VERSION
 090818		last modif

 *--------------------------------------------------------------------*/

#if defined(__FreeBSD__) || defined(__Darwin__) || defined(Cygwin)
#define ENOMSG 45
#endif


/*--- include files ---*/
#include <unistd.h>

#include <stdlib.h>
#include <limits.h>

#include "uxproto.h"
#include "version.h"
#include "subproc.h"
#include "sp_spmd.h"
#include "sp_pty.h"




extern void  UxMarkFileCloseOnExec();

/*--- global variables ---*/

spmd_info_t UxSpmd = { -1, 0, 0, {0}};

/*--- file global variables ---*/
M_FILE_VERSION("$Header")
int UxSpmdSubprocCount;
spmd_pid_pty_t UxSpmdStates[MAX_SUBPROC];
long UxSpmdInterval = LONG_INTERVAL;
int MultiByteMode;
static	int TimeOutId = -1;

/*--- forward declaration of static functions ---*/
static void SpmdCiao UXPROTO((int sig));
static void SpmdDeadProc UXPROTO((int i));
static void SpmdSigHandler UXPROTO((int sig));
static int SpmdReadPtys UXPROTO((void));
static void SpmdMain UXPROTO((void));
static void SpmdSendExitMsg UXPROTO((void));


/*----------------------------------------------------------------------
 * NAME:        <static int IsSpmdAlive()>
 * DESCRIPTION: <Verifies if the Spmd is still alive>
 * PARAMETERS:  none
 * RETURN:      True/False
 * EXT REFS:    UxSpmd
 * EXT EFFECTS:
 * ASSUMPTIONS:
 * REVISIONS:   25/04/93
 *--------------------------------------------------------------------*/
static  int IsSpmdAlive (tmsgp)
#if defined(__FreeBSD__) || defined(__Darwin__) || defined(Cygwin)
                struct mymsg *tmsgp;
#else
                struct msgbuf *tmsgp;
#endif

{
        if (tmsgp && (tmsgp->mtype == SPMD_EXIT)) {
                return False;
        }

        if ((UxSpmd.spmd_pc_msqid == -1) ||
            (UxSpmd.spmd_cp_msqid == -1) ||
            (UxSpmd.spmd_pid == -1)) {
                return False;
        }
        return True;
}


/*----------------------------------------------------------------------
 * NAME:    <int     UxExecSubprocess(sp, args)>
 * DESCRIPTION: <Send a message to the daemon to create and execute the
        process>
 * PARAMETERS:  SubprocInfo_t *sp - new process
        char    *args - command line
 * RETURN:  ERROR/NO_ERROR
 * EXT REFS:    UxSpmd
 * EXT EFFECTS: UxSpArray via sp
 * ASSUMPTIONS:
 * REVISIONS:   05/01/93    fix3810 clean-up
 *       	dd/mm/yy    fix#    fix_description
 *--------------------------------------------------------------------*/
int     UxExecSubprocess(sp, args)
SubprocInfo_t *sp;
char    *args;

{
int cmdlen;
char *process;
spmd_create_req req;
spmd_create_rsp *rsp;

    process = sp->process;
    req.mtype = SPMD_CREATE;
    req.echo = sp->echo;
    req.cmd[0] = '\0'; /*EOS*/

    if (args == NULL)  args = "";
    sprintf(req.cmd, "%s %s", process, args);

    cmdlen = strlen(req.cmd);   /* also used in msgsnd() */

    if (-1 == msgsnd(UxSpmd.spmd_pc_msqid, &req,
             cmdlen+sizeof(req.echo), IPC_NOWAIT))
    {
        perror("exec_subprocess: can not send SPMD_CREATE to daemon");
        return(ERROR);
    }

    /* now we must wait for the daemon to do it's magic ... */
    if (UxSpmdReadMsg(SPMD_RSP) == ERROR) {
        return(ERROR);
    }

    rsp = (spmd_create_rsp *)UxSpmd.spmd_rmsgp;

    if (rsp->response == NACK) {
        UxStandardError("Cannot start child process %s \n", process);
        return(ERROR);
    }

    sp->handle = rsp->handle;
    sp->pid = rsp->pid;
    return(NO_ERROR);
}



/*----------------------------------------------------------------------
 * NAME:	<int UxSpmdReadMsg (msgtyp)>
 * DESCRIPTION:	<check for and process any messages arriving from the
 *		     the Sub-Process Manager daemon
 *
 * We arrange for this routine to be called ``periodically'' to see if 
 * work has arrived from the daemon.  Each time it is called, we
 * schedule it to be called again after an UxSpmdInterval.
 * We use a longer UxSpmdInterval when no messages are found
 * than when there are messages, assuming activity may be bursty.
 *
 * Also called from UxExecSubprocess explicitly waiting for SPMD_RSP
 *
 * Note:  The timeout interval UxSpmdInterval could be adjusted in
 * many ways, to account for number of processes, or rate of activity;
 * instead of a timeout, XtWorkProcs could be used.
 *
 * The periodic use of this function is in the application (parent) process
 * so we can use XtAppAddTimeOut to schedule the calls.>
 * PARAMETERS: 	long msgtyp - message type we are waiting for
 * RETURN:	int - ERROR or 0	
 * EXT REFS:	UxSpArray, UxSpmd	
 * EXT EFFECTS: UxSpmd		
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSpmdReadMsg (msgtyp)
long msgtyp;		/* zero     -> called from timeout
		 	 * non-zero -> explicit rsp 
			 */
{
int i, rc, msgflg;
extern int errno;
int msgfound = 0;

	msgflg = msgtyp ? 0 : IPC_NOWAIT;

	UxSpmd.spmd_rmsgtyp = 0;	/* clear buffer */

	while ((rc=msgrcv(UxSpmd.spmd_cp_msqid, 
			  UxSpmd.spmd_rmsgp, MSGMAX, msgtyp, msgflg)) 
	       != -1) 
	{
		msgfound = 1;
		switch (UxSpmd.spmd_rmsgp->mtype) 
		{
		    case SPMD_OUTPUT:	/* output from child process */
		    {
			spmd_output_t *msg = (spmd_output_t *)UxSpmd.spmd_rmsgp;
			UxSpmd.spmd_rmsgrp = msg->output;
			UxSpmd.spmd_rmsgsiz = rc - sizeof(msg->pid);
			for (i=0; i<MAX_SUBPROC; i++) {
				if ((UxSpArray[i]) && 
				    (UxSpArray[i]->pid == msg->pid)) 
				{
					UxOutputHandler(UxSpArray[i]);
			    	break;
				}
			}
			ASSERT (i<MAX_SUBPROC);	/* should not fall off loop */
			break;
		    }
		    case SPMD_DIED:	/* A managed process has died */
			UX_CLOSE_COMMUNICATION();
			break;
		    case SPMD_RSP:
#ifdef DEBUG
			if (!msgtyp) {
				UxInternalError(
					"spmd_read_msg: timeout received RSP");
			}
#endif /*DEBUG*/
			return (0);
			/*NOTREACHED*/
			break;

		    default:	/* Either garbage or wrong directional msg */
#ifdef DEBUG
			UxInternalError("spmd_read_msg: protocol error");
#endif /*DEBUG*/
			break;
		}
	}
	if (rc == -1) {
		if (!IsSpmdAlive(NULL)) {
			SpmdCiao(0);
		}

		if (errno != ENOMSG) {
			/* nop body; skip interrupts */ ;
			perror("Subprocess manager: msgrcv failed");
#ifdef DEBUG
			exit (errno);
			/*NOTREACHED*/
#endif /*DEBUG*/
			return(ERROR);
		}
	}

	/*-----------------------------------------------------
	 * If called from the timer callback, re-arm.
	 *-----------------------------------------------------*/
	if (!msgtyp) {
		/* Assume that activity means there may be more.
		 */
		UxSpmdInterval = msgfound ? SHORT_INTERVAL : LONG_INTERVAL;
		TimeOutId = XtAppAddTimeOut(UxAppContext, 
					UxSpmdInterval, 
					(XtTimerCallbackProc)UxSpmdReadMsg, 0); 
	}
	return (0);
}

/*----------------------------------------------------------------------
 * NAME:	<static int ResetSpmdSubprocs()>
 * DESCRIPTION:	<Resets UxSpmdStates structure>
 * PARAMETERS:	none 
 * RETURN:	void
 * EXT REFS:	UxSpmdStates, UxSpmdSubprocCount
 * EXT EFFECTS:	UxSpmdStates, UxSpmdSubprocCount
 * ASSUMPTIONS:
 * REVISIONS:	25/04/93
 *--------------------------------------------------------------------*/
static void ResetSpmdSubprocs ()

{
int i;

	for (i=0;i<MAX_SUBPROC;i++) {
		UxSpmdStates[i].pid = UxSpmdStates[i].pty =
			UxSpmdStates[i].exit_status = -1;
	}
	UxSpmdSubprocCount = 0;
}
/*----------------------------------------------------------------------
 * NAME:	<static void SpmdMain()>
 * DESCRIPTION:	<This is the main routine of the manager daemon itself,
 *      called after the manager is fork/exec'd.>
 * PARAMETERS:	none 
 * RETURN:	void	
 * EXT REFS:	UxSpmdStates, UxSpmdSubprocCount, UxSpmd	
 * EXT EFFECTS:	UxSpmdStates, UxSpmdSubprocCount, UxSpmd	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
 *		25/03/93	fix4037	to deal with multibyte	
 *		29/04/93	fix4207	
 *--------------------------------------------------------------------*/
static void SpmdMain()

{
int i;
int rc = 0;

#if defined(__FreeBSD__) || defined(__Darwin__) || defined(Cygwin)
struct mymsg *tmsgp;

#else
struct msgbuf *tmsgp;
#endif

extern int errno;


#ifdef RUNTIME
	/*-----------------------------------------------------
	 * In DESIGN_TIME, UIM/X properly treats all these signals.
	 *-----------------------------------------------------*/
	signal(SIGINT, SpmdCiao);  /* to handle Ctrl-C */
	signal(SIGQUIT, SpmdCiao); 
#endif
	signal(SIGALRM, SpmdSigHandler);	/* arm the signal catcher */


#if defined(__FreeBSD__) || defined(__Darwin__) || defined(Cygwin)
	if ( !(tmsgp=(struct mymsg *)UxMalloc(SIZEOFMAXMSGBUF))) {
#else
	if ( !(tmsgp=(struct msgbuf *)UxMalloc(SIZEOFMAXMSGBUF))) {
#endif

		perror("Subprocess manager: memory allocation failed");
		exit (errno);
	}
	/*---------------------------------------------------------
	 * Note: on sun4 MB_CUR_MAX doesn't work properly. It is
	 * O which is wrong but doesn't break this code. It
	 * works for those with proper international sun os's.
	 *---------------------------------------------------------*/
	MultiByteMode = (MB_CUR_MAX > 1) ? 1 : 0; /* Set if in multibyte mode */

	ResetSpmdSubprocs ();


	/*
	 * Main processing loop is to wait on the message queue from the parent.
	 * Note: will be extended to select on this queue plus all managed
	 * sub-process pty's
	 */
	for(;;) {

		/* Read a message from the application 
		 * (more frequently if we have subprocs)
		 */

		alarm ((UxSpmdSubprocCount ? 1 : 5));
		while (SpmdReadPtys() && 
		       (rc=msgrcv(UxSpmd.spmd_pc_msqid,tmsgp, MSGMAX, 0, 0)) 
			 == -1 && 
		       errno == EINTR) 
		{
			if (!IsSpmdAlive(tmsgp)) {
				SpmdCiao(0);
			}
			/* nop body; skip interrupts */ ;
		}
		alarm(0);
		if (rc == -1) {
			if (!IsSpmdAlive(tmsgp)) {
				SpmdCiao(0);
			}
			perror("UIM/X: spmd_main: msgrcv failed");
#ifdef DEBUG
			/* check out the error ... could be fatal */
			exit (errno);
#endif /*DEBUG*/
		}
		if (!IsSpmdAlive(tmsgp)) {
			SpmdCiao(0);
		}

		/*-----------------------------------------------------
		 *  Process the message.
		 *-----------------------------------------------------*/
		switch (tmsgp->mtype) { 
		    case SPMD_CREATE:	/* create a sub-process */
		    { 
			int new_handle, new_pid;
			spmd_create_req *req = (spmd_create_req *)tmsgp;
			spmd_create_rsp msg;
			req->cmd[rc-sizeof(req->echo)] = '\0'; /* */

			/* Disable the SIGCHLD signal while we are
			   creating the process because it could
			   terminate and the daemon wouldn't know
			   its pid.
			*/
			signal(SIGCHLD, SIG_DFL); 	/* disable */
			if((i=UX_SPMD_EXEC_SUBPROCESS(req->cmd, req->echo,
					&new_handle, &new_pid)) 
			   == ERROR) {
				msg.response = NACK;
			} else {
				msg.response = ACK;
				for (i = 0; i < MAX_SUBPROC; i++)
				{
					if (UxSpmdStates[i].pid == -1)
					{
						break;
					}
				}
				UxSpmdStates[i].pid = new_pid;
				UxSpmdStates[i].pty = new_handle;
				msg.handle = new_handle;
				msg.pid = new_pid;
				UxSpmdSubprocCount++;
			}
			/* Now that the process is created, we can rearm the
			   signal and catch all the children that died during
			   the creation of the process.
			*/
			signal(SIGCHLD, SpmdSigHandler); /* arm the signal */

			msg.mtype = SPMD_RSP;
			while ( (msgsnd(UxSpmd.spmd_cp_msqid, &msg, sizeof(spmd_create_rsp)-sizeof(msg.mtype), 0) == -1) && (errno == EINTR) ) {
				/* nop body */ ;
			}
			break;
		    }
		    case SPMD_INPUT:	/* input for a process */
		    {
			spmd_input_t *msg = (spmd_input_t *)tmsgp;
			msg->input[rc-sizeof(msg->pid)] = '\0'; /* */
			for (i=0; i < MAX_SUBPROC; i++) {
				if (UxSpmdStates[i].pid == msg->pid) {
					break;
				}
			}

			if (i == MAX_SUBPROC) { /* App. gave us a bad pid */
#ifdef DEBUG
				fprintf(stderr, ">>(%d)) [spmd_main] SPMD_INPUT received BUT %d is a BAD PID !\n", getpid(), msg->pid);
#endif /*DEBUG*/

					break;	/* silently ignore */
			}
			if (UxSpmdStates[i].exit_status == -1) { 
				/* not dead yet ? */
				ASSERT (UxSpmdStates[i].pty >= 0);
				write(UxSpmdStates[i].pty, 
				      msg->input, rc-sizeof(msg->pid));
			}

			break;
		    }
		    default:		/* garbage or wrong directional msg */
#ifdef DEBUG
			fprintf(stderr,"UIM/X: spmd_main: protocol error\n");
#endif /*DEBUG*/

			break;
		}
		tmsgp->mtype = 0;	/* A.Nil. Legal value */
	}
}


/*----------------------------------------------------------------------
 * NAME:	<int UxSpmdCreateDaemon()>
 * DESCRIPTION:	<Create the Sub-Process Manager Daemon.
 *      set up the messages queues and message buffers.>
 * PARAMETERS:	none 
 * RETURN:	returns: 0 on success, non-0 on error/failure	
 * EXT REFS:	UxSpmd	
 * EXT EFFECTS:	spmd_info_t struct is filled in with relevant info.
		UxSpmd	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		23/04/93	fix4181	handle Ctrl-C at runtime	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSpmdCreateDaemon()

{
void SpmdMain();

	/* create directional message queues (one each direction) */
	UxSpmd.spmd_pc_msqid = msgget(IPC_PRIVATE,IPC_CREAT|S_IRUSR|S_IWUSR);
	UxSpmd.spmd_cp_msqid = msgget(IPC_PRIVATE,IPC_CREAT|S_IRUSR|S_IWUSR);

	if ( (UxSpmd.spmd_cp_msqid == -1) || (UxSpmd.spmd_pc_msqid == -1) ) 
	{
#ifdef DEBUG
		perror("spmd_create_daemon: msgget failed");
#endif /*DEBUG*/
		return(ERROR);
	}

#ifdef DEBUG

	fprintf(stderr,
		"spmd_create_daemon c->p msqid = 0x%x, p->c msqid = 0x%x\n", 
		UxSpmd.spmd_cp_msqid, UxSpmd.spmd_pc_msqid);
#endif /*DEBUG*/

	/* allocate the receive-message buffer */

#if defined(__FreeBSD__) || defined(__Darwin__) || defined(Cygwin)
	if ( !(UxSpmd.spmd_rmsgp=(struct mymsg *)UxMalloc(SIZEOFMAXMSGBUF)) ) 
#else
	if ( !(UxSpmd.spmd_rmsgp=(struct msgbuf *)UxMalloc(SIZEOFMAXMSGBUF)) ) 
#endif

	{
#ifdef DEBUG
		perror("spmd: spmd_create_daemon: UxMalloc failed");
#endif /*DEBUG*/
 
		return(ERROR);
	}

	switch (UxSpmd.spmd_pid=fork()) {
	    case -1:	/* forking error */
#ifdef DEBUG
		perror("spmd: spmd_create_daemon: fork failed");
#endif /*DEBUG*/
 
		return(ERROR);

	    case 0:	/* child: the sub-process manager daemon */
		SpmdMain();
		/*NOTREACHED*/
		break;

	    default:	/* parent: the application */
		/*-------------------------------------------
		 * Start reading the Spmd msg queue.
		 *-------------------------------------------*/
		TimeOutId = XtAppAddTimeOut(UxAppContext, 
					UxSpmdInterval, 
					(XtTimerCallbackProc)UxSpmdReadMsg, 0); 
		break;
	}

	return (0);
}


/*----------------------------------------------------------------------
 * NAME:	<static void SpmdSigHandler(sig)>
 * DESCRIPTION:	<Handler for signals of interest to subproc manager.
 *      SIGCHLD:        - mark dying process as dead in state table
 *      SIGALRM:        - used to induce EINTR break from blocked msgrcv(),
 *                        check that our parent process still owns us.>
 * PARAMETERS: 	int sig         -- signal being handled
 * RETURN:	void	
 * EXT REFS:	UxSpmdStates, UxSpmdSubprocCount	
 * EXT EFFECTS:	UxSpmdStates	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
static void SpmdSigHandler(sig)
int sig;

{
int status, pid, i;

	switch (sig) {
	    case SIGCHLD:
		while ( (pid=wait(&status)) == -1 && (errno == EINTR) ) {
			/* nop body */ ;
		}
		if (pid == -1) {
			perror("subprocess manager: wait failed");
		}
		
		for (i=0; i < MAX_SUBPROC; i++) 
		{	
			/* Mark the process dead. */
			if (UxSpmdStates[i].pid == pid) 
			{ 
				ASSERT (UxSpmdStates[i].exit_status == -1);
#ifdef DEBUG
				fprintf(stderr,
			"<<(%d) [spmd_sig_handler] pid %d died, status %d\n",
					getpid(), pid, status);
#endif /*DEBUG*/
				UxSpmdStates[i].exit_status = status;
				break;
			}
		}

		/*
		 * Don't check for falling off the end of the loop because
		 * a forked child would only be placed into the table if the
		 * handshake, pty's, exec, etc. all worked.
		 */
		break;

	    case SIGALRM:	/* used merely to break a blocked msgrcv(). */
		if (getppid() == 1) { /* have we been adopted by init ? */
			SpmdCiao(0);
		}
		alarm ((UxSpmdSubprocCount?1:5));
		break;
	    default:
		fprintf(stderr,"Unexpected signal caught. Impossible!\n");
		break;
	}

	signal(sig, SpmdSigHandler);	/* re-arm the signal catcher */
}



/*----------------------------------------------------------------------
 * NAME:	<static int SpmdReadPtys()>
 * DESCRIPTION:	<Read input from each of the ptys for active processes in turn,
 *      passing the input on to the application in a message.>
 * PARAMETERS: 	none
 * RETURN:	int - always 1	
 * EXT REFS:	UxSpmdSubprocCount, UxSpmdStates, UxSpmd	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
 *		02/02/93	fix3890	change size of message sent in msgsnd
 *		25/03/93	fix4037	to deal with multibyte	
 *		sept 93		fix4491 sun4 MB_CUR_MAX workaround.
 *--------------------------------------------------------------------*/

static int SpmdReadPtys()

{
int i;
int n;
wchar_t wc_buffer[MB_LEN_MAX * MSGMAX];
void SpmdDeadProc();

	if (!IsSpmdAlive(NULL)) {
		SpmdCiao(0);
	}

	/* while there are children ... */

	for (i=0; UxSpmdSubprocCount && (i < MAX_SUBPROC); i++) 
	{
		if (UxSpmdStates[i].pid != -1) 
		{
			/* slot is associated with a process */
			int rec = 0, wanted, num;
			spmd_output_t msg;
			char *buf = msg.output;

			/* subtract 1 to leave space for '\0' */
			if (MultiByteMode)
			{
#ifdef sun4
				wanted = sizeof(msg.output) - (MB_CUR_MAX > 0 ? MB_CUR_MAX : 1) - 1;
#else
				wanted = sizeof(msg.output) - MB_CUR_MAX - 1;
#endif

			}
			else
			{
				wanted = sizeof(msg.output) - 1;
			}

			/* read must be non-blocking */
			buf[0] = '\0';

			while ( wanted && 
				(num=read(UxSpmdStates[i].pty, buf, wanted)) ) 
			{
				if (num==-1) {
					if (errno == EINTR) 
					{
						continue;
					}
					break;
				}
				wanted -= num;
				buf[num] = '\0';	/* tail the string */
				buf += num; /* buffer pointer bump */
				rec += num; /* increment received counter */
			}

			/* In multibyte mode, be sure that we have a
			 * complete character, so that we pass meaningful
			 * strings along to the application.
			 */
			if (MultiByteMode)
			{
				/* Keep reading 1 char at a time until
				 * we get a complete mbs string.
				 */
                		while (mbstowcs(wc_buffer, msg.output, rec)
				       == -1)
				{
                        		n = read(UxSpmdStates[i].pty, buf, 1);
                        		if (n == -1)
                        		{
						if (errno == EINTR   ||
						    errno == EAGAIN)
						{
							continue;  /* while */
						} else {
							/* Read really failed.
							 * Give up.
							 */
							break;	
						}
                                        } else {
                        			rec += n;
                        			buf[n] = '\0'; 
						buf += n;
                			}
        			}
			}

			/* send output from process to the application */
			if (rec) 
			{
				msg.mtype = SPMD_OUTPUT;
				msg.pid = UxSpmdStates[i].pid; 

				/* send SPMD_OUTPUT message to application */

				while ((msgsnd(UxSpmd.spmd_cp_msqid, 
					       &msg, 
					       rec + sizeof(msg.pid),
					       0) == -1) 
				       && (errno == EINTR) ) 
				{
					/* nop body */ ;
				}
			}
			if (UxSpmdStates[i].exit_status != -1) 
			{  
				/* Process has died. */
				SpmdDeadProc(i);
			}
		}
	}
	return(1);	/* spmd_main() relies on this always succeeding :-) */
}


/*----------------------------------------------------------------------
 * NAME:	<static void SpmdDeadProc(i)>
 * DESCRIPTION:	<The speciefied process has died.
 *      Send a message to the application to this effect.
 *      Remove the process from the subprocess table>
 * PARAMETERS:	int i          -- subprocess table index 
 * RETURN:	void	
 * EXT REFS:	UxSpmdStates, UxSpmd, UxSpmdSubprocCount	
 * EXT EFFECTS:	UxSpmdStates, UxSpmdSubprocCount	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
static void SpmdDeadProc(i)
int i;

{
spmd_dead_msg msg;

	msg.mtype = SPMD_DIED;
	ASSERT((UxSpmdStates[i].pid != -1)&&(UxSpmdStates[i].exit_status != -1));

	msg.pid = UxSpmdStates[i].pid;
	msg.status = UxSpmdStates[i].exit_status;

	/* generate the SPMD_DIED message and send it to the application */
	while ( (msgsnd(UxSpmd.spmd_cp_msqid, &msg, 
			sizeof(msg)-sizeof(msg.mtype), 0) == -1) && 
		(errno == EINTR) ) 
	{
		/* nop body */ ;
	}

	/* Assume the PTY is done with. 
	 * This guy is dead. Bury him and forget him ! 
	 */

	(void) close(UxSpmdStates[i].pty);
	UxSpmdStates[i].pid = UxSpmdStates[i].pty 
			   = UxSpmdStates[i].exit_status = -1;
	UxSpmdSubprocCount--; 
	ASSERT(UxSpmdSubprocCount >= 0);
}
/*----------------------------------------------------------------------
 * NAME:	<void SpmdSendExitMsg>
 * DESCRIPTION:	<Send a message to the SPMD for cleaning up and exiting>
 * PARAMETERS: 	none
 * RETURN:	void	
 * EXT REFS:	UxSpmd
 * EXT EFFECTS:	
 * ASSUMPTIONS:
 * REVISIONS:	23/04/93        fix4181 handle Ctrl-C at runtime	
 *		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
static void SpmdSendExitMsg ()

{
int cmdlen;
spmd_dead_msg req;
int status, pid;

    	req.mtype = SPMD_EXIT;
	req.pid = 0;
    	req.status = -1;
    	cmdlen = 1;

    	if (-1 == msgsnd(UxSpmd.spmd_pc_msqid, &req,
             	cmdlen+sizeof(req.pid), IPC_NOWAIT)) {
        	return;
    	}

	/*-----------------------------------------------------
	 * Wait for the SPMD to die before handing control
	 * back to the main application.
	 *-----------------------------------------------------*/
	while ( (pid=wait(&status)) == -1 && (errno == EINTR) ) {
		/* nop body */ ;
	}
}
/*----------------------------------------------------------------------
 * NAME:	<void UxDestroySpmd()>
 * DESCRIPTION:	<The application disappeared. We follow suit.
 * Try and tidy up the message queues and destroy the SPMD deamon.>
 * PARAMETERS: 	none
 * RETURN:	void	
 * EXT REFS:	UxSpmd	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	23/04/93        fix4181 handle Ctrl-C at runtime	
 *		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxDestroySpmd()

{
	/*-----------------------------------------------------
	 * Now don't you just love looking at this code ?
	 * Look no further cause bugs you shall find none :-)
	 * but just in case you do, just send us the fix
	 * report instead of a bug report :-)
	 * After all, this code was mostly written by one of
	 * you collegues.
	 *-----------------------------------------------------*/

	/*-----------------------------------------------------
	 * If the SPMD is not running, nothing to do. Just return.
	 *-----------------------------------------------------*/
	if (!IsSpmdAlive(NULL)) {
		return;
	}

	UxMarkFileCloseOnExec(1);

	if (TimeOutId != -1) {
		XtRemoveTimeOut (TimeOutId);
		TimeOutId = -1;
	}

	/*-----------------------------------------------------
	 * When the parent process is about to die, send a msg
	 * to the SPMD so that it does its cleanup and exits.
	 *-----------------------------------------------------*/
	SpmdSendExitMsg ();

	/*-----------------------------------------------------
	 * In case the parent crashes or receives an interrupt
	 * signal, we are not guaranteed that the SPMD main loop
	 * will still be called so we force a cleanup of the msg
	 * queues at the application level.
	 *-----------------------------------------------------*/
	if ((UxSpmd.spmd_pid != 0) && (UxSpmd.spmd_pid != -1)) {
		SpmdCiao(0);
	}
			
	UxSpmd.spmd_pid = -1;

}
/*----------------------------------------------------------------------
 * NAME:	<static void SpmdCiao()>
 * DESCRIPTION:	<The application disappeared. We follow suit.
 * Try and tidy up the message queues. We can't do anything if it fails so
 * we don't even check the return status>
 * PARAMETERS: 	none
 * RETURN:	void	
 * EXT REFS:	UxSpmd	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 cleanup	
 *		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
static void SpmdCiao(sig)
int sig;

{

	/*-----------------------------------------------------
	 * Upon exiting, clean the message queues.
	 *-----------------------------------------------------*/

	if ((UxSpmd.spmd_pid != -1) && (UxSpmd.spmd_pc_msqid != -1)) {
		(void)msgctl(UxSpmd.spmd_pc_msqid, IPC_RMID,NULL);
		UxSpmd.spmd_pc_msqid = -1;
	}

	if ((UxSpmd.spmd_pid != -1) && (UxSpmd.spmd_cp_msqid != -1)) {
		(void)msgctl(UxSpmd.spmd_cp_msqid, IPC_RMID,NULL);
		UxSpmd.spmd_cp_msqid = -1;
	}

	/*-----------------------------------------------------
	 * The child process will kill all remaining subbprocess
	 * and exit itself.
	 *-----------------------------------------------------*/
	if (UxSpmd.spmd_pid == 0) {
		UxMarkFileCloseOnExec(1);
		UxKillAllSubprocs ();
		UxSpmd.spmd_pid = -1;
		exit(0);
	}
}


/*----------------------------------------------------------------------
 * NAME:	<int UxWhatSpIsDead(sp_status)>
 * DESCRIPTION:	<Looking at the dead message, it finds out the subprocess
*		that died.>
 * PARAMETERS: 	int *sp_status - returned status of the subprocess	
 * RETURN:	int - pid of dead subprocess	
 * EXT REFS:	UxSpmd	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	08/01/93	fix3810	creation	
 *		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxWhatSpIsDead(sp_status)
int *sp_status; 

{
int pid;

        spmd_dead_msg *msg = (spmd_dead_msg *)UxSpmd.spmd_rmsgp;
        pid = msg->pid;
        *sp_status = msg->status;
	return(pid);
}
