/*
 *   Copyright (c) International Business Machines  Corp., 2001
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
/*---------------------------------------------------------------------+
|                             sched_driver                             |
| ==================================================================== |
|                                                                      |
| Description:  This program uses system calls to change the           |
|               priorities of the throughput measurement testcases.    |
|               When real-time is in effect, priorities 50 through 64  |
|               are used.  (MAX_PRI and MIN_PRI)  When user-time       |
|               (normal) is in effect, 0-14 (corresponding to nice()   |
|               calls) is used.  The driver only keeps track of        |
|               values from 50 to 64, and the testcases will scale     |
|               them down to 0 to 14 when needed, to change the        |
|               priority of a user-time process.                       |
|                                                                      |
| Algorithm:    o  Parse command line arguments                        |
|               o  Set current priority                                |
|               o  Calcucations (process slots, short/long term slots) |
|               o  Perform throughput tests with high priority         |
|               o  Start long-term testcases                           |
|               o  While time remains                                  |
|                  - Start short-term tests                            |
|                  - Perform throughput tests with new priority        |
|                  - Kill short-term tests                             |
|                  - Increase priority                                 |
|                                                                      |
| Usage:        sched_driver [-s n] [-p n] [-t n] [-d] [-v]            |
|                                                                      |
|               where:                                                 |
|                 -s n  stress percentage                              |
|                 -p n  process slots                                  |
|                 -t n  execution time in hours                        |
|                 -d    enable debugging messages                      |
|                 -v    Turn on verbose mode                           |
|                                                                      |
| Last update:   Ver. 1.15, 4/10/94 23:04:23                           |
|                                                                      |
| Change Activity                                                      |
|                                                                      |
|   Version  Date    Name  Reason                                      |
|    0.1     072889  GEB   Initial draft                               |
|    1.2     120793  JAT   Changes for AIX 4.1                         |
|    1.3     041094  DJK   Rewrote protions...                         |
|    1.4     010402  Manoj Iyer Ported to Linux                        |
|                                                                      |
+---------------------------------------------------------------------*/

#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <pwd.h>
#include <time.h>
#include <limits.h>
#include "sched.h"

/*
 * Defines:
 *
 * MAXPROCS: maximum number of processes
 *
 * PRIINC: priority step value
 *
 * MAX_PRI: highest priority to use
 *
 * MIN_PRI: lowest priority to use
 *
 * DEFAULT_STRESS_PERCENTAGE: stress percentage (process slot multiplier)
 *
 * DEFAULT_PROCESS_SLOTS: number of processes test driver will try and create
 *
 * DEFAULT_TIME: time (hours) for which this test driver will run
 *
 * USAGE: usage statement
 */
#define MAXPROCS   100
#define PRIINC     2
#define MAX_PRI    55		/* was 50 */
#define MIN_PRI    75		/* was 64 */
#define DEFAULT_STRESS_PERCENTAGE	0.5
#define DEFAULT_PROCESS_SLOTS		16
#define DEFAULT_TIME			1.00
#define USAGE "Usage:  %s  [-s n] [-p n] [-t n] [-d] [-v]              \n" \
              "        -s n   stress percentage [0.0<n<1.0] (default 0.5) \n" \
              "        -p n   process slots (default 16)                  \n" \
              "        -t n   execution time in hours (default 1.0 hrs)   \n" \
              "        -d     enable debugging messages                   \n" \
              "        -v     Turn on verbose mode 	                  \n"

/*
 * Global variables:
 *
 * stress_percent: stress percentage
 *
 * :
 *
 * execution_time: execution time in hours
 *
 * debug: (option flag) enables debugging messages
 */
int numprocs,			/* number of process id's in table             */
 procs[MAXPROCS],		/* array of process id's for killing           */
 long_running,			/* number of long term testcases running       */
 short_running;			/* number of short term testcases running      */
float e4user,			/* previous elapsed seconds for tc 4-user      */
 e4real,			/* previous elapsed seconds for tc 4-real      */
 e5user,			/* previous elapsed seconds for tc 5-user      */
 e5real,			/* previous elapsed seconds for tc 5-real      */
 e6user0,			/* previous elapsed seconds for tc 6-user,nf   */
 e6real0,			/* previous elapsed seconds for tc 6-real,nf   */
 e6user1,			/* previous elapsed seconds for tc 6-user,f    */
 e6child;			/* previous elapsed seconds for tc 6-child     */
double stress_percent = DEFAULT_STRESS_PERCENTAGE;
double execution_time = DEFAULT_TIME;
int process_slots = DEFAULT_PROCESS_SLOTS;
int debug = 0;

/*
 * Function prototypes
 */
void startup(long);
int start_testcase(char *, char *, char *, char *, char *, char *);
int process_slots_in_use();
int available_user_process_slots();
float measure_test(char *, char *, char *, char *, float *);
void display_line(char *, int, int, float, float *, int);
void perform_throughput_tests(int);
void start_long_term_testcases(int, char *);
void kill_short_term_testcases();
void start_short_term_testcases(int, double, int);
void finishup(long);
void parse_args(int, char **);

/*---------------------------------------------------------------------+
|                               main ()                                |
| ==================================================================== |
|                                                                      |
| Function:  Main program                                              |
|                                                                      |
+---------------------------------------------------------------------*/
int main(int argc, char **argv)
{
	long runseconds,	/* number of seconds to run */
	 start_time;		/* time at start of driver */
	int current_priority,	/* current priority level for nice */
	 workslots,		/* number of free workslots */
	 long_term_slot_total,	/* workslots for long-term processes */
	 short_term_slot_total;	/* workslots for short-term */

	/*
	 * Parse command line arguments & printer program header...
	 */
	parse_args(argc, argv);
	printf("Scheduler Testsuite Program\n\n");
	fflush(stdout);

	/*
	 * Calculate number of seconds to run, then print out start info
	 */
	runseconds = (long)(execution_time * 60.0 * 60.0);
	start_time = time(NULL);

	startup(start_time);

	/*
	 * Calculate available workslots, long-term, and short-term slots
	 */
	workslots = available_user_process_slots() * stress_percent;
	long_term_slot_total = workslots / 2;
	if (debug) {
		printf("available slots:      %d\n",
		       available_user_process_slots());
		printf("workslots available:  %d\n", workslots);
		printf("stress_percent:       %f\n", stress_percent);
		printf("run-hours:            %f (hrs)\n", execution_time);
		printf("runseconds:           %ld (sec)\n", runseconds);
	}

	/*
	 * Run the first set of tests with an average priority
	 */
	perform_throughput_tests((MAX_PRI + MIN_PRI) / 2);
	fflush(stdout);

	/*
	 * Start the long-term testcases running
	 */
	start_long_term_testcases(long_term_slot_total, argv[2]);
	short_term_slot_total = workslots / 2;
	fflush(stdout);

	/*
	 * Loop while there is still time
	 */
	current_priority = MAX_PRI;
	while ((time(0) - start_time) < runseconds) {

		if (debug)
			printf("current priority: %d\n", current_priority);
		if (debug)
			printf("starting short term tests\n");

		start_short_term_testcases(short_term_slot_total,
					   stress_percent, current_priority);
		fflush(stdout);

		perform_throughput_tests(current_priority);
		fflush(stdout);

		if (debug)
			printf("killing short term tests\n");

		kill_short_term_testcases();
		fflush(stdout);

		if (current_priority + PRIINC > MIN_PRI)
			current_priority = MAX_PRI;
		else
			current_priority += PRIINC;
	}

	/*
	 * Exit with success...
	 */
	finishup(start_time);
	printf("\nsuccessful!\n");
	fflush(stdout);
	return (0);
}

/*------------------------------ startup() ------------------------------*/
/* This procedure opens the , and then outputs some starting	 *
 * information to the screen and .  It also initializes the	 *
 * process id list and other global variables.	 			 *
 *-----------------------------------------------------------------------*/
void startup(long start_time)
{
	char tempbuffer[50];	/* temporary buffer to hold names */

	/*
	 * Now output some diagnostic information
	 */
	printf("start time    = %s\n", ctime(&start_time));

	gethostname(tempbuffer, 40);
	printf("host name     = %s\n", tempbuffer);

	printf("user name     = %s\n", getpwuid(geteuid())->pw_name);

	printf("test duration = %4.2f (hours)\n", execution_time);

	printf("test stress   = %4.2f%%%%\n\n", 100 * stress_percent);

	/*
	 * Initialize the global variables
	 */
	numprocs = 0;
	long_running = 0;
	short_running = 0;
	e4user = 0.0;
	e4real = 0.0;
	e5user = 0.0;
	e5real = 0.0;
	e6user0 = 0.0;
	e6real0 = 0.0;
	e6user1 = 0.0;
	e6child = 0.0;
}

/*--------------------------- start_testcase() --------------------------*/
/* This procedure will run a specified testcase by forking a process, and*
 * then running the testcase with it.  It will also store the process id *
 * number in the process id table.  The process id of the child process  *
 * is returned to the calling program.					 *
 *	name1     pathname of testcase to run			         *
 *	name2     filename of testcase to run			         *
 *	param1    parameters to pass to the testcase			 *
 *	param2 			         			         *
 *	param3 			         			         *
 *	param4    if sched_tc6:  fork flag:  0=false, 1=true             *
 *-----------------------------------------------------------------------*/

int start_testcase(char *name1, char *name2, char *param1, char *param2,
		   char *param3, char *param4)
{
	int pid,		/* pid of currently running process */
	 pid_save;		/* saved pid of process */

	/*
	 * Fork a process that will run testcase and save the pid
	 */
	if (debug)
		printf("test: %s %s p1[%s] p2[%s] p3[%s] p4[%s]\n",
		       name1, name2, param1, param2, param3, param4);

	pid_save = pid = fork();

	/*
	 * If the pid returned is -1, fork failed.  If the pid returned is
	 * 0, then the process running is the child process, and we need
	 * to do an 'execl' to run the testcase.  If the pid returned is
	 * anything else, then the parent is running, and we return.
	 */
	switch (pid) {
	case -1:
		exit(-1);
	case 0:
		execl(name1, name2, param1, param2, param3, param4, NULL);
		printf("ERROR: start_testcase():  execl failed.\n");
		exit(-1);
	default:
		break;
	}
	if (debug)
		printf("testcase %s started -- pid is %d\n", name2, pid_save);

	/*
	 * If the process just forked is for a short-term testcase, then
	 * add the process id to the table.
	 */
	if (debug)
		printf("new process: %s ", name2);
	if (strstr(name2, "tc1") || strstr(name2, "tc3")) {
		procs[numprocs] = pid_save;
		numprocs++;
		short_running++;
		if (debug)
			printf("(%d short term)", short_running);
	}
	if (strstr(name2, "tc0") || strstr(name2, "tc2")) {
		long_running++;
		if (debug)
			printf("(%d long term)", long_running);
	}
	if (debug)
		printf("\n");

	return (pid_save);
}

/*------------------------- process_slots_in_use() ----------------------*/
/* This function will return the number of process slots currently in use*
 * by executing the 'ps' command.					 *
 *-----------------------------------------------------------------------*/
int process_slots_in_use()
{
	FILE *psfile;		/* temporary file to hold output of 'ps' command */
	int usedslots;		/* holds the number of used process slots */

	/*
	 * Call the 'ps' command and write the number of process slots to a file
	 */
	if (system("ps -e | wc -l > ps.out") < 0)
		sys_error("system failed", __FILE__, __LINE__);

	/*
	 * Open the output file
	 */
	if ((psfile = fopen("ps.out", "r")) == NULL) {
		exit(-1);
	}

	/*
	 * Read the number of process slots in use from the file
	 */
	fscanf(psfile, "%d", &usedslots);

	/*
	 * Close the output file
	 */
	if (fclose(psfile) == -1) {
		exit(-1);
	}

	/*
	 * Remove the output file
	 */
	if (system("/bin/rm ps.out") < 0)
		sys_error("system failed", __FILE__, __LINE__);

	return (usedslots - 1);
}

/*----------------------- available_user_process_slots() ----------------*/
/* This function returns the total number of available user process slots*
 * by subtracting the process slots currently in use from the maximum	 *
 * possible process slots.						 *
 *-----------------------------------------------------------------------*/
int available_user_process_slots()
{
	int num = process_slots_in_use();

	return ((process_slots < num) ? process_slots : process_slots - num);
}

/*---------------------------- measure_test() ---------------------------*/
/* This function executes a throughput measurement process and waits for *
 * that process to finish.  When finished, it reads the result from a 	 *
 * file and returns that result to the caller.  The file is then deleted.*
 * If sched_tc6 is called, then the second time is also read from the	 *
 * results file and returned to the caller.				 *
 *-----------------------------------------------------------------------*/
float measure_test(name, param1, param2, param3, t2)
char *name,			/* filename of testcase to run */
*param1,			/* user flag:  0=user, 1=real time */
*param2,			/* priority to run the throughput test at */
*param3;			/* if sched_tc6:  fork flag, 0=false, 1=true */
float *t2;			/* if sched_tc6:  second time returned from testcase */
{
	char temp[PATH_MAX],	/* holds pathname and returned floating number */
	 t2asc[50];		/* holds second returned floating number */
	int saved_pid;		/* process id of forked process */
	FILE *datafile;		/* file pointer for temporary file */

	/*
	 * Create the path name to be passed to the start_testcase() function
	 */
	sprintf(temp, "./%s", name);

	/*
	 * Send all the parameters, and start the testcase
	 */
	saved_pid = start_testcase(temp, name, param1,
				   "-lsch.measure", param2, param3);

	/*
	 * Wait for the testcase to finish
	 */
	if (debug)
		printf("waiting on child %d\n", saved_pid);
	while (wait(NULL) != saved_pid) ;

	/*
	 * Open the temporary file to get the returned number of seconds
	 */

	if ((datafile = fopen("sch.measure", "r")) == NULL) {
		sys_error("cannot open sch.measure", __FILE__, __LINE__);
	}

	/*
	 * Read the number of seconds
	 */
	fgets(temp, 50, datafile);
	/*added by mpt
	   printf("sched+driver: measure_test: number of seconds=%s\n",temp)
	   *********** */

	/*
	 * If this is sched_tc6, then there is another number we must return
	 */

	if (strcmp(name, "sched_tc6") == 0) {
		fgets(t2asc, 50, datafile);
		*t2 = atof(t2asc);
	}

	/*
	 * Close the temporary file
	 */
	if (fclose(datafile) != 0) {
		exit(-1);
	}

	/*
	 * Now try to remove the temporary file
	 */
	/*added by MPT
	   printf("measure_test:  REMOVING sch.measure\n");
	   fflush(stdout);
	   if  (system ("rm sch.measure") < 0)
	   sys_error ("system failed", __FILE__, __LINE__);
	 */
	return (atof(temp));
}

/*------------------------- display_line() ------------------------------*/
/* This procedure displays a line of output given the results of a	 *
 * throughput test.  It displays the testcase name, the current priority *
 * level, the user/real time flag, and the elapsed time in seconds, as	 *
 * well as the percent change between the current and previous times.	 *
 * It then updates the previous elapsed time to be the current one.	 *
 *-----------------------------------------------------------------------*/
void display_line(char *tcname, int pri, int f, float et, float *pet, int ff)
{
	static int display_header = 0;
	float pc;		/* holds percent change */

	/*
	 * Print header every eight lines...
	 */
	if (display_header-- == 0) {
		printf("\n  Test                Processes              "
		       "  Time        Notes\n"
		       "---------   ---------------------------   "
		       "---------------  -------\n"
		       "name        long  short  priority  mode   "
		       "elapsed  %%%%delta\n\n");
		display_header = 6;
	}

	/*
	 * Calculate the percent change in time
	 */
	pc = (*pet == 0.0) ? 0.0 : 100.0 * ((et - *pet) / *pet) + 0.05;

	printf("%-12s %2d    %2d      %2d      %4s   %06.4f  %+06.4f  %s\n",
	       tcname, long_running, short_running, pri,
	       (f == 0) ? "user" : "real", et, pc, (ff) ? "forked child" : " ");

	fflush(stdout);

	*pet = et;
}

/*------------------------- perform_throughput_tests() ------------------*/
/* This procedure is called each time throughput tests are to be	 *
 * performed.  This procedure executes each of the throughput tests, and *
 * records the results of each to the .	 			 *
 *-----------------------------------------------------------------------*/
void perform_throughput_tests(int current_priority)
{
	float esecs,		/* elapsed seconds returned from each testcase */
	 esecs2,		/* elapsed seconds (second part) for sched_tc6 */
	 pc;			/* percent change for sched_tc6 */
	char pristr[10];	/* holds ascii value of priority as parameter */

	sprintf(pristr, "-p%d", current_priority);

#if defined(_IA64) && !defined(__64BIT__)
	esecs =
	    measure_test("sched_tc4.32", "-tvariable", pristr, NULL, &esecs2);
	display_line("sched_tc4.32", current_priority, 0, esecs, &e4user, 2);
	esecs = measure_test("sched_tc4.32", "-tfixed", pristr, NULL, &esecs2);
	display_line("sched_tc4.32", current_priority, 1, esecs, &e4real, 2);
	esecs =
	    measure_test("sched_tc5.32", "-tvariable", pristr, NULL, &esecs2);
	display_line("sched_tc5.32", current_priority, 0, esecs, &e5user, 2);
	esecs = measure_test("sched_tc5.32", "-tfixed", pristr, NULL, &esecs2);
	display_line("sched_tc5.32", current_priority, 1, esecs, &e5real, 2);
	esecs =
	    measure_test("sched_tc6.32", "-tvariable", pristr, " -d ", &esecs2);
	display_line("sched_tc6.32", current_priority, 0, esecs, &e6user0, 0);
	esecs =
	    measure_test("sched_tc6.32", "-tfixed", pristr, " -d ", &esecs2);
	display_line("sched_tc6.32", current_priority, 1, esecs, &e6real0, 0);
	esecs =
	    measure_test("sched_tc6.32", "-tvariable", pristr, " -df ",
			 &esecs2);
	display_line("sched_tc6.32", current_priority, 0, esecs, &e6user1, 1);
#else
	esecs = measure_test("sched_tc4", "-tvariable", pristr, NULL, &esecs2);
	display_line("sched_tc4", current_priority, 0, esecs, &e4user, 2);
	esecs = measure_test("sched_tc4", "-tfixed", pristr, NULL, &esecs2);
	display_line("sched_tc4", current_priority, 1, esecs, &e4real, 2);
	esecs = measure_test("sched_tc5", "-tvariable", pristr, NULL, &esecs2);
	display_line("sched_tc5", current_priority, 0, esecs, &e5user, 2);
	esecs = measure_test("sched_tc5", "-tfixed", pristr, NULL, &esecs2);
	display_line("sched_tc5", current_priority, 1, esecs, &e5real, 2);
	esecs =
	    measure_test("sched_tc6", "-tvariable", pristr, " -d ", &esecs2);
	display_line("sched_tc6", current_priority, 0, esecs, &e6user0, 0);
	esecs = measure_test("sched_tc6", "-tfixed", pristr, " -d ", &esecs2);
	display_line("sched_tc6", current_priority, 1, esecs, &e6real0, 0);
	esecs =
	    measure_test("sched_tc6", "-tvariable", pristr, " -df ", &esecs2);
	display_line("sched_tc6", current_priority, 0, esecs, &e6user1, 1);
#endif

	/*
	 * Manually build the display line for the second part of sched_tc6
	 */

	/*
	 * Calculate the percent change in time
	 */
	pc = (e6child ==
	      0.0) ? 0.0 : 100 * ((esecs2 - e6child) / e6child) + 0.05;
	printf("%-12s forked child          %4s   %06.4f  %+06.4f\n",
	       "sched_tc6", "real", esecs2, pc);
	e6child = esecs2;
}

/*------------------------ start_long_term_testcases() ------------------*/
/* This procedure takes the number of long-term process slots available, *
 * and executes the long term testcases.				 *
 *-----------------------------------------------------------------------*/
void start_long_term_testcases(long_term_slot_total, execution_time)
int long_term_slot_total;	/* total number of long-term slots */
char *execution_time;		/* runtime hours to pass to each testcase */
{
	int i;

	/*
	 * Now use up the long_term_slot_total by starting testcases call
	 * half with real-time flag '1' set, other half user flag '0'
	 */
	if (debug)
		printf("long-term slots available:  %d\n",
		       long_term_slot_total);

	for (i = 0; i < (long_term_slot_total / 4); i++) {
#if defined(_IA64) && !defined(__64BIT__)
		start_testcase("./sched_tc0.32", "sched_tc0 -t", execution_time,
			       " -p1", NULL, NULL);
		start_testcase("./sched_tc2.32", "sched_tc2", execution_time,
			       "1", NULL, NULL);
		start_testcase("./sched_tc0.32", "sched_tc0 -t", execution_time,
			       " -p0", NULL, NULL);
		start_testcase("./sched_tc2.32", "sched_tc2", execution_time,
			       "0", NULL, NULL);
#else
		start_testcase("./sched_tc0", "sched_tc0 -t", execution_time,
			       " -p1", NULL, NULL);
		start_testcase("./sched_tc2", "sched_tc2", execution_time, "1",
			       NULL, NULL);
		start_testcase("./sched_tc0", "sched_tc0 -t", execution_time,
			       " -p0", NULL, NULL);
		start_testcase("./sched_tc2", "sched_tc2", execution_time, "0",
			       NULL, NULL);
#endif
	}
}

/*---------------------------------------------------------------------+
|                     start_short_term_testcases ()                    |
| ==================================================================== |
|                                                                      |
| Function:  Starts short term testcases (one for each process slot)   |
|                                                                      |
+---------------------------------------------------------------------*/
void start_short_term_testcases(int short_term_slot_total,
				double stress_percent, int pri)
{
	int i;
	int short_term_slots;	/* number of slots to use */

	/*
	 * Set up the short_term_slot_total by starting testcases call
	 * half with real-time flag '1' set, other half user flag '0'
	 */
	if (available_user_process_slots() < short_term_slot_total)
		short_term_slots =
		    available_user_process_slots() * stress_percent / 2;
	else
		short_term_slots = short_term_slot_total;

	printf("\n<< Starting %d short-term testcases>> \n\n",
	       short_term_slots);
	if (debug)
		printf("short-term slots available:  %d\n", short_term_slots);

	for (i = 0; i < (short_term_slots / 4); i++) {
#if defined(_IA64) && !defined(__64BIT__)
		start_testcase("./sched_tc1.32", "sched_tc1", "1", NULL, NULL,
			       NULL);
		start_testcase("./sched_tc3.32", "sched_tc3", "1", NULL, NULL,
			       NULL);
		start_testcase("./sched_tc1.32", "sched_tc1", "0", NULL, NULL,
			       NULL);
		start_testcase("./sched_tc3.32", "sched_tc3", "0", NULL, NULL,
			       NULL);
#else
		start_testcase("./sched_tc1", "sched_tc1", "1", NULL, NULL,
			       NULL);
		start_testcase("./sched_tc3", "sched_tc3", "1", NULL, NULL,
			       NULL);
		start_testcase("./sched_tc1", "sched_tc1", "0", NULL, NULL,
			       NULL);
		start_testcase("./sched_tc3", "sched_tc3", "0", NULL, NULL,
			       NULL);
#endif
#if 0
		perform_throughput_tests(pri);
#endif
	}
}

/*------------------------ kill_short_term_testcases() ------------------*/
/* This procedure goes through the process id table, and sends each	 *
 * process id number found in the table a signal in order to terminate	 *
 * it.  The signal sent is SIGUSR1.  It also re-initializes the table.	 *
 *-----------------------------------------------------------------------*/
void kill_short_term_testcases()
{
	int i;			/* loop counter to step through the list of process id's */

	/*
	 * Loop through the array of process id's one at a time, and
	 * attempt to kill each one.  If kill fails, report error and
	 * continue.
	 */
	if (debug)
		printf("killing short-term processes...\n");
	for (i = 0; i < numprocs; i++) {
		if (debug)
			printf("killing process [%d]\n", procs[i]);
		kill(procs[i], SIGUSR1);
	}

	/*
	 * Adjust the number of short_term_testcases
	 */
	short_running -= numprocs;

	/*
	 * Clear the table by setting number of entries to zero
	 */
	numprocs = 0;
}

/*----------------------------- finishup() ------------------------------*/
/* This procedure closing information to the about ending	 *
 * times, elapsed times, etc.  This procedure then closes the file*
 *-----------------------------------------------------------------------*/
void finishup(start_time)
long start_time;		/* starting time to calculate elapsed time */
{
	long end_time;		/* time when program finished */

	/*
	 * Get the end time and calculate elapsed time; write all this out
	 */
	end_time = time(NULL);

	printf("\nend time = %s\n", ctime(&end_time));

	printf("elapsed time = %4.2f hours\n",
	       ((end_time - start_time) / 3600.0));
}

/*---------------------------------------------------------------------+
|                             parse_args ()                            |
| ==================================================================== |
|                                                                      |
| Function:  Parse the command line arguments & initialize global      |
|            variables.                                                |
|                                                                      |
| Updates:   (command line options)                                    |
|                                                                      |
|            [-s] size: shared memory segment size                     |
|                                                                      |
+---------------------------------------------------------------------*/
void parse_args(int argc, char **argv)
{
	int opt;
	int sflg = 0, pflg = 0, tflg = 0;
	int errflag = 0;
	char *program_name = *argv;
	extern char *optarg;	/* Command line option */

	/*
	 * Parse command line options.
	 */
	while ((opt = getopt(argc, argv, "vs:p:t:l:d")) != EOF) {
		switch (opt) {
		case 's':	/* stress percentage */
			sflg++;
			stress_percent = atof(optarg);
			break;
		case 'p':	/* process slots */
			pflg++;
			process_slots = atof(optarg);
			break;
		case 't':	/* time (hours) */
			tflg++;
			execution_time = atof(optarg);
			break;
		case 'd':	/* Enable debugging messages */
			debug++;
			break;
		case 'v':	/* Enable verbose mode=debug mode */
			debug++;
			break;
		default:
			errflag++;
			break;
		}
	}

	/*
	 * Check percentage, execution time and process slots...
	 */
	if (sflg) {
		if (stress_percent < 0.0 || stress_percent > 1.0)
			errflag++;
	}
	if (pflg) {
		if (process_slots < 0 || process_slots > MAXPROCS)
			errflag++;
	}
	if (tflg) {
		if (execution_time < 0.0 || execution_time > 100.0)
			errflag++;
	}
	if (debug)
		printf("\n(debugging messages enabled)\n\n");
	if (errflag) {
		fprintf(stderr, USAGE, program_name);
		exit(2);
	}
}
