Beta Shell
v2.0 ยท web2.us.cloudlogin.co
[FM]
[CMD]
[PHP]
[DB]
[INFO]
[SEC]
File Manager
~
/
usr
/
share
/
fusioninventory
/
lib
/
FusionInventory
/
Agent
/
Daemon
Upload
3 items
Name
Size
Perms
Modified
Actions
[ .. / .. ]
Win32.pm
10.97 KB
-rw-r--r--
2020-08-04 07:36:42
Edit
Del
Editing: Win32.pm
(10.97 KB)
Path: /usr/share/fusioninventory/lib/FusionInventory/Agent/Daemon/Win32.pm
Back
package FusionInventory::Agent::Daemon::Win32; use strict; use warnings; use threads; use threads 'exit' => 'threads_only'; use File::Spec; use Cwd qw(abs_path); use Time::HiRes qw(usleep); use constant SERVICE_USLEEP_TIME => 200_000; # in microseconds use Win32; use Win32::Daemon; use FusionInventory::Agent::Version; use FusionInventory::Agent::Logger; use FusionInventory::Agent::Tools; use FusionInventory::Agent::Tools::Win32; use parent qw(FusionInventory::Agent::Daemon); my $PROVIDER = $FusionInventory::Agent::Version::PROVIDER; sub SERVICE_NAME { lc($PROVIDER) . "-agent"; } sub SERVICE_DISPLAYNAME { "$PROVIDER Agent"; } sub new { my ($class, %params) = @_; my $self = $class->SUPER::new(%params); $self->{last_state} = SERVICE_START_PENDING; bless $self, $class; return $self; } sub name { my ($self, $name) = @_; $self->{_name} = $name if $name; return $self->{_name} || SERVICE_NAME; } sub displayname { my ($self, $displayname) = @_; $self->{_displayname} = $displayname if $displayname; return $self->{_displayname} || SERVICE_DISPLAYNAME; } sub RegisterService { my ($self, %options) = @_; my $libdir = $options{libdir} || $self->{libdir} ; my $params = '"' . $options{program} . '"'; # Try to compute libdir from this module file if still not absolute $libdir = abs_path(File::Spec->rel2abs('../../../../..', __FILE__)) unless ($libdir && File::Spec->file_name_is_absolute($libdir) && -d $libdir); # Add path to lib if setup $params = '-I"' . $libdir . '" ' . $params if ($libdir && -d $libdir); my $service = { name => $self->name( $options{name} ), display => $self->displayname( $options{displayname} ), path => "$^X", parameters => $params }; if (!Win32::Daemon::CreateService($service)) { my $lasterr = Win32::Daemon::GetLastError(); if ($lasterr == 1073) { warn "Service still registered\n"; } elsif ($lasterr == 1072) { warn "Service marked for deletion.\n" . "Computer must be rebooted to register the same service name\n"; return 1; } else { my $error = Win32::FormatMessage($lasterr); warn "Service not registered: $lasterr: $error\n"; return 2; } } return 0; } sub DeleteService { my ($self, %options) = @_; if (!Win32::Daemon::DeleteService("",$self->name( $options{name} ))) { my $lasterr = Win32::Daemon::GetLastError(); if ($lasterr == 1060) { warn "Service not found\n"; } elsif ($lasterr == 1072) { warn "Service still marked for deletion. Computer must be rebooted\n"; return 1; } else { my $error = Win32::FormatMessage($lasterr); warn "Service not removed $lasterr: $error\n"; return 2; } } return 0; } sub StartService { my ($self) = @_; Win32::Daemon::StartService(); my $timer = time; my $lastQuery = 0; my $State = Win32::Daemon::State(); # Wait until service control manager is ready while ($State == SERVICE_NOT_READY) { usleep( SERVICE_USLEEP_TIME ); $State = Win32::Daemon::State(); } $State = Win32::Daemon::State( SERVICE_START_PENDING ); $self->{last_state} = $State; while ( SERVICE_STOPPED != $State) { if ( SERVICE_START_PENDING == $State ) { $self->_start_agent(); } elsif ( SERVICE_STOP_PENDING == $State ) { $self->_stop_agent(); last; } elsif ( SERVICE_PAUSE_PENDING == $State ) { if ($State != $self->{last_state} || time-$timer >= 10) { if ($self->{agent_thread} && $self->{agent_thread}->is_running()) { $self->{agent_thread}->kill('SIGSTOP'); } else { $self->{last_state} = SERVICE_STOP_PENDING; } $timer = time; } my @targets = $self->getTargets(); if ( scalar(grep { $_->paused() } @targets) == @targets ) { $self->{last_state} = SERVICE_PAUSED; $self->ApplyServiceOptimizations(); } else { $self->{last_state} = SERVICE_PAUSE_PENDING; } } elsif ( SERVICE_CONTINUE_PENDING == $State ) { if ($State != $self->{last_state} || time-$timer >= 10) { if ($self->{agent_thread} && $self->{agent_thread}->is_running()) { $self->{agent_thread}->kill('SIGCONT'); } else { $self->{last_state} = SERVICE_STOP_PENDING; } $timer = time; } my @targets = $self->getTargets(); if ( scalar(grep { $_->paused() } @targets) == 0 ) { $self->{last_state} = SERVICE_RUNNING; } else { $self->{last_state} = SERVICE_CONTINUE_PENDING; } } elsif ( SERVICE_PAUSED == $State ) { $self->{last_state} = $self->{agent_thread} && $self->{agent_thread}->is_running() ? SERVICE_PAUSED : SERVICE_STOP_PENDING ; } elsif ( SERVICE_RUNNING == $State ) { $self->{last_state} = $self->{agent_thread} && $self->{agent_thread}->is_running() ? SERVICE_RUNNING : SERVICE_STOP_PENDING ; } my $Query = Win32::Daemon::QueryLastMessage(); if ( $Query == SERVICE_CONTROL_INTERROGATE ) { Win32::Daemon::State( $self->{last_state} ); } elsif ($Query != $lastQuery && $Query != 0xFFFFFFFF) { $lastQuery = $Query; } if ( time-$timer >= 10 || $self->{last_state} != $State ) { Win32::Daemon::State( $self->{last_state}, 10000 ); $timer = time; } usleep( SERVICE_USLEEP_TIME ); $State = Win32::Daemon::State(); } Win32::Daemon::State(SERVICE_STOPPED); Win32::Daemon::StopService(); } sub AcceptedControls { my ($self, $controls) = @_; $controls = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE unless $controls; Win32::Daemon::AcceptedControls($controls); } sub _start_agent { my ($self) = @_; # Start service dedicated thread only if required unless (defined($self->{agent_thread})) { # Start agent in a dedicated thread $self->{agent_thread} = threads->create(sub { # First start a thread dedicated to Win32::OLE calls $self->{worker_thread} = FusionInventory::Agent::Tools::Win32::start_Win32_OLE_Worker(); $self->init(options => { service => 1 }); # install signal handler to handle pause/continue signals $SIG{STOP} = sub { $self->Pause(); }; $SIG{CONT} = sub { $self->Continue(); }; $self->run(); }); } Win32::Daemon::State(SERVICE_RUNNING); } sub _stop_agent { my ($self) = @_; my $timer = time-1; my $tries = 10; while ( $self->{agent_thread} ) { if ($self->{agent_thread}->is_running() && time-$timer >= 1) { $self->{agent_thread}->kill('SIGINT'); Win32::Daemon::State(SERVICE_STOP_PENDING, $tries * 1000); if (!$tries-- && !$self->{agent_thread}->is_detached()) { $self->{agent_thread}->detach(); last; } $timer = time; } elsif ($self->{agent_thread}->is_joinable()) { $self->{agent_thread}->join(); last; } usleep( SERVICE_USLEEP_TIME ); } delete $self->{agent_thread}; } sub Pause { my ($self) = @_; # Abort task thread if running if ($self->{task_thread} && $self->{task_thread}->is_running()) { $self->{task_thread}->kill('SIGINT')->detach(); delete $self->{task_thread}; } foreach my $target ($self->getTargets()) { $target->pause(); } $self->setStatus('paused'); $self->{logger}->info("$PROVIDER Agent paused") if $self->{logger}; } sub Continue { my ($self) = @_; $self->setStatus('waiting'); foreach my $target ($self->getTargets()) { $target->continue(); } $self->{logger}->info("$PROVIDER Agent resumed") if $self->{logger}; } sub ApplyServiceOptimizations { my ($self) = @_; # Setup worker Logger after service Logger FusionInventory::Agent::Tools::Win32::setupWorkerLogger(config => $self->{config}); $self->SUPER::ApplyServiceOptimizations(); # Win32 only service optimization # Preload is64bit result to avoid a lot of WMI calls is64bit(); # Also call running service optimization to free memory $self->RunningServiceOptimization(); } sub RunningServiceOptimization { my ($self) = @_; # win32 platform needs optimization if ($self->{logger} && $self->{logger}->debug_level()) { my ($WorkingSetSize, $PageFileUsage) = getAgentMemorySize(); # WSS=Working Set Size - PFU=Page File Usage $self->{logger}->debug("Agent memory usage before freeing memory: WSS=$WorkingSetSize PFU=$PageFileUsage") unless $WorkingSetSize < 0; } # Make working set memory available for the system FreeAgentMem(); if ($self->{logger}) { my ($WorkingSetSize, $PageFileUsage) = getAgentMemorySize(); $self->{logger}->info("$PROVIDER Agent memory usage: WSS=$WorkingSetSize PFU=$PageFileUsage") unless $WorkingSetSize < 0; } } sub terminate { my ($self) = @_; # Abort task thread if running if ($self->{task_thread} && $self->{task_thread}->is_running()) { $self->{task_thread}->kill('SIGINT')->detach(); delete $self->{task_thread}; } # Abort Win32::OLE worker thread if running if ($self->{worker_thread} && $self->{worker_thread}->is_running()) { $self->{worker_thread}->kill('SIGKILL')->detach(); delete $self->{worker_thread}; } $self->SUPER::terminate(); threads->exit(); } sub runTask { my ($self, $target, $name, $response) = @_; $self->setStatus("running task $name"); # service mode: run each task in a dedicated thread $self->{task_thread} = threads->create(sub { # We don't handle HTTPD interface in this thread delete $self->{server}; my $tid = threads->tid; # install signal handler to handle STOP/INT/TERM signals $SIG{STOP} = $SIG{INT} = $SIG{TERM} = sub { $self->{logger}->debug("aborting thread $tid which was handling task $name"); threads->exit(); }; $self->{logger}->debug("new thread $tid to handle task $name"); $self->runTaskReal($target, $name, $response); threads->exit(); }); while ( $self->{task_thread} ) { if ($self->{task_thread}->is_joinable()) { $self->{task_thread}->join(); my $thread = delete $self->{task_thread}; undef $thread; } $self->sleep(1); } } 1;