
/*
 *         PVM version 3.3:  Parallel Virtual Machine System
 *               University of Tennessee, Knoxville TN.
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *                   Emory University, Atlanta GA.
 *      Authors:  A. L. Beguelin, J. J. Dongarra, G. A. Geist,
 *    W. C. Jiang, R. J. Manchek, B. K. Moore, and V. S. Sunderam
 *                   (C) 1992 All Rights Reserved
 *
 *                              NOTICE
 *
 * 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 the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * Neither the Institutions (Emory University, Oak Ridge National
 * Laboratory, and University of Tennessee) nor the Authors make any
 * representations about the suitability of this software for any
 * purpose.  This software is provided ``as is'' without express or
 * implied warranty.
 *
 * PVM version 3 was funded in part by the U.S. Department of Energy,
 * the National Science Foundation and the State of Tennessee.
 */

/*
 *	lpvmcat.c
 *
 *	Child task output collection.
 *
$Log: lpvmcat.c,v $
 * Revision 1.3  1994/11/07  21:34:49  manchek
 * cast message tag for comparison as int
 *
 * Revision 1.2  1994/10/15  19:10:08  manchek
 * don't segfault if we get a TC_OUTPUT message and pvm_catchout never called
 *
 * Revision 1.1  1994/06/03  20:38:16  manchek
 * Initial revision
 *
 */


#include <stdio.h>
#ifdef IMA_BSD386
#include <machine/endian.h>
#endif
#ifdef IMA_LINUX
#include <endian.h>
#endif
#include <rpc/types.h>
#include <rpc/xdr.h>
#ifdef	SYSVSTR
#include <string.h>
#define	CINDEX(s,c)	strchr(s,c)
#else
#include <strings.h>
#define	CINDEX(s,c)	index(s,c)
#endif
#include <signal.h>
#include <pvm3.h>
#include "global.h"
#include "pvmalloc.h"
#include "tdpro.h"
#include "listmac.h"
#include "bfunc.h"
#include "tvdefs.h"
#include <pvmtev.h>
#include "tevmac.h"


/*
*	used to collect output from a task
*/

struct tobuf {
	struct tobuf *o_link, *o_rlink;		/* chain of all tasks in job */
	int o_tid;							/* task id */
	int o_len;							/* length of buf */
	char *o_buf;						/* buffered partial line */
	int o_maxl;							/* space in buf */
	int o_flag;							/* task state */
#define	GOTSPAWN	1
#define	GOTEOF		2
};


/***************
 **  Globals  **
 **           **
 ***************/

extern int pvm_errno;					/* from lpvm.c */
extern int pvmautoerr;					/* from lpvm.c */
extern int pvmmytid;					/* from lpvm.c */
extern int pvmouttid;					/* from lpvm.c */
extern int pvmoutcod;					/* from lpvm.c */
extern int pvmschedtid;					/* from lpvm.c */
extern int pvmtoplvl;					/* from lpvm.c */
extern Pvmtmask pvmtrcmask;				/* from lpvm.c */
extern int pvmtrctid;					/* from lpvm.c */


/***************
 **  Private  **
 **           **
 ***************/

static char rcsid[] = "$Id: lpvmcat.c,v 1.3 1994/11/07 21:34:49 manchek Exp $";
static char pvmtxt[512];				/* scratch for error log */
static struct tobuf *tobuflist = 0;
static FILE *outlogff = 0;


/**************************
 **  Internal Functions  **
 **                      **
 **************************/

int
tobuf_init()
{
	if (!tobuflist) {
		tobuflist = TALLOC(1, struct tobuf, "obuf");
		BZERO((char*)tobuflist, sizeof(struct tobuf));
		tobuflist->o_link = tobuflist->o_rlink = tobuflist;
	}
	return 0;
}


struct tobuf *
tobuf_new(tid)
	int tid;
{
	struct tobuf *op = tobuflist->o_link;
	struct tobuf *op2;

	while (op != tobuflist && op->o_tid < tid)
		op = op->o_link;
	if (op->o_tid != tid) {
		op2 = TALLOC(1, struct tobuf, "tobuf");
		BZERO((char*)op2, sizeof(struct tobuf));
		op2->o_tid = tid;
		LISTPUTBEFORE(op, op2, o_link, o_rlink);
		op = op2;
	}
	return op;
}


tobuf_free(op)
	struct tobuf *op;
{
	LISTDELETE(op, o_link, o_rlink);
	if (op->o_buf)
		PVM_FREE(op->o_buf);
	PVM_FREE(op);
	return 0;
}


struct tobuf *
tobuf_find(tid)
	int tid;
{
	struct tobuf *op = tobuflist->o_link;

	while (op != tobuflist && op->o_tid < tid)
		op = op->o_link;
	if (op->o_tid == tid)
		return op;
	return (struct tobuf*)0;
}


tobuf_dump()
{
	struct tobuf *op;

	if (tobuflist)
		for (op = tobuflist->o_link; op != tobuflist; op = op->o_link) {
			printf("tobuf_dump() t%x\n", op->o_tid);
		}
	else
		printf("tobuf_dump() tobuflist null\n");
	return 0;
}


/*	pvmclaimo()
*
*	Called by pvmmctl() when a TC_OUTPUT message arrives.
*/

int
pvmclaimo()
{
	int tid;					/* task */
	int n;						/* length or event code */
	char *p;
	int ptid;
	struct tobuf *op;

/*
	if (TRACE) {
		checktrace(jp, cc, len, cod, src);
		return 0;
	}
*/

	pvm_upkint(&tid, 1, 1);
	pvm_upkint(&n, 1, 1);

	if (!tobuflist) {
		sprintf(pvmtxt, "pvmclaimo() message from task t%x\n", tid);
		pvmlogerror(pvmtxt);
		return 0;
	}

	if (n > 0) {	/* output printed by task */
		if (!(op = tobuf_find(tid))) {
			sprintf(pvmtxt, "pvmclaimo() bogus message, no task t%x\n", tid);
			pvmlogerror(pvmtxt);
			return 0;
		}
		if (n + op->o_len >= op->o_maxl) {
			op->o_maxl = op->o_len + n + 1;
/*
			printf("REALLOC t%x buf to %d\n", tid, op->o_maxl);
*/
			if (op->o_buf)
				op->o_buf = TREALLOC(op->o_buf, op->o_maxl, char);
			else
				op->o_buf = TALLOC(op->o_maxl, char, "");
		}
		pvm_upkbyte(op->o_buf + op->o_len, n, 1);
		op->o_buf[op->o_len + n] = 0;
/*
		printf("UNPACK t%x {%s}\n", tid, op->o_buf + op->o_len);
*/
		p = op->o_buf + op->o_len;
		op->o_len += n;
		while (p = CINDEX(p, '\n')) {
			*p++ = 0;
			if (outlogff)
				fprintf(outlogff, "[t%x] %s\n", tid, op->o_buf);
			op->o_len -= p - op->o_buf;
			BCOPY(p, op->o_buf, op->o_len);
			p = op->o_buf;
		}

	} else {
		switch (n) {

		case 0:		/* EOF from task */
			if (!(op = tobuf_find(tid))) {
				sprintf(pvmtxt, "pvmclaimo() bogus message, no task t%x\n", tid);
				pvmlogerror(pvmtxt);
				return 0;
			}
			if (op->o_len > 0) {
				if (outlogff)
					fprintf(outlogff, "[t%x] %s\n", tid, op->o_buf);
				op->o_len = 0;
			}
			op->o_flag |= GOTEOF;
			if (op->o_flag == (GOTSPAWN|GOTEOF)) {
				if (outlogff)
					fprintf(outlogff, "[t%x] EOF\n", tid);
				tobuf_free(op);
			}
			break;

		case -1:	/* spawn creating new task */
			if (!(op = tobuf_find(tid))) {
				op = tobuf_new(tid);
				if (outlogff)
					fprintf(outlogff, "[t%x] BEGIN\n", tid);
			}
			op->o_flag |= GOTSPAWN;
			if (op->o_flag == (GOTSPAWN|GOTEOF)) {
				if (outlogff)
					fprintf(outlogff, "[t%x] EOF\n", tid);
				tobuf_free(op);
			}

			break;

		case -2:	/* new task starting up */
			if (!(op = tobuf_find(tid))) {
				op = tobuf_new(tid);
				if (outlogff)
					fprintf(outlogff, "[t%x] BEGIN\n", tid);
			}
			break;

		default:
			sprintf(pvmtxt, "pvmclaimo() bogus message from task t%x\n", tid);
			pvmlogerror(pvmtxt);
			break;
		}
	}
	return 0;
}


/*	pvmflusho()
*
*	Called by pvm_exit() to make sure all task output is flushed.
*	If outlogff is nonzero, make sure all child tasks have exited
*	else just toss the output.
*/

int
pvmflusho()
{
	struct tobuf *op;
	int cc;

	if (outlogff) {
		if (tobuflist && tobuflist->o_link != tobuflist) {
			pvmlogerror("pvm_exit() child task(s).  waiting...\n");
			while (tobuflist->o_link != tobuflist) {

	/* XXX mroute() does return after ctrl messages received */

				if ((cc = mroute(0, 0, 0, (struct timeval *)0)) < 0)
					break;
			}
		}
		pvm_catchout((FILE *)0);
	}

	if (tobuflist) {
		while (tobuflist->o_link != tobuflist)
			tobuf_free(tobuflist->o_link);
		PVM_FREE(tobuflist);
		tobuflist = 0;
	}
	return 0;
}


/************************
 **  Libpvm Functions  **
 **                    **
 ************************/

/*	pvm_catchout()
*
*	Log output of tasks spawned by us to file.
*/

int
pvm_catchout(ff)
	FILE *ff;
{
	if (ff) {
		tobuf_init();
		pvm_setopt(PvmOutputTid, pvm_mytid());
		pvm_setopt(PvmOutputCode, (int)TC_OUTPUT);
		outlogff = ff;

	} else {
		pvm_setopt(PvmOutputCode, pvmoutcod);
		pvm_setopt(PvmOutputTid, pvmouttid);
		outlogff = 0;
	}
	return PvmOk;
}


