Question

I wrote a program to use ls command in Linux terminal to read the content of the folder and write the text from ls to the screen with my C program. Here's the code I wrote:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int pipe_ends[2];
    if(pipe(pipe_ends)) 
    {
        printf("Could not create pipe\n");
        return -1;
    }
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(1);
    }
    if(!pid)
    {
        dup2(pipe_ends[0],1);
        char* args[3];
        args[0] = "/bin/ls";
        args[1] = "-l";
        args[2] = NULL;
        execv("/bin/ls",args);
        printf("something went wrong\n");
    }

    char buff[10240];
    int count = read(pipe_ends[1],buff,10240);
    buff[count] = '\0';
    printf("here goes nothing......\n");
    printf("%s",buff);
    return 0;   
}

The output I get for this program is:

here goes nothing......
od@od-Inspiron-N5110:~/Documents/work/new/CO/project1$ /bin/ls: write error: Bad file descriptor

od@od-Inspiron-N5110:~/Documents/work/new/CO/project1$

It seems that reading has been done before writing. But I thought read is blocking. Please help me to find the error here.

Thanks in advance.

Was it helpful?

Solution 2

You had three problems:

  1. From the manpage: The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. You were using pipefd[0] to write and pipefd[1] to read. That won't work. This was the root cause of the EBADF (bad file descriptor) error. If you want bidirectionality, use socketpair()

  2. When you fork(), you need to close the file descriptors you don't need. That's the other end of the pipe.

  3. I believe you should close() an fd before you dup2 over it, though the man page is not explicit. In this case close() the existing STDOUT before you use dup2().

Here is an amended version that works (minimal amendments made to get it working annotated with comments, there is more you could do to make it perfect):

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int
main ()
{
  int pipe_ends[2];
  if (pipe (pipe_ends))
    {
      printf ("Could not create pipe\n");
      return -1;
    }
  pid_t pid = fork ();
  if (pid < 0)
    {
      perror ("fork");
      exit (1);
    }
  if (!pid)
    {
      close (1); /* close previous stdout */
      /* dup write end of pipe */
      dup2 (pipe_ends[1], 1);
      /* close read end */
      close (pipe_ends[0]);
      char *args[3];
      args[0] = "/bin/ls";
      args[1] = "-l";
      args[2] = NULL;
      execv ("/bin/ls", args);
      printf ("something went wrong\n");
    }

  close (pipe_ends[1]); /* close write end of pipe */

  char buff[10240];
  int count = read (pipe_ends[0], buff, 10240);
  buff[count] = '\0';
  printf ("here goes nothing......\n");
  printf ("%s", buff);
  return 0;
}

And the proof of the pudding:

amb@nimrod-ubuntu:~/so$ ./p
here goes nothing......
total 32
-rwxrwxr-x 1 amb amb 8889 Jan 19 09:19 p
-rw-rw-r-- 1 amb amb  853 Jan 19 09:19 p.c
-rwxrwxr-x 1 amb amb 8456 Jan 18 20:47 test
-rw-rw-r-- 1 amb amb  243 Jan 18 20:47 test.c

OTHER TIPS

You have to close the unused ends of your pipes — especially the write end of the pipe in the parent process since the read() in the parent will not get EOF while the parent has the write end of the pipe still open. Also, you need to read from the read end of the pipe (pipe_ends[0]), and duplicate the write end of the pipe to the standard output of ls.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    int pipe_ends[2];
    if (pipe(pipe_ends))
    {
        printf("Could not create pipe\n");
        return -1;
    }
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(1);
    }
    if (pid == 0)
    {
        dup2(pipe_ends[1], 1);
        close(pipe_ends[0]);
        close(pipe_ends[1]);
        char *args[3];
        args[0] = "/bin/ls";
        args[1] = "-l";
        args[2] = NULL;
        execv("/bin/ls", args);
        printf("something went wrong\n");
        return 1;
    }

    close(pipe_ends[1]);
    char buff[10240];
    int count = read(pipe_ends[0], buff, 10240);
    buff[count] = '\0';
    printf("here goes nothing (count = %d)......\n", count);
    printf("%s", buff);
    return 0;
}

The code should be more consistent about error reporting (sometimes it uses printf() to write to standard output; sometimes, it uses perror() to write to standard error), and exiting (sometimes using return -1;, sometimes using exit(1); — and I added a return 1; to ensure that the child process doesn't end up reading stuff if it fails to execute ls).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top