Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

waitpid blocking when it shouldn't

I am writing a mini-shell(no, not for school :P; for my own enjoyment) and most of the basic functionality is now done but I am stuck when trying to handle SIGTSTP.

Supposedly, when a user presses Ctrl+Z, SIGTSTP should be sent to the Foreground process of the shell if it exists, and Shell should continue normally.

After creating each process(if it's a Foreground process), the following code waits:

if(waitpid(pid, &processReturnStatus, WUNTRACED)>0){//wait stopped too
    if(WIFEXITED(processReturnStatus) || WIFSIGNALED(processReturnStatus))
        removeFromJobList(pid);
}

And I am handling the signal as follows:

void sigtstpHandler(int signum)
{
    signum++;//Just to remove gcc's warning
    pid_t pid = findForegroundProcessID();
    if(pid > -1){
        kill(-pid, SIGTSTP);//Sending to the whole group
    }
}

What happens is that when I press Ctrl+Z, the child process does get suspended indeed(using ps -all to view the state of the processes) but my shell hangs at waitpid it never returns even though I passed WUNTRACED flag which as far as I understood is supposed to make waitpid return when the process is stopped too.
So what could I have possible done wrong? or did I understand waitpid's behavior incorrectly?

Notes:
-findForegroundProcessID() returns the right pid; I double checked that.
-I am changing each process's group when right after I fork
-Handling Ctrl+C is working just fine
-If I use another terminal to send SIGCONT after my shell hangs, the child process resumes its work and the shell reaps it eventually.
-I am catching SIGTSTP which as far as I read(and tested) can be caught. -I tried using waitid instead of waitpid just in case, problem persisted. EDIT:

void sigchldHandler(int signum)
{
    signum++;//Just to remove the warning
    pid_t pid;
    while((pid = waitpid(-1, &processReturnStatus, 0)) > 0){    
        removeFromJobList(pid);
    }
    if(errno != ECHILD)
        unixError("kill error");
}

My SIGCHLD handler.

like image 917
Fingolfin Avatar asked Dec 26 '12 00:12

Fingolfin


1 Answers

SIGCHLD is delivered for stopped children. The waitpid() call in the signal handler - which doesn't specify WUNTRACED - blocks forever.

You should probably not have the removeFromJobList() processing in two different places. If I had to guess, it sounds like it touches global data structures, and doesn't belong in a signal handler.

like image 154
fizzer Avatar answered Sep 25 '22 00:09

fizzer