#!/usr/bin/perl -w
use lib qw(. ./lib);
use DTA::CAB;
use DTA::CAB::Server::HTTP;
use DTA::CAB::Server::HTTP::UNIX; ##-- debug
use DTA::CAB::Utils qw(:version);
use IO::Socket::INET;
use Encode qw(encode decode);
use File::Basename qw(basename);
use Getopt::Long qw(:config no_ignore_case);
use Cwd qw(getcwd abs_path);
use Pod::Usage;
use strict;
##==============================================================================
## Constants & Globals
##==============================================================================
##-- program identity
our $prog = basename($0);
our $VERSION = $DTA::CAB::VERSION;
##-- General Options
our ($help,$man,$version);
our $verbose = 'INFO'; ##-- default log level
#BEGIN {
# binmode($DB::OUT,':utf8') if (defined($DB::OUT));
# binmode(STDIN, ':utf8');
# binmode(STDOUT,':utf8');
# binmode(STDERR,':utf8');
#}
no warnings 'utf8';
##-- Server config
our $serverConfigFile = undef;
our $serverClass = 'DTA::CAB::Server::HTTP';
our $serverHost = undef;
our $serverPort = undef;
our $serverSocketPath = undef;
our $serverSocketUser = undef;
our $serverSocketGroup = undef;
our $serverSocketPerms = '0666';
##-- Daemon mode options
our $daemonMode = 0; ##-- do a fork() ?
our $pidFile = undef; ##-- save PID to a file?
our $forceStart = undef; ##-- force start (overwrite old PID file?)
##-- default log level
#$DTA::CAB::Logger::defaultLogOpts{level}='INFO';
##==============================================================================
## Command-line
GetOptions(##-- General
'help|h' => \$help,
'man|m' => \$man,
'version|V' => \$version,
#'verbose|v=s' => \$verbose, ##-- see '-log-level' option
##-- Server configuration
'config|c=s' => \$serverConfigFile,
'tcp' => sub { $serverClass='DTA::CAB::Server::HTTP'; },
'unix' => sub { $serverClass='DTA::CAB::Server::HTTP::UNIX'; },
'tcp-addr|addr|a|bind|b=s' => \$serverHost,
'tcp-port|port|p=i' => \$serverPort,
'unix-socket-path|unix-path|usp|us=s' => \$serverSocketPath,
'unix-perms|up=s' => \$serverSocketPerms,
'unix-user|uu=s' => \$serverSocketUser,
'unix-group|ug=s' => \$serverSocketGroup,
##-- Daemon mode options
'daemon|d|fork!' => \$daemonMode,
'pid-file|pidfile|pid|P=s' => \$pidFile,
'force!' => \$forceStart,
##-- Log4perl stuff
DTA::CAB::Logger->cabLogOptions('verbose'=>1),
);
if ($version) {
print cab_version;
exit(0);
}
pod2usage({-exitval=>0, -verbose=>1}) if ($man);
pod2usage({-exitval=>0, -verbose=>0}) if ($help);
##==============================================================================
## Subs
##==============================================================================
##--------------------------------------------------------------
## Subs: daemon-mode stuff
## CHLD_REAPER()
## + lifted from perlipc(1) manpage
sub CHLD_REAPER {
my $waitedpid = wait();
##-- remove pidfile if it contains the reaped process's PID
if (defined($pidFile) && -r $pidFile) {
if (open(PIDFILE,"<$pidFile")) {
my $filepid = <PIDFILE>;
close PIDFILE;
chomp($filepid);
unlink($pidFile) if ($filepid && $filepid == $waitedpid);
}
}
# loathe sysV: it makes us not only reinstate
# the handler, but place it after the wait
$SIG{CHLD} = \&CHLD_REAPER;
}
##==============================================================================
## MAIN
##==============================================================================
##-- check for daemon mode
##-- log4perl initialization
DTA::CAB::Logger->logInit();
##-- create / load server object
my $module = $serverClass;
$module =~ s{::}{/}g;
require "$module.pm"
or DTA::CAB->logdie("failed to load server module $module.pm: $@");
our $srv = $serverClass->new(pidfile=>$pidFile);
$srv = $srv->loadFile($serverConfigFile) if (defined($serverConfigFile));
$srv->{daemonArgs}{LocalHost} = $serverHost if (defined($serverHost));
$srv->{daemonArgs}{LocalPort} = $serverPort if (defined($serverPort));
$srv->{daemonArgs}{Local} = $serverSocketPath if (defined($serverSocketPath));
$srv->{socketUser} = $serverSocketUser if (($serverSocketUser//'') ne '');
$srv->{socketGroup} = $serverSocketGroup if (($serverSocketGroup//'') ne '');
$srv->{socketPerms} = $serverSocketPerms if (($serverSocketPerms//'') ne '');
##-- serverMain(): main post-preparation code; run in subprocess if we're in daemon mode
sub serverMain {
##-- prepare & run server
$srv->info("serverMain(): initializing ", ref($srv), " on ", $srv->socketLabel);
$srv->info("serverMain(): using DTA::CAB version $DTA::CAB::VERSION");
$srv->info("serverMain(): CWD ", abs_path(getcwd));
$srv->prepare()
or $srv->logdie("prepare() failed!");
$srv->run();
$srv->finish();
$srv->info("exiting");
}
##-- check whether we can really bind the socket
DTA::CAB->logdie("cannot bind socket ", $srv->socketLabel, ": $!")
if (!$srv->canBindSocket);
##-- check for existing PID file (don't overrwrite)
if (defined($pidFile) && -e $pidFile) {
if ($forceStart) {
$srv->logwarn("serverMain(): PID-file '$pidFile' exists but -force specified: clobbering as requested");
} else {
$srv->logdie("serverMain(): PID-file '$pidFile' exists: NOT starting a new server (use -force to override)");
}
}
##-- check for daemon mode
if ($daemonMode) {
$SIG{CHLD} = \&CHLD_REAPER; ##-- set handler
my ($pid);
if ( ($pid=fork()) ) {
##-- parent
DTA::CAB->info("spawned daemon subprocess with PID=$pid\n");
} else {
##-- daemon-child
DTA::CAB->logdie("$prog: fork() failed: $!") if (!defined($pid));
serverMain();
}
} else {
##-- just run server
serverMain();
}
__END__
=pod
=head1 NAME
dta-cab-http-server.perl - standalone HTTP server for DTA::CAB queries
=head1 SYNOPSIS
dta-cab-http-server.perl [OPTIONS...]
General Options:
-help ##-- show short usage summary
-man ##-- show longer help message
-version ##-- show version & exit
-verbose LEVEL ##-- really just an alias for -log-level=LEVEL
Server Configuration Options:
-config PLFILE ##-- load server config from PLFILE
-tcp , -unix ##-- set socket type (default=-tcp)
-bind HOST ##-- override TCP host to bind or relay (default=all)
-port PORT ##-- override TCP port to bind or relay (default=8088)
-unix-socket PATH ##-- override UNIX socket path to bind (default=none)
-unix-user USER ##-- override UNIX socket ownershiip (default=current)
-unix-group GROUP ##-- override UNIX socket group (default=current)
-unix-perms PERMS ##-- override UNIX socket permissions (default=0666)
Daemon Mode Options:
-pidfile PIDFILE ##-- save server PID to PIDFILE
-daemon , -nodaemon ##-- do/don't fork() a server subprocess
-force , -noforce ##-- do/don't overwrite existing PIDFILE (default=don't)
Logging Options: ##-- see Log::Log4perl(3pm)
-log-level LEVEL ##-- set minimum log level (internal config only)
-log-file LOGFILE ##-- log to file LOGFILE (default: none)
-log-stderr , -nolog-stderr ##-- do/don't log to stderr (default: do)
-log-rotate , -no-rotate ##-- do/don't auto-rotate logs (default: if available)
-log-syslog , -no-syslog ##-- do/don't log to syslog (default: don't)
-log-config L4PFILE ##-- override log4perl config file
-log-watch SECONDS_OR_SIGNAL ##-- override: watch L4PFILE (delay SECONDS or on SIGNAL)
-nolog-watch ##-- override: don't watch L4PFILE
-log-option OPT=VALUE ##-- set any logging option (e.g. -log-option twlevel=trace)
=cut
##==============================================================================
## Description
##==============================================================================
=pod
=head1 DESCRIPTION
dta-cab-http-server.perl is a command-line utility for starting
a standalone HTTP server to perform L<DTA::CAB|DTA::CAB> token-, sentence-, and/or document-analysis
using the L<DTA::CAB::Server::HTTP|DTA::CAB::Server::HTTP>
module.
See L<dta-cab-http-client.perl(1)|dta-cab-http-client.perl> for a
command-line client using the L<DTA::CAB::Client::HTTP|DTA::CAB::Client::HTTP> module.
=cut
##==============================================================================
## Options and Arguments
##==============================================================================
=pod
=head1 OPTIONS AND ARGUMENTS
=cut
##==============================================================================
## Options: General Options
=pod
=head2 General Options
=over 4
=item -help
Display a short help message and exit.
=item -man
Display a longer help message and exit.
=item -version
Display program and module version information and exit.
=item -verbose LEVEL
Alias for L<-log-level LEVEL>.
=back
=cut
##==============================================================================
## Options: Server Configuration Options
=pod
=head2 Server Configuration Options
=over 4
=item -config PLFILE
Load server configuration from PLFILE,
which should be a perl source file parseable
by L<DTA::CAB::Persistent::loadFile()|DTA::CAB::Persistent/item_loadFile>
as a L<DTA::CAB::Server::HTTP|DTA::CAB::Server::HTTP> object.
=item -bind HOST
Override host on which to bind server socket.
Default is to bind on all interfaces of the current host.
=item -port PORT
Override port number to which to bind the server socket.
Default is whatever
L<DTA::CAB::Server::HTTP|DTA::CAB::Server::HTTP>
defaults to (usually 8088).
=back
=cut
##==============================================================================
## Options: Daemon Mode Options
=pod
=head2 Daemon Mode Options
=over 4
=item -daemon , -nodaemon
Do/don't fork() a server subprocess (default: don't).
If running in daemon mode, the program should simply spawn
a single server subprocess and exit, reporting the PID
of the child process.
Useful for starting persistent servers from system-wide
init scripts. See also L</"-pidfile FILE">.
=item -pidfile FILE
Writes PID of the server process to FILE before running the
server. Useful for system init scripts.
=back
=cut
##==============================================================================
## Options: Logging Options
=pod
=head2 Logging Options
The L<DTA::CAB|DTA::CAB> family of modules uses
the Log::Log4perl logging mechanism.
See L<Log::Log4perl(3pm)|Log::Log4perl> for details
on the general logging mechanism.
=over 4
=item -log-level LEVEL
Set minimum log level. Has no effect if you also specify L</-log-config>.
Known levels: (trace|debug|info|warn|error|fatal).
=item -log-config L4PFILE
User log4perl config file L4PFILE.
Default behavior uses the log configuration
string returned by L<DTA::CAB::Logger-E<gt>defaultLogConf()|DTA::CAB::Logger/item_defaultLogConf>.
=item -log-watch , -nowatch
Do/don't watch log4perl config file (default=don't).
Only sensible if you also specify L</-log-config>.
=back
=cut
##======================================================================
## Footer
##======================================================================
=pod
=head1 ACKNOWLEDGEMENTS
Perl by Larry Wall.
RPC::XML by Randy J. Ray.
=head1 AUTHOR
Bryan Jurish E<lt>moocow@cpan.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2010-2019 by Bryan Jurish. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.24.1 or,
at your option, any later version of Perl 5 you may have available.
=head1 SEE ALSO
L<dta-cab-analyze.perl(1)|dta-cab-analyze.perl>,
L<dta-cab-convert.perl(1)|dta-cab-convert.perl>,
L<dta-cab-http-server.perl(1)|dta-cab-http-server.perl>,
L<dta-cab-http-client.perl(1)|dta-cab-http-client.perl>,
L<dta-cab-xmlrpc-server.perl(1)|dta-cab-xmlrpc-server.perl>,
L<dta-cab-xmlrpc-client.perl(1)|dta-cab-xmlrpc-client.perl>,
L<DTA::CAB(3pm)|DTA::CAB>,
L<HTTP::Daemon(3pm)|HTTP::Daemon>,
L<perl(1)|perl>,
...
=cut