/* Demonstrate job control.                       WH 1994 DEC/1996 NOV
   Stopping and restarting a child process group.
   Giving terminal to child and getting it back. 
*/
#define SRCDIR "/home/??????" /* ## CHANGE ## to directory containing job.c */
#define TRACEFILE "/dev/console"             /* trace output file */

#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>

void prompt(void);
void change_tty_pg(pid_t pid, int ttyd);
pid_t wait_onchild(pid_t pid);

static FILE *ftrace;

int main()
   {
    pid_t child_pgid,main_pgid;
    int ttyd,pgrp;
    int c;

    fprintf(stderr,"Trace messages will go to " TRACEFILE "\n");
    if ((ftrace = fopen(TRACEFILE,"w")) == NULL)
       {fprintf(stderr,"Can not open " TRACEFILE "\n"); exit(1);
       }
    fprintf(ftrace,"\nNew run of jobcntl.\n");

    ttyd = open(ctermid(NULL) /* = "/dev/tty" */ ,O_RDWR,0700);
    main_pgid = getpid();
    pgrp = tcgetpgrp(ttyd);
    fprintf(ftrace,"Process group of terminal is %d.\n",pgrp);
    fflush(ftrace);

    if ((child_pgid = fork()) == -1) 
       {perror("jobcntl");
        exit(1);
       }
    else if (child_pgid == 0)     /* child */
       {close(ttyd);
        fclose(ftrace);

        setpgid(0,0);             /* Make child a process group leader */

        execlp(SRCDIR "job","job",NULL);
        perror("child of jobcntl");
        _exit(1); 
       }
    else                          /* parent */
       {fprintf(ftrace,"Child PGID is %d.\n",child_pgid);

        setpgid(child_pgid,0);    /* Make child a process group leader */
	                          /* Done in parent to defeat race */
        prompt();
        for (;;) switch (c = getchar())
           {case 'z':
                /* suspend process group */
                fprintf(ftrace,"SIGTSTP sent to %d\n",-child_pgid);
                kill(-child_pgid,SIGTSTP);  /* killpg(child_pgid,SIGTSTP); */
                break;
            case 'c':
                /* restart process group */
                fprintf(ftrace,"SIGCONT sent to %d\n",-child_pgid);
                kill(-child_pgid,SIGCONT);
                break;    
            case 'f':
                /* bring child pg to foreground */ 
                change_tty_pg(child_pgid,ttyd);  /* give tty to child pg */
                kill(-child_pgid,SIGCONT);       /* restart child pg */
                /* wait till child changes status */
                while (0 == wait_onchild(-child_pgid)) sleep(1);
                change_tty_pg(main_pgid,ttyd);   /* reclaim terminal */
                break;
            case EOF:
            case 'q':
                return 0;
            case '\n':
                prompt();
                break;
           }
       }
   }


void prompt(void)
   {
    printf("\nState: T=sTopped, S=Sleeping, R=Runable, O=running, Z=Zombie\n");
    system("ps -o pid,ppid,pgid,tty,s,comm");
    printf("jobcntl:\n"
      " z to suspend child pg\n"
      " c to continue/restart child pg\n"
      " f to foreground and restart child"
      " (Once in child, background child with ^Z)\n"
      " p to get ps listing and prompt\n"
      " q to quit\n Enter choice: ");
    fflush(stdout);
   }


void change_tty_pg(pid_t pid,int ttyd)
   {
    fprintf(ftrace,"tty PG was %d and ",tcgetpgrp(ttyd));
    signal(SIGTTOU,SIG_IGN);     
    if (-1 == tcsetpgrp(ttyd,pid)) perror("tcsetpgrp (child no longer exists?)");
    /* should reset SIGTTOU to previous disposition */
    fprintf(ftrace,"is now %d\n",tcgetpgrp(ttyd));
   }


pid_t wait_onchild(pid_t pid)
   {
    int status;
    pid_t retpid;

    retpid = waitpid(pid,&status, WNOHANG | WUNTRACED );
    fprintf(ftrace,"wait on %d returned with pid %d ",pid, retpid);
    if (retpid == 0) fprintf(ftrace,"- No child wishes to report.\n");
    else
       {fprintf(ftrace,"raw status: %04x (in hex)\n",status);
        if (WIFEXITED(status))
        fprintf(ftrace,"exit status: %02x\n",WEXITSTATUS(status));
        if (WIFSIGNALED(status))
        fprintf(ftrace,"signal status: %02x\n",WTERMSIG(status));
        if (WIFSTOPPED(status))
        fprintf(ftrace,"stopped status: %02x\n",WSTOPSIG(status));
       }
    return(retpid);
   }

