1

I am trying to implement the behaviour of Ctrl+Z in a mini C Shell. My code currently receives the SIGTSTP signal and correctly suspends the process, however I can't seem to get it to move to the background and put the shell process back in the foreground.

I have tried my best to change the process group of the child that is suspended so that my waitpid() loop can continue (because waitpid(-1, ...) should only find children with the same process group as the parent, right?), however it always gets stuck waiting for the suspended process to end.

The while loop gets to the point where it prints Continuing and then the waitpid() never ends, unless I kill the child process externally.

Ctrl+C works fine.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <string.h>

int main()
{
        char inbuf[256];
        pid_t pid;
        struct sigaction act;

        for ( ; ; )
        {
                tcsetpgrp(fileno(stdin), getpgrp());
                act.sa_handler = SIG_IGN;
                assert(sigaction(SIGINT, &act, NULL) == 0);
                assert(sigaction(SIGTSTP, &act, NULL) == 0);
                printf("> ");
                gets(inbuf);

                pid = fork();

                switch (pid)
                {
                        case -1:// error
                                perror("fork");
                                exit(EXIT_FAILURE);
                                break;
                        case 0: // child
                                setpgrp();
                                tcsetpgrp(fileno(stdin), getpgid(pid));
                                act.sa_handler = SIG_DFL;
                                assert(sigaction(SIGINT, &act, NULL) == 0);
                                assert(sigaction(SIGTSTP, &act, NULL) == 0);
                                execlp( inbuf, inbuf, (char *)0 );
                                printf("execlp failed\n");
                                exit(EXIT_FAILURE);
                                break;
                        default:// parent
                                setpgid(pid, pid);
                                int status = 0;
                                signal(SIGTTOU, SIG_IGN);
                                signal(SIGTSTP, SIG_IGN);
                                tcsetpgrp(fileno(stdin), getpgid(pid));
                                int parent_group = getpgid(pid);
                                while (1) {
                                        pid = waitpid(-1, &status, WUNTRACED);
                                        printf("PID: %d\n", pid);
                                        if (pid < 1) {
                                                printf("Breaking\n");
                                                break;
                                        }
                                        if (WIFSTOPPED(status)) {
                                                printf("STOPPED: %d!\n", pid);
                                                // I feel like I should be sending the process, pid, to the background here
                                                //tcsetpgrp(fileno(stdin), parent_group);
                                                //setpgid(pid, pid);
                                        }
                                        printf("Continuing\n");
                                }
                }

                memset(inbuf, '\0', sizeof(inbuf));
        }

        return 0;
}
Goulash
  • 3,708
  • 6
  • 29
  • 48
  • 1
    Probably unrelated to your problem, but: Don't put code that actually does something inside assert(), or you're in for a nasty surprise when someone compiles with NDEBUG defined! – Ture Pålsson Nov 13 '18 at 13:20

0 Answers0