3
sub run_command
{
    my $COMMAND         = shift;
    my @OUTPUT;
    my %CMD             = {};

    $CMD{pid}           = open3(my $CH_IN, my $CH_OUT, my $CH_ERR, $COMMAND);
    $CMD{_STDIN}        = $CH_IN;
    $CMD{_STDOUT}       = $CH_OUT;
    $CMD{_STDERR}       = $CH_ERR;

    my $line            = readline $CMD{_STDOUT};
    print $line;

#    open my $CMDPROC, q{-|}, $COMMAND   or return;
#    foreach (<$CMDPROC>)
#    {
#        push @OUTPUT, "$ARG";
#    }
    close $CMDPROC                      or return;

    return @OUTPUT
}

The above code is part of a script I am writing which needs to run another script (called child). The child may or may not prompt for input, depending on the presence of a cookie file in /var/tmp (both scripts written on CentOS5 / perl 5.8.8)

I need to determine if and when the child is waiting for input, so that the parent can pass input from STDIN of parent. I also need to use open3 to open the child process, as I need for parent to pass the brutal (Severity 1) check of Perl::Critic.

I included the comments, because when the cookie file is already set, I can at least get parent to call child properly since child doesn't wait for input in that case.

I've checked around trying to find examples of how to determine if the child is waiting for input. The one example I found used strace (http://www.perlmonks.org/?node_id=964971) and I feel as though that might be too complex for what I am trying to do.

Any links to guide me will be greatly appreciated.

Speeddymon
  • 496
  • 2
  • 20
  • You can check if there's space in the pipe (using `select`), but I've never heard of the ability to check if a thread is blocked waiting to read from the pipe. – ikegami Jan 04 '17 at 05:22
  • Does the child process emit a prompt before reading from STDIN? – ikegami Jan 04 '17 at 05:23
  • Any reason you don't pass the input unconditionally? – ikegami Jan 04 '17 at 05:24
  • Other than there not being any reason to, no. There is a prompt – Speeddymon Jan 04 '17 at 05:29
  • Just provide the input without a check, then. Do use IPC::Run3 or IPC::Run instead of using IPC::Open3+IO::Select, though. The latter is waaay too complicated. (You're doing something wrong if you're not using `select` with `open3` here.) The other option is to use Expect to drive the child, but that's just needles work. – ikegami Jan 04 '17 at 05:32
  • I'll look into Run3 for sure, thanks! – Speeddymon Jan 04 '17 at 14:03

1 Answers1

3

You can check if there's space in the pipe (using select). You can even check how much space is available in the pipe. However, I've never heard of the ability to check if a thread is blocked waiting to read from the pipe. I think you should explore other avenues.


It seems to me that a program that only reads from STDIN when certain conditions unrelated to arguments are met would provide a prompt indicating it's waiting for input. If that's the case, one could use Expect to launch and control the child program.

But the simplest solution would be to write the data to STDIN unconditionally. Implementing this using IPC::Open3 is very complicated[1], so I recommend switching to IPC::Run3 (simpler) or IPC::Run (more flexible).

# Capture's child's STDERR
run3 [ $prog, @args ], \$text_for_stdin, \my $text_from_stdout, \my $text_from_stderr;

or

# Inherits parent's STDERR
run3 [ $prog, @args ], \$text_for_stdin, \my $text_from_stdout;

  1. When you both write to the child's STDIN and read from the child's STDOUT, you need to use select (or something else) to avoid deadlocks. IPC::Open3 is very low level and doesn't do this for you, whereas handling this are IPC::Run3 and IPC::Run raison d'être.
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Thanks! I don't know if expect will be allowed on these systems due to tight security requirements, but IPC::Run3 will almost definitely suffice in place of IPC::Open3. Some additional info and a followup question. The child is another perl script, and the only expected input for the child script is the password, as all other input is provided on the command line as options to GetOpt::Long.Will perl (as either the parent or the child) try to process the data as soon as its sent to STDIN, or will the pipe buffer until the child actually starts waiting for input? – Speeddymon Jan 04 '17 at 17:48
  • I imagine `run3` and `run` will pump data into the pipe as soon as they can. – ikegami Jan 04 '17 at 18:06
  • Yes but does the pipe buffer? That's the real question. If it buffers then I'll need to explicitly flush the buffer when the child asks for the password meaning I'm back to square one. If it doesn't buffer, then I may need to introduce an artificial delay of a few ms in the parent script so that the child has time to get to the password prompt. – Speeddymon Jan 04 '17 at 18:21
  • Pipes don't do anything. They're not code. I've answered the *real* question. My answer includes working code. – ikegami Jan 04 '17 at 20:21
  • What I am ultimately asking is this: If I send the input immediately after calling run3, but before the child script has prompted for the password, will that input still be taken in by the child and used as the password, or will the input simply be discarded? I'm testing the code right now, but had hoped to get an answer before I try. – Speeddymon Jan 04 '17 at 23:23
  • I am going to start up a new question. – Speeddymon Jan 05 '17 at 00:06
  • That makes no sense. The program finishes running before `run3` returns. – ikegami Jan 05 '17 at 04:27
  • Please see the new question: http://stackoverflow.com/q/41475421/1437387 – Speeddymon Jan 05 '17 at 04:30