0
  • 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?
mark
  • 6,308
  • 8
  • 46
  • 57

1 Answers1

0

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 */
gaetanoM
  • 41,594
  • 6
  • 42
  • 61
  • I read what you wrote, but some of the wording is confusing: "... to close the channel 2 togheter to channel 0 and 2 ...", what's that supposed to mean? And a question: why is the callee (ssh) dependant on such inner details what the process is doing, I mean it looks so fragile? E.g. It doesn't matter when I close the duped channel *after* the popen () , ssh won't exit either. – mark Nov 28 '14 at 05:41
  • I've rephrased my questions and also added the twist that even closing the fd **after** `popen` will not exit in the circumstances outlined. – mark Nov 28 '14 at 08:19
  • The ssh process if you look at the source code is like rlogin or telnet: after opening and estabilishing the connection with the remote program start it and so the input channel of ssh in mapped on the input channel of remote program, so for channel 1 and 2. The ssh will close the connection when all the channel opened (ie: 0, 1, 2) are closed. So because you duplicated the 1 channel in order to do some other works ssh will end when that process ends and close the duplicated channel 1. It seems very complicated to describe, i 30 years ago used a lot such a behaviour .... – gaetanoM Nov 28 '14 at 12:01
  • I get that, but it doesn't explain why it does hang **even** when I **close** the channel, albeit *after* the `popen`. Do you see where I'm getting at? – mark Nov 28 '14 at 22:53
  • Hi mark see and check my answer and let me know bye – gaetanoM Dec 01 '14 at 17:11