#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define	MAXLENGTH	50

/* Global variables for the tree structure */
#define	DEPTH		1 /* Must remain 1 for this program */
#define	BRANCHES	1 /* Must remain 1 for this program */
int	level=0;
int	pids[BRANCHES];

/* Global variables for the pipes communication */
#define R		0
#define W		1
struct descriptor {
	int	parent[2];
	int	child[2];
} 	fd[BRANCHES]; 
int	fdpw,fdpr;

/* Global variables for the signals*/
volatile int  child_continue=0;
volatile int  parent_continue=0;

void do_parent(void);
void do_child(void);

void initialize_handler(int n, void (*func)() );

void user1_handler(void);
void user2_handler(void);
void floating(void);
void no_pipe(void);
void interrupt(void);
void infantcide(void);

int  kill2(int process, char *s);

void pipe_close(void);
void spawn(void);

void itoa(int n, char s[] );
void reverse(char s[]);

main()
{

	int   i;
	
	/* Here we initialize all the signals we will be trapping */
	initialize_handler( SIGTERM,infantcide   );
	initialize_handler( SIGUSR1,user1_handler);
	initialize_handler( SIGUSR2,user2_handler); 
	initialize_handler( SIGFPE ,floating     );
	initialize_handler( SIGINT ,interrupt    );
	initialize_handler(_SIGPIPE,no_pipe      );
/*	initialize_handler( SIGSTOP,SIG_DFL      ); Cannot reset SIGSTOP */
	initialize_handler( SIGCONT,SIG_DFL      );

	fflush(stdout); /* clear buffer before recursion */

	/* spawn command creates a tree stucture of processes with    */
	/* level DEPTH and number of children per node BRANCHES.      */
	/* At each node two half-duples pipes are created between it  */
	/* and its parent and between it and each one if its children */

	spawn();
	

	/* The following code segement splits with respect to the global */
	/* variable 'level'. The root process has ( level == 0 ), a leaf */
	/* has ( level == DEPTH )                                        */

	if      (level==0) {
		do_parent();
		printf("Hit CTRL-C to continue\n");
		fflush(stdout);
		pause();
	}
	else if (level==DEPTH) 
		do_child();
	else {
		printf("Unknown level id %d\n",level);
		exit(1);	
	}

	printf("Exiting from level %d\n",level) ;
        exit(0);
}

void do_parent(void)
{
	int i;

	/* Child initally in a loop, sleep for a bit */
	sleep(5);
	printf("Parent %d sending SIGSTOP to Child %d\n",
			getpid(),pids[0]);

        if((i=kill2(pids[0],(char *)"SIGSTOP\0"))!=0){
		printf("Error in SIGSTOP, %d\n",i); 
		infantcide();
		exit(1);
	}

	/* Child is now stopped, write some output to show screen is */
	/* not just locked                                           */
	for(i=0;i<4;i++) {
		sleep(1);
		printf("Parent PID %d: in a loop\n",getpid());
        }

	/* Restart child*/
	printf("Parent %d sending SIGCONT to Child %d\n",
			getpid(),pids[0]);
        if((i=kill(pids[0],SIGCONT))!=0){
		printf("Error in SIGCONT, %d\n",i); 
		infantcide();
		exit(1);
	}

	/* Sleep for a bit to show new child output on screen */
	sleep(2);

	/* Send SIGUSR1 which will cause child to reset the global          */
	/* variable child_continue and thus break out of its infinite loop */
	printf("Parent %d sending SIGUSR1 to Child %d\n",
			getpid(),pids[0]);
        if((i=kill(pids[0],SIGUSR1))!=0){
		printf("Error in SIGUSR1, %d\n",i); 
		infantcide();
		exit(1);
	}

	/* Wait for SIGUSR2 which will reset the global variable  */
	/* parent_continue and thus break out of its infinite loop */
	while(!parent_continue) { 
		sleep(1);
		printf("Parent PID %d: in a loop\n",getpid());
	}
	printf("Parent %d saw parent_continue change.\n",
			getpid());

	/* Wait of child process id pids[0] to ensure that this code */
	/* does not continue till child has exited                   */
	while(!waitpid(pids[0],&i,WNOHANG)) 
		sleep(1);

	/* Now that child will have exited, write to non-existent child */
	/* to generate SIGPIPE */
	printf("Parent %d writing to non-existent child via pipes.\n",
			getpid());
	write(fd[0].parent[W],(char *)"0\0",2);

	sleep(1);

	return;
}

void do_child(void)
{
	int i;

	/* Wait for SIGUSR1 which will reset the global variable  */
	/* child_continue and thus break out of its infinite loop */
	while(!child_continue) { 
		sleep(1);
		printf("Child  PID %d: in a loop\n",getpid());
	}
	printf("Child  PID %d: Saw child_continue change\n",getpid());
	printf("Child  PID %d: Out of loop\n",getpid());

	/* sleep for a bit to make output less streaming */
	sleep(2);

	printf("Child  PID %d: Awoke from sleep\n",getpid());

	/* Divide by zero to raise the SIGFPE handler, which in turn */
	/* sends SIGUSR2 to parent to break it out of its loop       */
	printf("Child  PID %d: Dividing by zero\n",getpid());
	i=1/0;

	return;
}

void initialize_handler(int sig, void (*func)() )
{
	if(!signal(sig,func))
	  printf("Setting handler routine for %d\n",sig);
	else  {
	  printf("Cannot set the handler routine for signal %d\n",sig);
	  exit(1);
	}
}
	
void user1_handler(void)
{
	printf("SIGUSR1 received by PID %d, resetting child_continue.\n",
		getpid());
	child_continue=(child_continue==0)?1:0;
	return;
}

void user2_handler(void)
{
	printf("SIGUSR2 received by PID %d, resetting parent_continue.\n",
		getpid());
	parent_continue=(parent_continue==0)?1:0;
	return;
}

void no_pipe(void)
{
	printf("SIGPIPE received by PID %d: Closing all pipes.\n",
		getpid());
        pipe_close();	
	return;
}

void floating(void)
{
       int i,j; 
       if((i=getppid())<=0) {
         printf("SIGFPE received by root PID %d, sending children SIGTERM\n",
			getpid());
         infantcide();
       }
       else {
         printf("SIGFPE received by child PID %d, sending parent SIGUSR2\n",
			getpid());
         if((j=kill(i,SIGUSR2))!=0)
             printf("Error: FPE handler could not send SIGUSR2, %d\n",j);
       }

       exit(1);
}

void  interrupt(void)
{
	printf("Process %d received SIGINT\n",getpid()); 
	return;
}

void  infantcide(void)
/* If you are a leaf then just exit upon SIGTERM, else pass SIGTERM */
/* onto your kids and then return */
{
    int		i;

    switch (level) {
	case DEPTH:
	 printf("Leaf : pid %d , level %d : Received SIGTERM, exiting\n",
		getpid(),level); 
		exit(0);
	default:
	 for (i=0;i DEPTH %d\n",level,DEPTH);
		exit(1);
	}
}


/* itoa: convert n to characters is s. K&R p.64 */
void itoa(int n, char s[])
{
	int i, sign;

	if((sign = n ) < 0)	/* record sign */
		n=-n;		/* make n >0   */
	i=0;
	do {		/*generate digits in reverse order */
		s[i++] = n % 10 + '0';	/* get next digit */
	} while ((n /= 10) > 0);		/* delete it */
	if (sign < 0 )
		s[i++] = '-';
	s[i] = '\0';
	reverse(s);
}

/* reverse: reverse string s in place. K&R p.62 */
void reverse(char s[])
{
	int c,i,j;
	for(i=0,j=strlen(s)-1; i < j; i++,j--) {
		c=s[i];
		s[i]=s[j];
		s[j]=c;
	}
}