2

I have a sample program in 2 formats perl & embperl

The perl version works as a CGI but the embperl version does not work.

Any suggestions or pointers to solutions would be appreciated

OS: Linux version 2.6.35.6-48.fc14.i686.PAE (...) (gcc version 4.5.1 20100924 (Red Hat 4.5.1-4) (GCC) ) #1 SMP Fri Oct 22 15:27:53 UTC 2010

NOTE: I originally posted this question to perlmonks [x] and the embperl mailing list [x] but didn't get a solution.

perl working script

#!/usr/bin/perl
use warnings;
use strict;
use IPC::Open3;

print "Content-type: text/plain\n\n";

my $cmd = 'ls';

my $pid = open3(*HIS_IN, *HIS_OUT, *HIS_ERR, $cmd);
close(HIS_IN);  # give end of file to kid, or feed him
my @outlines = <HIS_OUT>;              # read till EOF
my @errlines = <HIS_ERR>;              # XXX: block potential if massive
print "STDOUT: ", @outlines, "\n";
print "STDERR: ", @errlines, "\n";

waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;

print "child_exit_status: $child_exit_status\n";

embperl non-working script

[-
  use warnings;
  use strict;
  use IPC::Open3;

  my $cmd = 'ls';

  my $pid = open3(*HIS_IN, *HIS_OUT, *HIS_ERR, $cmd);

  close(HIS_IN);  # give end of file to kid, or feed him

  my @outlines = <HIS_OUT>;              # read till EOF
  my @errlines = <HIS_ERR>;              # XXX: block potential if massive
  print OUT "STDOUT: ", @outlines, "\n";
  print OUT "STDERR: ", @errlines, "\n";

  waitpid( $pid, 0 );
  my $child_exit_status = $? >> 8;

  print OUT "child_exit_status: $child_exit_status\n";
-]

Here is the output I receive

STDERR: ls: write error: Bad file descriptor

child_exit_status: 2
Donavon Lerman
  • 343
  • 1
  • 6
  • 19

2 Answers2

1

open3 redirects the file descriptor associated with STDOUT, excepting it to be fd 1 (what the program you exec will consider STDOUT). But it's not 1. It doesn't even have a file descriptor associated with it! I consider this a bug in open3. I think you can work around it as follows:

local *STDOUT;
open(STDOUT, '>&=', 1) or die $!;
...open3...
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • I added this before the call to open3 and received the following error "Error in Perl code: Bad file descriptor" – Donavon Lerman May 22 '14 at 16:01
  • fd 1 is closed?!? yuck! You'll need to 1) open an arbitrary file handle (say `/dev/null`), 2) Use `POSIX::dup2` to copy that descriptor to descriptor 1 if it's not already 1. 3) Then you use the workaround I posted. – ikegami May 22 '14 at 16:18
  • This is a bit over my head... Can you provide or point me to an example for 1) & 2). – Donavon Lerman May 22 '14 at 18:28
  • `open(my $fh, '>', '/dev/null') or die $!; dup2(fileno($fh), 1) or die $! if fileno($fh) != 1;` – ikegami May 22 '14 at 18:34
  • This is awesome. Can the same be used for STDIN? open(my $fh, '<', '/dev/null') or die $!; dup2(fileno($fh), 1) or die $! if fileno($fh) != 1; local *STDIN; open(STDIN, '<&=', 1) or die $!; – Donavon Lerman May 22 '14 at 21:02
  • yeah, but fd `0`. Do STDIN before STDOUT. – ikegami May 23 '14 at 02:34
  • I added the following before STDOUT. It creates a **** process. _Should I create another question for this?_: `open($fhIN, '<', '/dev/null') or die $!; dup2(fileno($fhIN), 0) or die $! if fileno($fhIN) != 0; local *STDIN; open(STDIN, '<&=', 0) or die $!;` – Donavon Lerman May 23 '14 at 18:10
  • You need to reap the process after it's ended. (e.g. `waitpid( $pid, 0 );`) – ikegami May 23 '14 at 18:30
  • I made a separate follow up question for the STDIN with the complete code. [embperl - Using IPC::Open3 to call wkhtmltopdf. STDIN Not working](http://stackoverflow.com/questions/23838336/embperl-using-ipcopen3-to-call-wkhtmltopdf-stdin-not-working) – Donavon Lerman May 23 '14 at 21:08
0

Thank you sooo much to ikegami!!!!

Here is the embperl code that works. P.S. There is a similar problem with STDIN. I don't know the solution to that yet, but I think it is similar.

[-
  use warnings;
  use strict;
  use IPC::Open3;
  use POSIX;

  $http_headers_out{'Content-Type'} = "text/plain";

  my $cmd = 'ls';

  open(my $fh, '>', '/dev/null') or die $!; 

  dup2(fileno($fh), 1) or die $! if fileno($fh) != 1;

  local *STDOUT;
  open(STDOUT, '>&=', 1) or die $!;

  my $pid = open3(*HIS_IN, *HIS_OUT, *HIS_ERR, $cmd);

  close(HIS_IN);  # give end of file to kid, or feed him

  my @outlines = <HIS_OUT>;              # read till EOF
  my @errlines = <HIS_ERR>;              # XXX: block potential if massive
  print OUT "STDOUT: ", @outlines, "\n";
  print OUT "STDERR: ", @errlines, "\n";

  waitpid( $pid, 0 );
  my $child_exit_status = $? >> 8;

  print OUT "child_exit_status: $child_exit_status\n";
-]
Community
  • 1
  • 1
Donavon Lerman
  • 343
  • 1
  • 6
  • 19