#!/usr/bin/perl # Main OpenTrafficShaper program # Copyright (C) 2007-2013, AllWorldIT # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. use strict; use warnings; # Set the dirs we look for library files in use lib('/usr/local/lib/opentrafficshaper-1.0','/usr/lib/opentrafficshaper-1.0', '/usr/lib64/opentrafficshaper-1.0','opentrafficshaper','awitpt'); # System stuff we need use Config::IniFiles; use Getopt::Long; use POE; use POSIX qw(setsid); use Time::HiRes qw(time); # Our own stuff use opentrafficshaper::version; use opentrafficshaper::logger; # Main config my $globals; # We just create the logger first, its only using STDERR here my $logger = new opentrafficshaper::logger; # # MAIN # parseCfgCmdLine(); # Check if we must use a log file instead if (defined($globals->{'config'}->{'log_file'})) { $logger->open($globals->{'config'}->{'log_file'}); } $logger->setLevel($globals->{'config'}->{'log_level'}); # Check if we need to go background if (defined($globals->{'config'}->{'background'})) { daemonize(); } displayBanner(); init(); start(); $logger->log(LOG_NOTICE,"[MAIN] Entering RUNNING state"); POE::Kernel->run(); exit; # Function to display banner sub displayBanner { $logger->log(LOG_NOTICE,"[MAIN] OpenTrafficShaper v".VERSION." - Copyright (c) 2007-2013, AllWorldIT"); } # Function to parse our config and commandline sub parseCfgCmdLine { # Set defaults my $cfg; $cfg->{'config_file'} = "/etc/opentrafficshaper.conf"; $cfg->{'background'} = "yes"; $cfg->{'pid_file'} = "/var/run/opentrafficshaper/opentrafficshaperd.pid"; $cfg->{'log_level'} = 2; $cfg->{'log_file'} = "/var/log/opentrafficshaper/opentrafficshaperd.log"; # Parse command line params my $cmdline; %{$cmdline} = (); GetOptions( \%{$cmdline}, "help", "config:s", "debug", "fg", ) or die "Error parsing commandline arguments"; # Check for some args if ($cmdline->{'help'}) { displayHelp(); exit 0; } if (defined($cmdline->{'config'}) && $cmdline->{'config'} ne "") { $cfg->{'config_file'} = $cmdline->{'config'}; } # Check config file exists if (! -f $cfg->{'config_file'}) { die("No configuration file '".$cfg->{'config_file'}."' found!\n"); } # Use config file, ignore case tie my %inifile, 'Config::IniFiles', ( -file => $cfg->{'config_file'}, -nocase => 1 ) or die "Failed to open config file '".$cfg->{'config_file'}."': $!"; # Copy config my %config = %inifile; # Pull in params for the server my @server_params = ( 'log_level','log_file', 'pid_file', 'background', ); foreach my $param (@server_params) { $cfg->{$param} = $config{'system'}{$param} if (defined($config{'system'}{$param})); } # Override if ($cmdline->{'debug'}) { $cfg->{'log_level'} = 4; $cfg->{'debug'} = 1; } # If we set on commandline for foreground, keep in foreground if ($cmdline->{'fg'} || (defined($config{'system'}{'background'}) && $config{'system'}{'background'} eq "no" )) { $cfg->{'background'} = undef; $cfg->{'log_file'} = undef; $cfg->{'pid_file'} = undef; } # # System plugins # if (ref($config{'plugins'}{'load'}) eq "ARRAY") { foreach my $plugin (@{$config{'plugins'}{'load'}}) { $plugin =~ s/\s+//g; # Skip comments next if ($plugin =~ /^#/); push(@{$cfg->{'plugin_list'}},$plugin); } } elsif (defined($config{'plugins'}{'load'})) { my @pluginList = split(/\s+/,$config{'plugins'}{'load'}); foreach my $plugin (@pluginList) { # Skip comments next if ($plugin =~ /^#/); push(@{$cfg->{'plugin_list'}},$plugin); } } $globals->{'file.config'} = \%config; $globals->{'config'} = $cfg; } # Display help sub displayHelp { displayBanner(); print(STDERR<<EOF); Usage: $0 [args] --config=<file> Configuration file --debug Put into debug mode --fg Don't go into background EOF } # Initialize things we need sub init { # Certain things we need $globals->{'users'} = { }; $globals->{'logger'} = $logger; $globals->{'version'} = VERSION; # Setup master session POE::Session->create( inline_states => { '_start' => \&main_session_start, '_stop' => \&main_session_stop, 'main_SIGHUP' => \&main_signal_SIGHUP, 'main_SIGINT' => \&main_signal_SIGINT, }, ); $logger->log(LOG_NOTICE,"[MAIN] Entering INITIALIZATION state"); $logger->log(LOG_INFO,"[MAIN] Initializing plugins..."); # Core configuration manager my $res = eval(" use opentrafficshaper::plugins::configmanager; plugin_register(\$globals,\"configmanager\",\$opentrafficshaper::plugins::configmanager::pluginInfo); "); if ($@ || (defined($res) && $res != 0)) { $logger->log(LOG_WARN,"[MAIN] Error loading config manager, things WILL BREAK! ($@)"); } # Load plugins foreach my $plugin (@{$globals->{'config'}->{'plugin_list'}}) { # Load plugin my $res = eval(" use opentrafficshaper::plugins::${plugin}::${plugin}; plugin_register(\$globals,\"${plugin}\",\$opentrafficshaper::plugins::${plugin}::pluginInfo); "); if ($@ || (defined($res) && $res != 0)) { $logger->log(LOG_WARN,"[MAIN] Error loading plugin '$plugin' ($@)"); } else { $logger->log(LOG_DEBUG,"[MAIN] Plugin '$plugin' loaded."); } } $logger->log(LOG_INFO,"[MAIN] Plugins initialized."); } # Register plugin info sub plugin_register { my ($globals,$plugin,$info) = @_; # If no info, return if (!defined($info)) { $logger->log(LOG_WARN,"WARNING: Plugin info not found for plugin => $plugin\n"); return -1; } # Set real module name & save $info->{'Plugin'} = $plugin; push(@{$globals->{'plugins'}},$info); # If we should, init the module if (defined($info->{'Init'})) { $info->{'Init'}($globals); } return 0; } # Function to start things up sub start { $logger->log(LOG_NOTICE,"[MAIN] Entering STARTING state"); # Loop with plugins and call the start function for those that exist foreach my $plugin (@{$globals->{'plugins'}}) { # Load the function up my $callStart = $plugin->{'Start'}; if (defined($callStart)) { $callStart->(); } } } # Become daemon sub daemonize { chdir '/' or die "Can't chdir to /: $!"; open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; open STDOUT, '> /dev/null' or die "Can't open stdout log: $!"; defined(my $pid = fork) or die "Can't fork: $!"; exit if $pid; # Write out our PID if we have a file to do it if (defined($globals->{'config'}->{'pid_file'})) { if (open(FH,"> ".$globals->{'config'}->{'pid_file'})) { print(FH $$); close(FH); } else { $logger->log(LOG_WARN,"[CORE] Unable to write PID to '".$globals->{'config'}->{'pid_file'}."': $!"); } } setsid or die "Can't start a new session: $!"; open STDERR, '> /dev/null' or die "Can't open stderr log: $!"; } # Function to fire up our main session sub main_session_start { my $kernel = $_[KERNEL]; $kernel->alias_set('main'); # Register signal handlers $kernel->sig('HUP', 'main_SIGHUP'); $kernel->sig('INT', 'main_SIGINT'); } # Function to dispose of anything as a final stage to shutting down sub main_session_stop { $logger->log(LOG_WARN,"[MAIN] Exiting"); } # Function to handle SIGHUP sub main_signal_SIGHUP { my ($kernel,$signal_name) = @_[KERNEL,ARG0]; $logger->log(LOG_WARN,"[MAIN] Got SIGHUP, triggering config reload"); # Loop with plugins and call the handler for SIGHUP foreach my $plugin (@{$globals->{'plugins'}}) { # Load the function up my $callSIGHUP = $plugin->{'signal_SIGHUP'}; if (defined($callSIGHUP)) { $callSIGHUP->(); } } $kernel->sig_handled(); } # Function to handle SIGINT sub main_signal_SIGINT { my ($kernel, $signal_name) = @_[KERNEL, ARG0]; $logger->log(LOG_WARN,"[MAIN] Got SIGINT, shutting down..."); # Loop with plugins and call the handler for SIGINT foreach my $plugin (@{$globals->{'plugins'}}) { # Load the function up my $callSIGINT = $plugin->{'signal_SIGINT'}; if (defined($callSIGINT)) { $callSIGINT->(); } } $kernel->signal($kernel,'handle_SIGINT'); } # vim: ts=4