#!/usr/bin/env perl
##########################################
# This program launches the yote server. #
##########################################
# note, use forks inside of the Yote package should be called before use strict or use warnings.
use strict;
use warnings;
no warnings 'uninitialized';
use vars qw($VERSION);
use Daemon::Daemonize qw/ :all /;
use Data::Dumper;
use IO::Socket;
use Yote;
$VERSION = '0.00021';
$SIG{ __DIE__ } = sub {
Carp::confess( @_ );
};
my $args = Yote::get_args();
my $config = $args->{ config };
my $cmd = $args->{ command };
my( $web_socket, $internal_socket );
if( $config->{ profile } ) {
require Yote::PerfAspect;
}
unshift @INC, "$config->{ yote_root }/lib";
my $pidfile = $config->{ pidfile } || '/var/run/yote.pid';
if( $cmd eq 'stop' ) {
if( my $pid = check_pidfile( $pidfile ) ) {
print "Stopping $0 ( $pid )\n";
kill 'SIGINT', $pid;
sleep 2;
print "Done\n";
exit;
}
print "yote is not running\n";
exit;
}
if( $cmd eq 'start' ) {
if( check_pidfile( $pidfile ) ) {
print "yote is already running\n";
exit;
}
start_yote();
exit;
}
if( $cmd eq 'restart' ) {
if( my $pid = check_pidfile( $pidfile ) ) {
print "Stopping $0\n";
kill 'SIGINT', $pid;
sleep 2;
print "Done\n";
}
start_yote();
exit;
}
if( $cmd eq 'status' ) {
print check_pidfile( $pidfile ) ? "yote is running\n" : "yote is not running\n";
exit;
}
# ------------------------------------------------------------------
my $in_shutdown = 0;
my( $serv_proc, $cron_proc, $eng_proc );
sub start_yote {
#
# open internal socket
#
until( $internal_socket = new IO::Socket::INET(
Listen => 10,
# TODO : make sure there is an internal_port
LocalPort => $config->{internal_port}
) )
{
if( $! =~ /Address already in use/i ) {
print STDERR "Address '$config->{internal_port}' already in use. Retrying.\n";
sleep( 5 );
} else {
die "Unable to start internal socket : $!";
}
} # until connection established
print STDERR "Connected engine socket.\n";
print STDERR Data::Dumper->Dump(["ENG SOCK", $internal_socket]);
$config->{ internal_socket } = $internal_socket;
#
# open webserver socket
#
until( $web_socket ) {
$web_socket = new IO::Socket::INET(Listen => 10, LocalPort => $config->{port});
unless( $web_socket ) {
if( $! =~ /Address already in use/i ) {
print STDERR "Address already in use. Retrying.\n";
sleep( 5 );
} else {
die "Unable to start webserver socket : $!";
}
}
}
print STDERR "Connected web socket.\n";
$config->{ web_socket } = $web_socket;
daemonize( close => 0 );
write_pidfile( $pidfile );
print STDERR Data::Dumper->Dump(["WROTE PIDFILE $pidfile $$"]);
$SIG{ INT } = $SIG{ TERM } = sub {
print STDERR "Got term or int signal\n";
$in_shutdown = 1;
for my $cpid ($cron_proc, $eng_proc, $serv_proc) {
next unless $cpid;
print STDERR Data::Dumper->Dump(["KILLING '$cpid'"]);
kill 'SIGINT', $cpid;
}
sleep 1;
while( (my $cpid = waitpid( -1, 0 )) > 0 ) {
}
print STDERR "cleaned up processes\n";
$internal_socket->close if $internal_socket;
$web_socket->close if $web_socket;
print STDERR "cleaned up sockets\n";
exit;
};
start_engine();
start_server();
start_cron(); #TODO - enable this
$0 = 'yote';
print "Started yote\n";
while( ! $in_shutdown ) {
my $cpid = waitpid( -1, 0 );
if( $cpid > 0 ) {
if( $cron_proc == $cpid ) {
start_cron();
}
elsif( $eng_proc == $cpid ) {
# TODO - maybe shut down or wait a bit.
start_engine( 1 );
}
elsif( $serv_proc == $cpid ) {
start_server( 1 );
}
else {
# TODO - error for unknown pid
}
}
}
print "Exiting program\n";
} #start_yote
sub start_server {
my $reconnect_socket = shift;
if( $reconnect_socket ) {
$web_socket->close if $web_socket;
until( $web_socket ) {
$web_socket = new IO::Socket::INET(Listen => 10, LocalPort => $config->{port});
unless( $web_socket ) {
if( $! =~ /Address already in use/i ) {
print STDERR "Address already in use. Retrying.\n";
sleep( 5 );
} else {
print STDERR "Unable to restart webserver socket : $!";
exit;
}
}
}
}
my $cpid = fork;
if( $cpid > 0 ) {
$serv_proc = $cpid;
} elsif( defined $cpid ) { #child
$0 = 'yote appserver manager';
print STDERR Data::Dumper->Dump(["Starting server manager process"]);
require Yote::WebAppServer;
my $server = Yote::WebAppServer->new( $config );
$server->start();
} else {
print STDERR "$0 : Unable to fork server process\n";
}
} #start_server
sub start_cron {
my $cpid = fork;
if( $cpid > 0 ) {
$cron_proc = $cpid;
} elsif( defined $cpid ) { #child
$0 = 'yote cron';
# TODO : handle signals in the cron itself, it will know how to handle wrap up
$SIG{ TERM } = $SIG{ INT } = $SIG{ PIPE } = sub { print STDERR "$0 $$ got signal. exiting\n"; exit; };
require Yote::Cron;
Yote::Cron::start( $config );
} else {
print STDERR "$0 : Unable to fork cron process\n";
}
} #start_cron
#
# The 'engine' in this case is a single process that executes and communicates to the db. The other processes
# communicate via sockets to this one.
#
sub start_engine {
my $reconnect_socket = shift;
if( $reconnect_socket ) {
$internal_socket->close if $internal_socket;
until( $internal_socket = new IO::Socket::INT(
Listen => 10,
# TODO : make sure there is an internal_port
LocalPort => $config->{internal_port}
) )
{
if( $! =~ /Address already in use/i ) {
print STDERR "Address '$config->{internal_port}' already in use. Retrying.\n";
sleep( 5 );
} else {
print STDERR "Unable to restart internal socket : $!";
exit;
}
} # until connection established
print STDERR "Connected engine socket.\n";
print STDERR Data::Dumper->Dump(["ENG SOCK", $internal_socket]);
$config->{ internal_socket } = $internal_socket;
}
my $cpid = fork;
if( $cpid > 0 ) {
$eng_proc = $cpid;
} elsif( defined $cpid ) { #child
$0 = 'yote engine';
# TODO : handle signals in the engine itself, it will know how to handle wrap up
$SIG{ TERM } = $SIG{ INT } = $SIG{ PIPE } = sub { print STDERR "$0 $$ got signal. exiting\n"; exit; };
require Yote::Engine;
eval {
Yote::Engine::start( $config );
};
if( $@ ) {
print STDERR Data::Dumper->Dump([$@]);
}
} else {
die "Unable to fork engine\n";
}
} #start_engine
__END__
=head1 NAME
yote_server - Turn on and off the Yote Server/Webserver
=head1 SYNOPSIS
The Yote server serves up web pages and IO for javascript Yote requests.
yote_server --help
yote_server --show_config
yote_server --generate # create new configuration and run yote
yote_server start # run yote
=head1 DESCRIPTION
This program is the Yote server. At the time of writing this is not daemonized
( there are some issues using the forks module together with daemonization ).
This uses the configuration in the yote.conf file in the yote root directory.
The yote root directory is set upon installation and is usually /opt/yote.
When yote is run for the first time, it asks a series of configuration questions.
These can be revisited by
=head1 FILES
yote.conf
=head1 DIAGNOSTICS
Though Yote has unit tests that are run upon install, its web based components are
written in javascript, and a javascript interpreter has not been created for this test
framework yet. There is a test that can be manually run. To run, start the yote server
and point a browser to http://localhost:yoteport/yote/unit_tests.html. Also included
are tests for file uploads at http://localhost:yoteport/yote/upload_test.html
=head1 CAVEATS
Most systems will require root permissions to run this.
Since this cannot be run at this time as a daemon, it can be run manually in a screen.
To stop the server, hit control C.
=head1 BUGS
There are no known bugs, but since this software is Beta or below, bugs are highly likely
to exist. Please inform the author if bugs are encountered.
=head1 AUTHOR
Eric Wolf
coyocanid@gmail.com
http://madyote.com
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2011-2013 Eric Wolf
This module is free software; it can be used under the same terms as perl
itself.
=cut