#!/usr/bin/perl -w
#
# Copyright (C) 2000, 2003, 2004, 2005 Eric de la Clergerie
# dyalog_server is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# $Id: parserd,v 1.14 2005/11/28 08:42:16 clerger Exp $
#

package Parserd;

use 5.006;
use strict;
use warnings;
use Carp;

use sigtrap;
use IO::Socket;
use Net::hostent;              # for OO version of gethostbyaddr
use IPC::Open2;
use IPC::Open3;

use IPC::Run qw/start/;

use POSIX 'setsid';

use FindBin qw($Bin $Script);
use Net::Server::Fork;
use vars qw(@ISA $VERSION);

use IO::Handle;
#use IO::Pipe;

@ISA = qw(Net::Server::Fork);
$VERSION = '0.0.1';

use Forest::LP::Parser;
use Forest::RCG::Parser;
use Forest::XTAG::Parser;
use Forest::XML::Writer;
use Forest::HTML::Writer;
use Forest::Dot::Writer;
use Forest::Dependency::Writer;
use Forest::Tagger::Writer;

use AppConfig qw/:argcount :expand/;

my $config = AppConfig->new({ CREATE => '_(cmd|options|label|lexer|forest|examples|origin|grammar|easy|language|info)',
			      GLOBALS => { ARGCOUNT => ARGCOUNT_ONE
				       }
			      },
			    # package variables
                            "package=f",
                            "path=f@" => { DEFAULT => [qw{/bin /usr/bin}] },
			    # Server Admin variables
                            "port|p=i" => { DEFAULT => 8999 },
                            "setsid!" => { DEFAULT => 1 },
                            "verbose|v!" => { DEFAULT => 0 },
                            "log_file=f" =>  {DEFAULT => '/var/log/parserd.log'},
                            "log_level=i" => {DEFAULT => 3},
                            "maxclients=i" => { DEFAULT => 1 },
                            "user=s" => {DEFAULT => 'nobody'},
                            "group=s" => {DEFAULT => 'nogroup'},
                            "pid_file=f" => {DEFAULT => '/var/run/parserd.pid'},
                            "syslog_ident=s" => {DEFAULT => 'parserd' },
                            "admin=s" => {DEFAULT => 'parserd'},
                            "server!" => { DEFAULT => 0 },
			    "cache=i" => { DEFAULT => 10 },
			    # parserd specifics
			    "parsers=s"

			    );

my $conffile = "/etc/parserd.conf";

# read configuration file
if ($conffile) {
  $config->file("$conffile")
    || die "can't open or process configuration file $conffile";
}

$config->args();                # parse remaining args

if ($config->server()){
  # Securize
  delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
}

# setting path
$ENV{'PATH'} = join(':',@{$config->path()});

my $verbose = $config->verbose();


### set up some server parameters
sub configure_hook {
  my $self = shift;
  open(STDIN, '</dev/null') || die "Can't close STDIN [$!]";
  open(STDOUT,'>/dev/null') || die "Can't close STDOUT [$!]";
  open(STDERR,'>/dev/null') || die "Can't close STDERR [$!]";
  #  open(STDERR,'>&STDOUT')   || die "Can't close STDERR [$!]";

  $self->MyInit();

}

### this occurs after the request has been processed
### this is server type specific (actually applies to all by INET)
sub post_accept {
  my $self = shift;
  my $prop = $self->{server};

  ### duplicate some handles and flush them
  ### maybe we should save these somewhere - maybe not
  if( defined $prop->{client} ){
    *MYSTDIN  = \*{ $prop->{client} };
    *MYSTDOUT = \*{ $prop->{client} } if ! $prop->{client}->isa('IO::Socket::SSL');
    *MYSTDERR = \*{ $prop->{client} } if ! $prop->{client}->isa('IO::Socket::SSL');
    MYSTDIN->autoflush(1);
    MYSTDOUT->autoflush(1);
    MYSTDERR->autoflush(1);
    select(STDOUT);
  }else{
    $self->log(1,"Client socket information could not be determined!");
  }
  
  ### keep track of the requests
  $prop->{requests} ++;

}

### user customizable hook
sub post_process_request_hook {
  my $self = shift;
  $self->killgroup;
}


######################################################################
#    Configuration

my %cfg = (
	   'cmd' => { 'mandatory' => 1},
	   'label' => { 'mandatory' => 1},
	   'options' => { 'default' => [], 
			  'action' => \&split_options},
	   'forest' => { 'default' => 'lp' },
	   'examples' => { 'default' => [],
			   'action' => \&open_examples
			 },
	   'lexer' => { 'default' => \&lexer, 
			'action' => \&get_lexer},
	   'grammar' => {},
	   'easy' => {},
	   'language' => { 'default' => 'french' },
	   'info' => {}
	  );

sub MyInit {
  my $self = shift;

  $self->{last} = {
		   'time' => 0, 
		   'sentence' => '',
		   'parser' => '',
		   'forest' => '',
		   'lexer' => '',
		   'sid' => undef,
		  };

  $self->{cache} = {};
  $self->{cachelist} = [];

  my $lastkeys = join('|',keys %{$self->{last}});
  $self->{lastkeys} = qr/($lastkeys)\b/o;

  $self->{parser} = {};
  foreach my $parser (split(/\s+/,$config->parsers())) {
    my %info = ();
    foreach my $key (keys %cfg) {
      my $cfg = $cfg{$key};
      my $label = $parser."_$key";
      if ( defined($cfg->{'mandatory'}) && !defined $config->get($label)) {
	die "Bad configuration file";
      } elsif ($config->get($label)) {
	my $val = $config->get($label);
	$val = &{$cfg->{'action'}}($self,$val,\%info) if (defined $cfg->{'action'});
	$info{$key} = $val;
      } elsif (defined $cfg->{'default'}) {
	$info{$key} = $cfg->{'default'};
      } 
    }
    $self->{parser}{$parser} = { %info };
  }

  my $parsers = join('|',keys %{$self->{parser}});
  $self->{parsers} = qr/($parsers)\b/o;
}

######################################################################


my %inputs = ( 'lp' => \&Forest::LP::Parser::parse,
	       'rcg' => \&Forest::RCG::Parser::parse,
	       'xtag' => \&Forest::XTAG::Parser::parse,
	       'xrcg' => \&Forest::LP::XRCG::Parser::parse,
#	       'line' => \&Forest::Line::Parser::parse,
	       );

my %outputs = (	'html' => \&html_output,
		'xml' => \&xml_output,
		'dot' => \&dot_output,
		'dependency' => \&dependency_output,
		'xmldep' => \&xmldep_output,
		'lpdep' => \&lpdep_output,
		'stats' => \&stats_output,
		'depnorm' => \&depnorm_output,
		'tagger' => \&tagger_output,
		'yesno' => \&yesno_output,
		'easy' => \&easy_output,
#		'line' => \&line_output,
		);

my $inputs = join('|',keys %inputs);
$inputs = qr/($inputs)/o;

my %options = ( 'forest' => { 'value' => 'xml',
			      'default' => 'xml',
			      'help' => 'forest format',
			      'values' => [ sort ('raw',keys %outputs) ]
			      }
		);

######################################################################
# Server Loop with forking

sub process_request {
    my $self=shift;
    $self->{timeout} ||= 60;  # by default give the user 60 seconds to type a line
    eval {
	local $SIG{ALRM} = sub { die "Timed Out!\n" };
	my $previous_alarm = alarm($self->{timeout});
	my $prompt='';
	my $o;
	my $v;

	$self->{child} = [];

	while( <MYSTDIN> ){
	    s/\r?\n$//;
	    $prompt='yes', next unless /\S/;       # blank line
	    if    (/^(quit|exit)/oi)    { last; }
	    elsif (($o,$v) = /^set\s+(\w+)\s+(\w+)/oi) {
		$self->usage(), next 
		  unless (defined $options{$o} && grep( /^$v$/, @{$options{$o}{'values'}}));
		$options{$o}{'value'} = $v;
	    } elsif (($o) = /^show\s+(\w+)/) {
		$self->usage(), next unless (defined $options{$o});
		print MYSTDOUT "option $o: $options{$o}{value}\n";
	    } elsif (($o) = /^reset\s+(\w+)/) {
		$self->usage(), next unless (defined $options{$o});
		$options{$o}{'value'} = $options{$o}{'default'};
	    } elsif (($o) = /^input\s+$inputs\s+(\w+)\s+(.*)/) {
	      $self->handle_input_forest($1,$2,$3);
	    } elsif (/^grammar\s+$self->{parsers}/) {
	      next unless defined $self->{parser}{$1}{'grammar'};
	      print MYSTDOUT "$self->{parser}{$1}{grammar}\n";
	    } elsif (/^examples\s+$self->{parsers}/) {
	      next unless defined $self->{parser}{$1}{'examples'};
	      foreach my $sentence (@{$self->{parser}{$1}{'examples'}}) {
		print MYSTDOUT "$sentence\n";
	      }
	    } elsif (/^language\s+$self->{parsers}/) {
	      next unless defined $self->{parser}{$1}{'language'};
	      print MYSTDOUT "$self->{parser}{$1}{'language'}\n";
	    } elsif (/^info\s+$self->{parsers}/) {
	      next unless defined $self->{parser}{$1}{'info'};
	      print MYSTDOUT "$self->{parser}{$1}{'info'}\n";
	    } elsif (/^timeout\s+(\d+)/) {
	      $self->{timeout} = $1;
	    } elsif (/^last $self->{lastkeys}/) {
	      my $key = $1;
	      if ($key eq 'forest') {
		my $forest = $self->{last}{$key};
		next unless $forest;
		my $outputformat = $options{'forest'}{'value'};
		open(STDOUT,">&MYSTDOUT") || die "can't dup MYSTDOUT: $!";
		&{$outputs{$outputformat}}($forest,$self,$self->{last}{parser});
		open(STDOUT,'>/dev/null') || die "Can't close STDOUT [$!]";
		$self->log(3,"Done forest");
	      } else {
		next unless (defined $self->{last}{$key});
		print MYSTDOUT "last $key=$self->{last}{$key}\n";
	      }
	    } elsif (/^clean\s+cache/){
	      $self->{cache} = {};
	      $self->{cachelist} = [];
	    } elsif (s/^$self->{parsers}//oi ) {
	      $self->handle_parser($1,$_);
	      ##		last;
	    } elsif (s/^(\d+)\s+$self->{parsers}//oi) {
	      $self->handle_parser($2,$_,$1);
	    } elsif (s/^dag\s+(\d+)\s+$self->{parsers}\s*//oi) {
	      my $sid = $1;
	      my $parser = $2;
	      my $sentence = $_;
	      my @dag = ();
	      while (<MYSTDIN>) {
		chomp;
		push(@dag,$_);
		last if  ($_ =~ /^##\s*(DAG|MAF)\s+END/);
	      }
	      $self->handle_parser($parser,join("\n",@dag),$sid,$sentence);
	    } else {
		$self->usage();
	    }
	    alarm($self->{timeout});
	} continue {
	    if ($prompt) {
		print MYSTDOUT "Welcome to $0; type help for command list.\n";
		print MYSTDOUT "Command? ";
	    }
	}
	alarm($previous_alarm);
    };

    if( $@=~/timed out/i ){
	print MYSTDOUT "Timed Out.\n";
	if ( @{$self->{child}}) {
	  ## print MYSTDOUT "ALIVE @{$self->{child}}.\n";
	  ##	  my $ctl= kill 14, @{$self->{child}}; 
	  $self->killgroup;
	  ## print MYSTDOUT "KILL $ctl\n";
	}
	return;
    }

}

sub usage {
  my $self=shift;
  print MYSTDOUT <<EOF ;

$0 is a server of parsers. Given a parser and a sentence, it returns
the derivation forest for the sentence. When possible, this forest is
emitted in XML.

Contact: Eric de la Clergerie <Eric.De_La_Clergerie\@inria.fr>

Commands: 
      quit 
    | help 
    | <parser> <sentence>
    | show <option>
    | set <option> <value>
    | reset <option>
    | (examples|grammar|language|info) <parser>
    | last (time|sentence|parser|forest)
    | clean cache

Parsers:
EOF

    foreach my $parser (sort keys %{$self->{parser}}) {
	print MYSTDOUT "\t$parser\t-- $self->{parser}{$parser}{label}\n";
    }

print MYSTDOUT <<EOF;

Options:
EOF
   
    foreach my $option (sort keys %options) {
	print MYSTDOUT <<EOF;
\t$option\t-- $options{$option}{help}
\t\tcurrent value: $options{$option}{value}
\t\tall values   : @{$options{$option}{values}}
\t\tdefault      : $options{$option}{default}

EOF

    }

}

######################################################################
# Misc. Functions

# Parsing and producing a forest

sub handle_parser {
  my $self=shift;
  my $parser = shift;
  my $sentence = shift;
  my $sid = shift;
 
  if ($sentence =~ /^\s*$/) {
    print "sentence? ";
    $sentence = <MYSTDIN>;
  }

  my $xsentence = shift || $sentence;

  return if ($self->try_cache($sentence,$xsentence,$parser,$sid));

  $self->{last}{sid} = $sid;

  my $cmd = 'time -p ' . $self->{parser}{$parser}{cmd};
  $cmd .= ' ' . join(' ', @{$self->{parser}{$parser}{options}}) if $self->{parser}{$parser}{options};

  my $pid = open3(\*WRITER,\*READER,\*ERROR, $cmd) or die "Can't run $cmd";
  push(@{$self->{child}},$pid);
  
  $self->log(3,"Running lexer on $sentence");
  
  &{$self->{parser}{$parser}{lexer}}($self,\*WRITER,$sentence,$parser);

  close WRITER || die "can't close: $!";
  
  $self->log(3,"start cmd ok");

  my $time = 0;

  $self->handle_forest(\*READER,
		       $self->{parser}{$parser}{'forest'},
		       $parser,
##		       $self->{parser}{$parser}{'label'},
		       $xsentence,
		       $time,
		       $self->{parser}{$parser}{'grammar'},
		      );

  while(<ERROR>) {
    $time = $self->{last}{time} = $1,last if (/^\s*user\s+(.+)/);
  }
  close ERROR;

  $self->push_cache;

}

sub try_cache {
  my $self = shift;
  my $sentence = shift;
  my $xsentence = shift;
  my $parser = shift;
  my $sid = shift;
  
  return 0 unless  (exists $self->{cache}{"$sentence"}{$sid}{$parser});
  my $info = $self->{cache}{"$sentence"}{$sid}{$parser};
  my $forest = $info->{forest};
  return 1 unless $forest;
  $self->{last} = { sid => $sid,
		    parser => $parser,
		    forest => $forest,
		    time => $info->{time},
		    lexer => $info->{lexer},
		    sentence => $sentence
		  };
  my $outputformat = $options{'forest'}{'value'};
  open(STDOUT,">&MYSTDOUT") || die "can't dup MYSTDOUT: $!";
  &{$outputs{$outputformat}}($forest,$self,$self->{last}{parser});
  open(STDOUT,'>/dev/null') || die "Can't close STDOUT [$!]";
  $self->log(3,"Done forest");
  return 1;
}

sub push_cache {
  my $self = shift;
  my $last = $self->{last};
  my $sentence = $last->{sentence};
  my $parser = $last->{parser};
  my $sid = $last->{sid};
  my $new = { time => $last->{time},
	      lexer => $last->{lexer},
	      forest => $last->{forest}
	    };
  if (@{$self->{cachelist}} > $config->cache) {
    ## remove first element from cache
    my $first = shift @{$self->{cachelist}};
    delete $self->{cache}{$first->{sentence}}{$first->{sid}}{$first->{parser}};
  }
  push(@{$self->{cachelist}},{sentence => $sentence, sid => $sid, parser => $parser });
  $self->{cache}{$sentence}{$sid}{$parser} = $new;
}

sub handle_error{
  my $self = shift;
  my $stderr = shift;
  while (<$stderr>) {
    return $1 if /^user\s+(\S+)/;
  }
  return 0;
}

				# Reading directly the forest

sub handle_input_forest {
  my $self = shift;
  #    my $handle = shift;
  my $input = shift;
  my $parser = shift;
  my $sentence = shift;

  chop $sentence if ($sentence =~ /\n$/);

  # this seems to be necessary
  $| = 1; # $| = 0; # Flush
  pipe(*READER, *WRITER) || die "couldn't pipe";

  while (<STDIN>) {
    last if /^END INPUT FOREST/;
    print WRITER;
  }
  close WRITER || die "cannot close: $!";
  
  $self->handle_forest(\*READER,$input,$parser,$sentence,-1,undef);
}

				# Parsing the forest and emitting it in the selected format

sub handle_forest {
  my $self = shift;
  my $handle = shift;
  my $inputformat = shift;
  my $sparser = shift;
  my $parser = $self->{parser}{$sparser}{label} || $sparser;
  my $sentence = shift;
  my $time = shift;
  my $grammar = shift;

  $self->{last}{time}=$time;
  $self->{last}{sentence}=$sentence;
  $self->{last}{parser}=$sparser;

  my $outputformat = $options{'forest'}{'value'};
  
  if ($outputformat eq 'raw') {
    $self->{last}{forest} = '';
    print MYSTDOUT "TIME $time\nPARSER $parser\nSENTENCE $sentence\n";
    raw_output($handle);
  } else {
    $self->log(3,"Reading forest");
    my $forest = &{$inputs{$inputformat}}($handle,
					  'sentence' => $sentence,
					  'time' => $time,
					  'parser' => $parser
					 );
    $self->{last}{forest} = $forest;
    $self->log(3,"Printing forest");
    open(STDOUT,">&MYSTDOUT") || die "can't dup MYSTDOUT: $!";
    $forest->{grammar} =  $grammar if (defined $grammar);
    &{$outputs{$outputformat}}($forest,$self,$sparser);
    close(STDOUT);
    open(STDOUT,'>/dev/null') || die "Can't close STDOUT [$!]";
    $self->log(3,"Done forest");
  }
  close $handle || die "cannot close: $!";
}


				# Lexers
sub dummy_lexer {
  my $writer = shift;
  my $sentence = shift;
  print $writer "$sentence";
}

sub lexer {

    my $self = shift;
    my $writer = shift;
    my $sentence = shift;
    my $i = 0;

    foreach ($sentence =~ /\S+/g) {
        last if (/\#/);
        if (/^(\d+)$/) {
            print $writer "'C'($i,$1,",++$i,").\n";
        } else {
	  print $writer "'C'($i,'",$_,"',",++$i,").\n" ;
        }
      }
    print $writer "'N'(",$i,").\n";
}

sub remote_lexer {
    my $self = shift;
    my $writer = shift;
    my $sentence = shift;
    my $parser = shift;
    my @lexer = @{$self->{parser}{$parser}{'remote_lexer'}};
    $self->log(3,"In remote lexer sentence = $sentence parser=$parser");
    open2(*LEXOUT,*LEXIN,@lexer) || die "couldn't run lexer";
    LEXOUT->autoflush(1);
    LEXIN->autoflush(1);
    print LEXIN "$sentence";
    close LEXIN || die "can't close: $!";
    print $writer <LEXOUT>;
    close LEXOUT || die "can't close: $!";
}

sub persistent_lexer {
  my $self = shift;
  my $writer = shift;
  my $sentence = shift;
  my $parser = shift;
  my $lexer = $self->{parser}{$parser}{'remote_lexer'};
  my $key = join(' ',@$lexer);
  if (!exists $self->{persistent}{lexer}{$key}) {
    ## Start a persistent lexer
    ## More than one one parser may share the same lexer
    my $plexer = $self->{persistent}{lexer}{$key} = {};
    $self->log(3,"Starting a persistent lexer: $key");
    $plexer->{lexin} = '';
    $plexer->{lexout} = '';
    $plexer->{lexerr} = '';
    $plexer->{pid} = 
      start $lexer, 
	'<',\$plexer->{lexin},
	  '>pty>',\$plexer->{lexout},
	    '2>',\$plexer->{lexerr},
	      || die "couldn't run lexer";
  }
  ## use a persistent lexer
  my $plexer = $self->{persistent}{lexer}{$key};
  $self->log(3,"Using persistent lexer $key: sentence=$sentence parser=$parser");
  $plexer->{lexin} = "$sentence\n";
  $plexer->{pid}->pump until ($plexer->{lexout} =~ /SENTENCE\s+DIV/o);
  $plexer->{lexout} =~ s/\r\n/\n/og;
  $self->log(4,"Get from lexer\n$plexer->{lexout}");
  print $writer $plexer->{lexout};
  $self->{last}{lexer} = "\n$plexer->{lexout}";
  $plexer->{lexout} = '';
}

				# Writing forests

sub raw_output {
    my $handle = shift;
    print MYSTDOUT <$handle>;
}

sub xml_output {
    my $forest = shift;
    $forest->pretty_print(0);
}

sub html_output {
    my $forest = shift;
    $forest->html;
}

sub dot_output {
    my $forest = shift;
    $forest->dot;
}

sub dependency_output {
    my $forest = shift;
    $forest->dependency;
}

sub xmldep_output {
    my $forest = shift;
    $forest->xmldep;
}

sub lpdep_output {
    my $forest = shift;
    $forest->lpdep;
}

sub tagger_output {
    my $forest = shift;
    $forest->tagger;
}

sub yesno_output {
  my $forest = shift;
  my $status = "Failure";
  if ($forest->{nb} >= 0) {
    $status = "Success";
    $status = "Partial" if ($forest->{mode} eq 'robust');
  }
  print MYSTDOUT "$status\n";
}

sub stats_output {
    my $forest = shift;
    $forest->stats;
}

sub depnorm_output {
    my $forest = shift;
    $forest->dependency_normalize;
}

sub line_output {
    my $forest = shift;
    $forest->line;
}

sub easy_output {
  my ($forest,$self,$parser) = @_;
  if (!exists $self->{parser}{$parser}{easy}) {
    print "EASy output format not available for this parser:\n\t$parser\n";
    return;
  }
  my $easycmd = $self->{parser}{$parser}{easy};
  $easycmd .= " -e $self->{last}{sid}" if (defined $self->{last}{sid});
  my $pid = open2(*EASYOUT,*EASYIN,$easycmd) || die "couldn't run easycmd";
  push(@{$self->{child}},$pid);
  EASYOUT->autoflush(1);
  EASYIN->autoflush(1);
  open(STDOUT,">&EASYIN") || die "can't dup EASYIN: $!";
  $forest->xmldep;
  close STDOUT || die "can't close: $!";
  close EASYIN || die "can't close: $!";
  open(STDOUT,">&MYSTDOUT") || die "can't dup MYSTDOUT: $!";
  while (<EASYOUT>) {
    next if /^\s*$/;
    print;
  }
  close EASYOUT || die "can't close: $!";
}

sub clean_sentence {
    my $sentence=shift;
    $sentence =~ s/\s+/ /og;
    $sentence =~ s/^\s+//og;
    $sentence =~ s/\s+$//og;
    return $sentence;
}

######################################################################
# Handling configuration file

sub split_options {
  my $self = shift;
  my $options = shift;
  return [ split(/\s+/,$options) ];
}

sub get_lexer {
  my $self = shift;
  my $lexer = shift;
  my $info = shift;
  my @lexer = split(/\s+/,$lexer);
  $lexer = shift @lexer;
  if ($lexer eq 'default') {
	return \&lexer;
      } elsif ($lexer eq 'remote') {
	$info->{'remote_lexer'} = [ @lexer ] ;
	return \&remote_lexer;
      } elsif ($lexer eq 'persistent') {
	$info->{'remote_lexer'} = [ @lexer ] ;
	return \&persistent_lexer;
      } elsif ($lexer eq 'dummy') {
	return \&dummy_lexer;
      } else {
	die "bad configuration file";
      }
}

sub open_examples {
  my $self = shift;
  my $file = shift;
  open(EXAMPLES,"<$file") || return [];
  my @sentences = ();
  while (<EXAMPLES>) {
    next if (/^\d*\s*$/);
    next if (/^\#/);
    push(@sentences, clean_sentence($_));
  }
  close(EXAMPLES);
  return [@sentences];
}

sub notify_cfg {
  my $state = shift;
    my $var   = shift;
  my $val   = shift;
  
  print "$var set to $val\n";
}

######################################################################
# Start the server

print "Running the server\n";

__PACKAGE__->run(
		 port => $config->port(),
                 log_file => $config->log_file(),
                 syslog_ident => $config->syslog_ident(),
                 log_level => $config->log_level(),
                 user => $config->user(),
                 group => $config->group(),
                 pid_file => $config->pid_file(),
                 setsid   => $config->setsid,
                 maxclients => $config->maxclients()
		 );

exit;

## Need to kill descendant because of command time
## kiliing 'time' do not kill descendant of it !
sub killgroup {
  my $self = shift;
  my $signal = shift;
  $signal = 9 if (!defined $signal);
  my @pids = @{$self->{child}};
  ## Then look for live descendant and kill them
  my $jobs = `/bin/ps axlr`;
  foreach (split(/\n/,$jobs)) {
    ##    print MYSTDOUT "$_\n";
    my ($pid,$ppid) = (split(/\s+/,$_))[2,3];
    ##    print MYSTDOUT "found $pid son of $ppid\n";
    if (grep( $_ == $ppid, @pids)) {
      ##      print MYSTDOUT "Going to kill $pid\n";
      kill $signal, $pid;
    }
  }
  kill $signal, @pids;
}

1;

__END__

=head1 NAME

parserd - A perl script to run a daemon server of parsers

=head1 SYNOPSIS

parserd [<port>];

=head1 DESCRIPTION

parserd starts a daemonized server of parsers. The default port is
8999. It is configured to access several parsers built with DyALog,
but other can easily be added.

=head1 AUTHOR

Eric de la Clergerie, Eric.De_La_Clergerie@inria.fr

=head1 SEE ALSO

perl (1)
parser_client (1)
parser.cgi 

DyALog <http://atoll.inria.fr/~clerger>

=cut
