Forking a daemonic Socket listener from a CGI script - browser times out

C

Clyde Ingram

I have 2 separate Perl programs:

1. Socket server - which listens for a connection request from a socket
client, writes some data records, and exits.

2. CGI program - which sends a Java applet to the browser (Navigator 4.6).
The applet requests a socket connection to the "Socket server", reads
records, and displays them.

All works well if the "Socket server" and "CGI program" run as separate
processes - the browser requests and gets the applet from the "CGI server";
the applet starts, connects with the "Socket server", reads the data records
from it, and displays the records..

However, I combine them into one CGI program, which forks a child process to
performs the long-running "Socket server" function, the HTML document sent
by the "CGI server" does not complete loading in the browser.

At least, not until a 5 minute timeout. Then the applet runs, and
successfully connects to the "Socket server" and reads and displays the
data. The indicates on screen that an error occurred. The server weblog
records a timeout error.

I have tried various tricks from the books on detaching the "Socket server"
(child) process from the "CGI program" (parent) process, but cannot fix this
"daemonic" problem in document loading.

For example, the child process:

- calls POSIX::setsid
- closes and re-opens some of its file handles

Can any helpful person suggest what I have missed here, please?

Here is my web server log - spot the 5 minute delay before the "Socket
server" writes out its data.
This is followed by the complete CGI program - a bit long, I know, but the
"fork" call is in the main code.
(Because this posting is so long, I am not posting the Java applet - yet. I
think it is not the problem, since it works fine with the original separate
2 Perl server programs.)

Ta,
Clyde

+++++++++++++++++++++++++++ web server log
12:45:08: Process 22587 forking
12:45:08: Parent: closing STDIN - unwanted
12:45:08: Parent: writing HTML page including socket client applet,
instructing to use port 4444
12:45:08: Child: closing STDIN - unwanted
12:45:08: Child: closing STDOUT - unwanted
12:45:08: \ main::write_html_page_incl_socket_client_applet( 4444 )
12:45:08: Parent: generate HTML document
12:45:08: Child: Creating and listening on socket for tcp port 4444
12:45:08: Child: shutting down read stream of socket - unwanted
12:45:08: Child: Wait for incoming socket client connection request
12:45:08:
12:45:08: Parent: prepared this HTML document:
12:45:08:
12:45:08: Content-Type: text/html; charset=ISO-8859-1

12:45:08:

12:45:08: <?xml version="1.0" encoding="utf-8"?>
12:45:08: <!DOCTYPE html
12:45:08: PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
12:45:08: "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">
12:45:08: <html xmlns="http://www.w3.org/1999/xhtml"
lang="en-US"><head><title>Applet running a Socket Client</title>
12:45:08: </head><body>
12:45:08: <h3>
12:45:08: Applet running a Socket Client
12:45:08: </h3>
12:45:08: <applet width="100%" code="MonitorAdrLoaderApplet.class"
name="MonitorAdrLoaderApplet" height="100%" codebase="http://web/admin-ui/">
12:45:08: <param value="host.domain" name="serverhostname"> <param
value="4444" name="serverport"> <param value="33" name="appletrows"> <param
value="77" name="appletcols"> <param value="12" name="appletfontsize">
<param value="Courier" name="appletfontname"> <param value="."
name="socketeofmarker">
12:45:08: </applet>
12:45:08: </body></html>
12:45:08:
12:45:08:
12:45:08: Parent: sending HTML document to browser:
12:45:08:
12:45:08: / main::write_html_page_incl_socket_client_applet( )
12:45:08: Parent: closing STDOUT - unwanted
12:45:08: Parent: closing STDERR - unwanted
12:45:08: Parent 22587: exiting
[12/Dec/2003:12:50:08] failure ( 8561): for host 127.0.0.1 trying to GET
/admin-ui/test_socket_applet.cgi, cgi_send reports: error occurred while
sending script output (IO timeout error)
[12/Dec/2003:12:50:08] warning ( 8561): for host 127.0.0.1 trying to GET
/admin-ui/MonitorAdrLoaderApplet.class.sec, send-file reports: can't find
/opt/jocs/website/admin/MonitorAdrLoaderApplet.class.sec (No such file or
directory)
12:50:09: Child: Flush socket client file handle, and write records out to
it
12:50:09: Child: -> Homer: D'oh
12:50:09: Child: -> Marge: A deer
12:50:09: Child: -> Lisa: A female deer
12:50:09: Child: ->
12:50:09: Child: -> Bart: Hello. I'm Bart Simpson.
12:50:09: Child: -> Bart: Who the hell are you?
12:50:09: Child: ->
12:50:09: Child: -> Homer: Mmmmm
12:50:09: Child: -> Homer: 64
12:50:09: Child: -> Homer: slices
12:50:09: Child: -> Homer: of
12:50:09: Child: -> Homer: American
12:50:09: Child: -> Homer: Cheese
12:50:09: Child: ->
12:50:09: Child: -> Homer: Sacrilicious!
12:50:09: Child: ->
12:50:09: Child: -> Homer: Yummie, yummie, yummie -- I've got love in my
tummy, and I think I'm loving you. D'oh, d'oh, d'oh!!
12:50:09: Child: ->
12:50:09: Child: -> Homer: Mmmmm, chocolate!
12:50:09: Child: -> .
12:50:09: Child: Closing socket for tcp port 4444
12:50:09: Child: Exiting

+++++++++++++++++++++++++++ CGI program

#!/bin/perl -wT

$ENV{'PATH'} = '/bin:/usr/bin';
$ENV{'SHELL'} = '/bin/sh' if ( exists $ENV{'SHELL'} );
delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' };

print STDERR join ( "\n", @INC ) . "\n";

use strict;
use POSIX; # For dup() and setsid() functions
use English;
use IO::Socket;

$OUTPUT_AUTOFLUSH++;

use constant HOSTNAME => scalar 'host.domain';
use constant SERVER_PORT => scalar '4444';
use constant PROTOCOL => scalar 'tcp';
use constant APPLET_ROWS => scalar '33';
use constant APPLET_COLS => scalar '77';
use constant APPLET_FONTSIZE => scalar '12';
use constant APPLET_FONTNAME => scalar 'Courier';
use constant EOF_MARKER => scalar '.';

BEGIN {
use CGI
qw/ -oldstyle_urls :standard :html3 :form :cgi cgi_error $POST_MAX
$DISABLE_UPLOAD :netscape /;

$CGI::pOST_MAX = 2 * 1024 * 1024;
$CGI::DISABLE_UPLOAD = 1;
use CGI::Carp qw( fatalsToBrowser carpout );

my $server_error_log_file =
'/opt/netscape/suitespot/https/logs/errors';

open( LOG, ">>$server_error_log_file" )
or die "$server_error_log_file: unable to append to log file:
$ERRNO\n";
carpout(*LOG);

use CGI::pretty qw ( :html3 );
}

local $SIG{'CHLD'} = 'IGNORE';

my $identity = 'Process';
my $child_pid;

my @socket_data = <DATA>;
chomp @socket_data;

warn("$identity $PID forking\n");

if ( $child_pid = fork() ) { # Parent

$identity = 'Parent';

# warn( "$identity: closing server socket - unwanted\n" );
# close( $server_socket );
warn("$identity: closing STDIN - unwanted\n");
close(STDIN);
open( *STDIN, "+< /dev/null" )
or die "$identity failed to close STDIN: $ERRNO";

warn(
"$identity: writing HTML page including socket client applet,
instructing to use port "
. +SERVER_PORT . "\n" );

write_html_page_incl_socket_client_applet( +SERVER_PORT )
or die "Failed to write HTML page with socket client applet: $ERRNO";

warn("$identity: closing STDOUT - unwanted\n");
warn("$identity: closing STDERR - unwanted\n");

warn("$identity $PID: exiting\n");

close(STDOUT);
close(STDERR);
open( *STDOUT, "+< /dev/null" )
or die "$identity failed to close STDOUT: $ERRNO";
open( *STDERR, "+< /dev/null" )
or die "$identity failed to close STDERR: $ERRNO";

exit(1);
}
else { # Child

$identity = 'Child';

local $SIG{'PIPE'} = 'IGNORE';

local $SIG{'INT'} = $SIG{'TERM'} = $SIG{'HUP'} = sub {

warn( "Signal handler: $identity got INT or TERM or HUP signal"
. ( $CHILD_ERROR >> 8 ) . "\n" );
exit(0);
};

warn("$identity: closing STDIN - unwanted\n");
warn("$identity: closing STDOUT - unwanted\n");

# warn( "$identity: closing STDERR - unwanted\n" );
close(STDIN);
close(STDOUT);

# close( STDERR );
open( *STDIN, "+< /dev/null" )
or die "$identity failed to close STDIN: $ERRNO";
open( *STDOUT, "+< /dev/null" )
or die "$identity failed to close STDOUT: $ERRNO";

# open( *STDERR, "+< /dev/null" ) or die "$identity failed to close
STDERR: $ERRNO";

# Dissociate from the parent and stop being part of the process
# group we had been a member of

POSIX::setsid()
or die "Cannot start a new session: $ERRNO";

# Listen for a socket connection request from the Java applet
listen_and_write_to_socket()
or die "Failed to listen for service socket: $ERRNO";

exit(1);
}

exit(1);

#-----------------------------------------------------------------------

sub write_html_page_incl_socket_client_applet {

trace_in( ( caller(0) )[3], @_ );

my ($server_port_nr) = @_;

warn("$identity: generate HTML document\n");

my $html_document =
header()
. start_html( -Title => 'Applet running a Socket Client' )
. h3('Applet running a Socket Client')
 
R

Randal L. Schwartz

Clyde> I have tried various tricks from the books on detaching the "Socket server"
Clyde> (child) process from the "CGI program" (parent) process, but cannot fix this
Clyde> "daemonic" problem in document loading.

Read the stuff I've repeated in at least three or four of my
columns (of the 198 I've done so far). Google for:

site:stonehenge.com cgi fork

That should point you in the right direction.

print "Just another Perl hacker,"
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top