/*****************************************************************************
* File: netstress.c
* Desc: Networking stress utility
*****************************************************************************/

/*****************************************************************************
* Copyright (c) 2005, Avocent Corp.
* Written by Jim Gleason - July 5, 2005
* Copyright (c) 2006/2007, Helius, Inc.
* Enhanced by Jim Gleason - 2006 .. May 2007
******************************************************************************
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
*   this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
*   this list of conditions and the following disclaimer in the documentation
*   and/or other materials provided with the distribution.
* * Neither the name of its copyright holders nor the names of its contributors
*   may be used to endorse or promote products derived from this software without
*   specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/prctl.h>
#include <sys/uio.h>

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>

/* Define the min & max packet size (min == packet_number & seed & length) */
#define KB					(1024)
#define LHEADER_SIZE		(3)
#define HEADER_SIZE			(LHEADER_SIZE*sizeof(uint32_t))
#define MAX_PACKET_SIZE		(4*KB)
#define LMAX_PACKET_SIZE	(MAX_PACKET_SIZE / sizeof(uint32_t))

#define DEFAULT_PORT	5678

#define DEFAULT_REPORT_EVERY_N_SECONDS		5
#define DEFAULT_BENCHMARK_EVERY_N_REPORTS	3

#define DAYS_IN_SECONDS		(24*60*60)
#define HOURS_IN_SECONDS	(60*60)
#define MINUTES_IN_SECONDS	(60)

/*** Global Variables *******************************************************/

int verbosity = 0;	/* 0 = no_verbosity, 1...n = verbosity_level */
int debug = 0;		/* 0 = no_debug, 1...n = debug_level */

int debugging_receive_loop = 0;
int debugging_transmit_loop = 0;


/****************************************************************************/

#define MAX_SLAVES		32

// NOTE: We limit the length of the childs connectionName and  identity here
//       because it doesn't make sense to print a name so long that we lose
//       reference to the actual message or data.
// NOTE: The identity is a combination of the connection ID & name
#define MAX_CONNECTIONNAME	32
#define MAX_IDENTITY	(MAX_CONNECTIONNAME+4)

typedef struct children_t {
	pid_t tx_pid;
	pid_t rx_pid;
	// The connectionID is really just the index (passed from master to slave)
	int32_t connectionID;
	// The connectionName is optional (passed from slave to master)
	char connectionName[MAX_CONNECTIONNAME];
	char identity[MAX_IDENTITY];
} children_t;

typedef struct context_t
{
	char *progname;						/* Program name */

	int master;							/* 1 = master, 0 = slave */
	char *host;							/* Hostname or IP Address to use */
	int port;							/* Port number to use */
	uint32_t seed;						/* Random number seed */

	time_t connect_time;				/* Actual connect time */
	int report_using_current_time;		/* Default = 0 = Report elapsed time */
	int report_every_n_seconds;			/* Default = 5 */
	int benchmark_every_n_reports;		/* Default = 3 */

	int benchmark_in_bytes_per_second;	/* 0 = bits, 1 = bytes */
	unsigned char measure;				/* m=1000000|k=1000|b=1 */
	float f_measure;					/* Benchmark divider */
	char s_measure[64];					/* Benchmark divider in string format */

	int master_write_slave_read_test;	/* default is 1 (enabled) */
	int master_read_slave_write_test;	/* default is 1 (enabled) */

	struct hostent *hostent;
	int socket_fd;						/* Socket file descriptor returned from socket() */
	struct sockaddr_in this_sockaddr;	/* Our socket address information */
	struct sockaddr_in that_sockaddr;	/* Their socket address information */

	pid_t pid;
	children_t children[MAX_SLAVES];
	// NOTE: The connectionName is optional
	//       The context->connectionName is the name of the master
	char connectionName[MAX_CONNECTIONNAME];

	/*
	* Provide a fixed random buffer (we pre-fill this)
	* This buffer is used UNLESS real_random is set.
	* When this buffer is used, we convert the seed to
	* an offset into this buffer.
	*/
	int real_random;		/* Default = 0 */
	unsigned char randomTxBuffer[(MAX_PACKET_SIZE+sizeof(long)) * 2];
	unsigned char randomRxBuffer[(MAX_PACKET_SIZE+sizeof(long)) * 2];

	/*
	* Run "Fast" as we can - Don't check the data!
	* NOTE: This overrides real_random!
	*/
	int fast;				/* Default = 0 */

} context_t;

context_t *global_context = NULL;

/****************************************************************************/

extern int errno;

/****************************************************************************/

/* "seconds_counter" needs to be shared amongst all processes */


#if (1)
#	define MY_SHM_KEY		0xDEADBEEF
#else
#	define MY_SHM_KEY		IPC_PRIVATE
#endif

static int ShmID;

struct shmid_ds Myshmid_ds;

static uint32_t *seconds_counter = NULL;
#define seconds_counter_SIZE	sizeof(uint32_t)

/****************************************************************************/

int main(
		int argc,
		char *argv[],
		char *envv[]);

static int parse_options(
		int argc,
		char *argv[],
		char *envv[],
		context_t *context);

static void give_usage(
		context_t *context);

static children_t *which_child(context_t *context, pid_t pid);

static char *SIGstring(int sig);

static void watch_for_DEATH_OF_PARENT(void);

static void DEATH_OF_PARENT_handler(
		int sig);

static void DEATH_OF_CHILD_handler(
		int sig);

static void SIGALRM_handler(
		int sig);

//static void generic_SIG_handler(
//		int sig);

static int handshakeConnectionInformation(
		context_t *context,
		int socket_fd,
		children_t *child);

static int netstress_master(
		context_t *context);

static int netstress_slave(
		context_t *context);

static char *time_stamp(context_t *context);
static char *elapsed_time_stamp(context_t *context);
static char *current_time_stamp(context_t *context);

static char *transfer_speed(
		context_t *context,
		time_t total_time, uint64_t total_byte_count,
		time_t delta_time, uint32_t delta_byte_count);

static void receive_loop(
		context_t *context,
		int socket_fd,
		char *identity);

static void transmit_loop(
		context_t *context,
		int socket_fd,
		char *identity);

/*****************************************************************************
* Func: Vprintf
* Desc: This is basically printf with a leading argument that is a debug level.
*       This also prepends a "DEBUG: " string
*       If debug is >= this debug level the printf will be performed.
*****************************************************************************/
void Vprintf(int verbosity_lvl, const char *format, ...)
{
	if (verbosity >= verbosity_lvl) {
		//pid_t pid = getpid();
		va_list ap;
		va_start(ap, format);
		//printf("VERBOSE(%d): ", pid);
		vprintf(format, ap);
		va_end(ap);
	}
	return;
} /* Vprintf */

/*****************************************************************************
* Func: Dprintf
* Desc: This is basically printf with a leading argument that is a debug level.
*       This also prepends a "DEBUG: " string
*       If debug is >= this debug level the printf will be performed.
*****************************************************************************/
void Dprintf(int dbg_lvl, const char *format, ...)
{
	if (debug >= dbg_lvl) {
		pid_t pid = getpid();
		va_list ap;
		va_start(ap, format);
		printf("DEBUG(%d): ", pid);
		vprintf(format, ap);
		va_end(ap);
	}
	return;
} /* Dprintf */

void setChildIdentity(children_t *child)
{
	// NOTE: The length of the child identity is limited
	//       because it doesn't make sense to print a name so
	//       long we lose reference to the actual message or data.
	if (child->connectionName[0] == '\0') {
		sprintf(child->identity, "%d", child->connectionID);
	} else {
		sprintf(child->identity, "%d=%s",
				child->connectionID, child->connectionName);
	}
	return;
} /* setChildIdentity */

/*****************************************************************************
* Func: main
* Desc: Main controlling function
*****************************************************************************/
int main(
		int argc,
		char *argv[],
		char *envv[])
{
	context_t _context;
	context_t *context = &_context;
	//int sts;
	char *cp;
	char *buff;
	int buff_size;
	int i;

	global_context = context;

	/* Initialize the context structure */
	memset(context, 0, sizeof(context_t));
	context->seed = (uint32_t)time(NULL);
	context->port = DEFAULT_PORT;
	context->report_using_current_time = 0;		/* 0 = Report elapsed time */
	context->report_every_n_seconds = DEFAULT_REPORT_EVERY_N_SECONDS;
	context->benchmark_every_n_reports = DEFAULT_BENCHMARK_EVERY_N_REPORTS;
	context->benchmark_in_bytes_per_second = 0;	/* 0=bits, 1=bytes */
	context->measure = 'm';		/* m=1000000|k=1000|b=1 */
	context->master_write_slave_read_test = 1;	/* default is 1 (enabled) */
	context->master_read_slave_write_test = 1;	/* default is 1 (enabled) */
	context->real_random = 0;					/* default is 0 (disabled) */
	context->fast = 0;							/* default is 0 (disabled) */
	context->pid = getpid();
	for (i = 0; i < MAX_SLAVES; i++) {
		// Let container know its own index (ID)
		context->children[i].connectionID=i;
	}

	/* Get the program name while Striping off any associated path */
	cp = rindex(argv[0],'/');
	if (cp) {
		cp++;
		context->progname = cp;
	} else {
		context->progname = argv[0];
	}

	parse_options(argc, argv, envv, context);

	if (context->fast) {
		context->real_random = 0;	/* Can't do "Fast" and real_random */
		printf("WARNING: Data validataion checks have been disabled\n");
	}

	/* Determine some stuff based on the options */
	buff = &context->s_measure[0];
	buff_size = sizeof(context->s_measure);
	if (context->measure == 'm') {
		context->f_measure = 1000000.0;
		*buff = 'm'; buff++;
		buff_size--;
	} else if (context->measure == 'k') {
		context->f_measure = 1000.0;
		*buff = 'k'; buff++;
		buff_size--;
	} else /* (context->measure == 'b') */ {
		context->f_measure = 1.0;
	}
	if (context->benchmark_in_bytes_per_second) {
		snprintf(buff, buff_size, "%s", "bytes");
	} else {
		snprintf(buff, buff_size, "%s", "bits");
		context->f_measure /= 8.0;
	}

#ifdef NOT_YET
	sts = setpriority(PRIO_PROCESS, 0, context->priority);
	if (sts < 0) {
		printf("ERROR: Unable to set priority to %d\n",
				context->priority);
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
	}
#endif /* NOT_YET */

	Dprintf(1, "%s %s Host=%s Port=%d Seed=%lu\n",
			context->progname,
			context->master?"Master":"Slave",
			context->host,
			context->port,
			context->seed);

	/* Setup shared memory */
	/* Setup "seconds_counter" as shared memory for all processes */
		ShmID = shmget(MY_SHM_KEY, seconds_counter_SIZE, 0600 | IPC_CREAT);
		if (ShmID != -1) {
			/* Attach to the shared memory */
			seconds_counter = shmat(ShmID, 0, SHM_W);
			if (seconds_counter) {
				*seconds_counter = 0L;
				/* Catch and handle death of child processes */
				(void) signal(SIGCHLD, &DEATH_OF_CHILD_handler);
				/* Catch and handle an alarm every 1 second */
				(void) signal(SIGALRM, &SIGALRM_handler);
				(void) alarm(1);
//				(void) signal(SIGHUP,  &generic_SIG_handler); // = 1
//				(void) signal(SIGINT,  &generic_SIG_handler); // = 2 = ^C
//				(void) signal(SIGQUIT, &generic_SIG_handler); // = 3
//				(void) signal(SIGABRT, &generic_SIG_handler); // = 6
//				(void) signal(SIGKILL, &generic_SIG_handler); // = 9
//				(void) signal(SIGTERM, &generic_SIG_handler); // = 15 = kill default
//				(void) signal(SIGSTOP, &generic_SIG_handler); // = 19
				/* Farm off and do the real work */
				if (context->master) {
					/*sts =*/ netstress_master(context);
				} else {
					/*sts =*/ netstress_slave(context);
				}
			}
			/* Remove shared memory */
			shmctl(ShmID, IPC_RMID, &Myshmid_ds);
			seconds_counter = NULL;
		}

	exit(0);
	return(0);
} /* main() */

/*****************************************************************************
* Func: parse_options
* Desc: Parse the command line options
*****************************************************************************/
static int parse_options(
		int argc,
		char *argv[],
		char *envv[],
		context_t *context)
{
	int retval = 0;		/* Success */
	int c = -1;
	int optind = 0;
	int master_set = 0;
	int slave_set = 0;

	/***** For short_options *****/
	/* () --> no arg */
	/* (:) --> arg is required */
	/* (::) --> arg is optional */
	static char short_options[] = "m::s::h:p:S:cn:N:bMKBwrRfvd?";
	/***** For long_options *****/
	/* ARG1 = NAME */
	/* ARG2 = Has_Arg --> 0=no, 1=required, 2=optional */
	/* FLAG  = How results are returned for long option (see man page) */
	/* VAL  = Value to return if long option specified if FLAG == 0 */
	static struct option long_options[] = {
		{"master",		2, 0, 'm'},
		{"slave",		2, 0, 's'},
		{"host",		1, 0, 'h'},
		{"port",		1, 0, 'p'},
		{"seed",		1, 0, 'S'},
		{"ctime",		0, 0, 'c'},
		{"nsec",		1, 0, 'n'},
		{"nrep",		1, 0, 'N'},
		{"bytes",		0, 0, 'b'},
		{"mps",			0, 0, 'M'},
		{"kps",			0, 0, 'K'},
		{"bps",			0, 0, 'B'},
		{"mwo",			0, 0, 'w'},
		{"mro",			0, 0, 'r'},
		{"realrandom",	0, 0, 'R'},
		{"fast",		0, 0, 'f'},
		{"verbosity",	0, 0, 'v'},
		{"debug",		0, 0, 'd'},
		{"help",		0, 0, '?'},
		{0, 			0, 0, 0}
	};

	for ( ; ; ) {
		c = getopt_long(argc, argv, short_options, long_options, &optind);
		/* The following needs checked outside of switch so break work right */
		if (c < 0) {
			break;		/* Done with options, get out of loop */
		}
		/* See what options we were passed */
		switch (c) {
		case 'm':	/* Master */
			master_set = 1;
			context->master = 1;		/* 1 = master, 0 = slave */
			// Note: context was already initialized to zeroes
			if ((optarg != NULL) && (*optarg != '\0')) {
				strncpy(context->connectionName, optarg,
						sizeof(context->connectionName)-1);
			}
			break;
		case 's':	/* Slave */
			slave_set = 1;
			context->master = 0;		/* 1 = master, 0 = slave */
			// Note: context was already initialized to zeroes
			if ((optarg != NULL) && (*optarg != '\0')) {
				// NOTE: In slave mode, we always use the first child...
				strncpy(context->children[0].connectionName, optarg,
						sizeof(context->children[0].connectionName)-1);
			}
			break;
		case 'h':	/* Host or IP Address */
			context->host = optarg;
			break;
		case 'p':	/* Port */
			context->port = atoi(optarg);
			break;
		case 'S':	/* Seed */
			context->seed = atol(optarg);
			break;
		case 'c':	/* Report using current time */
			context->report_using_current_time = 1;
			break;
		case 'n':	/* Report every nnn seconds */
			context->report_every_n_seconds = atol(optarg);
			break;
		case 'N':	/* Benchmark every nnn reports */
			context->benchmark_every_n_reports = atol(optarg);
			break;
		case 'b':	/* bytes/sec (default is bits/sec) */
			context->benchmark_in_bytes_per_second = 1;	/* 0=bits, 1=bytes */
			break;
		case 'M':	/* mbytes/sec or mbits/sec */
			context->measure = 'm';
			break;
		case 'K':	/* kbytes/sec or kbits/sec */
			context->measure = 'k';
			break;
		case 'B':	/* bytes/sec or bits/sec */
			context->measure = 'b';
			break;
		case 'w':	/* Only test master write / slave read */
			context->master_write_slave_read_test = 1;
			context->master_read_slave_write_test = 0;
			break;
		case 'r':	/* Only test master read / slave write */
			context->master_write_slave_read_test = 0;
			context->master_read_slave_write_test = 1;
			break;
		case 'R':	/* Real-Random */
			context->real_random = 1;
			break;
		case 'f':	/* Fast (don't check data) */
			context->fast = 1;
			break;
		case 'v':	/* Verbosity */
			verbosity++;		/* 0 = no_verbosity, 1...n = verbosity_level */
			break;
		case 'd':	/* Debug */
			debug++;		/* 0 = no_debug, 1...n = debug_level */
			break;
		case '?':	/* Help */
		default:
			give_usage(context); /* NOTE: give_usage() doesn't return */
			break;
		}
	}
	if (((master_set == 0) && (slave_set == 0))
	||  ((master_set == 1) && (slave_set == 1))) {
		printf("\n");
		printf("ERROR: Either \"Master\" or \"Slave\" must be set\n");
		give_usage(context); /* NOTE: give_usage() doesn't return */
	}
	if (context->master == 0) {
		if (context->host == NULL) {
			printf("\n");
			printf("ERROR: \"Slave\" mode requires a HOST to be specified\n");
			give_usage(context); /* NOTE: give_usage() doesn't return */
		}
	}
	return(retval);
} /* parse_options() */

/*****************************************************************************
* Func: give_usage
* Desc: Give the command line usage (help)
* NOTE: This function does NOT return.
*****************************************************************************/
static void give_usage(
		context_t *context)
{
	printf("\n");
	printf("Usage: %s -m[NAME] | --master=[NAME] [OPTIONS]\n",
			context->progname);
	printf("    --- or ---\n");
	printf("Usage: %s -s[NAME] | --slave=[NAME] -hHOST|--host=HOST [OPTIONS]\n",
			context->progname);
	printf("    --- or ---\n");
	printf("Usage: %s --help|-?\n",
			context->progname);

	printf("\n");
	printf("%s is designed to stress/benchmark a network device or\n",
			context->progname);
	printf("connection. Two or more sessions are required to use this utility.\n");
	printf("One must specify \"--master\" while the others specify \"--slave\".\n");
	printf("\n");
	printf("Benchmark timings are given independently for read and write\n");
	printf("transactions. Benchmark times are reported in xxx/yyy format\n");
	printf("where xxx is the total transfer speed and yyy is the transfer\n");
	printf("speed for transfers since it was last reported.\n");

	printf("\n");
	printf("  OPTIONS:\n");
	printf("  -m[NAME]|--master[=NAME]   = This instance is the master\n");
	printf("  -s[NAME]|--slave[=NAME]    = This instance is the slave\n");
	printf("  -h|--host nnn.nnn.nnn.nnn  = Use this host or IP address\n");
	printf("  -p|--port nnn    = Use this port (default=%d)\n", DEFAULT_PORT);
	printf("  --seed|-S nnn    = Use this random number seed (master only)\n");
	printf("  -c|--ctime       = Report current time (default reports elapsed time)\n");
	printf("  -n|--nsec nnn    = Report every nnn seconds (default=%d)\n",
			DEFAULT_REPORT_EVERY_N_SECONDS);
	printf("  -N|--nrep nnn    = Benchmark every nnn reports (default=%d)\n",
			DEFAULT_BENCHMARK_EVERY_N_REPORTS);
	printf("  -b|--bytes       = Benchmark using bytes (default is bits)\n");
	printf("  -M|--mps         = Benchmark using mbytes/sec or mbits/sec (default)\n");
	printf("  -K|--kps         = Benchmark using kbytes/sec or kbits/sec\n");
	printf("  -B|--bps         = Benchmark using  bytes/sec or  bits/sec\n");
	printf("  -w|--mwo         = Only test master write / slave read\n");
	printf("  -r|--mro         = Only test master read / slave write\n");
	printf("  -R|--realrandom  = Do real random test (not psuedo random)\n");
	printf("  -f|--fast        = Run Fast (no checking, no real_random)\n");
	printf("  -v|--verbosity   = Enable verbosity (multiples increase level)\n");
	printf("  -d|--debug       = Enable debugging (multiples increase level)\n");
	printf("  -?|--help        = Give this information\n");
	printf("\n");
	exit(-1);
} /* give_usage() */

/*****************************************************************************
* Func: which_child
* Desc: The funtion used to determine which children are asssociated with a pid
*****************************************************************************/
static children_t *which_child(context_t *context, pid_t pid)
{
	children_t *child = NULL;
	int i;
	if (pid > 0) {
		for (i = 0; i < MAX_SLAVES; i++) {
			if ((context->children[i].tx_pid == pid)
			||  (context->children[i].rx_pid == pid)) {
				child = &(context->children[i]);
				break;
			}
		}
	}
	return(child);
} /* which_child() */

/*****************************************************************************
* Func: SIGstring
* Desc: Return a string of the signal specified.
*****************************************************************************/
static char *SIGstring(int sig)
{
	char *str;
	static char *SIGstrings[] =
	{
	"1 SIGHUP",       "2 SIGINT",       "3 SIGQUIT",      "4 SIGILL",
	"5 SIGTRAP",      "6 SIGABRT",      "7 SIGBUS",       "8 SIGFPE",
	"9 SIGKILL",      "10 SIGUSR1",     "11 SIGSEGV",     "12 SIGUSR2",
	"13 SIGPIPE",     "14 SIGALRM",     "15 SIGTERM",     "16 SIGSTKFLT",
	"17 SIGCHLD",     "18 SIGCONT",     "19 SIGSTOP",     "20 SIGTSTP",
	"21 SIGTTIN",     "22 SIGTTOU",     "23 SIGURG",      "24 SIGXCPU",
	"25 SIGXFSZ",     "26 SIGVTALRM",   "27 SIGPROF",     "28 SIGWINCH",
	"29 SIGIO",       "30 SIGPWR",      "31 SIGSYS",      "34 SIGRTMIN",
	"35 SIGRTMIN+1",  "36 SIGRTMIN+2",  "37 SIGRTMIN+3",  "38 SIGRTMIN+4",
	"39 SIGRTMIN+5",  "40 SIGRTMIN+6",  "41 SIGRTMIN+7",  "42 SIGRTMIN+8",
	"43 SIGRTMIN+9",  "44 SIGRTMIN+10", "45 SIGRTMIN+11", "46 SIGRTMIN+12",
	"47 SIGRTMIN+13", "48 SIGRTMIN+14", "49 SIGRTMIN+15", "50 SIGRTMAX-14",
	"51 SIGRTMAX-13", "52 SIGRTMAX-12", "53 SIGRTMAX-11", "54 SIGRTMAX-10",
	"55 SIGRTMAX-9",  "56 SIGRTMAX-8",  "57 SIGRTMAX-7",  "58 SIGRTMAX-6",
	"59 SIGRTMAX-5",  "60 SIGRTMAX-4",  "61 SIGRTMAX-3",  "62 SIGRTMAX-2",
	"63 SIGRTMAX-1",  "64 SIGRTMAX"
	};

	if ((sig >= 1) && (sig <= 64)) {
		str = SIGstrings[sig-1];
	} else {
		str = "Unknown";
	}
	return(str);
} /* SIGstring() */

#define SIGPARENT SIGUSR1
/*****************************************************************************
* Func: watch_for_DEATH_OF_PARENT
* Desc: Prepare to catch a signal indicating the death of our parent process
*		Cleanup and notify the user if one of our parent dies
*****************************************************************************/
void watch_for_DEATH_OF_PARENT(void)
{
	uint32_t junk = 0;
	uint32_t new_sig = SIGPARENT;
	int sts;
	sts = prctl(PR_SET_PDEATHSIG, new_sig, junk, junk, junk);
	Dprintf(1, "watch_for_DEATH_OF_PARENT: sts=%d, new_sig=%s\n",
			sts, SIGstring((int)new_sig));
	return;
} /* watch_for_DEATH_OF_PARENT() */

/*****************************************************************************
* Func: DEATH_OF_PARENT_handler
* Desc: The funtion used to catch death of a child signals
*		Cleanup and notify the user if one of our children dies
* Rule: If our parent dies, we are to die.
*       and we attempt to kill our sibling (this last piece is extra I think)
*****************************************************************************/
void DEATH_OF_PARENT_handler(
		int sig)
{
	children_t *child;
	pid_t pid;
	if (sig == SIGPARENT) {
		/* Let the user know what happened */
		printf("SIGNAL: %s: Our parent process died!\n", SIGstring(sig));
		/* Determine what child we are so we can comment and cleanup */
		pid = getpid();
		child = which_child(global_context, pid);
		if (child) {
			if (child->tx_pid == pid) {
				printf("WARNING: ID=%s: TX process terminating\n", child->identity);
				child->tx_pid = 0;
			} else /* (child->rx_pid == pid) */ {
				printf("WARNING: ID=%s: RX process terminating\n", child->identity);
				child->rx_pid = 0;
			}
		}
		exit(0);
	}
	return;
} /* DEATH_OF_PARENT_handler() */

/*****************************************************************************
* Func: DEATH_OF_CHILD_handler
* Desc: The funtion used to catch death of a child signals
*		Cleanup and notify the user if one of our children dies
* Rule: If we are the master: Kill the associated RX or TX child.
* Rule: If we are the slave: Kill all our children and die.
*****************************************************************************/
void DEATH_OF_CHILD_handler(
		int sig)
{
	pid_t pid;
	children_t *child = NULL;
	int i;
	if (sig == SIGCHLD) {
		int status;
		int terminating_signal = -1;
		int ret_value = -1;
		char *process_type = "A";
		char *description = "";
		/* Re-Enable the signal */
		(void) signal(sig, DEATH_OF_CHILD_handler);
		/* Wait for the child to be cleaned up */
		pid = waitpid (-1, &status, WNOHANG);
		/* Who died ? */
		child = which_child(global_context, pid);
		if (child) {
			if (child->rx_pid == pid) {
				process_type = "RX";
			}
			if (child->tx_pid == pid) {
				process_type = "TX";
			}
		}
		/* Let the user know what happened */
		if (WIFSIGNALED(status)) {
			terminating_signal = WTERMSIG(status);
			description = SIGstring(terminating_signal);
		}
		if (WIFEXITED(status)) {
			ret_value = WEXITSTATUS(status);
			description = strerror(status);
		}
		if (child) {
			printf("WARNING: ID=%s: %s child process died: pid=%d: sts=%d: sig=%d: ret=%d: %s!\n",
					child->identity, process_type, pid, status,
					terminating_signal, ret_value, description);
		} else {
			printf("WARNING: %s child process died: pid=%d: sts=%d: sig=%d: ret=%d: %s!\n",
					process_type, pid, status,
					terminating_signal, ret_value, description);
		}
		/* If a master: Kill our childs RX/TX sibling */
		/* If a slave: Kill all our other children */
		/* There should be only 1 set of children so the behavior is the same */
		if (child) {
			if (child->tx_pid == pid) {
				// Kill RX sibling
				if (child->rx_pid > 0) {
					printf("WARNING: ID=%s: Terminating RX process\n",
							child->identity);
					kill(child->rx_pid, SIGTERM);
					child->rx_pid = 0;
				}
			} else /* (child->rx_pid == pid) */ {
				// Kill TX sibling
				if (child->tx_pid > 0) {
					printf("WARNING: ID=%s: Terminating TX process\n",
							child->identity);
					kill(child->tx_pid, SIGTERM);
					child->tx_pid = 0;
				}
			}
		}
		// Look for children where (only) one child has died.
		for (i = 0; i < MAX_SLAVES; i++) {
			for (i = 0; i < MAX_SLAVES; i++) {
				int alive_count = 0;
				if (global_context->children[i].rx_pid > 0) alive_count++;
				if (global_context->children[i].tx_pid > 0) alive_count++;
				if (alive_count == 1) {
					// Kill the remaining child.
					if (global_context->children[i].rx_pid > 0) {
						printf("WARNING: ID=%s: Terminating RX process\n",
								global_context->children[i].identity);
						kill(global_context->children[i].rx_pid, SIGTERM);
						global_context->children[i].rx_pid = 0;
					}
					if (global_context->children[i].tx_pid > 0) {
						printf("WARNING: ID=%s: Terminating TX process\n",
								global_context->children[i].identity);
						kill(global_context->children[i].tx_pid, SIGTERM);
						global_context->children[i].tx_pid = 0;
					}
				}
			}
		}
		// If slave: Check if all children have died.  If so, die.
		if (global_context->master == 0) {
			for (i = 0; i < MAX_SLAVES; i++) {
				if ((global_context->children[i].rx_pid > 0)
				||  (global_context->children[i].tx_pid > 0)) {
					break;
				}
			}
			if (i >= MAX_SLAVES) {
				printf("WARNING: Slave parent process terminating\n");
				exit(0);
			}
		}
	}
	return;
} /* DEATH_OF_CHILD_handler() */

/*****************************************************************************
* Func: SIGALRM_handler
* Desc: The funtion used to catch SIGALRM signals
*		Receive and handle an alarm every 1 second
*****************************************************************************/
static void SIGALRM_handler(int sig)
{
	if (sig == SIGALRM) {
		/* Re-Enable the signal and submit another alarm */
		(void) signal(sig, SIGALRM_handler);
		(void) alarm(1);
		(*seconds_counter)++;
		//Dprintf(2, "SIGNAL: %s: %lu\n", SIGstring(sig), *seconds_counter);
		Vprintf(1, "%lu seconds\n", *seconds_counter);
	}
	return;
} /* SIGALRM_handler() */

/*****************************************************************************
* Func: generic_SIG_handler
* Desc: The funtion used to catch generic signals
*****************************************************************************/
//static void generic_SIG_handler(int sig)
//{
//	//DON'T//(void) signal(sig, generic_SIG_handler);
//	printf("SIGNAL: %s\n", SIGstring(sig));
//	if ((sig == SIGINT)
//	||  (sig == SIGTERM))
//	{
//		// ^C
//		exit(0);
//	}
//	return;
//} /* generic_SIG_handler() */

/*****************************************************************************
* Func: fillRandomBuffer
* Desc: Fill the TX or RX random buffer twice the size of MAX_PACKET_SIZE with
*       random numbers we can use instead of getting random numbers each time.
*       Basically we can randomly point into the first half of this data
*       as a starting point, and then use a random size of data no greater
*       than MAX_PACKET_SIZE.
*****************************************************************************/
void fillRandomBuffer(
		context_t *context,
		char bufferID,  /* "T"x or "R"x */
		uint32_t seed)
{
	static int tx_done_once = 0;
	static int rx_done_once = 0;
	int done_once = 0;
	int i;
	int llength;
	uint32_t *lbuffer = NULL;
	if ((bufferID == 'T') || (bufferID == 'T')) {
		/* Use TX BUffer */
		lbuffer = (uint32_t *)context->randomTxBuffer;
		llength = sizeof(context->randomTxBuffer) / sizeof(uint32_t);
		done_once = tx_done_once;
		tx_done_once = 1;
	} else {
		/* Use RX BUffer */
		lbuffer = (uint32_t *)context->randomRxBuffer;
		llength = sizeof(context->randomRxBuffer) / sizeof(uint32_t);
		done_once = rx_done_once;
		rx_done_once = 1;
	}
	if ( ! done_once) {
		Dprintf(1, "RANDOM: %c: %lu\n", bufferID, seed);
		srandom((unsigned int)seed);
		for (i = 0; i < llength; i++) {
			lbuffer[i] = random();
		}
	}
	return;
} /* fillRandomBuffer() */

/*****************************************************************************
* Func: handshakeConnectionInformation
* Desc: exchange the connectionID and connectionName 
* Desc: Send the Master's random seed against the Slave's
*****************************************************************************/
static int handshakeConnectionInformation(
		context_t *context,
		int socket_fd,
		children_t *child)
{
	int retval = 0;		/* Success */
	uint32_t checksum = 0;
	int i;
	if (context->master) {
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// NOTE: The sum here includes the connectionID and the sum of the name
		checksum = child->connectionID;
		// write(child->connectionID)
		(void) write(socket_fd, &child->connectionID,
				sizeof(child->connectionID));
		// write(context->connectionName)
		(void) write(socket_fd, context->connectionName,
				sizeof(context->connectionName));
		// write(sum(context->connectionName))
		for (i = 0; i < sizeof(context->connectionName); i++) {
			checksum+=context->connectionName[i];
		}
		(void) write(socket_fd, &checksum, sizeof(checksum));
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// read(child->connectionName)
		(void) read(socket_fd, child->connectionName,
				sizeof(child->connectionName));
		// read(sum(child->connectionName))
		(void) read(socket_fd, &checksum, sizeof(checksum));
		//Test sum just read
		// NOTE: The sum here is the sum of the name only
		for (i = 0; i < sizeof(child->connectionName); i++) {
			checksum-=child->connectionName[i];
		}
		if (checksum != 0) {
			printf("ERROR: connectionName sum mismatch\n");
			printf("Connection aborted...\n");
			retval = -1;
		}
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	} else /* SLAVE */ {
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// read(child->connectionID)
		(void) read(socket_fd, &child->connectionID,
				sizeof(child->connectionID));
		// read(context->connectionName)
		(void) read(socket_fd, context->connectionName,
				sizeof(context->connectionName));
		// read(sum(context->connectionName))
		(void) read(socket_fd, &checksum, sizeof(checksum));
		//Test sum just read
		// NOTE: The sum here includes the connectionID and the sum of the name
		for (i = 0; i < sizeof(context->connectionName); i++) {
			checksum-=context->connectionName[i];
		}
		if (checksum != child->connectionID) {
			printf("ERROR: connectionName sum mismatch\n");
			printf("Connection aborted...\n");
			retval = -1;
		}
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		// write(child->connectionName)
		(void) write(socket_fd, child->connectionName, sizeof(child->connectionName));
		// write(sum(child->connectionName))
		// NOTE: The sum here is the sum of the name only
		checksum = 0;
		for (i = 0; i < sizeof(child->connectionName); i++) {
			checksum+=child->connectionName[i];
		}
		(void) write(socket_fd, &checksum, sizeof(checksum));
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	}
	setChildIdentity(child);
	return(retval);
} /* handshakeConnectionInformation() */

/*****************************************************************************
* Func: netstress_master
* Desc: The master portion of the stressnet utility.
*****************************************************************************/
static int netstress_master(
		context_t *context)
{
	int retval = 0;		/* Success */
	int i;
	children_t *child = NULL;

	Dprintf(1, "%s(ENTER): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	/* Open/Esstablish the socket connection(s) to the specified host */

	context->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (context->socket_fd < 0) {
		retval = -1;		/* Failure */
		printf("ERROR: Unable to create socket %s:%d\n",
				context->host, context->port);
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
		return(retval);
	}

	/* Initialize our socket information */
	memset(&context->this_sockaddr, 0, sizeof(struct sockaddr_in));
	context->this_sockaddr.sin_family = AF_INET;
	context->this_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	context->this_sockaddr.sin_port = htons(context->port);

	retval = bind(context->socket_fd,
			(struct sockaddr *)&context->this_sockaddr,
			sizeof(struct sockaddr_in));
	if (retval < 0) {
		printf("ERROR: Unable to bind to socket %s:%d\n",
				context->host, context->port);
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
		return(retval);
	}

	retval = listen(context->socket_fd, 5);
	if (retval < 0) {
		printf("ERROR: Unable to listen to socket %s:%d\n",
				context->host, context->port);
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
		return(retval);
	}

	for ( ; ; ) {
		// Find an unused set of pid's for the new child and point to them.
		// For the master side of this, we rotate through all entries.
		int newsocket_fd;	/* Socket file descriptor returned from accept() */
		child = NULL;
		for (i = 0; i < MAX_SLAVES; i++) {
			if (context->children[i].tx_pid <= 0) {
				child = &(context->children[i]);
				break;
			}
		}
		if (child == NULL) {
			sleep(1);
			continue;
		}

		newsocket_fd = accept(context->socket_fd, NULL, NULL);
		if (newsocket_fd >= 0) {
			/* We have someone trying to talk to us */
		} else {
			printf("ERROR: Unable to accept a connecting socket %s:%d\n",
					context->host, context->port);
			printf("errno = %d = \"%s\"\n", errno, strerror(errno));
			continue;
		}
		/*
		* ((( Master )))
		* First handshake the connectionID and connectionName
		* so we can reference any output by its associated connection
		*/
		retval = handshakeConnectionInformation(context,
				newsocket_fd, child);
		if (retval < 0) {
			/* An error occurred ... Abort the connection */
			/* A message has already been given */
			close(newsocket_fd);
			continue;
		}
		/* Now fork a tx process and an rx process */
		child->tx_pid = fork();
		if (child->tx_pid == 0) {
			/* Child Process */
			transmit_loop(context, newsocket_fd, child->identity);
			child->tx_pid = 0;	/* Leave empty when done */
			exit(0);
		} else {
			/* Parent Process */
			if (child->tx_pid < 0) {
				printf("ERROR: Unable to fork a TX process\n");
				printf("errno = %d = \"%s\"\n", errno, strerror(errno));
				child->tx_pid = 0;	/* Leave empty when done */
			}
		}
		child->rx_pid = fork();
		if (child->rx_pid == 0) {
			/* Child Process */
			receive_loop(context, newsocket_fd, child->identity);
			child->rx_pid = 0;	/* Leave empty when done */
			exit(0);
		} else {
			/* Parent Process */
			if (child->rx_pid < 0) {
				printf("ERROR: Unable to fork an RX process\n");
				printf("errno = %d = \"%s\"\n", errno, strerror(errno));
				printf("WARNING: ID=%s: Terminating TX process\n",
						child->identity);
				kill(child->tx_pid, SIGTERM); // = 15 = kill default
				child->tx_pid = 0;	/* Leave empty when done */
				child->rx_pid = 0;	/* Leave empty when done */
			}
		}
	}
	Dprintf(1, "%s(LEAVE): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	return(retval);
} /* netstress_master() */

/*****************************************************************************
* Func: netstress_slave
* Desc: The slave portion of the stressnet utility.
*****************************************************************************/
static int netstress_slave(
		context_t *context)
{
	int retval = 0;		/* Success */
	children_t *child = NULL;

	Dprintf(1, "%s(ENTER): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	/* Open/Esstablish the socket connection(s) to the specified host */

	if (context->host == NULL) {
		printf("ERROR: No hostname or IP was specified\n");
		return(retval);
	}

	context->hostent = gethostbyname(context->host);
	if (context->hostent == NULL) {
		printf("ERROR: Invalid hostname or IP address specified: %s\n",
				context->host);
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
		return(retval);
	}

	context->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (context->socket_fd < 0) {
		printf("ERROR: Unable to create socket\n");
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
		return(retval);
	}

	/* Initialize their socket address information */
	memset(&context->that_sockaddr, 0, sizeof(struct sockaddr_in));
	context->that_sockaddr.sin_family = context->hostent->h_addrtype;
	memcpy((char *)&context->that_sockaddr.sin_addr.s_addr,
			context->hostent->h_addr_list[0],
			context->hostent->h_length);
	context->that_sockaddr.sin_port = htons(context->port);

	/* Initialize our socket address information */
	memset(&context->this_sockaddr, 0, sizeof(struct sockaddr_in));
	context->this_sockaddr.sin_family = AF_INET;
	context->this_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	context->this_sockaddr.sin_port = htons(0);

	retval = bind(context->socket_fd,
			(struct sockaddr *)&context->this_sockaddr,
			sizeof(struct sockaddr_in));
	if (retval < 0) {
		printf("ERROR: Unable to bind to socket%s:%d\n",
				context->host, context->port);
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
		return(retval);
	}

	retval = connect(context->socket_fd,
			(struct sockaddr *)&context->that_sockaddr,
			sizeof(struct sockaddr_in));
	if (retval < 0) {
		printf("ERROR: Unable to connect to socket %s:%d\n",
				context->host, context->port);
		printf("errno = %d = \"%s\"\n", errno, strerror(errno));
		return(retval);
	}

	// NOTE: In slave mode, we always use the first child...
	child = &(context->children[0]);
	/*
	* ((( Slave )))
	* First handshake the connectionID and connectionName
	* so we can reference any output by its associated connection
	*/
	retval = handshakeConnectionInformation(context,
			context->socket_fd, child);
	if (retval < 0) {
		/* An error occurred ... Abort the connection */
		/* A message has already been given */
		return(retval);
	}
	if (child != NULL) {
		/* Now fork a tx process and an rx process */
		child->tx_pid = fork();
		if (child->tx_pid == 0) {
			/* Child Process */
			transmit_loop(context, context->socket_fd, child->identity);
			child->tx_pid = 0;	/* Leave empty when done */
			printf("WARNING: Terminating Slave Parent process\n");
			kill(context->pid, SIGTERM);
			context->pid = 0;
			exit(0);
		} else {
			/* Parent Process */
			if (child->tx_pid < 0) {
				printf("ERROR: Unable to fork a TX process\n");
				printf("errno = %d = \"%s\"\n", errno, strerror(errno));
				child->tx_pid = 0;	/* Leave empty when done */
			}
		}
		child->rx_pid = fork();
		if (child->rx_pid == 0) {
			/* Child Process */
			receive_loop(context, context->socket_fd, child->identity);
			child->rx_pid = 0;	/* Leave empty when done */
			exit(0);
		} else {
			/* Parent Process */
			if (child->rx_pid < 0) {
				printf("ERROR: Unable to fork an RX process\n");
				printf("errno = %d = \"%s\"\n", errno, strerror(errno));
				printf("WARNING: ID=%s: Terminating TX process\n",
						child->identity);
				kill(child->tx_pid, SIGTERM); // = 15 = kill default
				child->tx_pid = 0;	/* Leave empty when done */
				child->rx_pid = 0;	/* Leave empty when done */
				printf("WARNING: Terminating Slave Parent process\n");
				kill(context->pid, SIGTERM);
				context->pid = 0;
			}
		}
	}
	for ( ; ; ) {
		sleep(1);
	}
	Dprintf(1, "%s(LEAVE): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	return(retval);
} /* netstress_slave() */

/*****************************************************************************
* Func: time_stamp
* Desc: Returns an ascii string representing either
*       the elapsed time or the current time
* elapsed time example output:  "0d 19h 32m 04s"
* current time example output:  "Mon Dec 14 19:32:04 2009"
*****************************************************************************/
static char *time_stamp(context_t *context)
{
	if (context->report_using_current_time == 0) {
		return(elapsed_time_stamp(context));
	} else {
		return(current_time_stamp(context));
	}
} /* time_stamp() */

/*****************************************************************************
* Func: elapsed_time_stamp
* Desc: Returns an ascii string representing the elapsed time
* example output:  "0d 09h 32m 04s"
*****************************************************************************/
static char *elapsed_time_stamp(context_t *context)
{
	static char time_buffer[64];
	time_t current_time;
	time_t elapsed_time;
	long days, hours, minutes, seconds;
	current_time = time(NULL);
	if (context->connect_time == 0L) {
		elapsed_time = 0; /* Not set yet - Assume times are the same */
	} else {
		elapsed_time = current_time - context->connect_time;
	}
	days = elapsed_time / DAYS_IN_SECONDS;
	if ((days * DAYS_IN_SECONDS) > elapsed_time) days--;
	elapsed_time -= (days * DAYS_IN_SECONDS);
	hours = elapsed_time / HOURS_IN_SECONDS;
	if ((hours * HOURS_IN_SECONDS) > elapsed_time) hours--;
	elapsed_time -= (hours * HOURS_IN_SECONDS);
	minutes = elapsed_time / MINUTES_IN_SECONDS;
	if ((minutes * MINUTES_IN_SECONDS) > elapsed_time) minutes--;
	elapsed_time -= (minutes * MINUTES_IN_SECONDS);
	seconds = elapsed_time;
	sprintf(time_buffer, "%ldd %02ldh %02ldm %02lds",
			days, hours, minutes, seconds);
	return(time_buffer);
} /* elapsed_time_stamp() */

/*****************************************************************************
* Func: current_time_stamp
* Desc: Returns an ascii string representing the current time
* example output:  "Mon Dec 14 19:32:04 2009"
*****************************************************************************/
static char *current_time_stamp(context_t *context)
{
	static char time_buffer[64];
	time_t current_time;
	char *cp;
	current_time = time(NULL);
	strncpy(time_buffer, ctime(&current_time), sizeof(time_buffer));
	cp = strchr(time_buffer, '\n');
	if (cp) {
		*cp = '\0';
	}
	return(time_buffer);
} /* current_time_stamp() */

/*****************************************************************************
* Func: transfer_speed
* Desc: return the transfer speed information in string format
*****************************************************************************/
static char *transfer_speed(
		context_t *context,
		time_t total_time, uint64_t total_byte_count,
		time_t delta_time, uint32_t delta_byte_count)
{
	static char speed_buffer[64];
	speed_buffer[0] = '\0';
	if (delta_time != 0L) {
		snprintf(speed_buffer, sizeof(speed_buffer), "%0.1f/%0.1f %s/sec",
				(((double)total_byte_count / (double)total_time) / context->f_measure),
				(((float)delta_byte_count / (float)delta_time) / context->f_measure),
				context->s_measure);
	}
	return(speed_buffer);
} /* transfer_speed() */

/*****************************************************************************
* Func: read_nBytes
* Desc: Read an incoming data packet of nBytes2Read size
* Returns: N < 0 == ERROR, N >= 0 == number of bytes read
*****************************************************************************/
static long read_nBytes(
		context_t *context,
		int socket_fd,
		unsigned char *buffer,
		long nBytes2Read)
{
	int total_nBytesRead = 0;
	int nBytesRead = 0;
	unsigned char *cp = buffer;
	for ( ; ; ) {
		nBytesRead = read(socket_fd, cp, (int)nBytes2Read);
		if (nBytesRead < 0) {
			if ((errno == EINTR) || (errno == EAGAIN)) {
				printf("WARNING: read interrupted, errno = %d = \"%s\"\n",
						errno, strerror(errno));
				continue;
			}
			printf("ERROR: read failure, errno = %d = \"%s\"\n",
					errno, strerror(errno));
			total_nBytesRead = nBytesRead; /* NOTE: nBytesRead < 0 == ERROR */
			break;
		} else {
			total_nBytesRead += nBytesRead;
			cp += nBytesRead;
			nBytes2Read -= nBytesRead;
			if (nBytes2Read <= 0) {
				/* NOTE: nBytesRead >= 0 == number of bytes read */
				break;
			}
		}
	} /* End of loop */
	return((long)total_nBytesRead);
} /* read_nBytes() */

/*****************************************************************************
* Func: receive_loop
* Desc: The receive (RX) loop
*****************************************************************************/
static void receive_loop(
		context_t *context,
		int socket_fd,
		char *identity)
{
	uint32_t loopcount = 0;
	unsigned char buffer[MAX_PACKET_SIZE+sizeof(long)];
	uint32_t *lbuffer = (uint32_t *)buffer;
	uint32_t *lrbuffer = (uint32_t *)context->randomRxBuffer;

	uint32_t delta_byte_count = 0L;
	uint64_t total_byte_count = 0L;
	uint32_t our_last_seconds_counter = *seconds_counter;
	uint32_t our_seconds_counter = 0L;
	int our_report_count = 0;
	time_t start_time = 0L;

	/*
	* Allow the developer to pause to attach a debugger
	*/
	if (debugging_receive_loop) {
		int wait_here = 30;
		while(wait_here > 0) {
			wait_here--;
			sleep(1);
		}
	}

	/*
	* Don't do unwanted test
	*/
	if (((context->master == 1)&&(context->master_read_slave_write_test == 0))
	||  ((context->master == 0)&&(context->master_write_slave_read_test == 0)))
	{
		for ( ; ; ) {
			sleep(1);
			continue;
		}
		exit(0);
		return;
	}

	Dprintf(1, "%s(ENTER): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	Dprintf(1, "%s(): %s: PPid:Pid=%d:%d\n",
			__FUNCTION__,
			context->master?"Master":"Slave",
			getppid(), getpid());

	/* Catch and handle death of parent process */
	(void) signal(SIGPARENT, &DEATH_OF_PARENT_handler);
	watch_for_DEATH_OF_PARENT();

	printf("ID=%s: %s: %u read transactions\n",
			identity, time_stamp(context), loopcount);
	context->connect_time = time(NULL);
	start_time = context->connect_time;

	/*
	* Loop until we error out, detect data corruption, or quit
	*/
	for ( ; ; ) {

		static int initialized_randomRxBuffer = 0;
		uint32_t packet_number;
		uint32_t seed;
		long nBytes2Read;
		long nBytesRead;
		long nLongsRead;

		/*
		* Read an incoming header & buffer
		* [ packet_number:4 ] [ seed:4 ] [ length:4 ]
		*/
		nBytes2Read = HEADER_SIZE;
		nBytesRead =  read_nBytes(context, socket_fd, buffer, nBytes2Read);
		if (nBytesRead < nBytes2Read) {
			/*
			* Read encountered an error
			* OR
			* Read did not get enough data for a valid header
			*/
			break;
		}

		/*
		* Extract the header information
		*/
		packet_number = lbuffer[0];
		seed = lbuffer[1];
		nBytes2Read = lbuffer[2];
		Vprintf(2, "Read(N:%lu,S:%lu,L:%lu)\n", packet_number, seed, nBytes2Read);
		//printf("-");

		/*
		* Fill a buffer twice the size of MAX_PACKET_SIZE with random
		* numbers we can use instead of getting random numbers each time.
		*/
		if ( ! initialized_randomRxBuffer) {
			initialized_randomRxBuffer++;
			fillRandomBuffer(context, 'R', seed);
		}

		/*
		* Read an incoming buffer
		* [ data:length ]
		*/
		nBytesRead =  read_nBytes(context, socket_fd, buffer, nBytes2Read);
		if (nBytesRead < nBytes2Read) {
			/*
			* Read encountered an error
			* OR
			* Read did not get specified amount of data
			*/
			break;
		}
		nLongsRead = nBytesRead / sizeof(uint32_t);

		/*
		* Verify the data read
		*/
		if ( ! context->fast) {
			/* Use seed if using real_random, else use seed converted to an offset */
			if (context->real_random) {
				int i;
				srandom((unsigned int)seed);
				for (i = 0; i < nLongsRead; i++) {
					uint32_t random_number = random();
					if (lbuffer[i] != random_number) {
						printf("!!! READ: Data error @ %d !!!!!!!!!!!!!!!!!!!\n", i);
						break;
					}
				}
			} else {
				int offset = (seed%(uint32_t)(LMAX_PACKET_SIZE)) & (~3);
				Vprintf(2, "READ: %lu, %u: %lu:%lu\n",
						seed, (size_t)nBytesRead,
						lbuffer[0], lrbuffer[offset]);
				if (memcmp(buffer, &(lrbuffer[offset]), nBytesRead) != 0) {
					printf("!!! READ: Data error !!!!!!!!!!!!!!!!!!!\n");
					break;
				}
			}
		}

		/*
		* Gather statistics
		*/
		loopcount++;
		delta_byte_count += (uint32_t)nBytesRead + (uint32_t)HEADER_SIZE;
		total_byte_count += (uint64_t)nBytesRead + (uint32_t)HEADER_SIZE;
		if (our_last_seconds_counter != *seconds_counter) {
			our_last_seconds_counter = *seconds_counter;
			our_seconds_counter++;
			//printf("Read second counter = %lu\n", our_seconds_counter);
			//printf("Read loop counter = %lu\n", loopcount);
		}

		/*
		* Report statistics
		*/
		if (our_seconds_counter >= context->report_every_n_seconds) {
			time_t current_time = 0L;
			time_t delta_time = 0L;
			time_t total_time = 0L;
			our_seconds_counter = 0L;
			our_report_count++;
			printf("ID=%s: %s: %u read transactions",
					identity, time_stamp(context), loopcount);
			if (our_report_count >= context->benchmark_every_n_reports) {
				our_report_count = 0;
				current_time = time(NULL);
				delta_time = current_time - start_time;
				total_time = current_time - context->connect_time;
				printf(" @ %s", transfer_speed(context,
							total_time, total_byte_count,
							delta_time, delta_byte_count));
				start_time = current_time;
				delta_byte_count = 0L;
			}
			printf("\n");
		}

	} /* End of outer loop */
	Dprintf(1, "%s(LEAVE): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	exit(0);
	return;
} /* receive_loop() */

/*****************************************************************************
* Func: write_nBytes
* Desc: Write an outgoing data packet of nBytes2Write size
* Returns: N < 0 == ERROR, N >= 0 == number of bytes written
*****************************************************************************/
static long write_nBytes(
		context_t *context,
		int socket_fd,
		unsigned char *buffer,
		long nBytes2Write)
{
	int total_nBytesWritten = 0;
	unsigned char *cp = buffer;
	for ( ; ; ) {
		long nBytesWritten = write(socket_fd, cp, (int)nBytes2Write);
		if (nBytesWritten < 0) {
			if ((errno == EINTR) || (errno == EAGAIN)) {
				printf("WARNING: write interrupted, errno = %d = \"%s\"\n",
						errno, strerror(errno));
				continue;
			}
			printf("ERROR: write failure, errno = %d = \"%s\"\n",
					errno, strerror(errno));
			total_nBytesWritten = nBytesWritten; /* NOTE: nBytesWritten < 0 == ERROR */
			break;
		} else {
			total_nBytesWritten += nBytesWritten;
			cp += nBytesWritten;
			nBytes2Write -= nBytesWritten;
			if (nBytes2Write <= 0) {
				/* NOTE: nBytesWritten >= 0 == number of bytes written */
				break;
			}
		}
	} /* End of loop */
	return((long)total_nBytesWritten);
} /* write_nBytes() */

/*****************************************************************************
* Func: transmit_loop
* Desc: The transmit (TX) loop
*****************************************************************************/
static void transmit_loop(
		context_t *context,
		int socket_fd,
		char *identity)
{
	uint32_t packet_number = 0;
	uint32_t loopcount = 0;

	unsigned char buffer[MAX_PACKET_SIZE+sizeof(long)+HEADER_SIZE];
	uint32_t *lbuffer = (uint32_t *)buffer;
	uint32_t *lrbuffer = (uint32_t *)context->randomTxBuffer;

	uint32_t next_seed=0L;

	uint32_t delta_byte_count = 0L;
	uint64_t total_byte_count = 0L;
	uint32_t our_last_seconds_counter = *seconds_counter;
	uint32_t our_seconds_counter = 0L;
	int our_report_count = 0;
	time_t start_time = 0L;

	/*
	* Allow the developer to pause to attach a debugger
	*/
	if (debugging_transmit_loop) {
		int wait_here = 30;
		while(wait_here > 0) {
			wait_here--;
			sleep(1);
		}
	}

	/*
	* Don't do unwanted test
	*/
	if (((context->master == 1)&&(context->master_write_slave_read_test == 0))
	||  ((context->master == 0)&&(context->master_read_slave_write_test == 0)))
	{
		for ( ; ; ) {
			sleep(1);
			continue;
		}
		exit(0);
		return;
	}

	Dprintf(1, "%s(ENTER): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	Dprintf(1, "%s(): %s: PPid:Pid=%d:%d\n",
			__FUNCTION__,
			context->master?"Master":"Slave",
			getppid(), getpid());

	/* Catch and handle death of parent process */
	(void) signal(SIGPARENT, &DEATH_OF_PARENT_handler);
	watch_for_DEATH_OF_PARENT();

	/*
	* Fill a buffer twice the size of MAX_PACKET_SIZE with random
	* numbers we can use instead of getting random numbers each time.
	*/
	fillRandomBuffer(context, 'T', context->seed);

	/* Use seed if using real_random */
	next_seed = context->seed;
	printf("ID=%s: %s: %u write transactions\n",
			identity, time_stamp(context), loopcount);
	context->connect_time = time(NULL);
	start_time = context->connect_time;

	/*
	* Loop until we error out, detect data corruption, or quit
	*/
	for ( ; ; ) {
		int nBytes2Write, total_nBytes2Write;
		int nBytesWritten;
		uint32_t seed;
		int offset;
		/*
		* Increment the packet number
		*/
		packet_number++;

		/*
		* Construct an outgoing header & buffer
		* [ packet_number:4 ] [ seed:4 ] [ length:4 ]
		* [ data:length ]
		*/

		/*
		* Fill in the header information
		*/
		seed = next_seed;
		offset = (seed%(uint32_t)(LMAX_PACKET_SIZE)) & (~3);
		next_seed = random();
		nBytes2Write = random()%(uint32_t)(MAX_PACKET_SIZE-HEADER_SIZE);
		/* Ensure at least the seed & next length get sent */
		lbuffer[0] = packet_number;			/* Packet Number */
		lbuffer[1] = seed;		/* Seed */
		lbuffer[2] = nBytes2Write;			/* Data Length */
		total_nBytes2Write = nBytes2Write + HEADER_SIZE;

		/*
		* Fill in the buffer
		*/
		if (context->real_random) {
			int i;
			long total_nLongs2Write = total_nBytes2Write / sizeof(uint32_t);
			srandom((unsigned int)seed);
			for (i = LHEADER_SIZE; i < total_nLongs2Write; i++) {
				uint32_t random_number = random();
				lbuffer[i] = random_number;
			}
		} else {
			memcpy(&(lbuffer[LHEADER_SIZE]), &(lrbuffer[offset]), (size_t)nBytes2Write);
			Vprintf(3, "WRITE: %lu, %u: %lu:%lu\n",
					seed, (size_t)nBytes2Write,
					lbuffer[LHEADER_SIZE], lrbuffer[offset]);
		}

		Vprintf(3, "Writing(N:%lu,S:%lu,L:%lu)\n", packet_number, seed, nBytes2Write);
		//printf("+");

		/*
		* Write an outgoing header & buffer
		* [packet_number:4] [ seed:4 ] [ next_length:4 ] [ data:length ]
		*/
		nBytesWritten = write_nBytes(context, socket_fd, buffer, total_nBytes2Write);
		if (nBytesWritten < total_nBytes2Write) {
			/*
			* Write encountered an error
			* OR
			* Write did not send all of the data
			*/
			break;	/* Outer loop */
		}

		/*
		* Gather statistics
		*/
		loopcount++;
		delta_byte_count += (uint32_t)nBytesWritten;
		total_byte_count += (uint64_t)nBytesWritten;
		if (our_last_seconds_counter != *seconds_counter) {
			our_last_seconds_counter = *seconds_counter;
			our_seconds_counter++;
			//printf("Write second counter = %lu\n", our_seconds_counter);
			//printf("Write loop counter = %lu\n", loopcount);
		}

		/*
		* Report statistics
		*/
		if (our_seconds_counter >= context->report_every_n_seconds) {
			time_t current_time = 0L;
			time_t delta_time = 0L;
			time_t total_time = 0L;
			our_seconds_counter = 0L;
			our_report_count++;
			printf("ID=%s: %s: %u write transactions",
					identity, time_stamp(context), loopcount);
			if (our_report_count >= context->benchmark_every_n_reports) {
				our_report_count = 0;
				current_time = time(NULL);
				delta_time = current_time - start_time;
				total_time = current_time - context->connect_time;
				printf(" @ %s", transfer_speed(context,
							total_time, total_byte_count,
							delta_time, delta_byte_count));
				start_time = current_time;
				delta_byte_count = 0L;
			}
			printf("\n");
		}

	} /* End of outer loop */
	Dprintf(1, "%s(LEAVE): %s\n",
			__FUNCTION__, context->master?"master":"Slave");
	exit(0);
	return;
} /* transmit_loop() */



//----------------------------------------------------------------------------//
// Emacs Editor Settings
//
// Local Variables:
// indent-tabs-mode:t
// End:
//----------------------------------------------------------------------------//
