/*-
 * reginfo.c --
 *	Find out who's registered.
 *
 * 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: reginfo.c,v 1.39 2001/02/17 01:09:58 stolcke Exp $ ICSI (Berkeley)";
#endif /* not lint */

#include    <stdio.h>
#include    <string.h>
#include    <netdb.h>
#include    <sys/time.h>
#include    <ctype.h>

#include    "customs.h"

typedef struct {
    char	*name;
    Lst		attributes;
    Rpc_Long	avail;
    Rpc_Long    rating;
    Rpc_Long    arch;
    int		numClients;
    int		*clients;
} Host;

static Host   	*allHosts;
static char	*progname;
static Boolean	rateHosts = FALSE;

static int
HostCompare(h1, h2)
    int *h1, *h2;
{
    if (rateHosts) {
	return (-(allHosts[*h1].rating - allHosts[*h2].rating));
    } else {
	return strcmp(allHosts[*h1].name, allHosts[*h2].name);
    }
}

void
Usage ()
{
    fprintf (stderr,
"Usage: %s [-hosts] [-rate] [-showattr] [-avail] [-up] [-down] [-master]\\\n\
\t[-arch <num>] [-attr <string> ...] [-port <port>] [<host>]\n",
	             progname);
    exit (2);
}

/*ARGSUSED*/
main(argc, argv)
    int	    argc;
    char    **argv;
{
    char    	  	infoBuf[MAX_INFO_SIZE];
    int	    	  	i, j;
    Rpc_Long	    	network;
    int	    	  	numHosts;
    char    	  	*cp;
    struct sockaddr_in	sin;
    int			*sortHosts;
    int			k, totalHosts = 0, availHosts = 0;
    int			restrictedHosts = 0, downHosts = 0;
    int			lastAlloc;
    Boolean		showAttr = FALSE, showHosts = FALSE, onlyMaster = FALSE;
    Boolean		onlyUp = FALSE, onlyDown = FALSE, onlyAvail = FALSE;
    Lst			attributes;
    int			arch = -1;
    char		*service = NULL;

    if (progname = strrchr(argv[0], '/')) {
	progname++;
    } else {
	progname = argv[0];
    }

    attributes = Lst_Init(FALSE);

    /*
     * Parse attribute options
     */
    for (argv++, argc--; argc > 0; argv++, argc--) {
	if (strcmp (argv[0], "-attr") == 0) {
	    if (argc == 1) {
		fprintf(stderr, "%s: missing attribute value\n", progname);
		exit (2);
	    } else {
		(void)Lst_AtEnd (attributes, (ClientData)argv[1]);
		argv++; argc--;
	    }
	} else if (strcmp (argv[0], "-arch") == 0) {
	    if ((argc == 1) || !isdigit(*argv[1]))  {
		fprintf(stderr, "%s: missing architecture code\n", progname);
		exit (2);
	    } else {
		arch = atoi(argv[1]);
		argv++; argc--;
	    }
	} else if (strcmp (argv[0], "-port") == 0) {
	    if (argc == 1) {
		fprintf(stderr, "%s: missing port name\n", progname);
		exit (2);
	    } else {
		service = argv[1];
		argv++; argc--;
	    }
	} else if (strcmp (argv[0], "-showattr") == 0) {
	    showAttr = TRUE;
	    showHosts = FALSE;
	} else if (strcmp (argv[0], "-hosts") == 0) {
	    showHosts = TRUE;
	    showAttr = FALSE;
	} else if (strcmp (argv[0], "-rate") == 0) {
	    rateHosts = TRUE;
	} else if (strcmp (argv[0], "-up") == 0) {
	    onlyUp = TRUE;
	} else if (strcmp (argv[0], "-down") == 0) {
	    onlyDown = TRUE;
	} else if (strcmp (argv[0], "-avail") == 0) {
	    onlyAvail = TRUE;
	} else if (strcmp (argv[0], "-master") == 0) {
	    onlyMaster = TRUE;
	} else if (*argv[0] == '-') {
	    Usage ();
	    /*NOTREACHED*/
	} else {
	    break;
	}
    }

    if (argc > 1) {
	Usage ();
	/*NOTREACHED*/
    }

    /*
     * If a non-standard host or service was specified we have to use
     * the Customs_MasterForHost call.
     */
    if (service != NULL || argc > 0) {
	struct sockaddr_in sin2;
	struct hostent *he;
	struct servent *se;

	sin2.sin_family = AF_INET;

	if (argc == 0) {
	    sin2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	} else {
	    sin2.sin_addr.s_addr = inet_addr(argv[0]);
	    if (sin2.sin_addr.s_addr == -1) {
		he = gethostbyname(argv[0]);
		if (he == (struct hostent *)NULL) {
		    fprintf (stderr, "%s: %s: unkown host\n",
				     progname, argv[0]);
		    exit(1);
		}
		memcpy (&sin2.sin_addr, he->h_addr, he->h_length);
	    }
	}

	if (service != NULL) {
	    sin2.sin_port = htons(atoi(service));
	} else {
	    sin2.sin_port = 0;
	    service = CUSTOMS_SERVICE_NAME;
	}

	if (sin2.sin_port == 0) {
	    se = getservbyname(service, "udp");
	    if (se == (struct servent *)NULL) {
		fprintf (stderr, "%s: %s: unknown service\n",
				 progname, service);
		exit(1);
	    }
	    sin2.sin_port = se->s_port;
	}

	if (Customs_MasterForHost(&sin2, &sin) != RPC_SUCCESS) {
	    fprintf(stderr, "%s: ", progname);
	    Customs_PError("Couldn't find master");
	    exit(1);
	}
    } else {
	if (Customs_Master(&sin) != RPC_SUCCESS) {
	    fprintf(stderr, "%s: ", progname);
	    Customs_PError("Couldn't find master");
	    exit(1);
	}
    }

    if (Customs_Info(&sin, infoBuf) != RPC_SUCCESS) {
	fprintf(stderr, "%s: ", progname);
	Customs_PError("Couldn't read registration info");
	exit(1);
    }
    cp = infoBuf;
    network = *(Rpc_Long *)cp;
    cp += sizeof(Rpc_Long);
    numHosts = *(Rpc_Long *)cp;
    cp += sizeof(Rpc_Long);
    allHosts = (Host *)emalloc(numHosts * sizeof(Host));
    sortHosts = (int *)emalloc(numHosts * sizeof(int));
    for (i = 0; i < numHosts; i++) {
	int attrLen;

	allHosts[i].name = cp;
	cp += strlen(cp) + 1;
	allHosts[i].attributes = Customs_ArgvToLst (cp, &attrLen);
	cp += attrLen;
	cp = Customs_Align(cp, char *);
	allHosts[i].avail = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);
	allHosts[i].rating = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);
	allHosts[i].arch = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);
	allHosts[i].numClients = *(Rpc_Long *)cp;
	cp += sizeof(Rpc_Long);
	allHosts[i].clients = (Rpc_Long *)cp;
	cp += allHosts[i].numClients * sizeof(Rpc_Long);
	sortHosts[i] = i;
    }
    
    /* sort by hostname */
    qsort(sortHosts, numHosts, sizeof(int), HostCompare);

    lastAlloc = *(Rpc_Long *)cp;

    for (k = 0; k < numHosts; k++) {
	char status[80];
	int index = -1;
	i = sortHosts[k];

	/*
	 * Check attributes and architecture code, and print only
	 * matching hosts.
	 * In rating mode, display only available hosts.
	 */
	if ((arch != -1) && (allHosts[i].arch != arch) ||
	    (rateHosts || onlyAvail) && allHosts[i].avail ||
	    onlyUp && (allHosts[i].avail & AVAIL_DOWN) ||
	    onlyDown && !(allHosts[i].avail & AVAIL_DOWN) ||
	    onlyMaster && (i != 0) ||
	    !Customs_CheckAttr (attributes, allHosts[i].attributes))
	{
	    continue;
	}

	if (totalHosts == 0 && !showHosts) {
	    if (!showAttr) {
		printf ("HOST          ARCH   STATUS                          INDEX   CLIENTS\n");
	    } else {
		printf ("HOST          ARCH   ATTRIBUTES\n");
	    }
	}

	totalHosts++;

	if (showHosts) {
	    printf ("%s\n", allHosts[i].name);
	    continue;
	} else {
	    printf ("%-12.12s %5d %c ", allHosts[i].name, allHosts[i].arch,
		    (i == 0 ? 'M' : (i == lastAlloc ? 'L' : ' ')));
	}

	if (!showAttr) {
	    if (allHosts[i].avail & AVAIL_DOWN) {
		int days = allHosts[i].rating / (60*60*24);

		if (days > 0) {
		    sprintf(status, "host down (%d day%s)",
			    days, days > 1 ? "s" : "");
		} else {
		    sprintf(status, "host down (%d:%02d hours)",
			    allHosts[i].rating / (60*60),
			    (allHosts[i].rating % (60*60)) / 60);
		}
		downHosts++;
	    } else if (allHosts[i].avail & AVAIL_CLOCK) {
		sprintf(status, "clock out of sync (%ld secs)",
			allHosts[i].rating);
	    } else if (allHosts[i].avail & AVAIL_VERSION) {
		sprintf(status, "incompatible version");
	    } else if (allHosts[i].avail & AVAIL_IDLE) {
		if (allHosts[i].rating > 0) {
		    sprintf(status, "not idle (%d:%02d minutes)",
			    allHosts[i].rating / 60, allHosts[i].rating % 60);
		} else {
		    sprintf(status, "not idle");
		}
	    } else if (allHosts[i].avail & AVAIL_SWAP) {
		sprintf(status, "not enough swap space (%d%% free)",
			allHosts[i].rating);
	    } else if (allHosts[i].avail & AVAIL_LOAD) {
		sprintf(status, "load average too high (%.2f)",
			(double)allHosts[i].rating / LOADSCALE);
	    } else if (allHosts[i].avail & AVAIL_IMPORTS) {
		sprintf(status, "too many imported jobs (%d)",
			allHosts[i].rating);
	    } else if (allHosts[i].avail & AVAIL_PROC) {
		sprintf(status, "too many processes (%d free)",
			allHosts[i].rating);
	    } else if (allHosts[i].avail & AVAIL_SERVER) {
		struct in_addr serverAddr;

		serverAddr.s_addr = htonl(allHosts[i].rating);
		sprintf(status, "server %s down",
				Customs_Hostname(&serverAddr));
	    } else if (allHosts[i].avail & AVAIL_EXCLUSIVE) {
		sprintf(status, "in exclusive use (%d jobs)",
			allHosts[i].rating);
	    } else if (allHosts[i].avail) {
		sprintf(status, "not available (reason unknown)");
	    } else {
		sprintf(status,"available");
		index = allHosts[i].rating;
		if (allHosts[i].numClients == 0) {
		    availHosts++;
		} else {
		    restrictedHosts++;
		}
	    }
	    if (index < 0) {
		printf ("%-37s   ", status);
	    } else {
		printf ("%-30s%7d   ", status, index);
	    }
	    if (allHosts[i].numClients != 0) {
		for (j = 0; j < allHosts[i].numClients; j++) {
		    printf ("%s%s", j == 0 ? "": " ",
			    allHosts[allHosts[i].clients[j]].name);
		}
		printf ("\n");
	    } else {
		printf ("ALL\n");
	    }
	} else {
	    /*
	     * Print host attributes only if an -attr flag as been specified
	     */
	    if (Lst_IsEmpty (allHosts[i].attributes)) {
		printf ("NONE\n");
	    } else {
		Lst lst;
		LstNode ln;

		lst = allHosts[i].attributes;
		(void)Lst_Open (lst);

		printf  ("%s", (char *)Lst_Datum (Lst_Next(lst)));
		for (ln=Lst_Next(lst); !Lst_IsAtEnd (lst); ln=Lst_Next(lst)) {
		    printf  (" %s", (char *)Lst_Datum (ln));
		}
		Lst_Close (lst);

		printf ("\n");
	    }
	}
    }
    if (!showHosts) {
	printf ("Network %d: %d host(s)", network, totalHosts);
	if (!showAttr) {
	    printf (" total, %d available unrestricted, %d restricted, %d down",
		    availHosts, restrictedHosts, downHosts);
	}
	printf ("\n");
    }
    exit (totalHosts == 0);
}
