home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Practical mod_perlPractical mod_perlSearch this book

B.17. Core Apache Modules

B.17.3. Apache::SubProcess—Interface to Apache Subprocess API

The output of system( ), exec( ), and open(PIPE,"|program") calls will not be sent to the browser unless your Perl interpreter was configured with sfio.

One workaround is to use backticks:

print `command here`;

But a cleaner solution is provided by the Apache::SubProcess module. It overrides the exec( ) and system( ) calls with calls that work correctly under mod_perl.

Let's look at a few examples. This example overrides the built-in system( ) function and sends the output to the browser:

use Apache::SubProcess qw(system);
my $r = shift;
$r->send_http_header('text/plain');

system "/bin/echo hi there";

This example overrides the built-in exec( ) function and sends the output to the browser. As you can guess, the printstatement after the exec( ) call will never be executed.

use Apache::SubProcess qw(exec);
my $r = shift;
$r->send_http_header('text/plain');

exec "/usr/bin/cal"; 

print "NOT REACHED\n";

The env( ) function sets an environment variable that can be seen by the main process and subprocesses, then it executes the /bin/env program via call_exec( ). The main code spawns a process, and tells it to execute the env( ) function. This call returns an output file handle from the spawned child process. Finally, it takes the output generated by the child process and sends it to the browser via send_fd( ), which expects the file handle as an argument:

use Apache::SubProcess ( );
my $r = shift;
$r->send_http_header('text/plain');

my $efh = $r->spawn_child(\&env);
$r->send_fd($efh);

sub env {
    my $fh = shift;
    $fh->subprocess_env(HELLO => 'world');
    $fh->filename("/bin/env");
    $fh->call_exec;
}

This example is very similar to the previous example, but it shows how you can pass arguments to the external process. It passes the string to print as a banner via a subprocess:

use Apache::SubProcess ( );
my $r = shift;
$r->send_http_header('text/plain');

my $fh = $r->spawn_child(\&banner);
$r->send_fd($fh);

sub banner {
    my $fh = shift;
    # /usr/games/banner on many Unices
    $fh->filename("/usr/bin/banner");
    $fh->args("-w40+Hello%20World");
    $fh->call_exec;
}

The last example shows how you can have full access to the STDIN, STDOUT, and STDERRstreams of the spawned subprocess, so that you can pipe data to a program and send its output to the browser:

use Apache::SubProcess ( );
my $r = shift;
$r->send_http_header('text/plain');

use vars qw($string);
$string = "hello world";
my($out, $in, $err) = $r->spawn_child(\&echo);
print $out $string;
$r->send_fd($in);

sub echo {
    my $fh = shift;
    $fh->subprocess_env(CONTENT_LENGTH => length $string);
    $fh->filename("/tmp/pecho");
    $fh->call_exec;
}

The echo( ) function is similar to the earlier example's env( ) function. /tmp/pecho is as follows:

#!/usr/bin/perl 
read STDIN, $buf, $ENV{CONTENT_LENGTH}; 
print "STDIN: '$buf' ($ENV{CONTENT_LENGTH})\n";

In the last example, a string is defined as a global variable, so its length could be calculated in the echo( ) function. The subprocess reads from STDIN, to which the main process writes the string ("hello world"). It reads only the number of bytes specified by the CONTENT_LENGTH environment variable. Finally, the external program prints the data that it read to STDOUT, and the main program intercepts it and sends it to the client's socket (i.e., to the browser).

This module is also discussed in Chapter 10.

Available from CPAN. See the module manpage for more information.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.