File Coverage

blib/lib/FusionInventory/Agent/Tools/Win32.pm
Criterion Covered Total %
statement 48 173 27.7
branch 0 58 0.0
condition 0 9 0.0
subroutine 16 27 59.2
pod 8 8 100.0
total 72 275 26.1


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Tools::Win32;
2              
3 24     24   7101457 use strict;
  24         36  
  24         859  
4 24     24   114 use warnings;
  24         27  
  24         773  
5 24     24   112 use base 'Exporter';
  24         67  
  24         3898  
6 24     24   15501 use utf8;
  24         229  
  24         153  
7              
8 24     24   881 use constant KEY_WOW64_64 => 0x100;
  24         37  
  24         2743  
9 24     24   121 use constant KEY_WOW64_32 => 0x200;
  24         30  
  24         1055  
10              
11 24     24   125 use Cwd;
  24         29  
  24         2681  
12 24     24   14801 use Encode;
  24         177490  
  24         2079  
13 24     24   148 use English qw(-no_match_vars);
  24         28  
  24         278  
14 24     24   29624 use File::Temp qw(:seekable tempfile);
  24         384596  
  24         4049  
15 24     24   10511 use Win32::Job;
  24         1752  
  24         245  
16 24     24   10107 use Win32::OLE qw(in);
  24         6841  
  24         130  
17 24     24   1178 use Win32::OLE::Const;
  24         36  
  24         128  
18             use Win32::TieRegistry (
19 24         122 Delimiter => '/',
20             ArrayValues => 0,
21             qw/KEY_READ/
22 24     24   9285 );
  24         3241  
23              
24 24     24   6963 use FusionInventory::Agent::Tools;
  24         51  
  24         3421  
25 24     24   9615 use FusionInventory::Agent::Tools::Network;
  24         55  
  24         42912  
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     any { $_->{AddressWidth} eq 64 }
47 0     0 1   getWMIObjects(
48             class => 'Win32_Processor', properties => [ qw/AddressWidth/ ]
49             );
50             }
51              
52             sub getLocalCodepage {
53 0 0   0 1   if (!$localCodepage) {
54 0           $localCodepage =
55             "cp" .
56             getRegistryValue(
57             path => 'HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Nls/CodePage/ACP'
58             );
59             }
60              
61 0           return $localCodepage;
62             }
63              
64             sub encodeFromRegistry {
65 0     0 1   my ($string) = @_;
66              
67             ## no critic (ExplicitReturnUndef)
68 0 0         return undef unless $string;
69              
70 0 0         return $string if Encode::is_utf8($string);
71              
72 0           return decode(getLocalCodepage(), $string);
73             }
74              
75             sub getWMIObjects {
76 0     0 1   my (%params) = (
77             moniker => 'winmgmts:{impersonationLevel=impersonate,(security)}!//./',
78             @_
79             );
80              
81 0 0         my $WMIService = Win32::OLE->GetObject($params{moniker})
82             or return; #die "WMI connection failed: " . Win32::OLE->LastError();
83              
84 0           my @objects;
85 0           foreach my $instance (in(
86             $WMIService->InstancesOf($params{class})
87             )) {
88 0           my $object;
89 0           foreach my $property (@{$params{properties}}) {
  0            
90 0 0 0       if (defined $instance->{$property} && !ref($instance->{$property})) {
    0          
91             # string value
92 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           utf8::upgrade($object->{$property});
97             } elsif (defined $instance->{$property}) {
98             # list value
99 0           $object->{$property} = $instance->{$property};
100             } else {
101 0           $object->{$property} = undef;
102             }
103             }
104 0           push @objects, $object;
105             }
106              
107 0           return @objects;
108             }
109              
110             sub getRegistryValue {
111 0     0 1   my (%params) = @_;
112              
113 0           my ($root, $keyName, $valueName);
114 0 0         if ($params{path} =~ m{^(HKEY_\S+)/(.+)/([^/]+)} ) {
115 0           $root = $1;
116 0           $keyName = $2;
117 0           $valueName = $3;
118             } else {
119 0 0         $params{logger}->error(
120             "Failed to parse '$params{path}'. Does it start with HKEY_?"
121             ) if $params{logger};
122 0           return;
123             }
124              
125 0           my $key = _getRegistryKey(
126             logger => $params{logger},
127             root => $root,
128             keyName => $keyName
129             );
130              
131 0 0         if ($valueName eq '*') {
132 0           my %ret;
133 0           foreach (keys %$key) {
134 0           s{^/}{};
135 0           $ret{$_}=$key->{"/$_"};
136             }
137 0           return \%ret;
138             } else {
139 0           return $key->{"/$valueName"};
140             }
141             }
142              
143             sub getRegistryKey {
144 0     0 1   my (%params) = @_;
145              
146 0           my ($root, $keyName);
147 0 0         if ($params{path} =~ m{^(HKEY_\S+)/(.+)} ) {
148 0           $root = $1;
149 0           $keyName = $2;
150             } else {
151 0 0         $params{logger}->error(
152             "Failed to parse '$params{path}'. Does it start with HKEY_?"
153             ) if $params{logger};
154 0           return;
155             }
156              
157 0           return _getRegistryKey(
158             logger => $params{logger},
159             root => $root,
160             keyName => $keyName
161             );
162             }
163              
164             sub _getRegistryKey {
165 0     0     my (%params) = @_;
166              
167             ## no critic (ProhibitBitwise)
168 0 0         my $rootKey = is64bit() ?
169             $Registry->Open($params{root}, { Access=> KEY_READ | KEY_WOW64_64 } ) :
170             $Registry->Open($params{root}, { Access=> KEY_READ } ) ;
171              
172 0 0         if (!$rootKey) {
173 0 0         $params{logger}->error(
174             "Can't open $params{root} key: $EXTENDED_OS_ERROR"
175             ) if $params{logger};
176 0           return;
177             }
178 0           my $key = $rootKey->Open($params{keyName});
179              
180 0           return $key;
181             }
182              
183             sub runCommand {
184 0     0 1   my (%params) = (
185             timeout => 3600 * 2,
186             @_
187             );
188              
189 0           my $job = Win32::Job->new();
190              
191 0           my $buff = File::Temp->new();
192              
193 0           my $winCwd = Cwd::getcwd();
194 0           $winCwd =~ s{/}{\\}g;
195              
196 0           my ($fh, $filename) = File::Temp::tempfile( "$ENV{TEMP}\\fusinvXXXXXXXXXXX", SUFFIX => '.bat');
197 0           print $fh "cd \"".$winCwd."\"\r\n";
198 0           print $fh $params{command}."\r\n";
199 0           print $fh "exit %ERRORLEVEL%\r\n";
200 0           close $fh;
201              
202 0           my $args = {
203             stdout => $buff,
204             no_window => 1
205             };
206              
207 0           $job->spawn(
208             "$ENV{SYSTEMROOT}\\system32\\cmd.exe",
209             "start /wait cmd /c $filename",
210             $args
211             );
212              
213 0           $job->run($params{timeout});
214 0           unlink($filename);
215              
216 0           $buff->seek(0, SEEK_SET);
217              
218 0           my $exitcode;
219              
220 0           my ($status) = $job->status();
221 0           foreach my $pid (%$status) {
222 0           $exitcode = $status->{$pid}{exitcode};
223 0           last;
224             }
225              
226 0           return ($exitcode, $buff);
227             }
228              
229             sub getInterfaces {
230              
231 0     0 1   my @configurations;
232              
233 0           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 0 0         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             };
247              
248 0 0         if ($object->{DefaultIPGateway}) {
249 0           $configuration->{IPGATEWAY} = $object->{DefaultIPGateway}->[0];
250             }
251              
252 0 0         if ($object->{DNSServerSearchOrder}) {
253 0           $configuration->{dns} = $object->{DNSServerSearchOrder}->[0];
254             }
255              
256 0 0         if ($object->{IPAddress}) {
257 0           foreach my $address (@{$object->{IPAddress}}) {
  0            
258 0           my $prefix = shift @{$object->{IPSubnet}};
  0            
259 0           push @{$configuration->{addresses}}, [ $address, $prefix ];
  0            
260             }
261             }
262              
263 0           $configurations[$object->{Index}] = $configuration;
264             }
265              
266 0           my @interfaces;
267              
268 0           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 0 0         next unless $object->{PNPDeviceID};
275              
276 0           my $pciid;
277 0 0         if ($object->{PNPDeviceID} =~ /PCI\\VEN_(\w{4})&DEV_(\w{4})&SUBSYS_(\w{4})(\w{4})/) {
278 0           $pciid = join(':', $1 , $2 , $3 , $4);
279             }
280              
281 0           my $configuration = $configurations[$object->{Index}];
282              
283 0 0         if ($configuration->{addresses}) {
284 0           foreach my $address (@{$configuration->{addresses}}) {
  0            
285              
286 0           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             };
295              
296 0 0         if ($address->[0] =~ /$ip_address_pattern/) {
297 0           $interface->{IPADDRESS} = $address->[0];
298 0           $interface->{IPMASK} = $address->[1];
299 0           $interface->{IPSUBNET} = getSubnetAddress(
300             $interface->{IPADDRESS},
301             $interface->{IPMASK}
302             );
303 0           $interface->{IPDHCP} = $configuration->{IPDHCP};
304 0           $interface->{IPGATEWAY} = $configuration->{IPGATEWAY};
305             } else {
306 0           $interface->{IPADDRESS6} = $address->[0];
307 0           $interface->{IPMASK6} = getNetworkMaskIPv6($address->[1]);
308 0           $interface->{IPSUBNET6} = getSubnetAddressIPv6(
309             $interface->{IPADDRESS6},
310             $interface->{IPMASK6}
311             );
312             }
313              
314 0 0         $interface->{SPEED} = $object->{Speed} / 1_000_000
315             if $object->{Speed};
316 0           $interface->{VIRTUALDEV} = _isVirtual($object, $configuration);
317              
318 0           push @interfaces, $interface;
319             }
320             } else {
321 0 0         next unless $configuration->{MACADDR};
322              
323 0           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             };
332              
333 0 0         $interface->{SPEED} = $object->{Speed} / 1_000_000
334             if $object->{Speed};
335 0           $interface->{VIRTUALDEV} = _isVirtual($object, $configuration);
336              
337 0           push @interfaces, $interface;
338             }
339              
340             }
341              
342             return
343 0           @interfaces;
344              
345             }
346              
347             sub _isVirtual {
348 0     0     my ($object, $configuration) = @_;
349              
350             # PhysicalAdapter only work on OS > XP
351 0 0         if (defined $object->{PhysicalAdapter}) {
352 0 0         return $object->{PhysicalAdapter} ? 0 : 1;
353             }
354              
355             # http://forge.fusioninventory.org/issues/1166
356 0 0 0       if ($configuration->{DESCRIPTION} &&
      0        
357             $configuration->{DESCRIPTION} =~ /RAS/ &&
358             $configuration->{DESCRIPTION} =~ /Adapter/i
359             ) {
360 0           return 1;
361             }
362              
363 0 0         return $object->{PNPDeviceID} =~ /^ROOT/ ? 1 : 0;
364             }
365              
366              
367             1;
368             __END__