/*
 * tools.c
 *
 * $Author: dm3e $
 * $Date: 1993/03/04 00:16:39 $
 * $Revision: 1.17 $
 * $State: Exp $
 * $Locker:  $
 */

/***********************************************************
        Copyright 1991 by Carnegie Mellon University

                      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 CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

#include <sys/wait.h>
#include <stdio.h>
#include <varargs.h>
#include <errno.h>
#include "emtd_err.h"
#include "rxemt.h"
#include "globals.h"
#include "tools.h"
#include "../server/emtd.h"

tree_t trees;

char *copy_string(str)
char *str;
{
    char *newstr;

    if (NULL == str) return copy_string("");
    if (NULL == (newstr = (char *)malloc(strlen(str)+2))) {
	fprintf(stderr, "Not able to malloc enough space.\n");
	fflush(stderr);
	exit(EMTD_BAD_MALLOC);
    }
    strcpy(newstr, str);
    return newstr;
}

char *make_volume_name(sys_name, app_name, version_number, type)
char *sys_name, *app_name, *version_number;
int type;
{
    char buffer[MAXBUFSIZE];

    type = type & (OBJ_VOL | DEST_VOL | SRC_VOL | RCS_VOL | COMMON_RCS_VOL
		   | COMMON_VOL);
    switch (type) {
    case OBJ_VOL:
	sprintf(buffer, ".%s.%s.%s", sys_name, app_name, version_number);
	break;
    case DEST_VOL:
	sprintf(buffer, "%s.%s.%s", sys_name, app_name, version_number);
	break;
    case SRC_VOL:
	sprintf(buffer, "src.%s.%s", app_name, version_number);
	break;
    case RCS_VOL:
	sprintf(buffer, "rcs.%s.%s", app_name, version_number);
	break;
    case COMMON_RCS_VOL:
	sprintf(buffer, "rcs.%s", app_name, version_number);
	break;
    case COMMON_VOL:
	sprintf(buffer, "common.%s.%s", app_name, version_number);
	break;
    default:
	return NULL;
    }
    return copy_string(buffer);
}

tree_t get_tree(tree)
char *tree;
{
    tree_t c_tree;

    c_tree = trees;
    while (NULL != c_tree) {
	if (!strcmp(c_tree->name, tree)) {
	    break;
	}
	c_tree = c_tree->next;
    }
    return c_tree;
}

edb_t get_environment(tree, env)
char *tree;
char *env;
{
    tree_t c_tree;
    edb_t c_env;

    if (NULL == (c_tree = get_tree(tree))) return NULL;
    if (!strcmp(env, "dest")) return c_tree->dest;
    if (!strcmp(env, "obj")) return c_tree->obj;
    c_env = c_tree->envs;
    while (NULL != c_env) {
	if (!strcmp(c_env->name, env)) {
	    return c_env;
	}
	c_env = c_env->next;
    }
    return NULL;
}

edb_sys_t get_system(c_env, sys_name)
edb_t c_env;
char *sys_name;
{
    edb_sys_t c_sys;

    if (NULL == c_env) return NULL;
    c_sys = c_env->systems;
    while (NULL != c_sys) {
	if (!strcmp(c_sys->name, sys_name)) return c_sys;
	c_sys = c_sys->next;
    }
    return NULL;
}

char *gen_group_name(app_name)
char *app_name;
{
    char buffer[1024];

    sprintf(buffer, "sysmaint:%s.commanders", app_name);
    return copy_string(buffer);
}

/***************************************************************************
 * [exported] FlushPackets
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Thu Aug  6 16:03:05 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long FlushPackets(call)
struct rx_call *call;
{
    struct rx_connection *conn = call->conn;
    struct rx_peer *peer = call->conn->peer;

#ifdef NO_FLUSH
    return OK;
#else
    if (call->currentPacket) {
	struct rx_packet *cp = call->currentPacket;

/*	cp->header.flags = RX_MORE_PACKETS; */
	clock_NewTime(); /* Bogus:  need new time package */
	cp->length = rx_MaxUserDataSize(conn) - call->nFree;
	/* The 0, below, specifies that it is not the last packet:
	   there will be others */
	/* The following routine may alter the packet length by up to
	   conn->securityMaxTrailerSize */
	call->currentPacket = (struct rx_packet *) 0;
	call->nFree = 0;
	rxi_PrepareSendPacket(call, cp, 0);
	rxi_Send(call, cp);
	queue_Append(&call->tq, cp);
	rxi_Start(0, call);
	if (call->currentPacket = 
	    (struct rx_packet *)rxi_AllocSendPacket(call)) {
	    call->nFree = rx_MaxUserDataSize(conn);
	    call->bufPtr = rx_UserDataOf(conn, call->currentPacket);
	    }
    }
#endif NO_FLUSH
}

/***************************************************************************
 * [exported] SendOpCode
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Thu Aug  6 15:18:48 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long SendOpCode(call, opcode)
struct rx_call *call;
int opcode;
{
    XDR z_xdrs;

    xdrrx_create(&z_xdrs, call, XDR_ENCODE);

    if ((!xdr_int(&z_xdrs, &opcode))) {
	return RXGEN_CC_MARSHAL;
    }
    FlushPackets(call);
}

/***************************************************************************
 * [exported] SendString
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Fri Sep 25 16:42:59 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long SendString(va_alist)
va_dcl
{
    struct rx_call *call;
    char str[2048];
    char *strp;
    XDR z_xdrs;
    int opcode = EMTD_STRING;
    int string_code;
    va_list ap;
    char *format;

    va_start(ap);
    call = va_arg(ap, struct rx_call *);
    string_code = va_arg(ap, int);
    format = va_arg(ap, char *);
    vsprintf(str, format, ap);
    strp = copy_string(str);
    va_end(ap);

    xdrrx_create(&z_xdrs, call, XDR_ENCODE);

    if ((!xdr_int(&z_xdrs, &opcode))) {
	return RXGEN_CC_MARSHAL;
    }
    if ((!xdr_int(&z_xdrs, &string_code))) {
	return RXGEN_CC_MARSHAL;
    }
    if ((!xdr_string(&z_xdrs, &strp, ~0))) {
	return RXGEN_CC_MARSHAL;
    }
    FlushPackets(call);
}

/***************************************************************************
 * [exported] emtd_LWPopen
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Oct 20 11:17:12 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long emtd_LWPopen(va_alist)
va_dcl
{
    va_list ap;
    int             pid,
                    fd[2];
    fd_set          readfds,
                    exceptfds;
    union wait      status;
    struct timeval timeout;
    char            buffer[MAXBUFSIZE];
    char *file, *c, *args[100];
    int argno = 0;
    long code = 0;
    long i;
    struct rx_call *call;
    int prock;
    comreq_t request;

    va_start(ap);
    request = va_arg(ap, struct comreq *);
    file = va_arg(ap, char *);
    while (args[argno++] = va_arg(ap, char *));
    va_end(ap);

    signal(SIGTTIN, SIG_IGN);
    fflush(stdout);
    pipe(fd);
    if ((pid = fork()) < 0) {
	fprintf(stderr, "emtd: error in vfork\(\)\n");
	close(fd[0]);
	close(fd[1]);
	return EMTD_COULD_NOT_VFORK;
    }
    if (!pid) {
        register int i, size = getdtablesize();

	signal(SIGCHLD, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	for (i = 0; i < size; i++) {
	    if (i == fd[1] || i == (int)stdin || i == (int)stdout || i == (int)stderr) continue;
	    close(i);
	}
	dup2(fd[1], 1);
	close(fd[0]);
	execv(file, args);
	exit(-1);		/* forked child exits */
    }
    close(fd[1]);
    for (;;) {
	struct timeval last;

	FD_ZERO(&readfds);
	FD_SET(fd[0], &readfds);
	FD_ZERO(&exceptfds);
	FD_SET(fd[0], &exceptfds);
	timeout.tv_sec = EMTD_SEND_ACK_INTERVAL;
	timeout.tv_usec = 0;
	code = IOMGR_Select(getdtablesize(), &readfds, 0, &exceptfds, &timeout);
	if (code < 0) {
	    close(fd[0]);
	    for (i = 0; i < EMTD_MAX_MONITORS; i++) {
		if (NULL != (call = request->monitor[i])) {
		    SendOpCode(call, EMTD_LAST_ACK);
		}
	    }
	    return errno;
	    break;
	} else if (code == 0) {
	    for (i = 0; i < EMTD_MAX_MONITORS; i++) {
		if (NULL != (call = request->monitor[i])) {
		    SendOpCode(call, EMTD_ACK);
		}
	    }
	    continue;
	}
	if (FD_ISSET(fd[0], &readfds)) {
	    bzero(buffer, MAXBUFSIZE);
	    code = read(fd[0], buffer, MAXBUFSIZE);
	    if (0 < code) {
		for (c = buffer; c < buffer + MAXBUFSIZE; c++)
		  if (('\n' == *c) && ('\0' == *(c + 1))) {
		      *c = '\0';
		      break;
		  }
		for (i = 0; i < EMTD_MAX_MONITORS; i++) {
		    if (NULL != (call = request->monitor[i])) {
			SendString(call, EMTD_LOGXTRA, "%s\n", buffer);
		    }
		}
		puts(buffer);
		bzero(buffer, MAXBUFSIZE);
	    }
	}
	code = wait3(&status, WNOHANG, NULL);
	if (0 == code) {
	    LWP_DispatchProcess();
	    IOMGR_Poll();
	    continue;
	} else if (pid == code) {
	    if (WIFEXITED(status)) {
		code = WEXITSTATUS(status) & ((int) 0xff);
		break;
	    } else if (WIFSIGNALED(status)) {
		Log("Child died due to a signal that was not caught.\n");
		code = -1;
		break;
	    } else if (WIFSTOPPED(status)) {
		Log("Child process is currently stopped.\n");
		code = -1;
		break;
	    } else {
		Log("The child suffered a miserable fate.\n");
		code = -2;
		break;
	    }
	} else if (code > 0) {
	    Log("Another child was waited upon (pid = %ld).\n", code);
	    if (WIFEXITED(status)) {
		Log("Other child exited (code = %ld).\n",
		    WEXITSTATUS(status) & ((int) 0xff));
	    } else if (WIFSIGNALED(status)) {
		Log("Other child died due to a signal not caught.\n");
	    } else if (WIFSTOPPED(status)) {
		Log("Other child process is currently stopped.\n");
	    } else {
		Log("The child suffered a miserable fate.\n");
	    }
	    LWP_DispatchProcess();
	    IOMGR_Poll();
	} else if (code < 0) {
	    Log("The child process no longer exists (pid = %ld).\n", pid);
	    break;
	} else {
	    Log("Another child process is signaling (pid = %ld).\n", code);
	    LWP_DispatchProcess();
	    IOMGR_Poll();
	}
    }
    close(fd[0]);
    for (i = 0; i < EMTD_MAX_MONITORS; i++) {
	if (NULL != (call = request->monitor[i])) {
	    SendOpCode(call, EMTD_LAST_ACK);
	}
    }
    return code;
}
