Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ netdiscovery/netinventory:
* Use more ordered processing to make debug output more predictable
* Add support for InfoBlox networking devices

collect:
* New features:
* Support Registry existence check
* Support Registry defined value check
* Support in-depth registry values collect (max 10 depth levels)
* With Glpi-Inventory plugin >= v1.6.9, we can submit multiple registry values one by one

packaging:
* Update Windows packaging to use:
- 7-Zip v26.02
Expand Down
16 changes: 12 additions & 4 deletions lib/GLPI/Agent/Task/Collect.pm
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ JOB:
my $module = $modules{$function};
my $collect = $module->new(
logger => $self->{logger},
plugin => $self->{target}->getTaskVersion("collect") // "1.0.0",
job => $job
);

Expand All @@ -194,28 +195,35 @@ JOB:
$results = [] unless ref($results) eq 'ARRAY';

my $count = int(@{$results});
my $cpt = $count;

# Glpi-Inventory plugin >= v1.6.9 supports multiple values submission one by one
# and if we can add the _count value at least for registry collect
my $updated_plugin = $collect->pluginSupport("1.6.9");

# Add an empty hash ref so send an answer with _cpt=0
push @{$results}, {} unless $count ;

foreach my $result (@{$results}) {
next unless ref($result) eq 'HASH';
next unless ( !$count || keys %$result );
next unless !$cpt || keys %$result;
$result->{uuid} = $job->{uuid};
$result->{action} = "setAnswer";
$result->{_cpt} = $count;
$result->{_count} = $count
if $updated_plugin && $count == $cpt;
$result->{_cpt} = $cpt;
$result->{_glpi_csrf_token} = $token
if $token ;
$result->{_sid} = $job->{_sid}
if (exists($job->{_sid}));
$answer = $self->{client}->send(
url => $remoteUrl,
method => $method,
filename => sprintf('collect_%s_%s.js', $job->{uuid}, $count),
filename => sprintf('collect_%s_%s.js', $job->{uuid}, $cpt),
args => $result
);
$token = $answer && exists($answer->{token}) ? $answer->{token} : '';
$count--;
$cpt--;

# Handle CSRF access denied
if ($has_csrf_token && empty($token)) {
Expand Down
9 changes: 9 additions & 0 deletions lib/GLPI/Agent/Task/Collect/Common.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,27 @@ use constant OPTIONAL => 0;
use constant MANDATORY => 1;
use constant OPTIONAL_EXCLUSIVE => 2;

use GLPI::Agent::Tools;

sub new {
my ($class, %params) = @_;

my $self = $params{job} // {};

$self->{logger} = $params{logger};
$self->{plugin} = glpiVersion($params{plugin} // "1.0.0");

bless $self, $class;

return $self;
}

sub pluginSupport {
my ($self, $version) = @_;

return (glpiVersion($version) <= $self->{plugin}) ? 1 : 0;
}

sub _validateSpec {
my ($self, $base, $key, $spec) = @_;

Expand Down
124 changes: 113 additions & 11 deletions lib/GLPI/Agent/Task/Collect/Registry.pm
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,30 @@ use constant function => "getFromRegistry";
use constant OPTIONAL => 0;
use constant MANDATORY => 1;

use constant MAX_DEPTH => 10;

use constant json_validation => {
path => MANDATORY,
timeout => OPTIONAL,
exists => OPTIONAL,
defined => OPTIONAL,
depth => OPTIONAL,
};

use constant REG_BINARY => 3;
use constant REG_MULTI_SZ => 7;
use constant REG_RESOURCE_LIST => 8;

sub _encodeRegistryValueForCollect {
my ($value, $type) = @_ ;

return $value unless defined($type);

# Dump REG_BINARY/REG_RESOURCE_LIST/REG_FULL_RESOURCE_DESCRIPTOR as hex strings
if (defined($type) && ($type == 3 || $type >= 8)) {
if ($type == REG_BINARY || $type >= REG_RESOURCE_LIST) {
$value = join(" ", map { sprintf "%02x", ord } split(//, $value));
} elsif ($type == REG_MULTI_SZ && ref($value) eq 'ARRAY') {
$value = join(",", @{$value});
}

return $value;
Expand Down Expand Up @@ -50,6 +63,10 @@ sub results {

$self->{logger}->debug("Looking for '$self->{path}' registry key...");

return $self->_exists() if $self->{exists};
return $self->_defined() if $self->{defined};
return $self->_depth() if defined($self->{depth});

# Here we need to retrieve values with their type, getRegistryValue API
# has been modify to support withtype flag as param
my $values = GLPI::Agent::Tools::Win32::getRegistryValue(
Expand All @@ -59,29 +76,114 @@ sub results {

return unless $values;

# Glpi-Inventory plugin >= v1.6.9 supports multiple values submission one by one
my $updated_plugin = $self->pluginSupport("1.6.9");

my $results = [];
my $result = {};
if (ref($values) eq 'HASH') {
foreach my $k (keys %$values) {
# Skip sub keys
next if ($k =~ m|/$|);
my ($value, $type) = @{$values->{$k}};
$result->{$k} = _encodeRegistryValueForCollect($value, $type);
$self->{logger}->debug2("Found $RegistryType[$type] value: ".$result->{$k});
if ($updated_plugin) {
$value = _encodeRegistryValueForCollect($value, $type) // "";
push @{$results}, {
_path => $k,
_value => $value,
};
$self->{logger}->debug2("Found".(defined($type) && $type < scalar(@RegistryType) ? " ".$RegistryType[$type] : "")." value for $k: ".$value);
} else {
$result->{$k} = _encodeRegistryValueForCollect($value, $type) // "";
$self->{logger}->debug2("Found".(defined($type) && $type < scalar(@RegistryType) ? " ".$RegistryType[$type] : "")." value for $k: ".$result->{$k});
}
}
} else {
my ($k) = $self->{path} =~ m|([^/]+)$| ;
my ($value,$type) = @{$values};
if (ref($value) eq 'ARRAY') {
my @values = map { _encodeRegistryValueForCollect($_) } @{$value};
$result->{$k} = join(",", @values);
map { $self->{logger}->debug2("Found $RegistryType[$type] value: $_") } @{$value};
my ($value, $type) = @{$values};
$result->{$k} = _encodeRegistryValueForCollect($value,$type);
$self->{logger}->debug2("Found".(defined($type) && $type < scalar(@RegistryType) ? " ".$RegistryType[$type] : "")." value: ".$result->{$k});
push @{$results}, {
_path => $k,
_value => $result->{$k},
} if $updated_plugin;
}

return $updated_plugin ? $results : [ $result ];
}

sub _exists() {
my ($self) = @_;

my $key = GLPI::Agent::Tools::Win32::getRegistryKey(path => $self->{path});

return [
{
_exists => defined($key) ? 1 : 0
}
];
}

sub _defined() {
my ($self) = @_;

my $value = GLPI::Agent::Tools::Win32::getRegistryValue(path => $self->{path});

return [
{
_defined => defined($value) ? 1 : 0
}
];
}

sub _depth() {
my ($self, $depth) = @_;

return unless $self->{depth} =~ /^\d+$/;

my $key = GLPI::Agent::Tools::Win32::getRegistryKey(path => $self->{path});

return unless defined($key);

$self->{_results} = [];

$depth = int($self->{depth}) > MAX_DEPTH ? MAX_DEPTH : int($self->{depth});

$self->_recursive($key, "", $depth);

return delete $self->{_results};
}

sub _recursive() {
my ($self, $key, $path, $depth) = @_;

return unless $key;

my @subkeys;

# First handle values in this leaf
foreach my $k (sort keys(%{$key})) {
# Skip sub keys by now, but keep / as it is the key default registry value
if ($k =~ m|/$| && $k ne '/') {
push @subkeys, $k if $depth > 0;
} else {
$result->{$k} = _encodeRegistryValueForCollect($value,$type);
$self->{logger}->debug2("Found $RegistryType[$type] value: ".$result->{$k});
($k) = $k =~ m|([^/]+)$|;
my $info = GLPI::Agent::Tools::Win32::getRegistryKeyValue($key, $k, 1);
next unless ref($info) eq "ARRAY";
my ($value, $type) = @{$info};
$value = _encodeRegistryValueForCollect($value, $type);
push @{$self->{_results}}, {
_path => $path.$k,
_value => $value
};
$self->{logger}->debug2("Found".(defined($type) && $type < scalar(@RegistryType) ? " ".$RegistryType[$type] : "")." value for $path$k: ".$value);
}
}

return [ $result ];
# Then handle recursive calls
foreach my $sk (@subkeys) {
$self->_recursive($key->{$sk}, $path.$sk, $depth-1);
}
}

1;
Loading
Loading