/*
 * 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., 59
 * Temple Place - Suite 330, Boston MA 02111-1307, 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;
 *
 *	extern int Forker_pids[];
 *	extern int Forker_npids;
 *
 *    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>
#include "forker.h"

extern int errno;

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 childern 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 */
