Hi! I am trying to create my own shell in CPP/C. I am having some trouble with the children produced when pipelines are used. First of all, I am using process groups to achieve this, because I want the ^Z and ^C signals to work for the child processes that are created using fork and actually do what they are supposed to do.
Let's suppose I have the command
PHP Code:
ls -al | grep something
. Since they are pipelined there should be two processes that are being forked and that run parallel. I am also using process groups to achieve this goal. My approach has been: On a function for the job, inside a for loop, run for any intermediate process the code as should be expected: a fork(), the appropriate
PHP Code:
setpgid(), tcsetpgrp()
calls for the child as well as any signal handling. If the function has reached the final process (e.g. grep something from the above example) run everything normally as above, but also add the waiting from the father.
The father waits for every process in a process group to end using the following code:
PHP Code:
while(waitpid(-job->pgid, &status, WUNTRACED) != -1) {
if (WSTOPSIG(status)) {
break;
}
};
If it detects a child was stopped, it breaks so it does not wait anymore for it. I am also checking for any zombie processes at the beginning of the main loop as shown below:
PHP Code:
std::string userInput;
while (1) {
int pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0) {
// Caught zombie process
}
myshell_promt(); // Just a print
std::getline(std::cin, userInput);
if (userInput == "") { continue; }
if (userInput == "exit") { break; }
// Parse and execute command
printf("Next command!\n");
}
Although it works (or so it seems I guess), there are times the result is like this:
PHP Code:
Next command!
-rw-r--r-- 1 something something 536 Apr 19 18:25 something_else.cpp
mysc:> -rw-r--r-- 1 something something 414 Apr 20 10:37 something.cpp
or even:
PHP Code:
Next command!
mysc:>
-rw-r--r-- 1 something something 536 Apr 19 18:25 something_else.cpp
-rw-r--r-- 1 something something 414 Apr 20 10:37 something.cpp
and then I wait for the next command from the user. However, this is not what I want. I want to see the prompt after the end result. I can't seem to figure out what exactly goes wrong.
The following shows the execution for the processes, where forks are made:
PHP Code:
pid_t childpid;
int status = 0;
childpid = fork();
if (childpid < 0) {
perror("Creating child process");
return -1;
} else if (childpid == 0) {
// Update the process class to hold the correct pid
proc->set_pid(getpid());
// Will only get executed once per job
// to set the job's group id to that of the first process' ID
if (job->get_pgid() == -1 ) {
// Initialize pgid, only for the first process
job->set_pgid(proc->get_pid());
}
// Attach the process to the process group
setpgid(0, job->get_pgid());
signal(SIGINT, SIG_DFL);
signal(SIGSTOP, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
// Play with pipes ...
if (execvp(proc->argv[0], proc->argv) < 0) {
printf("mysh: %s: command not found\n", proc->argv[0]);
exit(0);
}
printf("Assert it does not come here!\n");
exit(0);
} else {
// Same exact code as the child, in order to assure it doesn't matter which gets here first
// the correct things will happen, and avoid races
proc->set_pid(childpid);
if (job->get_pgid() == -1 ) {
job->set_pgid(proc->get_pid());
}
setpgid(childpid, job->get_pgid());
// Only the last process will get called with the mode set as FOREGROUND_EXECUTION
// So it can wait for all previous ones to end
if (mode == FOREGROUND_EXECUTION) {
// If foreground, set it to have control of the terminal
// close pipes ...
// give control of the terminal to the process group
tcsetpgrp(0, job->get_pgid());
// now wait for the job
status = shell->wait_for_job(job->id);
signal(SIGTTOU, SIG_IGN);
// Get back control of the terminal
tcsetpgrp(0, getpid());
signal(SIGTTOU, SIG_DFL);
}
}
return status;
I thought about using a signal handler for SIGCHLD, but I don't know how to do that exactly, or if that would even work. Any thoughts? Thanks a lot!