/*
 *
 *   Copyright (c) International Business Machines  Corp., 2002
 *
 *   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
 */

/* 06/30/2001	Port to Linux	nsharoff@us.ibm.com */
/* 11/01/2002	Port to LTP  	robbiew@us.ibm.com */

			   /* page01.c */
/*======================================================================
	=================== TESTPLAN SEGMENT ===================
CALLS:	malloc(3)

	Run with KILL flag

>KEYS:  < paging behavior
>WHAT:  < Does the system balk at heavy demands on it's paging facilities?
>HOW:   < Create a number of process, each of which requests a large
	< chunk of memory to be assigned to an array.  Write to each
	< element in that array, and verify that what was written/stored
	< is what was expected.
>BUGS:  <
======================================================================*/

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int bd_arg(char *);

/** LTP Port **/
#include "test.h"
#include "usctest.h"

void blenter(void);
void setup(void);
void anyfail(void);
void ok_exit(void);
void forkfail(void);
void terror(char *);
int instress(void);

#define FAILED 0
#define PASSED 1

int local_flag = PASSED;
int block_number;
FILE *temp;

char *TCID = "page01";		/* Test program identifier.    */
int TST_TOTAL = 1;		/* Total number of test cases. */
/**************/

int main(argc, argv)
int argc;
char *argv[];
{
	int nchild;
	int memory_size;
	int error_count, i, j, pid, status;
	int *number_pointer;
	int *memory_pointer;
	int child, count;

	setup();

	if (argc < 2) {
		memory_size = 256 * 1024;
		nchild = 50;
	} else if (argc == 3) {
		if (sscanf(argv[1], "%d", &memory_size) != 1)
			bd_arg(argv[1]);
		if (sscanf(argv[2], "%d", &nchild) != 1)
			bd_arg(argv[2]);
	} else {
		printf("page01 [memory size (words)]  [nchild]\n");
		tst_resm(TCONF, "\tBad arg count.\n");
		exit(1);
	}

	blenter();

	error_count = 0;

	/****************************************/
	/*                                      */
	/*      attempt to fork a number of     */
	/*      identical processes             */
	/*                                      */
	/****************************************/

	for (i = 1; i <= nchild; i++) {
		if ((pid = fork()) == -1) {
			terror("Fork failed (may be OK if under stress)");
			if (instress())
				ok_exit();
			forkfail();
		} else if (pid == 0) {
			/********************************/
			/*                              */
			/*   allocate memory  of size   */
			/*    "memory_size"             */
			/*                              */
			/********************************/

			memory_pointer =
			    (int *)malloc(memory_size * sizeof(int));
			if (memory_pointer == 0) {
				tst_resm(TBROK,
					 "Cannot allocate memory - malloc failed.\n");
				if (i < 2) {
					tst_resm(TBROK,
						 "This should not happen for first two children.\n");
					tst_resm(TFAIL, "Child %d - fail.\n",
						 i);
					tst_exit();
				} else {
					tst_resm(TCONF,
						 "This is ok for all but first two children.\n");
					tst_resm(TCONF, "Child %d - ok.\n", i);
					tst_exit();
				}
			}
			number_pointer = memory_pointer;

			/********************************/
			/*                              */
			/*         write to it          */
			/*                              */
			/********************************/

			for (j = 1; j <= memory_size; j++)
				*(number_pointer++) = j;
			sleep(1);

			/********************************/
			/*                              */
			/*      and read from it to     */
			/*  check that what was written */
			/*       is still there         */
			/*                              */
			/********************************/

			number_pointer = memory_pointer;
			for (j = 1; j <= memory_size; j++) {
				if (*(number_pointer++) != j)
					error_count++;
			}
			exit(error_count);
		}
	}

	/****************************************/
	/*                                      */
	/*      wait for the child processes    */
	/*      to teminate and report the #    */
	/*      of deviations recognized        */
	/*                                      */
	/****************************************/

	count = 0;
	while ((child = wait(&status)) > 0) {
#ifdef DEBUG
		tst_resm(TINFO, "Test {%d} exited status %d\n", child, status);
#endif
		if (status)
			local_flag = FAILED;
		count++;
	}

	if (count != nchild) {
		tst_resm(TWARN, "Wrong number of children waited on.\n");
		tst_resm(TWARN, "Count = %d, expected = %d.\n", count, nchild);
	}

	anyfail();
	/** NOT REACHED **/
	tst_exit();
}

int bd_arg(str)
char *str;
{
	tst_resm(TCONF, "\tCannot parse %s as a number.\n", str);
	exit(1);
}

/** LTP Port **/
/*
 * setup
 *
 * Do set up - here its a dummy function
 */
void setup()
{
	tst_tmpdir();
	temp = stderr;
}

/*
 * Function: blenter()
 *
 * Description: Print message on entering a new block
 */
void blenter()
{
	local_flag = PASSED;
	return;
}

/*
 *
 * Function: anyfail()
 *
 * Description: Exit a test.
 */
void anyfail()
{
	(local_flag == FAILED) ? tst_resm(TFAIL, "Test failed")
	    : tst_resm(TPASS, "Test passed");
	tst_rmdir();
	tst_exit();
}

/*
 * ok_exit
 *
 * Calling block passed the test
 */
void ok_exit()
{
	local_flag = PASSED;
	return;
}

/*
 * forkfail()
 *
 * exit on failure
 */
void forkfail()
{
	tst_resm(TBROK, "Reason: %s\n", strerror(errno));
	tst_rmdir();
	tst_exit();
}

/*
 * Function: terror
 *
 * Description: prints error message this may not be because some part of the
 *              test case failed, for example fork() failed. We will log this
 *              failure as TBROK instead of TFAIL.
 */
void terror(char *message)
{
	tst_resm(TBROK, "Reason: %s:%s\n", message, strerror(errno));
	return;
}

/*
 * instress
 *
 * Assume that we are always running under stress, so this function will
 * return > 0 value always.
 */
int instress()
{
	tst_resm(TINFO, "System resource may be too low, fork() malloc()"
		 " etc are likely to fail.\n");
	return 1;
}
