I'm emulating bash commands in C language using Exec family of functions.
This is just a partial code of bigger project. Most of the things are hard-coded for now for simplicity, but in future I need to execute any bash command with Pipe or Redirection operator so that's the reason the code is in loop. I know I can use single file descriptor(with out 2d array) to achieve the result for current example but I need to generalise the code for multiple |, > commands. I can't remove the loop.
I'm getting Bad descriptor error even after closing all the descriptors correctly. Unable to find the reason.
int main()
{
int numOfCmds = 2;
int numOfPipes = numOfCmds -1;
int pipes[numOfPipes][2];
for(int i=0;i<numOfPipes;i )
if(pipe(pipes[i])<0) return 1;
for(int i=0;i<numOfCmds;i )
{
int child = fork();
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i )
{
close(pipes[i][0]);
}
printf("hey\n");
dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i )
{
close(pipes[i][1]);
}
printf("bye\n");
dup2(pipes[i][0], STDIN_FILENO);
close(pipes[i][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
}
return 0;
}
}
for(int i=0;i<numOfCmds;i )
{
for(int j=0;i<2;j )
close(pipes[i][j]);
}
for(int i=0;i<numOfCmds;i ) wait(NULL);
return 0;
Error:
hey bye wc: 'standard input': Bad file descriptor 0 wc: -: Bad file descriptor Segmentation fault (core dumped)
CodePudding user response:
Your problem is that you are reusing (nested) definitions of i
. Use the -Wshadow
option to GCC to avoid doing that. And the value of i
is sometimes beyond the end of the array of pairs of file descriptors, which leads to chaos (undefined behaviour).
You have:
for(int i=0;i<numOfCmds;i )
{
int child = fork();
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i )
{
close(pipes[i][0]);
}
printf("hey\n");
dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i )
{
close(pipes[i][1]);
}
printf("bye\n");
dup2(pipes[i][0], STDIN_FILENO);
close(pipes[i][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
}
return 0;
}
}
Your (outer) loop control variable is i
; you then have an inner loop controlled by a different i
that closes some pipes. And the GCC I use reports that you are accessing pipes[i][0]
out of bounds in the dup2()
call for wc
.
You need to rework your use of i
(use some different names — j
and k
(for 'kids'?) are usable). And re-review which file pipe descriptors you are closing carefully.
Your parental loop closing file descriptors is:
for(int i=0;i<numOfCmds;i )
{
for(int j=0;i<2;j )
close(pipes[i][j]);
}
You need to use the numOfPipes
instead of numOfCmds
, and you need your inner loop to test j
, not i
.
Here's some heavily instrumented code that works. But the fixes are not very general:
/* SO 7165-1018 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int numOfCmds = 2;
int numOfPipes = numOfCmds - 1;
int pipes[numOfPipes][2];
for(int i=0;i<numOfPipes;i )
if(pipe(pipes[i])<0) return 1;
for (int i = 0; i < numOfPipes; i )
printf("%d: pipe[%d][0] = %d; pipe[%d][1] = %d\n",
getpid(), i, pipes[i][0], i, pipes[i][1]);
for(int i=0;i<numOfCmds;i )
{
int child = fork();
printf("%d: child = %d\n", getpid(), child);
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i )
{
printf("%d: ls: close %d\n", getpid(), pipes[i][0]);
close(pipes[i][0]);
}
printf("%d: hey\n", getpid());
dup2(pipes[i][1], STDOUT_FILENO);
printf("%d: ls: close %d\n", getpid(), pipes[i][1]);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
fprintf(stderr, "failed to execute %s\n", cmd1_args[0]);
exit(EXIT_FAILURE);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i )
{
printf("%d: wc: close %d\n", getpid(), pipes[i][1]);
close(pipes[i][1]);
}
printf("%d: bye\n", getpid());
dup2(pipes[0][0], STDIN_FILENO);
printf("%d: wc: close %d\n", getpid(), pipes[0][0]);
close(pipes[0][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
fprintf(stderr, "failed to execute %s\n", cmd2_args[0]);
exit(EXIT_FAILURE);
}
return 0;
}
}
for(int i=0;i<numOfPipes;i )
{
for(int j=0;j<2;j )
{
printf("parent (%d): close %d\n", getpid(), pipes[i][j]);
close(pipes[i][j]);
}
}
for(int i=0;i<numOfCmds;i )
{
int corpse;
int status;
if ((corpse = wait(&status)) > 0)
printf("%d: child %d exited with status 0x%.4X\n", getpid(), corpse, status);
}
return 0;
}
One sample run (of program sh83
, compiled from sh83.c
) yielded:
42654: pipe[0][0] = 3; pipe[0][1] = 4
42654: child = 42655
42655: child = 0
42655: ls: close 3
42654: child = 42656
parent (42654): close 3
parent (42654): close 4
42655: hey
42656: child = 0
42656: wc: close 4
42656: bye
42656: wc: close 3
128
42654: child 42655 exited with status 0x0000
42654: child 42656 exited with status 0x0000
If you're working with multiple processes, I find it is a great help to prefix most messages with a PID — as shown in the output.