From 78283f7894955e3657b2c179ff8ec3e1e4b6077d Mon Sep 17 00:00:00 2001 From: slerena Date: Wed, 2 Jul 2008 19:41:06 +0000 Subject: [PATCH] 2008-07-02 Sancho Lerena * pandora_wmi: Finished first functional version. Uses wmic from samba project (Ubuntu has in official repos). This is a first functional prototype, not to be tested on production sites. * pandora_plugin: fixed some bad strings. git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@923 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f --- pandora_server/ChangeLog | 8 + pandora_server/bin/pandora_plugin | 6 +- pandora_server/bin/pandora_wmi | 363 +++++++++++++++++++++++++++--- 3 files changed, 347 insertions(+), 30 deletions(-) diff --git a/pandora_server/ChangeLog b/pandora_server/ChangeLog index e7ffb1aa9f..ff7a248100 100644 --- a/pandora_server/ChangeLog +++ b/pandora_server/ChangeLog @@ -1,3 +1,11 @@ +2008-07-02 Sancho Lerena + + * pandora_wmi: Finished first functional version. Uses wmic from + samba project (Ubuntu has in official repos). This is a first + functional prototype, not to be tested on production sites. + + * pandora_plugin: fixed some bad strings. + 2008-06-30 Sancho Lerena * util/plugin/udp_nmap_plugin.sh: Added UDP port check plugin diff --git a/pandora_server/bin/pandora_plugin b/pandora_server/bin/pandora_plugin index c3c0e3fca8..465fc1d3be 100755 --- a/pandora_server/bin/pandora_plugin +++ b/pandora_server/bin/pandora_plugin @@ -117,9 +117,9 @@ sub pandora_shutdown { } ########################################################################## -# SUB pandora_network_subsystem -# Subsystem to process network modules -# This module runs each X seconds (server threshold) checking for network modules status +# SUB pandora_plugin_consumer_subsystem +# Subsystem to process Plugin modules +# This module runs each X seconds (server threshold) checking for Plugin modules status ########################################################################## sub pandora_plugin_consumer ($$) { my $pa_config = $_[0]; diff --git a/pandora_server/bin/pandora_wmi b/pandora_server/bin/pandora_wmi index ec5b6290ad..053ceb7e9b 100755 --- a/pandora_server/bin/pandora_wmi +++ b/pandora_server/bin/pandora_wmi @@ -57,28 +57,45 @@ pandora_loadconfig (\%pa_config, 6); # Audit server starting pandora_audit (\%pa_config, "Pandora FMS WMI server starting", "SYSTEM", "System"); -print " [*] Starting up plugin threads\n"; +# Daemonize and put in background +if ( $pa_config{"daemon"} eq "1" ){ + if ($pa_config{"quiet"} eq "0"){ + print " [*] Backgrounding Pandora FMS WMI Server process.\n\n"; + } + &pandora_daemonize ( \%pa_config); +} -die ("hasta aqui hemos llegado"); +# Launch now all WMI server threads +for (my $i=0; $i < $pa_config{'wmi_threads'}; $i++){ + threads->new( \&pandora_wmi_consumer, \%pa_config, $i); +} -# This is a prototype, not real code !!! +# Launch now the producer thread +threads->new( \&pandora_wmi_producer, \%pa_config); -# Basic Skeleton to exec a WMI call remotely +# Last thread is the main process (this process) +if ($pa_config{"quiet"} == 0){ + print " [*] All threads loaded and running \n\n"; +} +# Start Pandora FMS loggin +pandora_startlog (\%pa_config); -$Win32::OLE::Warn = 3; +my $dbhost = $pa_config{'dbhost'}; +my $dbname = $pa_config{'dbname'}; +my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", + $pa_config{'dbuser'}, + $pa_config{'dbpass'}, + { RaiseError => 1, AutoCommit => 1 }); -my $wmipath = "root\\cimv2"; -my $user = "administrador"; # if ADO needs DOMAIN\user -my $pwd = "none"; -my $computer = "192.168.50.121"; +# Server keepalive thread running in main thread on a infinite loop +while (1) { + pandora_serverkeepaliver (\%pa_config, 6, $dbh); + threads->yield; + sleep ($pa_config{"server_threshold"}); +} -my $wmiwebloc = Win32::OLE->new('WbemScripting.SWbemLocator') || - die "Cannot access WMI on local machine: ", Win32::OLE->LastError; - -my $wmi = $wmiwebloc->ConnectServer($computer,$wmipath,$user,$pwd); - # New samples @@ -110,19 +127,6 @@ my $wmi = $wmiwebloc->ConnectServer($computer,$wmipath,$user,$pwd); # DatagramsSentPersec # SELECT * from Win32_PerfFormattedData_Tcpip_IP -my $colItems = $wmi->ExecQuery ("SELECT State FROM Win32_Service WHERE Name = 'Eventlog'"); -foreach my $objItem (in $colItems){ - print $objItem->{State}; - print "\n"; -} -# get all the service objects -my @services = in $wmi->InstancesOf("Win32_Service"); - -# Take 10 first services -for (my $a=0;$a<10;$a++){ - print "Service ", $services[$a]->Name, " is ", $services[$a]->Status, "\n"; -} - #------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------ @@ -142,3 +146,308 @@ sub pandora_shutdown { 0, 4, 0, 0, "system", $dbh); exit; } + +########################################################################## +# SUB pandora_wmi_consumer subsystem +# Subsystem to process WMI modules +# This module runs each X seconds (server threshold) checking for WMI modules status +########################################################################## +sub pandora_wmi_consumer ($$) { + my $pa_config = $_[0]; + my $thread_id = $_[1]; + + if ($pa_config->{"quiet"} == 0){ + print " [*] Starting up WMI Consumer Thread # $thread_id \n"; + } + + my $data_id_agent_module; + # Create Database handler + my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); + my $counter =0; + + LOOP: while (1) { + if ($counter > 10) { + threads->yield; + sleep (1); + $counter = 0; + } + + # Take the first element on the shared queue + # Insert this element on the current task hash + { + lock $queue_lock; + if (scalar(@pending_task) == 0){ + $counter++; + next LOOP; + } + + $data_id_agent_module = shift(@pending_task); + delete($pending_task_hash{$data_id_agent_module}); + $current_task_hash{$data_id_agent_module}=1; + } + eval { + exec_wmi_module ($pa_config, $data_id_agent_module, $dbh); + }; + if ($@){ + logger ($pa_config, "[ERROR] WMI Task for module $data_id_agent_module causes a system exception", 0); + logger ($pa_config, "ERROR Code: $@", 1); + } + + # Remove from queue. If catch an error, probably data is + # not been processed, but has been freed from task queue + { + lock $queue_lock; + delete ($current_task_hash {$data_id_agent_module} ); + } + $counter = 0; + threads->yield; + } +} + +sub pandora_wmi_producer ($) { + my $pa_config = $_[0]; + print " [*] Starting up WMI Producer Thread ...\n"; + + my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); + my $server_id = $pa_config->{'server_id'}; + + # Initialize variables for posterior usage + my $query1; + my @sql_data1; + my $data_id_agente_modulo; + my $data_flag; + my $exec_sql1; + + while (1) { + if ($pa_config->{"pandora_master"} != 1) { + # Query for normal server, not MASTER server + $query1 = "SELECT + tagente_modulo.id_agente_modulo, + tagente_modulo.flag + FROM + tagente, tagente_modulo, tagente_estado + WHERE + id_wmi_server = $server_id + AND + tagente_modulo.id_agente = tagente.id_agente + AND + tagente.disabled = 0 + AND + tagente_modulo.id_modulo = 6 + AND + tagente_modulo.disabled = 0 + AND + tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND ( + (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() + OR + tagente_modulo.flag = 1 + ) + ORDER BY + last_execution_try ASC "; + } else { + # Query for MASTER SERVER ! + $query1 = "SELECT + DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag + FROM + tagente, tagente_modulo, tagente_estado, tserver + WHERE + ( (tagente.id_wmi_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente) OR + (tagente.id_wmi_server != $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.id_plugin_server = tserver.id_server AND tserver.status = 0) + ) AND + tagente.disabled = 0 + AND + tagente_modulo.disabled = 0 + AND + tagente_modulo.id_modulo = 6 + AND + tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND + ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() OR tagente_modulo.flag = 1 ) + ORDER BY last_execution_try ASC"; + } + $exec_sql1 = $dbh->prepare($query1); + $exec_sql1 ->execute; + while (@sql_data1 = $exec_sql1->fetchrow_array()) { + $data_id_agente_modulo = $sql_data1[0]; + $data_flag = $sql_data1[1]; + + # Skip modules already queued + if ((!defined($pending_task_hash{$data_id_agente_modulo})) && + (!defined($current_task_hash{$data_id_agente_modulo}))) { + if ($data_flag == 1){ + $dbh->do("UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = $data_id_agente_modulo") + } + # Locking scope, do not remove redundant { } + { + lock $queue_lock; + push (@pending_task, $data_id_agente_modulo); + $pending_task_hash {$data_id_agente_modulo}=1; + } + } + } + $exec_sql1->finish(); + threads->yield; + sleep($pa_config->{"server_threshold"}); + } # Main loop +} + + +########################################################################## +# SUB exec_wmi_module (paconfig, id_agente_modulo, dbh ) +# Execute WMI module task +########################################################################## +sub exec_wmi_module { + my $pa_config = $_[0]; + my $id_am = $_[1]; + my $dbh = $_[2]; + + # Set global variables for this sub + my $timeout = $pa_config->{'plugin_timeout'}; + my $agent_module; # hash container for tagente_modulo record + + # Get a full hash for agent_plugin record reference ($agent_module) + my $query_sql = "SELECT * FROM tagente_modulo WHERE id_agente_modulo = $id_am"; + my $exec_sql = $dbh->prepare($query_sql); + $exec_sql ->execute; + $agent_module = $exec_sql->fetchrow_hashref; + + # Calculate min timeout for this call + if ($agent_module->{'max_timeout'} < $timeout){ + $timeout = $agent_module->{'max_timeout'}; + } + + # Initialize another global sub variables. + my $agent_name = dame_agente_nombre ($pa_config, $agent_module->{'id_agente'}, $dbh); + my $module_result = 1; # Fail by default + my $module_data = 0; # 0 data for default + my $module_interval = 0; + + # Build execution command to plugin + my $exec_output = ""; + my $wmi_command = "wmic "; + + # Sample exec: wmic -U Administrador%none //192.168.50.123 "SELECT State FROM Win32_Service WHERE Name = \"Eventlog\"" + + # This returns several lines that needs to be processed + # OM Win32_Service WHERE Name = \"Eventlog\"" + # CLASS: Win32_Service + # Name|State + # Eventlog|Running + + my $wmi_query = $agent_module->{'snmp_oid'}; + $wmi_query =~ s/"/\"/g; + $wmi_query =~ s/\"/\\\"/g; + + $wmi_command = $wmi_command . " -U ". $agent_module->{'plugin_user'} ."%". $agent_module->{'plugin_pass'}; + $wmi_command = $wmi_command . " //". $agent_module->{'ip_target'}; + $wmi_command = $wmi_command . " \"". $wmi_query . "\""; + logger ($pa_config, "Executing AM # $id_am WMI command '$wmi_command'", 9); + + eval { + alarm ($timeout); + $module_data = `$wmi_command`; + my @data = split("\n", $module_data); + my @data2 = split(/\|/, $data[2]); # get third line (containing data) + + # Take a specific field number from returned data. It uses tcp_port + # field from tagente_module table. + + $module_data = $data2[$agent_module -> {'tcp_port'}]; + alarm(0); # Cancel the pending alarm if plugin call returns alive + + # Look for errors + if ($module_data =~ /ERROR/) { + $module_result = 1; + } else { + $module_result = 0; # If comes here, this is a successfull exec + } + + # Special word to know is something is OK or not, + # for example "Running". Defined by user in console. It uses snmp_community + # field from tagente_modulo. + + if ($agent_module -> {'snmp_community'} ne ""){ + my $temp_filter1 = $agent_module -> {'snmp_community'}; + if ($module_data =~ /$temp_filter1/){ + $module_data = 1; + } else { + $module_data = 0; + } + } + }; + + # Timeout + if ($@ =~ /PANDORA WMI SERVER TIMED OUT/) { + logger ($pa_config, "[ERROR] WMI Task for module ".$agent_module->{'id_agente_modulo'}." causes a system timeout in exec", 1); + logger ($pa_config, "Executing WMI command '$wmi_command'", 9); + + # General error, not timed-out + } elsif ($module_result == 1) { + logger ($pa_config, "[ERROR] Plugin Task for module ".$agent_module->{'id_agente_modulo'}." causes an unknown system error", 1); + logger ($pa_config, "[ERROR] $@", 1); + } + + sub timed_out { + die "PANDORA WMI SERVER TIMED OUT"; + } + + # Get current timestamp + my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); + my $utimestamp = &UnixDate("today","%s"); + + # If module execution get a valid value + if ($module_result == 0) { + my %part; + $part{'name'}[0] = $agent_module->{'nombre'}; + $part{'description'}[0] = ""; + $part{'data'}[0] = $module_data; + my $tipo_modulo = dame_nombretipomodulo_idagentemodulo ($pa_config, $agent_module->{'id_tipo_modulo'}, $dbh); + + # 1 - generic_data + # 2 - generic_proc + # 3 - generic_data_string + # 4 - generic_data_inc + # 19, 20 - image + + if (1 == $agent_module->{'id_tipo_modulo'}) { + module_generic_data ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); + } + elsif (4 == $agent_module->{'id_tipo_modulo'}) { + module_generic_data_inc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); + } + elsif (3 == $agent_module->{'id_tipo_modulo'}) { + module_generic_data_string ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); + } + # Generic_proc + elsif (2 == $agent_module->{'id_tipo_modulo'}) { + module_generic_proc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); + } + elsif ( (19 == $agent_module->{'id_tipo_modulo'}) || (20 == $agent_module->{'id_tipo_modulo'}) ) { + module_generic_image ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); + } + else { # Unknown module!, this IS a problem + logger ($pa_config, "WMI Server Problem with unknown module type '$tipo_modulo'", 0); + $module_result = 1; + } + # Update agent last contact + # Insert Pandora version as agent version + pandora_lastagentcontact ($pa_config, $timestamp, $agent_name, $pa_config->{'servername'}.$pa_config->{"servermode"}, $pa_config->{'version'}, -1, $dbh); + } + + # If something went wrong in module processing... + if ($module_result != 0){ + + # If module execution get a INVALID value + if ($agent_module->{'intervalo'} == 0){ + $module_interval = dame_intervalo ($pa_config, $agent_module->{'id_agente'}, $dbh); + } + + # Modules who cannot connect or something go bad, update last_execution_try field + logger ($pa_config, "Cannot obtain exec WMI Module ".$agent_module->{'nombre'}." from agent $agent_name", 2); + my $query_act = "UPDATE tagente_estado SET current_interval = $module_interval, last_execution_try = $utimestamp WHERE id_agente_modulo = ".$agent_module->{'id_agente_modulo'}; + $dbh->do($query_act); + } + + $exec_sql->finish(); #close tagent_plugin hash reference +}