/*
 * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 *
 * http://www.sgi.com
 *
 * For further information regarding this notice, see:
 *
 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
 */
/**************************************************************
 *
 *    OS Testing - Silicon Graphics, Inc.
 *
 *    FUNCTION NAME     : forker
 *			  background
 *
 *    FUNCTION TITLE    : fork desired number of copies of the current process
 *			  fork a process and return control to caller
 *
 *    SYNOPSIS:
 *      int forker(ncopies, mode, prefix)
 *      int ncopies;
 *	int mode;
 *	char *prefix;
 *
 *	int background(prefix);
 *	char *prefix;
 *
 *    AUTHOR            : Richard Logan
 *
 *    CO-PILOT(s)       : Dean Roehrich
 *
 *    INITIAL RELEASE   : UNICOS 8.0
 *
 *    DESIGN DESCRIPTION
 *	The background function will do a fork of the current process.
 *	The parent process will then exit, thus orphaning the
 *	child process.  Doing this will not nice the child process
 *	like executing a cmd in the background using "&" from the shell.
 *	If the fork fails and prefix is not NULL, a error message is printed
 *      to stderr and the process will exit with a value of errno.
 *
 *	The forker function will fork <ncopies> minus one copies
 *	of the current process.  There are two modes in how the forks
 *	will be done.  Mode 0 (default) will have all new processes
 *	be childern of the parent process.    Using Mode 1,
 *	the parent process will have one child and that child will
 *	fork the next process, if necessary, and on and on.
 *	The forker function will return the number of successful
 *	forks.  This value will be different for the parent and each child.
 *	Using mode 0, the parent will get the total number of successful
 *	forks.  Using mode 1, the newest child will get the total number
 *	of forks.  The parent will get a return value of 1.
 *
 *	The forker function also updates the global variables
 *	Forker_pids[] and Forker_npids.  The Forker_pids array will
 *      be updated to contain the pid of each new process.  The
 *	Forker_npids variable contains the number of entries
 *	in Forker_pids.  Note, not all processes will have
 *	access to all pids via Forker_pids.  If using mode 0, only the
 *	parent process and the last process will have all information.
 *      If using mode 1, only the last child process will have all information.
 *
 *	If the prefix parameter is not NULL and the fork system call fails,
 *      a error message will be printed to stderr.  The error message
 *      the be preceeded with prefix string.  If prefix is NULL,
 *      no error message is printed.
 *
 *    SPECIAL REQUIREMENTS
 *	None.
 *
 *    UPDATE HISTORY
 *      This should contain the description, author, and date of any
 *      "interesting" modifications (i.e. info should helpful in
 *      maintaining/enhancing this module).
 *      username     description
 *      ----------------------------------------------------------------
 *	rrl	    This functions will first written during
 *		the SFS testing days, 1993.
 *
 *    BUGS/LIMITATIONS
 *     The child pids are stored in the fixed array, Forker_pids.
 *     The array only has space for 4098 pids.  Only the first
 *     4098 pids will be stored in the array.
 *
 **************************************************************/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>		/* fork, getpid, sleep */
#include <string.h>
#include <stdlib.h>		/* exit */
#include "forker.h"

int Forker_pids[FORKER_MAX_PIDS];	/* holds pids of forked processes */
int Forker_npids = 0;		/* number of entries in Forker_pids */

/***********************************************************************
 *
 * This function will fork and the parent will exit zero and
 * the child will return.  This will orphan the returning process
 * putting it in the background.
 *
 * Return Value
 *   0 : if fork did not fail
 *  !0 : if fork failed, the return value will be the errno.
 ***********************************************************************/
int background(char *prefix)
{
	switch (fork()) {
	case -1:
		if (prefix != NULL)
			fprintf(stderr,
				"%s: In %s background(), fork() failed, errno:%d %s\n",
				prefix, __FILE__, errno, strerror(errno));
		exit(errno);

	case 0:		/* child process */
		break;

	default:
		exit(0);
	}

	return 0;

}				/* end of background */

/***********************************************************************
 * Forker will fork ncopies-1 copies of self.
 *
 ***********************************************************************/
int forker(int ncopies,
	int mode,	/* 0 - all children of parent, 1 - only 1 direct child */
	char *prefix)	/* if ! NULL, an message will be printed to stderr */
			/* if fork fails. The prefix (program name) will */
			/* preceed the message */
{
	int cnt;
	int pid;
	static int ind = 0;

	Forker_pids[ind] = 0;

	for (cnt = 1; cnt < ncopies; cnt++) {

		switch (mode) {
		case 1:	/* only 1 direct child */
			if ((pid = fork()) == -1) {
				if (prefix != NULL)
					fprintf(stderr,
						"%s: %s,forker(): fork() failed, errno:%d %s\n",
						prefix, __FILE__, errno,
						strerror(errno));
				return 0;
			}
			Forker_npids++;

			switch (pid) {
			case 0:	/* child - continues the forking */

				if (Forker_npids < FORKER_MAX_PIDS)
					Forker_pids[Forker_npids - 1] =
					    getpid();
				break;

			default:	/* parent - stop the forking */
				if (Forker_npids < FORKER_MAX_PIDS)
					Forker_pids[Forker_npids - 1] = pid;
				return cnt - 1;
			}

			break;

		default:	/* all new processes are childern of parent */
			if ((pid = fork()) == -1) {
				if (prefix != NULL)
					fprintf(stderr,
						"%s: %s,forker(): fork() failed, errno:%d %s\n",
						prefix, __FILE__, errno,
						strerror(errno));
				return cnt - 1;
			}
			Forker_npids++;

			switch (pid) {
			case 0:	/* child - stops the forking */
				if (Forker_npids < FORKER_MAX_PIDS)
					Forker_pids[Forker_npids - 1] =
					    getpid();
				return cnt;

			default:	/* parent - continues the forking */
				if (Forker_npids < FORKER_MAX_PIDS)
					Forker_pids[Forker_npids - 1] = pid;
				break;
			}
			break;
		}
	}

	if (Forker_npids < FORKER_MAX_PIDS)
		Forker_pids[Forker_npids] = 0;
	return cnt - 1;

}				/* end of forker */

#if UNIT_TEST

/*
 * The following is a unit test main for the background and forker
 * functions.
 */

int main(argc, argv)
int argc;
char **argv;
{
	int ncopies = 1;
	int mode = 0;
	int ret;
	int ind;

	if (argc == 1) {
		printf("Usage: %s ncopies [mode]\n", argv[0]);
		exit(1);
	}

	if (sscanf(argv[1], "%i", &ncopies) != 1) {
		printf("%s: ncopies argument must be integer\n", argv[0]);
		exit(1);
	}

	if (argc == 3)
		if (sscanf(argv[2], "%i", &mode) != 1) {
			printf("%s: mode argument must be integer\n", argv[0]);
			exit(1);
		}

	printf("Starting Pid = %d\n", getpid());
	ret = background(argv[0]);
	printf("After background() ret:%d, pid = %d\n", ret, getpid());

	ret = forker(ncopies, mode, argv[0]);

	printf("forker(%d, %d, %s) ret:%d, pid = %d, sleeping 30 seconds.\n",
	       ncopies, mode, argv[0], ret, getpid());

	printf("%d My version of Forker_pids[],  Forker_npids = %d\n",
	       getpid(), Forker_npids);

	for (ind = 0; ind < Forker_npids; ind++) {
		printf("%d ind:%-2d pid:%d\n", getpid(), ind, Forker_pids[ind]);
	}

	sleep(30);
	exit(0);
}

#endif /* UNIT_TEST */