vendredi 1 mai 2020

Linux SIGSTOP causes waitpid to continue

I wrote a shell program for linux, and created the following code for pipe command:

void PipeCommand::execute() {
jobs_list->removeFinishedJobs();
if(signal(SIGTSTP , ctrlZHandlerPipe)==SIG_ERR) {
    perror("smash error: failed to set ctrl-Z handler");
    return;
}
if(signal(SIGINT , ctrlCHandlerPipe)==SIG_ERR) {
    perror("smash error: failed to set ctrl-C handler");
    return;
}
bool is_bg = false;
string new_command = command;
if (_isBackgroundComamnd(command.c_str())) {
    //Ends with &
    is_bg = true;

    char temp[COMMAND_ARGS_MAX_LENGTH];
    strcpy(temp, command.c_str());
    _removeBackgroundSign(temp);
    new_command = string(temp);
}

//Produce first and second command
int pipeIndex = new_command.find('|');
bool isStderrPipe = new_command[pipeIndex + 1] == '&';
string cmd1 = new_command.substr(0, pipeIndex);
string cmd2 = new_command.substr(pipeIndex + 1 + (int) isStderrPipe);

SmallShell &temp_smash = SmallShell::getInstance();
auto command1 = temp_smash.CreateCommand(cmd1.c_str());
auto command2 = temp_smash.CreateCommand(cmd2.c_str());

pid_t pid = fork();
if (pid < 0) {
    perror("smash error: fork failed");
    return;
} else if (pid == 0) {
    //Child
    setpgid(0,0);
    //pid_t gid = getpgid(0);
    //Create pipe
    int fd[2];
    if (pipe(fd) == -1) {
        perror("smash error: pipe failed");
        return;
    }

    pid_t pid_cmd1 = fork();
    if (pid_cmd1 < 0) {
        perror("smash error: fork failed");
        return;
    } else if (pid_cmd1 == 0) {

        //Child command 1
        if (isStderrPipe) {
            if (dup2(fd[1], STDERR_FILENO) == -1) {
                perror("smash error: dup2 failed");
                return;
            }
        } else {
            if (dup2(fd[1], STDOUT_FILENO) == -1) {
                perror("smash error: dup2 failed");
                return;
            }
        }
        close(fd[0]);
        close(fd[1]);
        command1->execute();
        exit(0);
    } else {

        //Parent command 1

        pid_t pid_cmd2 = fork();

        if (pid_cmd2 < 0) {
            perror("smash error: fork failed");
            return;
        } else if (pid_cmd2 == 0) {
            if (dup2(fd[0], STDIN_FILENO) == -1) {
                perror("smash error: dup2 failed");
                return;
            }
            close(fd[1]);
            close(fd[0]);
            command2->execute();
            exit(0);
        }
        else{
            close(fd[0]);
            close(fd[1]);

            int res1 = waitpid(pid_cmd1, nullptr, WUNTRACED);
            int res2= waitpid(pid_cmd2, nullptr, WUNTRACED);
            if (res1 == -1 || res2 == -1) {
                perror("smash error: waitpid failed");
                return;
            }
            exit(0);
        }
    }

} else {
    //Parent
    if (is_bg) {
        jobs_list->addJob(command, pid, false);
        return; //No need to wait...
    } else {
        jobs_list->setFg(command, pid, -1);
        int res = waitpid(pid, nullptr, WUNTRACED);
        if (res == -1) {
            perror("smash error: waitpid failed");
            return;
        }
        jobs_list->setFg("", -1, -1);
    }
    if(signal(SIGTSTP , ctrlZHandler)==SIG_ERR) {
        perror("smash error: failed to set ctrl-Z handler");
    }
    if(signal(SIGINT , ctrlCHandler)==SIG_ERR) {
        perror("smash error: failed to set ctrl-C handler");
    }
}

}

And the following handler for ctrZ which send SIGSTOP to the pipe and its inner commands:

void ctrlZHandlerPipe(int sig_num){
cout << "smash: got ctrl-Z" << endl;
SmallShell& smash = SmallShell::getInstance();
pid_t fg_pid = smash.getJobsList()->getFgPid();
if(fg_pid == smash.getSmashPid()){
    return;
}
if(fg_pid != -1){
    if(killpg(fg_pid, SIGSTOP) == -1){
        perror("smash error: kill failed");
    }
    else{
        if(!smash.getJobsList()->getBgToFg()){
            smash.getJobsList()->addJob(smash.getJobsList()->getFgCmd(),fg_pid, true);
        }
        else{
            smash.getJobsList()->getJobById(smash.getJobsList()->getFgId())->setJobStatus(false);
        }
        smash.getJobsList()->setFg("", -1, -1);
        smash.getJobsList()->setBgToFg(false);
        cout << "smash: process " << fg_pid << " was stopped" << endl;
    }
}
if(signal(SIGTSTP , ctrlZHandler)==SIG_ERR) {
    perror("smash error: failed to set ctrl-Z handler");
}

}

Now the problem is that while I run a pipe command, for example sleep 100|sleep 100, then pressing ctrlZ and then sending SIGCONT to the pipe again, it immediately continues and not waiting for its inner commands in the following lines:

int res1 = waitpid(pid_cmd1, nullptr, WUNTRACED);
int res2= waitpid(pid_cmd2, nullptr, WUNTRACED);

I cant figure out the problem might be..

Thank you

Aucun commentaire:

Enregistrer un commentaire