/* This file is part of the Project Athena Zephyr Notification System.
 * It contains the zstat program.
 *
 *      Created by:     David C. Jedlinsky
 *
 *      $Id: zstat.c,v 1.15 2000/10/08 19:13:16 assar Exp $
 *
 *      Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
 *      For copying and distribution information, see the file
 *      "mit-copyright.h". 
 */

/* 
 * There should be library interfaces for the operations in zstat; for now,
 * however, zstat is more or less internal to the Zephyr system. 
 */

#include <internal.h>

#include "zserver.h"

#if !defined(lint) && !defined(SABER)
static const char rcsid_zstat_c[] = "$Id: zstat.c,v 1.15 2000/10/08 19:13:16 assar Exp $";
#endif

const char *hm_head[] = {
    "Current server:",
    "Items in queue:",
    "Client packets received:",
    "Server packets received:",
    "Server changes:",
    "Version:",
    "Looking for a new server:",
    "Time running:",
    "Size:",
    "Machine type:"
};
#define	HM_SIZE	(sizeof(hm_head) / sizeof (char *))
const char *srv_head[] = { 
    "Current server version =",
    "Packets handled:",
    "Uptime:",
    "Server states:",
};
#define	SRV_SIZE	(sizeof(srv_head) / sizeof (char *))

int outoftime = 0;

int serveronly = 0,hmonly = 0, listonly = 0;
int oldhm = 0;
u_short srv_port;

static void do_stat(const char *host);
static void srv_stat(const char *host);
static void hm_stat(const char *host, int do_server);
static void list_realms(const char *host);


static RETSIGTYPE
timeout(int sig)
{
	outoftime = 1;
}

static void
usage(const char *s)
{
	fprintf(stderr,"usage: %s [-s] [-h] [-l] [-o] [host ...]\n",s);
	exit(1);
}


static void
do_stat(const char *host)
{
    if (listonly) {
	list_realms(host);
    } else if (serveronly) {
	srv_stat(host);
    } else {
	hm_stat(host, !hmonly);
    }
}

static void
print_hm_stat (ZNotice_t *notice, int do_server)
{
    char **line,*mp;
    int i,nf;
    time_t runtime;
    struct tm *tim;

    for (nf=0, mp = notice->z_message;
	 mp<notice->z_message+notice->z_message_len;
	 nf++, mp += strlen(mp)+1)
	;
    
    line = (char **) malloc(sizeof(char *)*nf);
    
    mp = notice->z_message;
    for (nf=0, mp = notice->z_message;
	 mp<notice->z_message+notice->z_message_len;
	 nf++, mp += strlen(mp)+1) 
	line[nf] = mp;
    
    
    printf("HostManager protocol version = %s\n\n",notice->z_version);
    
    for (i=0; i<nf; i++) {
	if (((i%(HM_SIZE+2)) == 0) && (i+HM_SIZE<nf)) {
	    printf("Zephyr realm = %s\n", line[i+HM_SIZE]);
	    printf("%s %s\n",hm_head[i%(HM_SIZE+2)],line[i]);
	} else if ((i%(HM_SIZE+2)) == 7) {
	    runtime = atol(line[i]);
	    tim = gmtime(&runtime);
	    printf("%s %d days, %02d:%02d:%02d\n", hm_head[i%(HM_SIZE+1)],
		   tim->tm_yday,
		   tim->tm_hour,
		   tim->tm_min,
		   tim->tm_sec);
	} else if ((i%(HM_SIZE+2)) == HM_SIZE) {
	    /* do nothing */
	} else if (((i%(HM_SIZE+2)) == (HM_SIZE+1)) ||
		   (i == nf-1)) {
	    printf("\n");
	    if (do_server
		&& strcmp (line[i-(i%(HM_SIZE+2))], "NO_SERVER") != 0) {
		srv_stat(line[i-(i%(HM_SIZE+2))]);
		printf("\n");
	    }
	} else {
	    printf("%s %s\n",hm_head[i%(HM_SIZE+2)],line[i]);
	}
    }
    free(line);
    
    printf("\n");
}


static void
host2inaddr (const char *host, struct sockaddr_in *sin, const char *msg)
{
    struct hostent *hp;

    if ((sin->sin_addr.s_addr = inet_addr(host)) == (unsigned)(-1)) {
	if ((hp = gethostbyname(host)) == NULL) {
	    fprintf(stderr,"Unknown host: %s\n",host);
	    return;
	}
	memcpy((char *) &sin->sin_addr.s_addr, hp->h_addr, hp->h_length);
	
	printf("%s: %s\n", msg, hp->h_name);
    } else {
	printf("%s: %s\n", msg, host);
    }
}

static void
hm_stat(const char *host, int do_server)
{
    struct sockaddr_in sin;
    Code_t ret;

    host2inaddr(host, &sin, "Hostmanager stats");
    
    if (oldhm) {
	ZNotice_t notice;
	
	ret = ZhmStat(&sin, &notice);
	if (ret != ZERR_NONE) {
	    com_err("zstat", ret, "getting hostmanager status");
	    exit(-1);
	}
	print_hm_stat (&notice, do_server);
	ZFreeNotice(&notice);
    } else {
	ZNotice_t notice;
	char **realms;
	int nrealms, i;
	
	ret = ZhmRealmList(&sin, &realms, &nrealms);
	if (ret) {
		com_err("zstat", ret, "getting hostmanager list of realms");
		exit(-1);
	}
	for (i = 0; i < nrealms; i++) {
	    printf ("Realm: %s\n", realms[i]);
	    ret = ZhmStatOneRealm(&sin, realms[i], &notice);
	    if (ret != ZERR_NONE) {
		com_err("zstat", ret, "getting hostmanager status");
		continue;
	    }
	    print_hm_stat (&notice, do_server);
	    ZFreeNotice(&notice);
	}
	ZhmFreeRealmList(realms);
    }
}

static void
list_realms (const char *host)
{
    Code_t ret;
    char **realms;
    int nrealms, i;
    struct sockaddr_in sin;

    host2inaddr(host, &sin, "Hostmanager realms");

    ret = ZhmRealmList(&sin, &realms, &nrealms);
    if (ret) {
	printf ("zhm returned %d\n", ret);
	return;
    }
    for (i = 0; i < nrealms; i++)
	printf ("%s\n", realms[i]);
    ZhmFreeRealmList(realms);
}


static void
srv_stat(const char *host)
{
	char *line[20],*mp;
	int sock,i,nf,ret;
	struct sockaddr_in sin;
	ZNotice_t notice;
	time_t runtime;
	struct tm *tim;
#ifdef _POSIX_VERSION
	struct sigaction sa;
#endif
		
	(void) memset((char *) &sin, 0, sizeof(struct sockaddr_in));

	sin.sin_port = srv_port;

	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket:");
		exit(-1);
	}
	
	sin.sin_family = AF_INET;

	host2inaddr(host, &sin, "Server stats");

	memset((char *)&notice, 0, sizeof(notice));
	notice.z_kind = UNSAFE;
	notice.z_port = 0;
	notice.z_class = ZEPHYR_ADMIN_CLASS;
	notice.z_class_inst = "";
	notice.z_opcode = ADMIN_STATUS;
	notice.z_sender = "";
	notice.z_recipient = "";
	notice.z_default_format = "";
	notice.z_message_len = 0;
	
	if ((ret = ZSetDestAddr(&sin)) != ZERR_NONE) {
		com_err("zstat", ret, "setting destination");
		exit(-1);
	}
	if ((ret = ZSendNotice(&notice, ZNOAUTH)) != ZERR_NONE) {
		com_err("zstat", ret, "sending notice");
		exit(-1);
	}

#ifdef _POSIX_VERSION
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sa.sa_handler = timeout;
	(void) sigaction(SIGALRM, &sa, (struct sigaction *)0);
#else
	(void) signal(SIGALRM,timeout);
#endif
	outoftime = 0;
	(void) alarm(10);
	if (((ret = ZReceiveNotice(&notice, (struct sockaddr_in *) 0))
	    != ZERR_NONE) &&
	    ret != EINTR) {
		com_err("zstat", ret, "receiving notice");
		return;
	}
	(void) alarm(0);
	if (outoftime) {
		fprintf(stderr,"No response after 10 seconds.\n");
		return;
	} 
	
	mp = notice.z_message;
	for (nf=0;mp<notice.z_message+notice.z_message_len;nf++) {
		line[nf] = mp;
		mp += strlen(mp)+1;
	}

	printf("Server protocol version = %s\n",notice.z_version);
	
	for (i=0; i < nf; i++) {
		if (i < 2)
			printf("%s %s\n",srv_head[i],line[i]);
		else if (i == 2) { /* uptime field */
			runtime = atol(line[i]);
			tim = gmtime(&runtime);
			printf("%s %d days, %02d:%02d:%02d\n",
			       srv_head[i],
			       tim->tm_yday,
			       tim->tm_hour,
			       tim->tm_min,
			       tim->tm_sec);
		} else if (i == 3) {
			printf("%s\n",srv_head[i]);
			printf("%s\n",line[i]);
		} else printf("%s\n",line[i]);
	}
	
	(void) close(sock);
	ZFreeNotice(&notice);
}

int
main(int argc, char *argv[])
{
	Code_t ret;
	int optchar;
	struct servent *sp;

	if ((ret = ZInitialize()) != ZERR_NONE) {
		com_err("zstat", ret, "initializing");
		exit(-1);
	}

	if ((ret = ZOpenPort((u_short *)0)) != ZERR_NONE) {
		com_err("zstat", ret, "opening port");
		exit(-1);
	}

	while ((optchar = getopt(argc, argv, "shol")) != EOF) {
		switch(optchar) {
		case 's':
			serveronly++;
			break;
		case 'h':
			hmonly++;
			break;
		case 'o':
			oldhm++;
			break;
		case 'l':
			listonly++;
			break;
		case '?':
		default:
			usage(argv[0]);
			exit(1);
		}
	}

	if (serveronly && hmonly && listonly) {
		fprintf(stderr,"Only one of -s, -l and -h may be specified\n");
		exit(1);
	}

	sp = getservbyname(SERVER_SVCNAME,"udp");
	srv_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;

	if (optind == argc) {
		do_stat("127.0.0.1");
		exit(0);
	}

	for (;optind<argc;optind++)
		do_stat(argv[optind]);

	exit(0);
}
