/*-
 * logd.c --
 *	A program to get usage info from all customs agents and
 *	log them in a file in a nice manner.
 *
 * Copyright (c) 1988, 1989 by the Regents of the University of California
 * Copyright (c) 1988, 1989 by Adam de Boor
 * Copyright (c) 1989 by Berkeley Softworks
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any non-commercial purpose
 * and without fee is hereby granted, provided that the above copyright
 * notice appears in all copies.  The University of California,
 * Berkeley Softworks and Adam de Boor make no representations about
 * the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 */
#ifndef lint
static char *rcsid =
"$Id: logd.c,v 1.21 1995/12/31 04:31:16 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */


#include    <netdb.h>
#include    <stdio.h>
#include    <sys/time.h>
#include    <pwd.h>
#include    <sys/wait.h>
#include    <string.h>
#ifdef __STDC__
#include    <stdarg.h>
#else
#include    <varargs.h>
#endif

#include    "customs.h"
#include    "log.h"

#define DEFAULT_LOG_FILE  "/usr/tmp/customs-logd"

typedef struct {
    char    	  	*name;
    Rpc_Long    	avail;
    Rpc_Long		rating;
    Rpc_Long		arch;
    int    	  	numClients;
    int    	  	*clients;
    struct in_addr	addr;
} Agent;

Agent 	  		*agents;
int	    	  	numAgents;
Rpc_Long	    	electToken;

typedef struct {
    char    	  **argv;
    int	    	  id;
} Job;

struct sockaddr_in	mca;
Boolean	    	  	force = TRUE;

extern int  	  	customs_Socket;
extern struct timeval	customs_RetryTimeOut;

FILE	    	  	*logFile;

/*-
 *-----------------------------------------------------------------------
 * LogPrintf --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
void
#ifdef __STDC__
LogPrintf(char *fmt, ...)
#else
LogPrintf(fmt , va_alist)
    char    	  *fmt;
    va_dcl
#endif
{
    va_list 	  args;

#ifdef __STDC__
    va_start(args, fmt);
#else
    va_start(args);
#endif
    vfprintf(logFile, fmt, args);
    va_end(args);
    fflush(logFile);
}

/*-
 *-----------------------------------------------------------------------
 * LogMark --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
void
LogMark(agent)
    Agent   *agent;
{
    time_t  	  now;
    struct tm	  *curTime;

    now = time((time_t *)0);
    curTime = localtime(&now);
    LogPrintf("%s %2d:%02d:%02d %d/%d -- ",
	      agent ? agent->name : "LOG", curTime->tm_hour,
	      curTime->tm_min, curTime->tm_sec, curTime->tm_mon + 1,
	      curTime->tm_mday);
}
/*-
 *-----------------------------------------------------------------------
 * FindAgent --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
Agent *
FindAgent(addr)
    struct in_addr	addr;
{
    register int i;

    for (i = 0; i < numAgents; i++) {
	if (agents[i].addr.s_addr == addr.s_addr) {
	    return (&agents[i]);
	}
    }
    return((Agent *)NULL);
}
/*-
 *-----------------------------------------------------------------------
 * Start --
 *	Note starting of a job somewhere.
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
void
Start (from, msg, len, data)
    struct sockaddr_in	*from;
    Rpc_Message	  	msg;
    int	    	  	len;
    char    	  	*data;
{
    XDR	    	  	xdr;
    ExportPermit  	permit;
    short   	  	euid, ruid;
    char    	  	**argv;
    struct passwd 	*pwd;

    xdrmem_create(&xdr, data, len, XDR_DECODE);
    argv = (char **)NULL;

    if (xdr_exportpermit(&xdr, &permit) &&
	xdr_short(&xdr, &euid) &&
	xdr_short(&xdr, &ruid) &&
	xdr_strvec(&xdr, &argv)) {
	    Agent  	*ad, *as;
	    int	  	i;

	    ad = FindAgent(from->sin_addr);
	    as = FindAgent(permit.addr);
	    LogMark(ad);
	    pwd = getpwuid(euid);
	    LogPrintf("#%u ", permit.id);
	    if (pwd != (struct passwd *)NULL) {
		LogPrintf("from %s@%s: ", pwd->pw_name, as->name);
	    } else {
		LogPrintf("from %d@%s: ", euid, as->name);
	    }
	    for (i = 0; argv[i]; i++) {
		LogPrintf("%s ", argv[i]);
	    }
	    LogPrintf("\n");
    }
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
}

/*-
 *-----------------------------------------------------------------------
 * Finish --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
void
Finish (from, msg, len, data)
    struct sockaddr_in	*from;
    Rpc_Message	  	msg;
    int	    	  	len;
    char    	  	*data;
{
    WAIT_STATUS	  stat;
    ExportPermit  permit;
    XDR	    	  xdr;
    Agent   	  *a;
    
    a = FindAgent(from->sin_addr);
    
    xdrmem_create(&xdr, data, len, XDR_DECODE);
    if (xdr_exportpermit(&xdr, &permit) &&
	xdr_int(&xdr, (int *)&stat)) {
	    LogMark(a);
	    LogPrintf("#%u finished ", permit.id);
	    if (WIFSIGNALED(stat)) {
		LogPrintf ("signal %d\n", WTERMSIG(stat));
	    } else {
		LogPrintf ("exit %d\n", WEXITSTATUS(stat));
	    }
    } else {
	LogPrintf ("BAD PACKET from %s\n", a->name);
    }
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
}

/*-
 *-----------------------------------------------------------------------
 * Stopped --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
Stopped(from, msg, len, data)
    struct sockaddr_in  *from;
    Rpc_Message		msg;
    int			len;
    Rpc_Opaque 	  	data;
{
    ExportPermit  permit;
    XDR	    	  xdr;
    Agent   	  *a;
    
    a = FindAgent(from->sin_addr);
    
    xdrmem_create(&xdr, data, len, XDR_DECODE);
    if (xdr_exportpermit(&xdr, &permit)) {
	LogMark(a);
	LogPrintf("#%u stopped\n", permit.id);
    } else {
	LogPrintf ("BAD PACKET from %s\n", a->name);
    }
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
}
/*-
 *-----------------------------------------------------------------------
 * NewAgent --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
NewAgent(from, msg, len, data)
    struct sockaddr_in  *from;
    Rpc_Message		msg;
    int			len;
    char    	  	*data;
{
    struct sockaddr_in	addr;
    XDR	    	  	xdr;
    Agent   	  	*a;

    xdrmem_create(&xdr, data, len, XDR_DECODE);
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
    
    if (xdr_sockaddr_in(&xdr, &addr)) {
	a = FindAgent(addr.sin_addr);
	if (a == (Agent *)NULL) {
	    struct hostent *he;
	    
	    agents = (Agent *)erealloc(agents, (numAgents+1) * sizeof(Agent));
	    a = &agents[numAgents];
	    numAgents++;

	    a->addr = addr.sin_addr;
	    a->numClients = 0;
	    he = gethostbyaddr((char *)&addr.sin_addr, sizeof(addr.sin_addr),
			       AF_INET);
	    if (he != (struct hostent *)NULL) {
		a->name = (char *)emalloc(strlen(he->h_name)+1);
		strcpy(a->name, he->h_name);
		if (strchr(a->name, '.')) {
		    *strchr(a->name, '.') = '\0';
		}
	    } else {
		a->name = "Unknown";
	    }
	}
	LogMark((Agent *)0);
	LogPrintf("Agent %s registered\n", a->name);
	(void) Rpc_Call(customs_Socket, &addr, (Rpc_Proc)CUSTOMS_LOG,
			sizeof(force), (Rpc_Opaque)&force,
			0, (Rpc_Opaque)0,
			CUSTOMS_RETRY, &customs_RetryTimeOut);
    }
}
			
/*-
 *-----------------------------------------------------------------------
 * NewMaster --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
NewMaster(from, msg, len, data)
    struct sockaddr_in  *from;
    Rpc_Message		msg;
    int			len;
    char    	  	*data;
{
    XDR	    	  	xdr;
    Agent   	  	*a;

    xdrmem_create(&xdr, data, len, XDR_DECODE);
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
    
    a = FindAgent(from->sin_addr);
    LogMark(a);
    LogPrintf("elected master -- %s\n", InetNtoA(from->sin_addr));
    mca = *from;
}
/*-
 *-----------------------------------------------------------------------
 * Evict --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
Evict(from, msg, len, data)
    struct sockaddr_in  *from;
    Rpc_Message		msg;
    int			len;
    Rpc_Opaque 	  	data;
{
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
}

/*-
 *-----------------------------------------------------------------------
 * Access --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
Access(from, msg, len, data)
    struct sockaddr_in  *from;
    Rpc_Message		msg;
    int			len;
    char    	  	*data;
{
    struct sockaddr_in	addr;
    XDR	    	  	xdr;
    char    	  	fromname[32];

    xdrmem_create(&xdr, data, len, XDR_DECODE);
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
    
    if (xdr_sockaddr_in(&xdr, &addr)) {
	LogMark((Agent *)0);
	LogPrintf("illegal access to %s attempted from %s\n",
		  strcpy(fromname, InetNtoA(from->sin_addr)),
		  InetNtoA(addr.sin_addr));
    }
}

/*-
 *-----------------------------------------------------------------------
 * Killed --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
Killed(from, msg, len, data)
    struct sockaddr_in  *from;
    Rpc_Message		msg;
    int			len;
    Rpc_Opaque 	  	data;
{
    ExportPermit  permit;
    int    	  signo;
    XDR	    	  xdr;
    Agent   	  *a;
    
    a = FindAgent(from->sin_addr);
    
    xdrmem_create(&xdr, data, len, XDR_DECODE);
    if ((xdr_exportpermit(&xdr, &permit)) &&
	(xdr_int(&xdr, &signo)))
    {
	LogMark(a);
	LogPrintf("#%u from %s signaled -- %d (%s)\n",
		  permit.id,
		  InetNtoA(permit.addr),
	          signo,
		  strsignal(signo));
    } else {
	LogPrintf ("BAD PACKET from %s\n", a->name);
    }
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
}

/*-
 *-----------------------------------------------------------------------
 * ExitFailed --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
/*ARGSUSED*/
void
ExitFailed(from, msg, len, data)
    struct sockaddr_in  *from;
    Rpc_Message		msg;
    int			len;
    Rpc_Opaque 	  	data;
{
    ExportPermit  permit;
    XDR	    	  xdr;
    Agent   	  *a;
    
    a = FindAgent(from->sin_addr);
    
    xdrmem_create(&xdr, data, len, XDR_DECODE);
    if (xdr_exportpermit(&xdr, &permit)) {
	LogMark(a);
	LogPrintf("#%u from %s: couldn't send EXIT\n",
		  permit.id,
		  InetNtoA(permit.addr));
    } else {
	LogPrintf ("BAD PACKET from %s\n", a->name);
    }
    Rpc_Return(msg, 0, (Rpc_Opaque)0);
}

/*-
 *-----------------------------------------------------------------------
 * main --
 *
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */
main(argc, argv)
    int	    argc;
    char    **argv;
{
    char    	  	infoBuf[MAX_INFO_SIZE];
    int	    	  	i;
    char    	  	*cp;
    struct sockaddr_in	sin;
    struct hostent	*he;
    extern struct sockaddr_in customs_AgentAddr;

    if (Customs_Master(&mca) != RPC_SUCCESS) {
	Customs_PError("Customs_Master");
	printf("Couldn't find master\n");
	exit(1);
    }
    
    if (Customs_Info(&mca, infoBuf) != RPC_SUCCESS) {
	Customs_PError("Customs_Info");
	printf("Couldn't read registration info\n");
    }

    if (argc != 2) {
	logFile = fopen (DEFAULT_LOG_FILE, "a");
    } else if (strcmp(argv[1], "-") != 0) {
	logFile = fopen (argv[1], "a");
    } else {
	logFile = stdout;
    }
    
    if (logFile == (FILE *)NULL) {
	printf ("Couldn't open log file\n");
	exit(1);
    }

    sin.sin_family = AF_INET;
    sin.sin_port = customs_AgentAddr.sin_port;

    /* initialize agents array first */
    LogMark((Agent *)0);
    cp = infoBuf;

    electToken = *(Rpc_Long *)cp;
    cp += sizeof(Rpc_Long);

    numAgents = *(Rpc_Long *)cp;
    cp += sizeof(Rpc_Long);

    LogPrintf("%d hosts registered on network %ld:\n", numAgents, electToken);
    agents = (Agent *)emalloc(numAgents * sizeof(Agent));

    for (i = 0; i < numAgents; i++) {
	int attrLen;
	Lst attributes;

	agents[i].name = cp;
	cp += strlen(cp) + 1;
	if (strchr(agents[i].name, '.')) {
	    *strchr(agents[i].name, '.') = '\0';
	}
	LogPrintf("\t%s: ", agents[i].name);

	attributes = Customs_ArgvToLst (cp, &attrLen);
	Lst_Destroy (attributes, free);
	cp += attrLen;

	cp = Customs_Align(cp, char *);

	agents[i].avail = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);

	agents[i].rating = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);

	agents[i].arch = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);

	agents[i].numClients = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);

	agents[i].clients = (Rpc_Long *)cp;
	cp += agents[i].numClients * sizeof(Rpc_Long);

	if (!(he = gethostbyname(agents[i].name))) {
	    LogPrintf("unknown\n");
	    continue;
	} 

	agents[i].addr = *(struct in_addr *)he->h_addr;
	sin.sin_addr = agents[i].addr;
	if (agents[i].avail & AVAIL_DOWN) {
	    LogPrintf("marked down\n");
	} else if (Rpc_Call(customs_Socket, &sin, (Rpc_Proc)CUSTOMS_LOG,
			 sizeof(force), (Rpc_Opaque)&force,
			 0, (Rpc_Opaque)0,
			 CUSTOMS_RETRY, &customs_RetryTimeOut) != RPC_SUCCESS)
	{
	    LogPrintf("couldn't contact.\n");
	} else {
	    LogPrintf("registered\n");
	}
			 
    }

    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_START, Start,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_FINISH, Finish,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_STOPPED, Stopped,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_NEWAGENT, NewAgent,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_NEWMASTER, NewMaster,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_EVICT, Evict,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_ACCESS, Access,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_KILL, Killed,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    Rpc_ServerCreate(customs_Socket, (Rpc_Proc)LOG_EXITFAIL, ExitFailed,
		     Rpc_SwapNull, Rpc_SwapNull, (Rpc_Opaque)0);
    
    Rpc_Run();
}

