execvp() inside a pipeline causing whole program to die - c

I'm currently writing a shell in C, and I'm having trouble with my pipeline.
I call the entire function process() line by line, with a parsed tree of commands as the input. process() works perfectly fine on simple commands and redirections so far, so I know that's not the problem.
In the below, my cmdList is an already-parsed I've structured my pipeline such that the child pipes its stdout into the stdin of the parent function. The child represents the left tree of cmdList, while the parent represents the right side. Here's what it looks like when I try to recursively call process():
else if (cmdList->type==PIPE)
{
int pipefd[2];
pid_t fork_result;
pipe(pipefd);
fork_result=fork();
if (fork_result < 0) {
fprintf(stderr, "Fork failure2\n");
exit(EXIT_FAILURE);
}
if (fork_result==0) {
int jo3 = close(pipefd[0]);
int jo1 = dup2(pipefd[1], STDOUT_FILENO);
process(cmdList->left);
exit(EXIT_failure);
}
else
{
close(pipefd[1]);
int status;
wait(&status);
dup2(pipefd[0], STDIN_FILENO);
process(cmdList->right); //dies here
fprintf(stderr,"FAIL\n");
exit(EXIT_FAILURE);
}
}
This causes my entire program to die, including the main function that calls process(). If I replace my recurisve process() calls with
execvp(cmdList->left->argv[0],cmdList->left->argv);
execvp(cmdList->right->argv[0],cmdList->right->argv);
then my program runs forever.
Things I've already tried:
All dup2's and wait's return as expected, without error.
Using a debugger, the program always gets to the execvp's with everything doing fine.
As stated, the rest of process() works perfectly on everything not having to do with the pipeline.
I'm not really sure what's going wrong with this. Is there anything that stands out?

Related

Why do my pipes not talk to each other?

I am trying to write a simple shell in c. Right now I'm trying to get pipes to work. I have a struct c which I'm feeding into this function, it contains a place to store the pipe file descriptor pipfd and also contains information about the ending tag of each command c->type (this can be | || && & etc). CommandPrev is just tracking the last command so I can see if the command immediately before had a pipe tag.
After I finish this function, I give the child pid (the return value) to waitpid to wait on the command I called with execvp
When I run commands such as echo foo | echo bar I get bar as an output exactly as I would expect and everything works great. My problem is when I try to run any command that actually uses the input from the first half of the pipe, everything gets stuck. If I run something like echo foo | wc -c I get no output and it just hangs forever.
I can see that this function finishes for these sort of commands because I print when it returns. What's happening is that the command that I'm calling with execvp is never happening so my waitpid waits forever.
I think that somehow my connection between the two ends of my pipe is broken. Either things are never getting written, or they're never being read, or the receiving end of the pipe never realizes that the writing side is finished and is just waiting forever. I call close immediately on all my pipes so I tend to doubt its the last one... but I'm really not sure how to go about testing any of these three scenarios.
This is my code:
pid_t start_command(command* c, pid_t pgid) {
(void) pgid;
// If its a pipe token, create a shared pipe descriptor
if (c->type == TOKEN_PIPE){
pipe(c->pipefd);
}
// Fork a child process, run the command using `execvp`
pid_t child = fork();
if (child == 0) {
// writing side of the pipe
if (c->type == TOKEN_PIPE){
dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
close(c->pipefd);
}
// receiving side of the pipe
else if (commandPrev->type == TOKEN_PIPE){
dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
close(commandPrev->pipefd);
}
// run the command
if (execvp(c->argv[0], c->argv) == -1) {
// fork failed
exit(-1);
}
}
else{
// clean up, clean up, everybody, everywhere
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd);
}
}
printf("return %i\n", getpid() );
return child;
}
Thank you!
As the other commenter says, you look like you're trying to close an array.
Something like this should work better:
// writing side of the pipe
if (c->type == TOKEN_PIPE){
close(c->pipefd[READ_SIDE]);
dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
close(c->pipefd[WRITE_SIDE]);
}
// receiving side of the pipe
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd[WRITE_SIDE]);
dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
close(commandPrev->pipefd[READ_SIDE]);
}
Alternatively, you can close the active sides of the pipe after a waitpid call in the parent. Something like this:
waitpid(child, &status, 0);
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd[READ_SIDE]);
}
if (c->type == TOKEN_PIPE){
close(c->pipefd[WRITE_SIDE]);
}

How to chain multiple pipes? (plus weird buggs)

I'm trying to write a simple shell that can handle pipe commands. I want to be able to handle multiple pipes all chained together but I'm having a hard time figuring out how to implement something like this.
This is my current attempt:
int status;
int lastToken = 0;
int pipe_pid;
//create the pipes
int pipefd[pipes][2];
// Loop to run all commands in the vertical list.
while(1){
if (c->type == TOKEN_PIPE){
// Here is where we deal with pipes
for (int i = 0; i < pipes; i++){
pipe(pipefd[i]);
pipe_pid = fork();
//this is a receiving pipe
if (pipe_pid == 0){
// create the write end of the pipe
dup2(pipefd[i][WRITE_SIDE], STDOUT_FILENO);
close(pipefd[i][READ_SIDE]);
close(pipefd[i][WRITE_SIDE]);
execvp(c->argv[0], c->argv);
// printf("parent pipe\n");
}
//this is a writing pipe
else{
close(pipefd[i][WRITE_SIDE]);
dup2(pipefd[i][READ_SIDE], STDIN_FILENO);
close(pipefd[i][READ_SIDE]);
// printf("child pipe\n");
}
}
// This stuff happens for all commands
lastToken = c->type;
// If it's the last command, we're done
if (c->next == NULL){
break;
}
else{
c = c->next;
}
}
the commands are chained together in a linked list, c is my command pointer
pipes is a variable that I create as I parse the in-string, so I know how many '|' I saw in the command. This should tell me the number of child processes I need to fork.
I use pipes to create a 2d array for the pipe descriptors.
Then I want to loop over the pipes and fork once for each, and use dup2 to map the inputs and outputs.
I'm getting inconsistent errors that I can't figure out. First of all, every time I run a pipe command, my shell immediately crashes with no segfault or other printed errors.
Second, if I run commands like echo foo | wc -c I sometimes get 4 and sometimes get 0 as the output.
I'm sure I'm just doing something dumb but I'm not sure what :/
I figured out what I was doing wrong, I was closing the pipes before all the threads were finished using them. I fixed it by pulling out the close calls.
// writing side of the pipe
if (c->type == TOKEN_PIPE){
close(c->pipefd[READ_SIDE]);
dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
}
// receiving side of the pipe
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd[WRITE_SIDE]);
dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
}
And then in the parent thread, right before I reep my zombies, I check for pipes that are finished being used and close them.
// writing side of the pipe
if (c->type == TOKEN_PIPE){
close(c->pipefd[READ_SIDE]);
dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
}
// receiving side of the pipe
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd[WRITE_SIDE]);
dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
close(commandPrev->pipefd[READ_SIDE]);
I'm not sure if this is the optimal way to do it but it works without errors for me.

fork / pipe / close in a recursive function

In order to realize a shell command interpretor, I try to execute pipes.
To do it, I use a recursive function in wich I use the pipe function and some redirections with dup2.
Here is my code :
void test_recurs(pid_t pid, char **ae)
{
char *const arg[2] = {"/bin/ls", NULL};
char *const arg2[3] = {"/bin/wc", NULL};
static int limit = 0;
int check;
int fd[2];
if (limit > 5)
return ;
if (pipe(fd) == -1)
{
printf("pipe failed\n");
return ;
}
pid = fork();
if(pid != 0)
{
printf("père %d\n",getpid());
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
if ((execve("/bin/ls", arg, ae)) == -1)
exit(125);
dprintf(2, "execution ls\n");
wait(&check);
}
else
{
printf("fils %d\n", getpid());
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
if ((execve("/bin/wc", arg2, ae)) == -1)
printf("echec execve\n");;
dprintf(2, "limit[%d]\n", limit);
limit++;
test_recurs(pid, ae);
}
}
The problem is it only execute "ls | wc" one time and then wait on the standard input. I know that the problem may come from the pipes (and the redirections).
It's a bit unclear how you are trying to use the function you present, but here are some notable points about it:
It's poor form to rely on a static variable to limit recursion depth because it's not thread-safe and because you need to do extra work to manage it (for example, to ensure that any changes are backed out when the function returns). Use a function parameter instead.
As has been observed in comments, the exec-family functions return only on failure. Although you acknowledge that, I'm not sure you appreciate the consequences, for both branches of your fork contain code that will never be executed as a result. The recursive call in particular is dead and will never be executed.
Moreover, the process in which the function is called performs an execve() call itself. The reason that function does not return is that it replaces the process image with that of the new process. That means that function test_recurs() also does not return.
Just as shell ordinarily must fork / exec to launch a single external command, it ordinarily must fork / exec for each command in a pipeline. If it fails to do so then afterward it is no longer running -- whatever it exec'ed without forking runs instead.
The problem is it only execute "ls | wc" one time and then wait on the standard input.
Certainly it does not recurse, because the recursive call is in a section of dead code. I suspect you are mistaken in your claim that it afterward waits on standard input, because the process that calls that function execs /bin/ls, which does not read from standard input. When the ls exits, however, leaving you with neither shell nor ls, what you then see might seem to be a wait on stdin.

c program freeze when trying to create another child process

My original code is too long to post so let me try to write a simple version
//this commands() function will be called multiple times
int commands()
{
pid_t pid = fork();
if(pid<0)
{
printf("Failed to open child process\n");
return -1;
}
if(pid == 0)
{
//we are in child
child_function(); // calls some function to do some work
//note that this function end up calling execvp() so it never comes back
}
else
{
int status=0;
int_returned_pid = waitpid(-1, &status,0);
if(status !=0)
return 1;
else
return 0;
}
}
so what happens is, when the program calls commands first time, it successfully creates a child, child process finishes and command returns.
But when commands function is called second time, again child is successfully created and child_function() is called. after this, the program just stops and it feels like its waiting for input or someting (it does not crash).
After spending several hours here, I have no idea what is the problem, Please help.
Edit: I am using pipes in my program and basically when commands function is called the first time, I need its children to read someting from stdin, do some stuff and then write to pipe. And then upon calling commands function second time, i need its children to read from that pipe that the 1st call children wrote to and then to write to stdout.
Do I need to close these pipes in a parent process ?

Fork() new process and write to files for child and parent processes

I'm new to fork(), parent and child processes and have some difficulty understanding the logic behind the code that I wrote, but did not perform what I expected. Here is what I have:
int main (int argc, char** argv)
{
FILE *fp_parent;
FILE *fp_child;
fp_parent = fopen ("parent.out","w");
fp_child = fopen ("child.out","w");
int test_pid;
printf ("GET HERE\n");
fprintf (fp_parent,"Begin\n"); // MY CONCERN
for (int i = 0; i < 1; i++) //for simplicity, just fork 1 process.
{ // but i want to fork more processes later
test_pid = fork();
if(test_pid < 0)
{
printf ("ERROR fork\n");
exit (0);
}
else if(test_pid == 0) // CHILD
{
fprintf(fp_child,"child\n");
break;
}
else //PARENT
{
fprintf(fp_parent,"parent\n");
}
}
fclose(fp_parent);
fclose(fp_child);
}
So the output of above code is:
to stdout: GET HERE
in parent.out:
Begin
parent
Begin
in child.out:
child
My main concern is that I don't quite understand why "Begin" get written to parent.out twice. If I remove the for loop completely, then only one "Begin" is written which is expected.
So I think that it's because of the fork() and definitely I miss or don't understand some logic behind it. Could you guys please help me explain?
My plan to be able to write something before the for loop in parent.out and write something during the for loop in parent.out. Child process will write to child.out.
In C, the input/output operations using FILE structure are buffered at the level of user process. In your case, the output you have written to fp_parent was not actually written onto the disk and was kept in a local buffer at the moment of fork. The fork creates a copy of the whole process including the buffer containing Begin and this is why it appears twice in your file. Try to put fflush(fp_parent); before the fork. This will flush the buffer and the dirty line will disappear from the file.

Resources