Home > Blockchain >  wc: 'standard input': Bad file descriptor
wc: 'standard input': Bad file descriptor

Time:03-29

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.

  • Related