/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *	Ohio Trollius
 *	Copyright 1997 The Ohio State University
 *	RBD/GDB/AKP
 *
 *	$Id: tping.c,v 6.5 1999/06/12 17:11:39 kmeyer1 Exp $
 *
 *	Function:	- bounces a message off the echo daemon
 */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <args.h>
#include <debug.h>
#include <ereq.h>
#include <net.h>
#include <priority.h>
#include <terror.h>
#include <typical.h>

#define plural(n)	((n != 1) ? "s" : "")

/*
 * local variables
 */
static int		delay;			/* delay between echos */
static int		fl_inf;			/* infinite # messages flag */
static int		fl_intr = FALSE;	/* trap interrupt flag */
static int		fl_verbose;		/* verbose flag */
static int4		msglen;			/* message length */
static int4		nmesg;			/* # messages to send */

static char		*usage=
	"tping [-vh] [-c <count>] [-d <delay>] [-l <length>] <nodes>\n";

/*
 * local functions
 */
static int		setdefaults();
static void		help();
static void		trap();


int
main(argc, argv)

int			argc;
char			*argv[];

{
	int		decr;			/* loop decrement (0 or 1) */
	int		n_index;		/* node index */
	int		n_flags;		/* node flags */
	int4		ret;			/* recho() return value */
	int4		nsent;			/* # messages sent */
	int4		nbytes;			/* # bytes sent */
	int4		nnodes;			/* # target nodes */
	int4		nodeid;			/* target node ID */
	int4		prevnode;		/* previous node ID */
	char		*outbuf = 0;		/* outgoing message buffer */
	char		*inbuf = 0;		/* incoming message buffer */
	double		tave;			/* average timing */
	double		tmin;			/* min timing */
	double		tmax;			/* max timing */
	double		ttotal;			/* total timing */
	double		ttrip;			/* message trip timing */

	/* Ensure that we are not root */

	if (getuid() == 0 || geteuid() == 0) {
	  show_help(NULL, "deny-root", NULL);
	  exit(EACCES);
	}

/*
 * Parse the command line.
 */
	validopts("hv");
	followed("cdl");

	if (do_args(&argc, argv)) {
		fprintf(stderr, usage);
		exit(errno);
	}
/*
 * Check for help request.
 */
	if (opt_taken('h')) {
		help();
		exit(0);
	}
/*
 * Set the defaults.
 */
	if (setdefaults()) {
		exit(errno);
	}
/*
 * Allocate in/out buffers.
 */
	if (msglen) {
		outbuf = malloc((unsigned) msglen);
		inbuf = malloc((unsigned) msglen);

		if ((outbuf == 0) || (inbuf == 0)) {
			perror("tping (malloc)");
			exit(errno);
		}
	}

	decr = (fl_inf) ? 0 : 1;
/*
 * Set up the trap handler.
 */
	if (signal(SIGINT, trap) == SIG_ERR) {
		fprintf(stderr, "tping: cannot trap SIGINT\n");
		exit(errno);
	}

	if (signal(SIGTERM, trap) == SIG_ERR) {
		fprintf(stderr, "tping: cannot trap SIGTERM\n");
		exit(errno);
	}
/*
 * Initialize the timing variables.
 */
	tmin = 1.0e+20;
	tmax = -1;
	ttotal = 0;
/*
 * Attach to the Trollius kernel.
 */
	if (kinit(PRCMD)) {
		terror("tping (kinit)");
		exit(errno);
	}

	if (nid_parse(&argc, argv) || (errno = (argc == 1) ? 0 : EUSAGE)) {
		fprintf(stderr, usage);
		kexit(errno);
	}
/*
 * Get the first node identifier.
 */
	nid_get(&n_index, &nodeid, &n_flags);

	if (n_index < 0) {
		fprintf(stderr, "usage: %s\n", usage);
		errno = EUSAGE;
		kexit(errno);
	}
/*
 * Loop over the # of messages.
 */
	for (nsent = 0; (nmesg > 0) && (!fl_intr); ++nsent, nmesg -= decr) {
		ttrip = ttime();
/*
 * Loop through all specified nodes.
 */
		nnodes = 0;

		do {
			prevnode = nodeid;
			++nnodes;
/*
 * Ping the node.
 */
			ret = recho(nodeid, outbuf, inbuf, msglen);

			if (ret != msglen) {

				if (ret < 0) {
					terror("tping (recho)");
				} else {
					fprintf(stderr,
						"tping: length error\n");
				}

				kexit(errno);
			}

			nid_get(&n_index, &nodeid, &n_flags);
		} while(n_index);
/*
 * Compute timing statistics.
 */
		ttrip = ttime() - ttrip;
		ttotal += ttrip;
		if (ttrip < tmin) {
			tmin = ttrip;
		}
		if (ttrip > tmax) {
			tmax = ttrip;
		}

		if (nnodes == 1) {
			VERBOSE("%3d byte%s from %s: %3.3f secs\n", msglen,
				plural(msglen), mnemonic(prevnode), ttrip);
		} else {
			VERBOSE("%3d byte%s from %d nodes: %3.3f secs\n",
				msglen, plural(msglen), nnodes, ttrip);
		}
/*
 * Wait for a while.
 */
		if (delay > 0) {
			sleep((unsigned) delay);
		}
	}
/*
 * Free the buffers.
 */
	if (outbuf) {
		free(outbuf);
	}

	if (inbuf) {
		free(inbuf);
	}
/*
 * Print out the final statistics.
 */
	if (nsent <= 0) {
		fprintf(stderr, "tping: sent %d messages!\n", nsent);
		kexit(1);
	}

	nbytes = msglen * nsent;
	tave = ttotal / nsent;

	printf("\n%d message%s, %d byte%s (%4.3fK), %4.3f secs (%4.3fK/sec)\n",
		nsent, plural(nsent), nbytes, plural(nbytes),
		((double) nbytes) / 1024, ttotal,
		((double) 2 * nbytes) / ttotal / 1024);

	printf("roundtrip min/avg/max: %3.3f/%3.3f/%3.3f\n", tmin, tave, tmax);

	kexit(0);
	return(0);
}

/*
 *	trap
 *
 *	Function:	- signal trap handler
 *			- set interrupt flag in order to exit the loop
 */
static void
trap()

{
	fl_intr = TRUE;
}

/*
 *	setdefaults
 *
 *	Function:	- set the default command parameters
 *	Returns:	- 0 or ERROR
 */
static int
setdefaults()

{
/*
 * Get the # of messages to send.
 */
	if (opt_taken('c')) {
		fl_inf = FALSE;
		intparam('c', &nmesg);
	} else {
		fl_inf = TRUE;
		nmesg = 1;
	}

	if (nmesg < 0) {
		fprintf(stderr, "tping: count (%d) must be >= 0\n", nmesg);
		errno = EUSAGE;
		return(LAMERROR);
	} else if (nmesg == 0) {
		fl_inf = TRUE;
		nmesg = 1;
	}
/*
 * Get the delay between messages.
 */
	if (opt_taken('d')) {
		intparam('d', &delay);
	} else {
		delay = 1;
	}

	if (delay < 0) {
		fprintf(stderr, "tping: delay (%d) must be >= 0\n", delay);
		errno = EUSAGE;
		return(LAMERROR);
	}
/*
 * Set the verbose flag.
 */
	fl_verbose = ! opt_taken('v');
/*
 * Set the message length.
 */
	if (opt_taken('l')) {
		intparam('l', &msglen);
	} else {
		msglen = 1;
	}

	if (msglen < 0) {
		fprintf(stderr, "tping: length (%d) must be >= 0\n", msglen);
		errno = EUSAGE;
		return(LAMERROR);
	}
	else if (msglen > MAXNMSGLEN) {
		fprintf(stderr, "tping: using max length (%d)\n", MAXNMSGLEN);
		msglen = MAXNMSGLEN;
	}

	return(0);
}

/*
 *	help
 *
 *	Function:	- prints helpful information on this command
 */
static void
help()

{
	printf("\nSynopsis:       tping [options] <nodes>\n");
	printf("\nOptions:        -h	Print this help message.\n");
	printf("\t\t-v      Turn off verbose mode.\n");
	printf("\t\t-c <#>  Echo <#> messages.\n");
	printf("\t\t-l <#>  Use <#> bytes per message.\n");
	printf("\t\t-d <#>  Delay <#> seconds between messages.\n");
	printf("\nDescription:    Echo messages to Trollius nodes.\n");
	mnusage();
	printf("\t\th\t(local), o (origin), N (all)\n");
	printf("\nExample:        tping n10 -c 100 -l 1024 -d 0\n");
	printf("\t\t\t\"Echo 100 messages of length 1024 to node 10\n");
	printf("\t\t\t without pausing between messages.\"\n");
}
