Skip to content

Commit 695a724

Browse files
committed
feat: Add base code to support usb device update on MSWin32
Also add usb support tests
1 parent 531464b commit 695a724

File tree

5 files changed

+530
-24
lines changed

5 files changed

+530
-24
lines changed

Diff for: Changes

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ inventory:
3030
found. This fixes monitors inventory on Apple M1.
3131
* Fix MacOSX software arch should be set in ARCH field
3232
* Fix Lenovo T24v-10 monitor serial number inventory
33+
* Add base code to support usb device update on MSWin32, mainly to report expected
34+
or fix serialnumber for specific device with well-know method to get required
35+
datas. This base code is required to fix #650.
3336

3437
remoteinventory:
3538
* Limit the number of attempts and reported errors when libssh2 fails but ssh command

Diff for: lib/GLPI/Agent/Task/Inventory/Win32/USB.pm

+25-24
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use warnings;
55

66
use parent 'GLPI::Agent::Task::Inventory::Module';
77

8-
use GLPI::Agent::Tools::Generic;
8+
use GLPI::Agent::Tools;
99
use GLPI::Agent::Tools::Win32;
10+
use GLPI::Agent::Tools::USB;
1011

1112
use constant category => "usb";
1213

@@ -28,36 +29,35 @@ sub doInventory {
2829
}
2930

3031
sub _getDevices {
32+
my (%params) = @_;
33+
3134
my @devices;
3235
my $seen;
3336

34-
foreach my $device (_getDevicesFromWMI(@_)) {
35-
next if $device->{VENDORID} =~ /^0+$/;
37+
foreach my $device (_getDevicesFromWMI(%params)) {
3638

37-
# avoid duplicates
38-
next if $seen->{$device->{SERIAL}}++;
39+
next if $device->skip();
3940

40-
# pseudo serial generated by windows
41-
delete $device->{SERIAL} if $device->{SERIAL} =~ /&/;
41+
# Avoid duplicates
42+
my $dedupkey = join('-', $device->vendorid, $device->productid, $device->serial);
43+
next if $seen->{$dedupkey}++;
4244

43-
my $vendor = getUSBDeviceVendor(id => lc($device->{VENDORID}), @_);
44-
if ($vendor) {
45-
$device->{MANUFACTURER} = $vendor->{name};
45+
# cleanup pseudo serial generated by windows
46+
$device->delete_serial() if $device->serial =~ /&/;
4647

47-
my $entry = $vendor->{devices}->{lc($device->{PRODUCTID})};
48-
if ($entry) {
49-
$device->{CAPTION} = $entry->{name};
50-
$device->{NAME} = $entry->{name};
51-
}
52-
}
48+
# Update device
49+
$device->update_by_ids();
50+
$device->update();
5351

54-
push @devices, $device;
52+
push @devices, $device->dump();
5553
}
5654

5755
return @devices;
5856
}
5957

6058
sub _getDevicesFromWMI {
59+
my (%params) = @_;
60+
6161
my @devices;
6262

6363
my ($vendorid, $productid, $serial);
@@ -71,13 +71,14 @@ sub _getDevicesFromWMI {
7171
# Support manufacturers wrongly using iSerial with fields definition
7272
$serial = $1 if $serial =~ /^S\/N:([0-9A-F]+)/i;
7373

74-
push @devices, {
75-
CAPTION => $object->{Caption},
76-
NAME => $object->{Name},
77-
VENDORID => $vendorid,
78-
PRODUCTID => $productid,
79-
SERIAL => $serial
80-
};
74+
push @devices, GLPI::Agent::Tools::USB->new(
75+
logger => $params{logger},
76+
caption => $object->{Caption},
77+
name => $object->{Name},
78+
vendorid => $vendorid,
79+
productid => $productid,
80+
serial => $serial
81+
);
8182
}
8283

8384
return @devices;

Diff for: lib/GLPI/Agent/Tools/USB.pm

+212
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
package GLPI::Agent::Tools::USB;
2+
3+
use strict;
4+
use warnings;
5+
6+
use English qw(-no_match_vars);
7+
8+
use GLPI::Agent::Logger;
9+
use GLPI::Agent::Tools;
10+
use GLPI::Agent::Tools::Generic;
11+
12+
my %loaded;
13+
14+
sub new {
15+
my ($class, %params) = @_;
16+
17+
my $self = {
18+
logger => $params{logger} || GLPI::Agent::Logger->new(),
19+
_vendorid => $params{vendorid},
20+
_productid => $params{productid},
21+
_caption => $params{caption},
22+
_name => $params{name},
23+
_serial => $params{serial},
24+
};
25+
26+
# Load any related sub-module
27+
my ($sub_modules_path) = $INC{module2file(__PACKAGE__)} =~ /(.*)\.pm/;
28+
29+
foreach my $file (File::Glob::bsd_glob("$sub_modules_path/*.pm")) {
30+
if ($OSNAME eq 'MSWin32') {
31+
$file =~ s{\\}{/}g;
32+
$sub_modules_path =~ s{\\}{/}g;
33+
}
34+
next unless $file =~ m{$sub_modules_path/(\S+)\.pm$};
35+
36+
my $module = __PACKAGE__ . "::" . $1;
37+
# reload can be set by unittests
38+
unless (defined($loaded{$module}) && !$params{reload}) {
39+
$loaded{$module} = 0;
40+
$module->require();
41+
if ($EVAL_ERROR) {
42+
$self->{logger}->info("$module require error: $EVAL_ERROR");
43+
next;
44+
}
45+
# Still disable module if module check fails
46+
$loaded{$module} = $module->enabled() ? 1 : 0;
47+
}
48+
49+
next unless $loaded{$module};
50+
51+
bless $self, $module;
52+
return $self if $self->supported();
53+
}
54+
55+
# Reset to USB device without support
56+
return bless $self, $class;
57+
}
58+
59+
# Method to implement in subclass if required to disable module on not supported environment
60+
sub enabled {
61+
return 1;
62+
}
63+
64+
# Method to implement in subclass to detect a subclass applies to an usb device
65+
sub supported {}
66+
67+
# Method to implement in subclass which should update usb device
68+
sub update {}
69+
70+
sub update_by_ids {
71+
my ($self) = @_;
72+
73+
# Update device by checking usb.ids
74+
unless (empty($self->{_vendorid})) {
75+
my $vendor = getUSBDeviceVendor(
76+
logger => $self->{logger},
77+
id => lc($self->{_vendorid})
78+
);
79+
if ($vendor) {
80+
$self->{_manufacturer} = $vendor->{name}
81+
unless empty($vendor->{name});
82+
83+
unless (empty($self->{_productid})) {
84+
my $entry = $vendor->{devices}->{lc($self->{_productid})};
85+
if ($entry && !empty($entry->{name})) {
86+
$self->{_caption} = $entry->{name};
87+
$self->{_name} = $entry->{name};
88+
}
89+
}
90+
}
91+
}
92+
}
93+
94+
sub vendorid {
95+
my ($self) = @_;
96+
return $self->{_vendorid} // "";
97+
}
98+
99+
sub productid {
100+
my ($self) = @_;
101+
return $self->{_productid} // "";
102+
}
103+
104+
sub serial {
105+
my ($self, $serial) = @_;
106+
107+
# Support setter mode
108+
$self->{_serial} = $serial if defined($serial);
109+
110+
return $self->{_serial} // "";
111+
}
112+
113+
sub delete_serial {
114+
my ($self) = @_;
115+
116+
delete $self->{_serial};
117+
}
118+
119+
sub skip {
120+
my ($self) = @_;
121+
122+
# Skip for invalid vendorid
123+
return empty($self->{_vendorid}) || $self->{_vendorid} =~ /^0+$/ ? 1 : 0;
124+
}
125+
126+
my %keymap = map { my ($key) = /^_(.+)$/; $_ => uc($key) } qw/
127+
_caption _name _vendorid _productid _serial _manufacturer
128+
/;
129+
130+
sub dump {
131+
my ($self) = @_;
132+
133+
my $dump = {};
134+
135+
foreach my $key (keys(%keymap)) {
136+
next if empty($self->{$key});
137+
$dump->{$keymap{$key}} = $self->{$key};
138+
}
139+
140+
return $dump;
141+
}
142+
143+
1;
144+
145+
__END__
146+
147+
=head1 NAME
148+
149+
GLPI::Agent::Tools::USB - Base class for usb device object
150+
151+
=head1 DESCRIPTION
152+
153+
This is an abstract class for usb device objects
154+
155+
=head1 METHODS
156+
157+
=head2 new(%params)
158+
159+
The constructor. The following parameters are allowed, as keys of the %params
160+
hash:
161+
162+
=over
163+
164+
=item I<logger>
165+
166+
the logger object to use (default: a new stderr logger)
167+
168+
=item I<vendorid>
169+
170+
discovered vendorid
171+
172+
=item I<productid>
173+
174+
discovered productid
175+
176+
=back
177+
178+
=head2 check()
179+
180+
Module API to by-pass module when usage context is wrong. Return true by default.
181+
182+
=head2 supported()
183+
184+
Method API to trigger support for a loaded class
185+
186+
=head2 skip()
187+
188+
Method API to tell USB device should be skipped
189+
190+
=head2 update_by_ids()
191+
192+
Method API to update USB device checking usb.ids database
193+
194+
=head2 update()
195+
196+
Method API to update USB device from subclass
197+
198+
=head2 vendorid()
199+
200+
USB device vendorid accessor.
201+
202+
=head2 productid()
203+
204+
USB device productid accessor.
205+
206+
=head2 serial()
207+
208+
USB device serialnumber accessor.
209+
210+
=head2 dump()
211+
212+
Method to dump datas to be inserted in inventory

Diff for: lib/GLPI/Agent/Tools/USB/Gertec.pm

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package GLPI::Agent::Tools::USB::Gertec;
2+
3+
use strict;
4+
use warnings;
5+
6+
use parent qw(GLPI::Agent::Tools::USB);
7+
8+
use GLPI::Agent::Tools;
9+
10+
# Actually supported only on MSWin32
11+
sub enabled {
12+
return OSNAME eq 'MSWin32';
13+
}
14+
15+
sub supported {
16+
my ($self) = @_;
17+
18+
return $self->vendorid =~ /^1753$/i;
19+
}
20+
21+
sub update {
22+
my ($self) = @_;
23+
24+
my $serial;
25+
26+
# TODO Implement serialnumber discovery in registry
27+
28+
$self->serial($serial)
29+
unless empty($serial);
30+
}
31+
32+
1;

0 commit comments

Comments
 (0)