How to prevent child from interfering with parent's stdin after fork()

3.4k views Asked by At

I have a program reading from stdin and whilst doing this it sometimes need to fork() and execute a command asynchronously. Sometimes shortly after this fork (probably whilst the command is running), the parent seems to lose data read from stdin. If only a single byte is lost, the program will never recover.

The man page for fork() says that the child inherits copies of the parent's set of open file descriptors, but trying close(STDIN_FILENO) just after fork() and before execvp() doesn't help.

My program basically does:

if (we_need_to_run_a_command_asynchronously) {
  if (!fork()) {
    close(STDIN_FILENO);
    char *args[] = { "command", "arg", "etc", 0 };
    setpriority(PRIO_PROCESS, 0, -19);
    execvp("command", args);
    exit(0);  // Ignore errors
  }
}

How can I be sure that the child cannot interfere with the parent's stdin (or anything else belonging to the parent)? The command executed is typically a shell script.

I don't know exactly what goes wrong in my program, but it runs fine as long as it doesn't run any commands. Only sometimes (not always) just after it runs a command, the data that it reads from stdin somehow gets corrupted, which is why I suspect that the problem is related to stdin after fork().

3

There are 3 answers

0
Steinar Midtskogen On

I think I've fixed the problem. It turned out that in a rarely taken path of my program which also executed the command I had forgot to do anything with stdin in the child after fork(). This code indeed works:

int pid = fork();
if (!pid) {
  int fd = open("/dev/null", O_RDWR);
  dup2(fd, 0);
  dup2(fd, 1);
  dup2(fd, 2);
  if (fd > 2)
    close(fd);

  execvp(...);

So, my mistake, but perhaps my question may still serve as an important reminder that it's usually necessary to do something with stdin/stdout/stderr when forking to execute a command which should run separately from the parent process. I think the above way is a clean way to do it.

1
missimer On

Instead of closing stdin you could open another file and use dup2 to duplicate the new file descriptor onto stdin. If you opened /dev/null and redirected it to stdin after the fork and before and exec the child will not interfere with the parent's stdin.

0
Jonathan Leffler On

I found an interesting note in the POSIX specification for execve() and the related functions:

If file descriptor 0, 1, or 2 would otherwise be closed after a successful call to one of the exec family of functions, implementations may open an unspecified file for the file descriptor in the new process image. If a standard utility or a conforming application is executed with file descriptor 0 not open for reading or with file descriptor 1 or 2 not open for writing, the environment in which the utility or application is executed shall be deemed non-conforming, and consequently the utility or application might not behave as described in this standard.

It is interesting to speculate whether your implementation is exercising the 'may' option. Opening /dev/null explicitly as standard input is safest.