Why does dup() and popen() prevent my process from exiting when called from another process?

373 views Asked by At
  • I've a program which popen() to another and also dup() stdout
  • When called from another process (like the PHP example) or via SSH, the process does not exit.

process_test.c:

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

int main() {
  int out;

  out = dup(STDOUT_FILENO);
  // close(out);

  popen("sleep 10\0", "r");
}

Compile with gcc process_test.c, run with:

  • ./a.out -> exits normally
  • ruby -e 'system("./a.out");' -> exits normally
  • php -r passthry("./a.out"); -> hangs
  • ssh remotehost ./a.out -> hangs
  • when I don't dup stdout or close the dup, it doesn't hang

This is the shortest reproducible code I could find which shows me a behavior I'd like to better understand.

It took hours to extract this from multiple PHP applications/frameworks using fork/pcntl/etc. to instrument their relations, i.e. I didn't wrote this or made this up; but obviously the whole sense of it got lost due me stripping everything apart.

Questions

  • Why do some invocations hang (php, ssh) and other not (ruby) ?
  • Even when I close the fd after the popen, my program hangs; why?
1

There are 1 answers

5
gaetanoM On

Because when calling the program via ssh a dup on stdoud is made by ssh, so when ssh try to end it cannot because the stdout has also another channel opened.

I try to give you a better answer: stdout is the channel number 1 if i remember correctly. Making the dup we have also the new channel 3 that is pointing to stdout. When ssh try to close the channel 2 togheter to channel 0 and 2 (stdinput and stderr) ssh cannot close itself because there is another resource that occupies the channel 3 or better the stdout.

I hope that's enough clear gaetano

If I run your demo as it is and I get errno = 15 or it could be 16 as described at the end of this answer. But, if I run it in different way:

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

int main() {
    int out;
    int status;

    out = dup(STDOUT_FILENO);
    // close(out);
    FILE *fp = popen("sleep 10\0", "r");
    status = pclose(fp);
    if (status == -1) {
            printf("pclose error: %d", errno);
    }
   close(out);
}

I don't get any more the error. To check the error under bash I use: echo $?

That means you have to realease all allocated resources before to exit the c program.

I hope that's all.

Best reagards gaetano

    #define ENOTBLK     15  /* Block device required */
    #define EBUSY       16  /* Device or resource busy */