5

I fork and set up a command like this:

pid_t pid;
pid = fork();
if (pid == 0)
{   // I am the child
    freopen("/input_pipe","r",stdin);
    freopen("/output_pip","w",stdout);
    execl("/bin/sh", "sh", "-c", command, (char *)NULL); // using execv is probably faster
    // Should never get here
    perror("execl");
    exit(1);
}

The /input_pipe has been created and filled with data before the processs is forked.

This works in almost all cases absolutely fine. The command reads (with read()) from its stdin and gets the data that was written to the pipe by another process.

But sometimes the command cannot read from its stdin stream and gets the "Bad file descriptor" error when trying to do so.

What could cause this error?

Edit: I have changed the freopen parts to this:

    pipe_in = open(pipename_in, O_RDONLY);
    pipe_out = open(pipename_out, O_WRONLY);

    dup2(pipe_in, 0);
    dup2(pipe_out, 1);

I will test this for a couple of days though as the error was only appearing very seldom.

Robby75
  • 3,285
  • 6
  • 33
  • 52
  • Does your "real" code check `freopen()` for error by testing the function return value against `NULL`? – alk Jan 22 '14 at 17:13

1 Answers1

5

One possible source of issues is that "stdin" an "stdout" don't correspond necessarily with file descriptors 0 and 1 respectively. In many implementations of the runtime library freopen may change the file descriptor belonging to the FILE*. File descriptors belong to the kernel, while FILEs belong to the runtime library, and they are not necessarily aligned.

Please have a look here:

http://man7.org/linux/man-pages/man3/stdout.3.html

"Applying freopen(3) to one of these streams can change the file descriptor number associated with the stream"

After execl, child process only expects that file descriptor 0 and 1 are standard input and output, but it may happen that freopen closed fd 0 and connected onother fd to (FILE*)stdin. I recommend you to use the "open", "close", "dup" etc... system call and deal with file descriptors 0 an 1 directly (or, better, the standard macros STDIN_FILENO and STDOUT_FILENO).

Giuseppe Guerrini
  • 4,274
  • 17
  • 32
  • Hmm, how do you translate freopen into open/close/dup calls? – Robby75 Jan 23 '14 at 07:38
  • Just don't translate. You don't need to modify the strandard streams (i.e. FILE *) stdin/stdout. File descriptors matter. This article explains how to do it: http://stackoverflow.com/questions/9084099/re-opening-stdout-and-stdin-file-descriptors-after-closing-them – Giuseppe Guerrini Jan 23 '14 at 07:42
  • So instead of freopen("/input_pipe","r",stdin); I use in_fd = open("/input_pipe", "r"); and dup2(in_fd, 0); correct? – Robby75 Jan 23 '14 at 07:47
  • Yes, more precisely: in=open("(input_pipe", O_RDONLY); dup2(in,0); close(in); (also add error checking etc...) – Giuseppe Guerrini Jan 23 '14 at 07:54
  • Here another article: http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/redirecting-standard-io.html (see the "Redirecting POSIX file descriptors" paragraph) – Giuseppe Guerrini Jan 23 '14 at 07:56
  • I am currently testing it without the "close(in);" part - it seems to work, why is close(in) needed? – Robby75 Jan 23 '14 at 07:58
  • "close" is suggested to (a) save (i.e. "economize") file descriptors and (b) be sure that if later the program executes a "close(0)" (or "close(1)") the underlying resource is really closed. For example, if (as in your case) the resource is a pipe, and your process wants to send a EOF to the process listening to your stdout, it can execute "close(1)", but this works only if "1" is the last fd owning the resource. – Giuseppe Guerrini 48 mins ago – Giuseppe Guerrini Jan 23 '14 at 09:27