Perl

NAVIGATION
CATEGORIES
REFERRENCE
LINKS
  • using open3 to interact with external program

    7 answers - 1039 bytes - related search similar search Add To My Delicious Add To My Stumble Upon Add To My Google Mark Add To My Facebook Add To My Digg Add To My Reddit

    Hi,
    I'm trying to use open3 to control input to an external program (in this
    case gpg). I would use Expect, but I need to use packages that are
    installed as standard with perl as this script will be running on many
    platforms. I simply want to enter interactive mode of gpg, pass it 3
    commands, then exit back to my script. The following code runs gpg in
    interactive mode, but none of the commands get passed to it and it just
    sits there till I exit manually, then it comes back to my script.
    use IPC::;
    local(*HIS_IN, *HISUT, *HIS_ERR);
    $childpid = open3(*HIS_IN, *HISUT, *HIS_ERR, 'gpg root');
    print HIS_IN "trust\n5\yes\n";
    close (HIS_IN);
    my @outlines = <HISUT>;
    my @errlines = <HIS_ERR>;
    close HISUT;
    close HIS_ERR;
    waitpid($childpid, 0);
    if ($?) {
    print "Child exited with status of $?\n";
    }
    If I remove the "close (HIS_IN)" statement
    Is it possible to do what I want with open3, and how?
    Thanks
    Darren
  • No.1 | | 2527 bytes | |

    Fri, 03 Jun 2005 08:35:32 +0100, darren (AT) birkett (DOT) com (D. J. Birkett)
    wrote:

    >I'm trying to use open3 to control input to an external program (in this
    >case gpg). I would use Expect, but I need to use packages that are
    >installed as standard with perl as this script will be running on many
    >platforms. I simply want to enter interactive mode of gpg, pass it 3
    >commands, then exit back to my script. The following code runs gpg in
    >interactive mode, but none of the commands get passed to it and it just
    >sits there till I exit manually, then it comes back to my script.


    >use IPC::;
    >
    >local(*HIS_IN, *HISUT, *HIS_ERR);
    >
    >$childpid = open3(*HIS_IN, *HISUT, *HIS_ERR, 'gpg root');
    >
    >print HIS_IN "trust\n5\yes\n";
    >close (HIS_IN);
    >my @outlines = <HISUT>;
    >my @errlines = <HIS_ERR>;
    >close HISUT;
    >close HIS_ERR;
    >waitpid($childpid, 0);
    >if ($?) {

    print "Child exited with status of $?\n";
    >}
    >
    >If I remove the "close (HIS_IN)" statement
    >
    >
    >Is it possible to do what I want with open3, and how?


    I'm not testing this locally, with gpg,
    but from what I know from using IPC::, you may have to put delays
    in your script, to allow
    gpg to return, before giving the next command.
    , gpg will get the 'trust\n5\yes\n' as 1 long command
    and won't understand it.

    Like:
    print HIS_IN "trust\n";
    sleep 1;
    print HIS_IN "5\n";
    sleep 1
    print HIS_IN "yes\n";

    but better yet, don't rely on delays because your
    system may be overloaded and the delay may not work.

    The best way is to print a command to HIS_IN and then read
    the HISUT and HIS_ERR and use regexes to process the output.
    If the expected output is there, print the next line and do the same.
    Then you are making IPC:: run like Expect.

    Something along these lines

    print HIS_IN "trust\n";
    chomp(my $answer = <HISUT>);
    if( $answer =~ /^How many years?$/ )
    { print HIS_IN "5\n"; }else{print "years error $!\n"; exit}

    chomp(my $answer1 = <HISUT>);
    if( $answer1 =~ /^Are you sure?$/ )
    { print HIS_IN "yes\n"; }else{print "confirm error $!\n"; exit}

    But you need to run the procedure yourself a few times, and
    see what the regexes should be.
  • No.2 | | 763 bytes | |

    Zentara wrote:

    Something along these lines

    print HIS_IN "trust\n";
    chomp(my $answer = <HISUT>);
    if( $answer =~ /^How many years?$/ )
    { print HIS_IN "5\n"; }else{print "years error $!\n"; exit}

    chomp(my $answer1 = <HISUT>);
    if( $answer1 =~ /^Are you sure?$/ )
    { print HIS_IN "yes\n"; }else{print "confirm error $!\n"; exit}

    But you need to run the procedure yourself a few times, and
    see what the regexes should be.

    K I've tried altering my code as you suggested, replacing the regexes
    with ones that would work. gpg still just sits there as soon as it has
    entered it's intereactive mode, and perl doesn't seem to be passing any
    commands to it at all.

    Any other ideas?
  • No.3 | | 1700 bytes | |

    Tue, 07 Jun 2005 14:38:41 +0100, darren (AT) birkett (DOT) com (D. J. Birkett)
    wrote:

    >Zentara wrote:
    >
    >
    >Something along these lines
    >
    >print HIS_IN "trust\n";
    >chomp(my $answer = <HISUT>);
    >if( $answer =~ /^How many years?$/ )
    >{ print HIS_IN "5\n"; }else{print "years error $!\n"; exit}
    >
    >chomp(my $answer1 = <HISUT>);
    >if( $answer1 =~ /^Are you sure?$/ )
    >{ print HIS_IN "yes\n"; }else{print "confirm error $!\n"; exit}
    >
    >
    >But you need to run the procedure yourself a few times, and
    >see what the regexes should be.
    >
    >
    >
    >
    >K I've tried altering my code as you suggested, replacing the regexes
    >with ones that would work. gpg still just sits there as soon as it has
    >entered it's intereactive mode, and perl doesn't seem to be passing any
    >commands to it at all.
    >
    >Any other ideas?


    Since you are not showing us your code, we can only guess.
    My guess is that you are not setting up the gpg command properly.

    The first thing that jumps out at me when I look at man gpg, is:

    Please remember that option parsing stops as soon as a non
    option is encountered, you can explicitly stop option
    parsing by using the special option "--".

    So gpg is just sitting there, waiting for more input, you need to find
    out why.

    Alot of little details can go wrong. Maybe you need to tweak the way
    the filehandles are referenced.

    But Thomas Baltzer's advice is right-on: look at the other modules, and
    see how they do it.
  • No.4 | | 2496 bytes | |

    Wed, 08 Jun 2005 06:39:29 -0400, zentara <zentara (AT) highstream (DOT) net>
    wrote:

    >>Any other ideas?

    >
    >Since you are not showing us your code, we can only guess.
    >My guess is that you are not setting up the gpg command properly.
    >
    >The first thing that jumps out at me when I look at man gpg, is:
    >

    Please remember that option parsing stops as soon as a non
    option is encountered, you can explicitly stop option
    parsing by using the special option "--".
    >
    >So gpg is just sitting there, waiting for more input, you need to find
    >out why.
    >
    >Alot of little details can go wrong. Maybe you need to tweak the way
    >the filehandles are referenced.
    >
    >But Thomas Baltzer's advice is right-on: look at the other modules, and
    >see how they do it.


    Yeah, I've tried running gpg thru IPC:: and you are right, it
    dosn't repond. Some apps are tricky the way they use
    STDIN, STDUT and STDERR.

    I looked at the modules that use it, and it looks like Crypt::GPG uses
    IPC::Run and it gets quite involved. thing I did notice is that you
    need to close the IN filehandle before gpg will return anything.

    It all looks very complicated, and you should probably just use the
    modules instead of trying to roll your own.

    Here is an IPC:: script, which does coax gpg to return output,
    but I see problems with having to close the IN filehandle, as shown in
    the code below. Maybe there is a way to send and 'eof' thru IN, without
    closing it, so you can send multiple commands? If I find it, I'll let
    you know. Maybe print a null char?

    #!/usr/bin/perl
    use warnings;
    use strict;
    use IPC::;
    use I::Select;
    $|++;

    my $pid1 = open3(\*IN, \*READ,\*ERRR,"gpg");
    #if \*ERRR is false, STDERR is sent to STDUT

    my $sel = new I::Select();

    $sel->add(\*READ);
    $sel->add(\*ERRR);

    print IN "\n";
    close IN;

    get_output();

    print IN "\n";
    close IN;

    get_output();

    sub get_output{

    if( $sel->can_read ){
    foreach my $h ($sel->can_read){
    my $buf = '';
    if ($h eq \*ERRR){
    $buf = <ERRR>;
    if($buf){print "ERRR-$buf\n"}
    }else{
    $buf = <READ>;
    if($buf){print "$buf\n"}
    }
    }
    }

    }
    __END__
  • No.5 | | 1744 bytes | |

    Zentara wrote:

    Since you are not showing us your code, we can only guess.
    My guess is that you are not setting up the gpg command properly.

    Here is what I now have

    use IPC::;

    local(*HIS_IN, *HISUT, *HIS_ERR);

    $childpid = open3(*HIS_IN, *HISUT, *HIS_ERR, 'gpg root');
    sleep 5;

    print HIS_IN "trust\n";
    chomp(my $answer = <HISUT>);
    print $answer;
    if ($answer =~ /Your decision/) {
    print HIS_IN "5\n";
    }
    else {
    print "decision error $!\n";
    exit;
    }

    chomp (my $answer1 = <HISUT>);
    if ($answer1 =~ /^Do you really/) {
    print HIS_IN "yes\n";
    }
    else {
    print "sure error $!\n";
    exit;
    }

    The first thing that jumps out at me when I look at man gpg, is:

    Please remember that option parsing stops as soon as a non
    option is encountered, you can explicitly stop option
    parsing by using the special option "--".

    So gpg is just sitting there, waiting for more input, you need to find
    out why.

    gpg is starting up fine. It is not a question of options that I'm
    passing it to open it. It's the fact that my code is not interacting
    with it once it's open

    Alot of little details can go wrong. Maybe you need to tweak the way
    the filehandles are referenced.

    This is really where I am after help as my perl is not really that advanced.

    But Thomas Baltzer's advice is right-on: look at the other modules, and
    see how they do it.

    yes I checked out Crypt::GPG, and that interacts with gpg by using
    IPC::Run in an Expect like manner. Unfortunately, IPC::Run does not
    come as part of the standard perl distro either.

    Thanks
    Darren
  • No.6 | | 269 bytes | |

    Here is what I now have
    use IPC::;
    use strict;
    use warnings;
    before IPC::
    local(*HIS_IN, *HISUT, *HIS_ERR);
    $childpid = open3(*HIS_IN, *HISUT, *HIS_ERR, 'gpg root');
    Make sure open3 worked
    open3() or die $!;
  • No.7 | | 2367 bytes | |

    Wed, 08 Jun 2005 15:52:21 +0100, darren (AT) birkett (DOT) com (D. J. Birkett)
    wrote:

    >Here is what I now have
    >
    >gpg is starting up fine. It is not a question of options that I'm
    >passing it to open it. It's the fact that my code is not interacting
    >with it once it's open


    >yes I checked out Crypt::GPG, and that interacts with gpg by using
    >IPC::Run in an Expect like manner. Unfortunately, IPC::Run does not
    >come as part of the standard perl distro either.
    >
    >Thanks
    >Darren


    I see what is happening but I don't know how to resolve it. I modified
    your code above to run on my machine, and it works, but interactively.
    When I first run the code, gpg grabs control of the tty away from the
    perl script, and it works. This interferes with IPC::'s attempt to
    sit between the terminal and gpg. I guess he does it for some security
    reason?

    I noticed in Crypt::GPG, that it uses the undocumented "" option
    of gpg. If you use that option, gpg will not grab the tty, but then you
    have another problemhow to talk to it, since you need to close HIS_IN
    to get gpg going?

    I see 2 choices: either find a way to use and put all the
    options on the original command line like (guessing):
    "gpg zentara"

    or

    find a way to intercept the tty which gpg is using. I noticed in the
    modules, that they do some tricky things with fileno, and they are
    probably using it, to grab the fileno's of the tty in order to read and
    write to them directly.

    #!/usr/bin/perl
    use warnings;
    use strict;
    use IPC::;
    use I::Select;
    local(*HIS_IN, *HISUT, *HIS_ERR);

    my $childpid = open3(*HIS_IN, *HISUT, *HIS_ERR, 'gpg zentara');
    #my $childpid = open3(*HIS_IN, *HISUT, *HIS_ERR, 'gpg
    zentara');

    close HIS_IN;
    #close HIS_IN;

    print HIS_IN "trust\n";
    chomp(my $answer = <HISUT>);
    print $answer;
    if ($answer =~ /Your decision/) {
    print HIS_IN "5\n";
    }else {
    print "decision error $!\n";
    exit;
    }

    chomp (my $answer1 = <HISUT>);
    if ($answer1 =~ /^Do you really/) {
    print HIS_IN "yes\n";
    }else{
    print "sure error $!\n";
    exit;
    }
    __END__

Re: using open3 to interact with external program


max 4000 letters.
Your nickname that display:
In order to stop the spam: 1 + 1 =
QUESTION ON "Perl"

EMSDN.COM