File Coverage

blib/lib/FusionInventory/Agent/Tools/Win32.pm
Criterion Covered Total %
statement 100 173 57.8
branch 29 62 46.7
condition 2 9 22.2
subroutine 19 27 70.3
pod 8 8 100.0
total 158 279 56.6


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Tools::Win32;
2              
3 37     37   8689276 use strict;
  37         99  
  37         1190  
4 37     37   263 use warnings;
  37         76  
  37         1379  
5 37     37   213 use base 'Exporter';
  37         217  
  37         5939  
6 37     37   31254 use utf8;
  37         407  
  37         243  
7              
8 37     37   1542 use constant KEY_WOW64_64 => 0x100;
  37         85  
  37         4411  
9 37     37   287 use constant KEY_WOW64_32 => 0x200;
  37         70  
  37         2214  
10              
11 37     37   222 use Cwd;
  37         78  
  37         4531  
12 37     37   16384 use Encode;
  37         196089  
  37         3576  
13 37     37   237 use English qw(-no_match_vars);
  37         72  
  37         497  
14 37     37   67174 use File::Temp qw(:seekable tempfile);
  37         667989  
  37         7215  
15 37     37   26604 use Win32::Job;
  37         2463  
  37         368  
16 37     37   25802 use Win32::OLE qw(in);
  37         11420  
  37         166  
17 37     37   2041 use Win32::OLE::Const;
  37         99  
  37         277  
18             use Win32::TieRegistry (
19 37         237 Delimiter => '/',
20             ArrayValues => 0,
21             qw/KEY_READ/
22 37     37   23485 );
  37         5476  
23              
24 37     37   11345 use FusionInventory::Agent::Tools;
  37         91  
  37         7346  
25 37     37   21936 use FusionInventory::Agent::Tools::Network;
  37         120  
  37         86609  
26              
27             Win32::OLE->Option(CP => Win32::OLE::CP_UTF8);
28              
29             my $localCodepage;
30              
31             our @EXPORT = qw(
32             is64bit
33             encodeFromRegistry
34             KEY_WOW64_64
35             KEY_WOW64_32
36             getInterfaces
37             getRegistryValue
38             getRegistryKey
39             getWMIObjects
40             getLocalCodepage
41             runCommand
42             );
43              
44             sub is64bit {
45             return
46 0     0   0 any { $_->{AddressWidth} eq 64 }
47 0     0 1 0 getWMIObjects(
48             class => 'Win32_Processor', properties => [ qw/AddressWidth/ ]
49             );
50             }
51              
52             sub getLocalCodepage {
53 0 0   0 1 0 if (!$localCodepage) {
54 0         0 $localCodepage =
55             "cp" .
56             getRegistryValue(
57             path => 'HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Nls/CodePage/ACP'
58             );
59             }
60              
61 0         0 return $localCodepage;
62             }
63              
64             sub encodeFromRegistry {
65 5     5 1 9 my ($string) = @_;
66              
67             ## no critic (ExplicitReturnUndef)
68 5 100       20 return undef unless $string;
69              
70 4 50       46 return $string if Encode::is_utf8($string);
71              
72 0         0 return decode(getLocalCodepage(), $string);
73             }
74              
75             sub getWMIObjects {
76 0     0 1 0 my (%params) = (
77             moniker => 'winmgmts:{impersonationLevel=impersonate,(security)}!//./',
78             @_
79             );
80              
81             my $WMIService = Win32::OLE->GetObject($params{moniker})
82 0 0       0 or return; #die "WMI connection failed: " . Win32::OLE->LastError();
83              
84 0         0 my @objects;
85 0         0 foreach my $instance (in(
86             $WMIService->InstancesOf($params{class})
87             )) {
88 0         0 my $object;
89 0         0 foreach my $property (@{$params{properties}}) {
  0         0  
90 0 0 0     0 if (defined $instance->{$property} && !ref($instance->{$property})) {
    0          
91             # string value
92 0         0 $object->{$property} = $instance->{$property};
93             # despite CP_UTF8 usage, Win32::OLE downgrades string to native
94             # encoding, if possible, ie all characters have code <= 0x00FF:
95             # http://code.activestate.com/lists/perl-win32-users/Win32::OLE::CP_UTF8/
96 0         0 utf8::upgrade($object->{$property});
97             } elsif (defined $instance->{$property}) {
98             # list value
99 0         0 $object->{$property} = $instance->{$property};
100             } else {
101 0         0 $object->{$property} = undef;
102             }
103             }
104 0         0 push @objects, $object;
105             }
106              
107 0         0 return @objects;
108             }
109              
110             sub getRegistryValue {
111 0     0 1 0 my (%params) = @_;
112              
113 0         0 my ($root, $keyName, $valueName);
114 0 0       0 if ($params{path} =~ m{^(HKEY_\S+)/(.+)/([^/]+)} ) {
115 0         0 $root = $1;
116 0         0 $keyName = $2;
117 0         0 $valueName = $3;
118             } else {
119             $params{logger}->error(
120             "Failed to parse '$params{path}'. Does it start with HKEY_?"
121 0 0       0 ) if $params{logger};
122 0         0 return;
123             }
124              
125             my $key = _getRegistryKey(
126             logger => $params{logger},
127 0         0 root => $root,
128             keyName => $keyName
129             );
130              
131 0 0       0 if ($valueName eq '*') {
132 0         0 my %ret;
133 0         0 foreach (keys %$key) {
134 0         0 s{^/}{};
135 0 0       0 $ret{$_} = $params{withtype} ? [$key->GetValue($_)] : $key->{"/$_"} ;
136             }
137 0         0 return \%ret;
138             } else {
139 0 0       0 return $params{withtype} ? [$key->GetValue($valueName)] : $key->{"/$valueName"} ;
140             }
141             }
142              
143             sub getRegistryKey {
144 0     0 1 0 my (%params) = @_;
145              
146 0         0 my ($root, $keyName);
147 0 0       0 if ($params{path} =~ m{^(HKEY_\S+)/(.+)} ) {
148 0         0 $root = $1;
149 0         0 $keyName = $2;
150             } else {
151             $params{logger}->error(
152             "Failed to parse '$params{path}'. Does it start with HKEY_?"
153 0 0       0 ) if $params{logger};
154 0         0 return;
155             }
156              
157             return _getRegistryKey(
158             logger => $params{logger},
159 0         0 root => $root,
160             keyName => $keyName
161             );
162             }
163              
164             sub _getRegistryKey {
165 0     0   0 my (%params) = @_;
166              
167             ## no critic (ProhibitBitwise)
168             my $rootKey = is64bit() ?
169             $Registry->Open($params{root}, { Access=> KEY_READ | KEY_WOW64_64 } ) :
170 0 0       0 $Registry->Open($params{root}, { Access=> KEY_READ } ) ;
171              
172 0 0       0 if (!$rootKey) {
173             $params{logger}->error(
174             "Can't open $params{root} key: $EXTENDED_OS_ERROR"
175 0 0       0 ) if $params{logger};
176 0         0 return;
177             }
178 0         0 my $key = $rootKey->Open($params{keyName});
179              
180 0         0 return $key;
181             }
182              
183             sub runCommand {
184 0     0 1 0 my (%params) = (
185             timeout => 3600 * 2,
186             @_
187             );
188              
189 0         0 my $job = Win32::Job->new();
190              
191 0         0 my $buff = File::Temp->new();
192              
193 0         0 my $winCwd = Cwd::getcwd();
194 0         0 $winCwd =~ s{/}{\\}g;
195              
196 0         0 my ($fh, $filename) = File::Temp::tempfile( "$ENV{TEMP}\\fusinvXXXXXXXXXXX", SUFFIX => '.bat');
197 0         0 print $fh "cd \"".$winCwd."\"\r\n";
198 0         0 print $fh $params{command}."\r\n";
199 0         0 print $fh "exit %ERRORLEVEL%\r\n";
200 0         0 close $fh;
201              
202 0         0 my $args = {
203             stdout => $buff,
204             no_window => 1
205             };
206              
207 0         0 $job->spawn(
208             "$ENV{SYSTEMROOT}\\system32\\cmd.exe",
209             "start /wait cmd /c $filename",
210             $args
211             );
212              
213 0         0 $job->run($params{timeout});
214 0         0 unlink($filename);
215              
216 0         0 $buff->seek(0, SEEK_SET);
217              
218 0         0 my $exitcode;
219              
220 0         0 my ($status) = $job->status();
221 0         0 foreach my $pid (%$status) {
222 0         0 $exitcode = $status->{$pid}{exitcode};
223 0         0 last;
224             }
225              
226 0         0 return ($exitcode, $buff);
227             }
228              
229             sub getInterfaces {
230              
231 2     2 1 20543 my @configurations;
232              
233 2         13 foreach my $object (getWMIObjects(
234             class => 'Win32_NetworkAdapterConfiguration',
235             properties => [ qw/Index Description IPEnabled DHCPServer MACAddress
236             MTU DefaultIPGateway DNSServerSearchOrder IPAddress
237             IPSubnet/ ]
238             )) {
239              
240             my $configuration = {
241             DESCRIPTION => $object->{Description},
242             STATUS => $object->{IPEnabled} ? "Up" : "Down",
243             IPDHCP => $object->{DHCPServer},
244             MACADDR => $object->{MACAddress},
245             MTU => $object->{MTU}
246 28 50       48641 };
247              
248 28 100       162 if ($object->{DefaultIPGateway}) {
249 3         12 $configuration->{IPGATEWAY} = $object->{DefaultIPGateway}->[0];
250             }
251              
252 28 100       81 if ($object->{DNSServerSearchOrder}) {
253 3         10 $configuration->{dns} = $object->{DNSServerSearchOrder}->[0];
254             }
255              
256 28 100       76 if ($object->{IPAddress}) {
257 3         7 foreach my $address (@{$object->{IPAddress}}) {
  3         11  
258 4         7 my $prefix = shift @{$object->{IPSubnet}};
  4         12  
259 4         9 push @{$configuration->{addresses}}, [ $address, $prefix ];
  4         21  
260             }
261             }
262              
263 28         80 $configurations[$object->{Index}] = $configuration;
264             }
265              
266 2         32 my @interfaces;
267              
268 2         16 foreach my $object (getWMIObjects(
269             class => 'Win32_NetworkAdapter',
270             properties => [ qw/Index PNPDeviceID Speed PhysicalAdapter
271             AdapterTypeId/ ]
272             )) {
273             # http://comments.gmane.org/gmane.comp.monitoring.fusion-inventory.devel/34
274 28 100       9762 next unless $object->{PNPDeviceID};
275              
276 25         28 my $pciid;
277 25 100       66 if ($object->{PNPDeviceID} =~ /PCI\\VEN_(\w{4})&DEV_(\w{4})&SUBSYS_(\w{4})(\w{4})/) {
278 2         16 $pciid = join(':', $1 , $2 , $3 , $4);
279             }
280              
281 25         42 my $configuration = $configurations[$object->{Index}];
282              
283 25 100       49 if ($configuration->{addresses}) {
284 2         4 foreach my $address (@{$configuration->{addresses}}) {
  2         6  
285              
286             my $interface = {
287             PNPDEVICEID => $object->{PNPDeviceID},
288             PCIID => $pciid,
289             MACADDR => $configuration->{MACADDR},
290             DESCRIPTION => $configuration->{DESCRIPTION},
291             STATUS => $configuration->{STATUS},
292             MTU => $configuration->{MTU},
293             dns => $configuration->{dns},
294 3         18 };
295              
296 3 100       29 if ($address->[0] =~ /$ip_address_pattern/) {
297 2         7 $interface->{IPADDRESS} = $address->[0];
298 2         3 $interface->{IPMASK} = $address->[1];
299             $interface->{IPSUBNET} = getSubnetAddress(
300             $interface->{IPADDRESS},
301             $interface->{IPMASK}
302 2         13 );
303 2         45 $interface->{IPDHCP} = $configuration->{IPDHCP};
304 2         6 $interface->{IPGATEWAY} = $configuration->{IPGATEWAY};
305             } else {
306 1         4 $interface->{IPADDRESS6} = $address->[0];
307 1         5 $interface->{IPMASK6} = getNetworkMaskIPv6($address->[1]);
308             $interface->{IPSUBNET6} = getSubnetAddressIPv6(
309             $interface->{IPADDRESS6},
310             $interface->{IPMASK6}
311 1         145 );
312             }
313              
314             $interface->{SPEED} = $object->{Speed} / 1_000_000
315 3 100       111 if $object->{Speed};
316 3         7 $interface->{VIRTUALDEV} = _isVirtual($object, $configuration);
317              
318 3         10 push @interfaces, $interface;
319             }
320             } else {
321 23 100       56 next unless $configuration->{MACADDR};
322              
323             my $interface = {
324             PNPDEVICEID => $object->{PNPDeviceID},
325             PCIID => $pciid,
326             MACADDR => $configuration->{MACADDR},
327             DESCRIPTION => $configuration->{DESCRIPTION},
328             STATUS => $configuration->{STATUS},
329             MTU => $configuration->{MTU},
330             dns => $configuration->{dns},
331 7         37 };
332              
333             $interface->{SPEED} = $object->{Speed} / 1_000_000
334 7 50       51 if $object->{Speed};
335 7         15 $interface->{VIRTUALDEV} = _isVirtual($object, $configuration);
336              
337 7         18 push @interfaces, $interface;
338             }
339              
340             }
341              
342             return
343 2         161 @interfaces;
344              
345             }
346              
347             sub _isVirtual {
348 10     10   17 my ($object, $configuration) = @_;
349              
350             # PhysicalAdapter only work on OS > XP
351 10 100       23 if (defined $object->{PhysicalAdapter}) {
352 3 50       13 return $object->{PhysicalAdapter} ? 0 : 1;
353             }
354              
355             # http://forge.fusioninventory.org/issues/1166
356 7 50 33     47 if ($configuration->{DESCRIPTION} &&
      33        
357             $configuration->{DESCRIPTION} =~ /RAS/ &&
358             $configuration->{DESCRIPTION} =~ /Adapter/i
359             ) {
360 0         0 return 1;
361             }
362              
363 7 100       33 return $object->{PNPDeviceID} =~ /^ROOT/ ? 1 : 0;
364             }
365              
366              
367             1;
368             __END__