brainsick has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I'm trying to execute some CGI scripts by creating a CGI-like environment in a "driver program" and then running the CGI scripts with an open3 call. My most basic "CGI script" just prints some output to STDOUT and STDERR and my driver program can read both sets of output. When I try to run a real CGI script, I get illegal seek errors while reading STDERR. How do I debug this?

What are likely causes of illegal seek errors? My first thought was that the CGI script is doing something funky with the file handles, but I've tried recreating that in my testprog.pl to no avail.

I've distilled the concept down this example: test_servicing_request.pl:
#!/usr/bin/perl use strict; use warnings; use IPC::Open3; use Symbol; my $input = ''; my $_env = { 'SCRIPT_NAME' => '', 'SERVER_NAME' => 'my.site.example.com', 'HTTP_REFERER' => 'https://myother.site.example.com/', 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate', 'PATH_INFO' => '/index.cgi', 'HTTP_CONNECTION' => 'Keep-Alive', 'REQUEST_METHOD' => 'GET', 'HTTP_ACCEPT' => '*/*', 'HTTP_UA_CPU' => 'x86', 'SERVER_SOFTWARE' => 'Apache', 'REMOTE_USER' => 'mylogonid', 'AUTH_TYPE' => 'Basic', 'HTTP_CLIENT_IP' => '112.113.114.115', 'HTTP_USER_AGENT' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows N +T 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 1.1.4322; +MS-RTC LM 8)', 'QUERY_STRING' => '', 'SERVER_PORT' => '443', 'HTTP_COOKIE' => '', 'HTTP_ACCEPT_LANGUAGE' => 'en-us', 'REMOTE_ADDR' => '123.124.125.126', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'GATEWAY_INTERFACE' => 'CGI/1.1', 'HTTP_HOST' => 'my.site.example.com', 'PATH_TRANSLATED' => '/content/my.site.example.com/proxy.cgi/index +.cgi' }; # save the calling applications environment my $env_snapshot = { %ENV }; # merge a CGI-style environment foreach my $key (keys(%{$_env})) { if (!exists($ENV{$key})) { $ENV{$key} = $_env->{$key}; } } #my $cmd = '/home2/servicing' . ($ENV{'PATH_INFO'} || '/index.cgi'); my $cmd = '/tmp/testprog.sh'; my $dir = `dirname $cmd`; chomp $dir; # If PROG_STDERR isn't initialized then open3 treats it as a false # value and sends STDERR to STDOUT. Dumb default behavior; I'm using +open3 # because I want to capture that output! my $PROG_STDERR = gensym(); print "Executing $cmd\n"; my $pid = open3(my $PROG_STDIN, my $PROG_STDOUT, $PROG_STDERR, 'cd ' . + $dir . ' && ' . $cmd) or die $@; print "PROG_STDIN: $PROG_STDIN, tell(PROG_STDIN): " . tell($PROG_STDIN +) . "\n"; if ($input) { print $PROG_STDIN $input or die "Error sending data to STDIN: $!"; } close($PROG_STDIN); waitpid $pid, 0; print "PROG_STDOUT: $PROG_STDOUT, tell(PROG_STDOUT): " . tell($PROG_ST +DOUT) . "\n"; my $output = join('', <$PROG_STDOUT>) or die "Error reading data from +STDOUT: $!"; close($PROG_STDOUT); print "output: $output\n"; print "PROG_STDERR: $PROG_STDERR, tell(PROG_STDERR): " . tell($PROG_ST +DERR) . "\n"; my $error = join('', <$PROG_STDERR>) or die "Error reading data from S +TDERR: $!"; close($PROG_STDERR); print "error: $error\n"; # restore our environment %ENV = %{ $env_snapshot };
testprog.sh
#!/bin/sh exec ./testprog.pl
testprog.pl
#!/usr/bin/perl print "Sleeping for 3 seconds\n"; sleep 3; print "Sleeping for 5 seconds\n"; sleep 5; print STDERR "The sky is falling.\n";
Which executes successfully:
Executing /tmp/testprog.sh PROG_STDIN: GLOB(0x17f250), tell(PROG_STDIN): -1 PROG_STDOUT: GLOB(0x17f268), tell(PROG_STDOUT): -1 output: Sleeping for 3 seconds Sleeping for 5 seconds PROG_STDERR: GLOB(0x17f220), tell(PROG_STDERR): -1 error: The sky is falling.
Changing which $cmd I'm executing results in the illegal seek error:
Executing /home2/servicing/index.cgi PROG_STDIN: GLOB(0x17f274), tell(PROG_STDIN): -1 PROG_STDOUT: GLOB(0x17f28c), tell(PROG_STDOUT): -1 output: Content-type: text/html <HTML> <HEAD> <TITLE>Error</TITLE> </HEAD> <BODY TEXT="#000000" LINK="#0000FF" VLINK="#0000FF" BGCOLOR="#C0C0C0"> <CENTER> <FONT SIZE="+2" COLOR="#FF0000"> <P> <br /> Your attempt to log onto website failed.&nbsp; This is due to a proble +m NOT related to correctly keying your User ID and Password. <P> In order to access our secure websites, please report this event to +at <a href="mailto:customer_service@sample.com" tabindex="-1">customer_se +rvice@sample.com</a> or 111-111-1111.&nbsp; We will research the problem and assist you with your data and transac +tion needs. <P> Thank you for your assistance. </FONT> </CENTER> </BODY> </HTML> PROG_STDERR: GLOB(0x17f244), tell(PROG_STDERR): -1 Error reading data from STDERR: Illegal seek at test_servicing_request +.pl line 74.

Replies are listed 'Best First'.
Re: open3 and illegal seeks
by ikegami (Patriarch) on Jul 07, 2009 at 17:09 UTC
    The following is wrong:
    my $error = join('', <$PROG_STDERR>) or die "Error reading data from STDERR: $!";

    Nothing in there indicates $! is meaningful. You're seeing the value left in $! by tell. At some level, tell($fh) is implemented as seek($fh,0,SEEK_CUR), but you can't use seek a pipe.

    The best you can do follows:

    my $error = join('', <$PROG_STDERR>); my $err = $!; die "Error reading data from STDERR: $err" if !eof($PROG_STDERR);
      ikegami,

      I only had the tell statements in there because I was trying to debug the illegal seek message and that was one of the straws that I grasped.

      Removing the tell statements and re-running the program still results in the illegal seek error:
      Error reading data from STDERR: Illegal seek at test_servicing_request +.pl line 74.
      Or did I miss the crux of your message?

      Also, how does that explain why my simple sample works but my real CGI script does not? Wouldn't the tell introduce the error in both instances?
        Did you also change your code to look like the following:
        my $error = join('', <$PROG_STDERR>); my $err = $!; die "Error reading data from STDERR: $err" if !eof($PROG_STDERR);

        If you didn't, you haven't shown than an error even occurred.

        Before I do anything else, I'm going to worry about making sure an error actually occurred and about getting the right error message when one does occur.