2008-06-17 Sancho Lerena <slerena@artica.es>

* config.pm: Added options for xprobe2, and autocreate.

	* tools.pm: Added functions pandora_create_agent(), pandora_get_os(),
	and pandora_event() (this has been moved from DB.pm).

	* pandora_server.conf: Added options for xprobe2, autocreate and
	autocreate_group.

	* pandora_network: Added support for TCP scanning (not implemented in
	console yet), parent detection using traceroute, OS fingerprinting with
	xprobe2, event creation using central functions, and some optimizations.

	* pandora_server: Added support for agent autocreation, OS detection from
	the XML header, and event notification "new_agent" type.

	* pandora_recon: First code to implement traceroute functionality
	using Pureperl module.  Added code for remote OS fingerprinting.





git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@874 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f
This commit is contained in:
slerena 2008-06-17 17:26:03 +00:00
parent af7d490070
commit 8d2fbe799c
8 changed files with 334 additions and 114 deletions

View File

@ -1,5 +1,20 @@
2008-06-17 Sancho Lerena <slerena@artica.es>
* config.pm: Added options for xprobe2, and autocreate.
* tools.pm: Added functions pandora_create_agent(), pandora_get_os(),
and pandora_event() (this has been moved from DB.pm).
* pandora_server.conf: Added options for xprobe2, autocreate and
autocreate_group.
* pandora_network: Added support for TCP scanning (not implemented in
console yet), parent detection using traceroute, OS fingerprinting with
xprobe2, event creation using central functions, and some optimizations.
* pandora_server: Added support for agent autocreation, OS detection from
the XML header, and event notification "new_agent" type.
* pandora_recon: First code to implement traceroute functionality
using Pureperl module. Added code for remote OS fingerprinting.

View File

@ -56,6 +56,7 @@ pandora_loadconfig (\%pa_config, 3);
# Audit server starting
pandora_audit (\%pa_config, "Pandora FMS Recon Daemon starting", "SYSTEM", "System");
sleep(1);
# Daemonize and put in background
@ -73,6 +74,7 @@ sleep(1);
# Connect ONCE to Database, we cannot pass DBI handler to all subprocess because
# cannot share DBI between threads without use method CLONE.
my $pa_config = \%pa_config;
my $dbhost = $pa_config->{'dbhost'};
my $dbuser = $pa_config->{'dbuser'};
@ -142,42 +144,12 @@ sub pandora_detect_os {
my $pa_config = $_[0];
my $host = $_[1];
# my $pa_config->{"xprobe2"} = "/usr/bin/xprobe2";
# my $xprobe2 = $pa_config->{"xprobe2"};
my $xprobe2 = "/usr/bin/xprobe2";
my $xprobe2 = $pa_config->{"xprobe2"};
if (! -e $xprobe2){
return 10;
}
my $command = `$xprobe2 $host 2> /dev/null | grep "Running OS" | head -1`;
if ($command =~ m/Windows/i){
return 9;
}
elsif ($command =~ m/Linux/i){
return 1;
}
elsif ($command =~ m/BSD/i){
return 4;
}
elsif ($command =~ m/Cisco/i){
return 7;
}
elsif ($command =~ m/SunOS/i){
return 2;
}
elsif ($command =~ m/Solaris/i){
return 2;
}
elsif ($command =~ m/AIX/i){
return 3;
}
elsif ($command =~ m/HP-UX/i){
return 5;
}
else {
return 10;
}
return pandora_get_os ($command);
}
##########################################################################
# SUB pandora_exec_task (pa_config, id_task)
@ -205,6 +177,8 @@ sub pandora_recon_exec_task {
my $status = $sql_data[10];
my $interval = $sql_data[11];
my $network_server_assigned = $sql_data[12];
my $extended_info = $sql_data[13];
my $extended_value = $sql_data[14];
my $target_network = $sql_data[4];
my $task_name = $sql_data[1];
my $position = 0;
@ -215,6 +189,8 @@ sub pandora_recon_exec_task {
my $list_ip = "";
my $list_host = "";
my $host_found = 0;
my $add_host = 0;
my $id_parent = 0;
# Asign target dir to netaddr object "space"
$space = new NetAddr::IP $target_network;
@ -232,27 +208,45 @@ sub pandora_recon_exec_task {
$target_ip = $ip2[0];
$space++; $position++;
$add_host = 0;
# Check ICMP for this IP
if (($task_type == 1) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1)){
$add_host = 1;
}
# Check TCP port for this IP
elsif (($task_type == 2) && (scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1)) {
if (scan_tcp ($target_ip, $pa_config->{'networktimeout'}, $extended_value) == 1){
$add_host = 1;
}
}
if ($add_host == 1){
# Is this IP listed for any agent ?
if (pandora_check_ip ($pa_config, $dbh, $target_ip) == 0){
$host_found ++;
my $target_ip_resolved = resolv_ip2name($target_ip);
$list_ip = $list_ip." ".$target_ip;
$list_host = $list_host." ".resolv_ip2name($target_ip_resolved);
$id_parent = pandora_getparent ($pa_config, $target_ip, $dbh);
# If has a network profile, create agent and modules
my $agent_id;
if ($task_ncprofile > 0){
# Create address, agent and more...
my $target_ip_id = pandora_task_create_address ($pa_config, $dbh, $id_task, $target_ip);
$agent_id = pandora_task_create_agent($pa_config, $dbh, $target_ip, $target_ip_id, $task_group, $network_server_assigned, $target_ip_resolved);
$agent_id = pandora_task_create_agent($pa_config, $dbh, $target_ip, $target_ip_id, $task_group, $network_server_assigned, $target_ip_resolved, $id_parent);
pandora_task_create_agentmodules($pa_config, $dbh, $agent_id, $task_ncprofile, $target_ip);
} else {
my $target_ip_id = pandora_task_create_address ($pa_config, $dbh, $id_task, $target_ip);
$agent_id = pandora_task_create_agent($pa_config, $dbh, $target_ip, $target_ip_id, $task_group,
$network_server_assigned, $target_ip_resolved, $id_parent);
}
my $title = "[RECON] New host [$target_ip_resolved] detected on network [$target_network]";
# Always create event about this detected IP
pandora_event ($pa_config, $title,$task_group, $agent_id, 2, 0, 0, 'recon_host_detected', $dbh);
}
}
my $progress = ceil($position / ($total_hosts / 100));
pandora_update_reconstatus ($pa_config, $dbh, $id_task, $progress);
} while ($space < $space->broadcast); # fin del buclie principal de iteracion de Ips
@ -311,6 +305,30 @@ sub scan_icmp {
}
}
##############################################################################
# pandora_ping_icmp (destination, timeout) - Do a ICMP scan, 1 if alive, 0 if not
##############################################################################
sub scan_tcp {
my $dest = $_[0];
my $l_timeout = $_[1];
my $dest_port = $_[2];
my $opened = 0;
eval {
alarm $l_timeout;
my $handle=IO::Socket::INET->new(
Proto=>"tcp",
PeerAddr=>$dest,
PeerPort=>$dest_port);
if ($handle){
$opened =1;
}
alarm 0;
};
return $opened;
}
##########################################################################
# SUB resolv_ip2name (ip_address)
@ -352,6 +370,19 @@ sub pandora_check_ip {
}
}
##########################################################################
# SUB pandora_get_agent_from_ip (pa_config, dbh, ip_address)
# Return Id_agent for agent with IP_Address passed as parameter
##########################################################################
sub pandora_get_agent_from_ip {
my $pa_config = $_[0];
my $dbh = $_[1];
my $ip_address = $_[2];
return get_db_free_field ("SELECT id_agent FROM taddress, taddress_agent WHERE taddress_agent.id_a = taddress.id_a AND ip = '$ip_address'", $dbh);
}
##########################################################################
# SUB pandora_update_reconstatus (pa_config, dbh, id_task, status)
# Update recontask status flag
@ -403,21 +434,17 @@ sub pandora_task_create_address {
# it returns created id_agent.
##########################################################################
sub pandora_task_create_agent {
my $pa_config = $_[0];
my $dbh = $_[1];
my $target_ip = $_[2];
my $pa_config = $_[0];
my $dbh = $_[1];
my $target_ip = $_[2];
my $target_ip_id = $_[3];
my $id_group = $_[4];
my $id_server= $_[5];
my $name = $_[6];
my $id_group = $_[4];
my $id_server = $_[5];
my $name = $_[6];
my $id_parent = $_[7];
logger($pa_config,"Recon Server: Creating agent for ip $target_ip ",2);
my $query_sql2 = "INSERT INTO tagente (nombre, direccion, comentarios, id_grupo, id_os, id_network_server, intervalo) VALUES ('$name', '$target_ip', 'Autogenerated by Pandora FMS Recon Server', $id_group, 11, $id_server, 300)";
$dbh->do ($query_sql2);
my $lastid = $dbh->{'mysql_insertid'};
my $query_sql3 = "INSERT INTO taddress_agent (id_a, id_agent) values ($target_ip_id, $lastid)";
$dbh->do($query_sql3);
return $lastid;
my $id_os = pandora_detect_os ($pa_config, $target_ip);
return pandora_create_agent ($pa_config, $dbh, $target_ip, $target_ip_id, $id_group, $id_server, $name, $id_parent, $id_os);
}
##########################################################################
@ -473,7 +500,7 @@ sub pandora_task_create_agentmodules {
my $last_id_agente_modulo = $dbh->{'mysql_insertid'};
logger($pa_config,"Recon Server: Creating module $name for agent $ip_adress",3);
my $query_sql4;
if (($type == 2) || ($type == 6) || ($type == 9) || ($type == 12) || ($type == 18)) {
if (($type == 2) || ($type == 6) || ($type == 9) || ($type == 18)) {
# for monitors
$query_sql4 = "INSERT INTO tagente_estado (id_agente_modulo, datos, timestamp, cambio, estado, id_agente, last_try, utimestamp, current_interval, running_by) VALUES ($last_id_agente_modulo, '', '0000-00-00 00:00:00', 0, 0, $agent_id, '0000-00-00 00:00:00', 0, $interval, 0)";
} else {
@ -486,40 +513,38 @@ sub pandora_task_create_agentmodules {
}
$exec_sql->finish();
}
sub pandora_traceroute {
sub pandora_getparent ($$){
my $pa_config = $_[0];
my $destination = $_[1];
my $dbh = $_[2];
my $t = new Net::Traceroute::PurePerl(
backend => 'PurePerl', # this optional
host => '192.168.50.2',
host => $destination,
debug => 0,
max_ttl => 15,
query_timeout => 2,
query_timeout => $pa_config->{"networktimeout"},
packetlen => 40,
protocol => 'icmp', # udp or icmp
);
my $timeout = 15;
my $success = 0;
eval {
local $SIG{ALRM} = sub { die "alarm" };
alarm $timeout;
alarm $pa_config->{"networktimeout"};
$success = $t->traceroute();
alarm 0;
};
if ($@){
print "Traceroute timed out\n" if ($@ and $@ eq "alarm");
return 0;
}
if ($success){
print "Total of ".$t->hops." hops for that target \n";
for (my $ax=1; $ax <= $t->hops; $ax++){
print $t->hop_query_host($ax, 0);
print "\n";
}
}
if ($t->hops > 1){
if ($success){
my $parent_ip = $t->hop_query_host($t->hops-1,0);
return pandora_get_agent_from_ip ($pa_config, $dbh, $parent_ip);
}
}
return 0;
}
########################################################################################

View File

@ -309,19 +309,19 @@ sub keep_alive_check {
## param_1 : XML datafile name
sub procesa_datos {
my $pa_config = $_[0];
my $datos = $_[1];
my $dbh = $_[2];
my $tipo_modulo;
my $agent_name;
my $timestamp;
my $interval;
my $os_version;
my $agent_version;
my $id_agente;
my $module_name;
my $pa_config = $_[0];
my $datos = $_[1];
my $dbh = $_[2];
my $tipo_modulo;
my $agent_name;
my $timestamp;
my $interval;
my $os_version;
my $os;
my $agent_version;
my $id_agente;
my $module_name;
$agent_name = $datos->{'agent_name'};
$timestamp = $datos->{'timestamp'};
$agent_version = $datos->{'version'};
@ -341,9 +341,22 @@ sub procesa_datos {
if ((!defined ($os_version)) || (length($os_version) == 0)){
$os_version = "N/A";
}
if (defined $agent_name){
$id_agente = dame_agente_id($pa_config,$agent_name,$dbh);
if ($id_agente == -1){
if ($pa_config->{'autocreate'} == 1){
$os = pandora_get_os ($datos->{'os'});
$id_agente = pandora_create_agent ($pa_config, $dbh, "", 0, $pa_config->{'autocreate_group'}, 0, $datos->{'agent_name'}, 0, $os);
# Always create event about this detected IP
} else {
logger($pa_config, "ERROR: There is no agent defined with name $agent_name", 2);
}
}
if ($id_agente > 0) {
pandora_lastagentcontact ($pa_config, $timestamp, $agent_name, $os_version, $agent_version, $interval, $dbh);
foreach my $part(@{$datos->{module}}) {
@ -382,9 +395,7 @@ sub procesa_datos {
}
}
}
} else {
logger($pa_config, "ERROR: There is no agent defined with name $agent_name ($id_agente)", 2);
}
}
} else {
logger($pa_config, "ERROR: Received data from an unknown agent", 1);
}

View File

@ -164,3 +164,15 @@ mta_address localhost
# mta_pass: MTA Pass (if needed for auth)
# mta_auth: MTA Auth system (if needed, support: LOGIN PLAIN CRAM-MD5 DIGEST-MD)
# xprobe: If provided, is used to detect with recon server, OS fingerprint
# of detected hosts. Xprobe2 is a GNU tool to detect OS types.
xprobe2 /usr/bin/xprobe2
# Default group id for new agents created with Pandora FMS Data Server
autocreate_group 2
# Set to 1 if want to autocreate agents with Pandora FMS Data Server,
# set to 0 to disable
autocreate 1

View File

@ -147,7 +147,7 @@ sub pandora_loadconfig {
$pa_config->{"alert_threshold"} = 60;
$pa_config->{"logfile"} = "/var/log/pandora_server.log";
$pa_config->{"errorlogfile"} = "/var/log/pandora_server.error";
$pa_config->{"networktimeout"} = 15; # By default, not in config file yet
$pa_config->{"networktimeout"} = 5; # By default, not in config file yet
$pa_config->{"pandora_master"} = 1; # on by default
$pa_config->{"pandora_check"} = 0; # on by default
$pa_config->{"version"} = $pandora_version;
@ -190,6 +190,11 @@ sub pandora_loadconfig {
$pa_config->{"mta_auth"} = 'none'; # Introduced on 2.0 (Support LOGIN PLAIN CRAM-MD5 DIGEST-MD)
$pa_config->{"mta_from"} = 'pandora@localhost'; # Introduced on 2.0
# Xprobe2 for recon OS fingerprinting (optional feature to detect OS)
$pa_config->{"xprobe2"} = "/usr/bin/xprobe2";
$pa_config->{'autocreate_group'} = 2;
$pa_config->{'autocreate'} = 1;
# Check for UID0
if ($pa_config->{"quiet"} != 0){
if ($> == 0){
@ -277,7 +282,6 @@ sub pandora_loadconfig {
elsif ($parametro =~ m/^mta_from\s(.*)/i) {
$pa_config->{'mta_from'}= clean_blank($1);
}
elsif ($parametro =~ m/^snmp_logfile\s(.*)/i) {
$pa_config->{'snmp_logfile'}= clean_blank($1);
}
@ -384,6 +388,16 @@ sub pandora_loadconfig {
$pa_config->{"keepalive"} = clean_blank($1);
$pa_config->{"keepalive_orig"} = clean_blank($1);
}
elsif ($parametro =~ m/^xprobe2\s([.*]*)/i) {
$pa_config->{'xprobe2'}= clean_blank($1);
}
elsif ($parametro =~ m/^autocreate\s([0-9*]*)/i) {
$pa_config->{'autocreate'}= clean_blank($1);
}
elsif ($parametro =~ m/^autocreate_group\s([0-9*]*)/i) {
$pa_config->{'autocreate_group'}= clean_blank($1);
}
} # end of loop for parameter #

View File

@ -53,7 +53,6 @@ our @EXPORT = qw(
pandora_updateserver
pandora_serverkeepaliver
pandora_audit
pandora_event
pandora_lastagentcontact
pandora_writedata
pandora_writestate
@ -1332,33 +1331,7 @@ sub pandora_lastagentcontact (%$$$$$$) {
$sag ->finish();
}
##########################################################################
## SUB pandora_event
## Write in internal audit system an entry.
## Params: config_hash, event_title, group, agent_id, severity, id_alertam
## id_agentmodule, event_type (from a set, as string), db_handle
##########################################################################
sub pandora_event (%$$$$$$$$) {
my $pa_config = $_[0];
my $evento = $_[1];
my $id_grupo = $_[2];
my $id_agente = $_[3];
my $severity = $_[4]; # new in 2.0
my $id_alert_am = $_[5]; # new in 2.0
my $id_agentmodule = $_[6]; # new in 2.0
my $event_type = $_[7]; # new in 2.0
my $dbh = $_[8];
my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S");
my $utimestamp; # integer version of timestamp
$utimestamp = &UnixDate($timestamp,"%s"); # convert from human to integer
$evento = $dbh->quote($evento);
$event_type = $dbh->quote($event_type);
$timestamp = $dbh->quote($timestamp);
my $query = "INSERT INTO tevento (id_agente, id_grupo, evento, timestamp, estado, utimestamp, event_type, id_agentmodule, id_alert_am, criticity) VALUES ($id_agente, $id_grupo, $evento, $timestamp, 0, $utimestamp, $event_type, $id_agentmodule, $id_alert_am, $severity)";
$dbh->do($query);
}
##########################################################################
## SUB pandora_incident (pa_config, dbh, title, text, priority, status, origin, id_group
@ -1420,7 +1393,7 @@ sub pandora_audit (%$$$$) {
##########################################################################
sub dame_agente_id (%$$) {
my $pa_config = $_[0];
my $agent_name = $_[1];
my $agent_name = $_[1];
my $dbh = $_[2];
if ( (defined($agent_name)) && ($agent_name ne "") ){
@ -1944,9 +1917,7 @@ sub get_db_value ($$$$$) {
sub get_db_free_field ($$) {
my $condition = $_[0];
my $dbh = $_[1];
my $query = $condition;
my $s_idag = $dbh->prepare($query);
my $s_idag = $dbh->prepare($condition);
$s_idag ->execute;
if ($s_idag->rows != 0) {
my @data = $s_idag->fetchrow_array();

View File

@ -38,8 +38,76 @@ our @EXPORT = qw(
is_numeric
clean_blank
pandora_sendmail
pandora_create_agent
pandora_get_os
pandora_event
);
##########################################################################
## SUB pandora_event
## Write in internal audit system an entry.
## Params: config_hash, event_title, group, agent_id, severity, id_alertam
## id_agentmodule, event_type (from a set, as string), db_handle
##########################################################################
sub pandora_event (%$$$$$$$$) {
my $pa_config = $_[0];
my $evento = $_[1];
my $id_grupo = $_[2];
my $id_agente = $_[3];
my $severity = $_[4]; # new in 2.0
my $id_alert_am = $_[5]; # new in 2.0
my $id_agentmodule = $_[6]; # new in 2.0
my $event_type = $_[7]; # new in 2.0
my $dbh = $_[8];
my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S");
my $utimestamp; # integer version of timestamp
$utimestamp = &UnixDate($timestamp,"%s"); # convert from human to integer
$evento = $dbh->quote($evento);
$event_type = $dbh->quote($event_type);
$timestamp = $dbh->quote($timestamp);
my $query = "INSERT INTO tevento (id_agente, id_grupo, evento, timestamp, estado, utimestamp, event_type, id_agentmodule, id_alert_am, criticity) VALUES ($id_agente, $id_grupo, $evento, $timestamp, 0, $utimestamp, $event_type, $id_agentmodule, $id_alert_am, $severity)";
$dbh->do($query);
}
##########################################################################
# SUB pandora_get_os (string)
# Detect OS using a string, and return id_os
##########################################################################
sub pandora_get_os ($) {
$command = $_[0];
if ($command =~ m/Windows/i){
return 9;
}
elsif ($command =~ m/Linux/i){
return 1;
}
elsif ($command =~ m/BSD/i){
return 4;
}
elsif ($command =~ m/Cisco/i){
return 7;
}
elsif ($command =~ m/SunOS/i){
return 2;
}
elsif ($command =~ m/Solaris/i){
return 2;
}
elsif ($command =~ m/AIX/i){
return 3;
}
elsif ($command =~ m/HP-UX/i){
return 5;
}
else {
return 10; # Unknown / Other
}
}
##########################################################################
# Sub daemonize ()
@ -70,6 +138,38 @@ sub pandora_daemonize {
# Pandora other General functions |
# -------------------------------------------+
##########################################################################
# SUB pandora_create_agent (pa_config, dbh, target_ip, target_ip_id,
# id_group, network_server_assigned, name, id_os)
# Create agent, and associate address to agent in taddress_agent table.
# it returns created id_agent.
##########################################################################
sub pandora_create_agent {
my $pa_config = $_[0];
my $dbh = $_[1];
my $target_ip = $_[2];
my $target_ip_id = $_[3];
my $id_group = $_[4];
my $id_server= $_[5];
my $name = $_[6];
my $id_parent = $_[7];
my $id_os = $_[8];
my $server = $pa_config->{'servername'}.$pa_config->{"servermode"};
logger($pa_config,"$server: Creating agent $name $target_ip ", 1);
my $query_sql2 = "INSERT INTO tagente (nombre, direccion, comentarios, id_grupo, id_os, id_network_server, intervalo, id_parent) VALUES ('$name', '$target_ip', 'Created by $server', $id_group, $id_os, $id_server, 300, $id_parent)";
$dbh->do ($query_sql2);
my $lastid = $dbh->{'mysql_insertid'};
pandora_event ($pa_config, "Agent '$name' created by ".$pa_config->{'servername'}.$pa_config->{"servermode"}, $pa_config->{'autocreate_group'}, $lastid, 2, 0, 0, 'new_agent', $dbh);
if ($target_ip_id > 0){
my $query_sql3 = "INSERT INTO taddress_agent (id_a, id_agent) values ($target_ip_id, $lastid)";
$dbh->do($query_sql3);
}
return $lastid;
}
##########################################################################
# SUB pandora_sendmail
# Send a mail, connecting directly to MTA

View File

@ -0,0 +1,72 @@
#!/bin/bash
# Pandora FMS Prediction, startup script
# Sancho Lerena, <slerena@gmail.com>
# Linux Version (generic)
# v2.0 Build 0080513
# Configurable path and filenames
PANDORA_HOME="/etc/pandora/pandora_server.conf"
PANDORA_PID_PATH="/var/run"
PANDORA_PID=$PANDORA_PID_PATH/pandora_prediction.pid
PANDORA_DAEMON=/usr/local/bin/pandora_prediction
# Main script
if [ ! -d "$PANDORA_PID_PATH" ]
then
echo "Pandora FMS cannot write it's PID file in $PANDORA_PID_PATH. Please create that directory"
exit
fi
if [ ! -f $PANDORA_DAEMON ]
then
echo "Pandora FMS Prediction Server not found, please check setup and read manual"
exit
fi
case "$1" in
start)
OLD_PATH="`pwd`"
PANDORA_PID=$(pidof -x $PANDORA_DAEMON)
if [ ! -z $PANDORA_PID ]
then
echo "Pandora FMS Prediction Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..."
exit 1
else
rm -f $PANDORA_PID
fi
$PANDORA_DAEMON $PANDORA_HOME -D
sleep 1
PANDORA_PID=$(pidof -x $PANDORA_DAEMON)
if [ ! -z "$PANDORA_PID" ]
then
echo "Pandora FMS Prediction Server is now running with PID $PANDORA_PID"
else
echo "Cannot start Pandora FMS Prediction Server. Aborted."
fi
cd "$OLD_PATH"
;;
stop)
PANDORA_PID=$(pidof -x $PANDORA_DAEMON)
if [ -z $PANDORA_PID ]
then
echo "Pandora FMS Prediction Server is not running, cannot stop it."
exit 1
else
echo "Stopping Pandora FMS Prediction Server"
kill $PANDORA_PID > /dev/null 2>&1
rm -f $PANDORA_PID
fi
;;
force-reload|restart)
$0 stop
$0 start
;;
*)
echo "Usage: pandora_prediction {start|stop|restart}"
exit 1
esac