Fixed Windows version check for CBS registry tree.
Partially implemented device-dependent driver installation.
This commit is contained in:
parent
61190595d5
commit
ff05673dcf
16
logging.pm
16
logging.pm
|
@ -21,6 +21,7 @@ require Exporter;
|
|||
set_log_level
|
||||
set_log_base_dir
|
||||
set_current_pkg_name
|
||||
get_win_major
|
||||
get_win_version
|
||||
get_default_vars
|
||||
set_datetime_vars
|
||||
|
@ -192,6 +193,13 @@ sub compare_versions ($$)
|
|||
}
|
||||
}
|
||||
|
||||
sub get_win_major ()
|
||||
{
|
||||
my ($osver, $osmajor, $osminor, $osbuild) = Win32::GetOSVersion();
|
||||
|
||||
return $osmajor;
|
||||
}
|
||||
|
||||
sub get_win_version ()
|
||||
{
|
||||
my ($osver, $osmajor, $osminor, $osbuild) = Win32::GetOSVersion();
|
||||
|
@ -199,6 +207,13 @@ sub get_win_version ()
|
|||
return $osmajor.'.'.$osminor;
|
||||
}
|
||||
|
||||
sub get_win_build ()
|
||||
{
|
||||
my ($osver, $osmajor, $osminor, $osbuild) = Win32::GetOSVersion();
|
||||
|
||||
return $osbuild;
|
||||
}
|
||||
|
||||
sub get_default_vars (;$)
|
||||
{
|
||||
my ($config) = @_;
|
||||
|
@ -219,6 +234,7 @@ sub get_default_vars (;$)
|
|||
$$vars{arch} = $arch;
|
||||
$$vars{xarch} = $xarch;
|
||||
$$vars{osversion} = get_win_version();
|
||||
$$vars{osbuild} = get_win_build();
|
||||
$$vars{programfiles32} = $programfiles32;
|
||||
$$vars{pkgtooldir} = $pkgtool_dir;
|
||||
$$vars{logdir} = $log_base_dir if defined $log_base_dir;
|
||||
|
|
|
@ -139,12 +139,16 @@ else {
|
|||
}
|
||||
my $error = scan_package_dirs($config, $base_directory);
|
||||
exit(1) if defined $error;
|
||||
#$error = scan_driver_dirs($config, $base_directory);
|
||||
#exit(1) if defined $error;
|
||||
$$config{'package-def'} = {} unless defined $$config{'package-def'};
|
||||
$$config{'global-variables'} = $globals;
|
||||
|
||||
my $db = {};
|
||||
read_installed_packages($db);
|
||||
read_installed_patches($db);
|
||||
read_present_devices($db);
|
||||
read_installed_infs($db);
|
||||
|
||||
my $counters = {
|
||||
RebootFlag => 0,
|
||||
|
|
365
pkgtool.pm
365
pkgtool.pm
|
@ -10,7 +10,10 @@ require Exporter;
|
|||
get_default_dnsdomain
|
||||
read_installed_patches
|
||||
read_installed_packages
|
||||
read_present_devices
|
||||
read_installed_infs
|
||||
scan_package_dirs
|
||||
scan_driver_dir
|
||||
handle_pkg
|
||||
get_install_sets
|
||||
);
|
||||
|
@ -33,6 +36,44 @@ use Win32::File::VersionInfo;
|
|||
use Win32API::File qw(:Func :Misc :FILE_SHARE_ :GENERIC_);
|
||||
use LWP::UserAgent;
|
||||
|
||||
my $driver_syntax = {
|
||||
Type => 'map',
|
||||
Elements => {
|
||||
Type => 'struct',
|
||||
Check => \&check_cfg_drvdef,
|
||||
Keywords => {
|
||||
'description' => {
|
||||
Type => 'string',
|
||||
Mandatory => 1
|
||||
},
|
||||
'inf-file' => {
|
||||
Type => 'string',
|
||||
Mandatory => 1
|
||||
},
|
||||
'cert-file' => {
|
||||
Type => 'string'
|
||||
},
|
||||
'device-filter' => {
|
||||
Type => 'list',
|
||||
Mandatory => 1,
|
||||
Elements => {
|
||||
Type => 'struct',
|
||||
Keywords => {
|
||||
'bus' => {
|
||||
Type => 'string',
|
||||
Mandatory => 1
|
||||
},
|
||||
'device' => {
|
||||
Type => 'string',
|
||||
Mandatory => 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
my $pkgdef_syntax = {
|
||||
Type => 'map',
|
||||
Elements => {
|
||||
|
@ -266,6 +307,13 @@ my $patchdef_syntax = {
|
|||
}
|
||||
};
|
||||
|
||||
my $driver_cfg_syntax = {
|
||||
Type => 'struct',
|
||||
Keywords => {
|
||||
'driver' => $driver_syntax,
|
||||
}
|
||||
};
|
||||
|
||||
my $pkgdef_cfg_syntax = {
|
||||
Type => 'struct',
|
||||
Keywords => {
|
||||
|
@ -378,6 +426,15 @@ my $global_cfg_syntax = {
|
|||
}
|
||||
}
|
||||
},
|
||||
'drivers' => {
|
||||
Type => 'struct',
|
||||
Keywords => {
|
||||
'filename' => {
|
||||
Type => 'string',
|
||||
Mandatory => 1
|
||||
}
|
||||
}
|
||||
},
|
||||
'mbr-drive' => {
|
||||
Type => 'string'
|
||||
},
|
||||
|
@ -406,6 +463,9 @@ my $global_cfg_syntax = {
|
|||
'mbr-source-file' => {
|
||||
Type => 'string'
|
||||
},
|
||||
'driver-directory' => {
|
||||
Type => 'string'
|
||||
},
|
||||
'remove-version' => {
|
||||
Type => 'string'
|
||||
},
|
||||
|
@ -438,6 +498,12 @@ my $global_cfg_syntax = {
|
|||
},
|
||||
'enabled' => {
|
||||
Type => 'integer'
|
||||
},
|
||||
'filename' => {
|
||||
Type => 'string'
|
||||
},
|
||||
'max-depth' => {
|
||||
Type => 'integer'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -683,8 +749,8 @@ sub read_installed_patches ($)
|
|||
|
||||
delete $$db{PatchesChanged};
|
||||
my $patches = {};
|
||||
my $winver = get_win_version();
|
||||
if ($winver ge '6.0') {
|
||||
my $winmajor = get_win_major();
|
||||
if ($winmajor >= 6) {
|
||||
my $cbspatches = $Registry->Open('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages\\', { Access => 'KEY_READ' });
|
||||
if (! defined $cbspatches) {
|
||||
print_log('global', ERROR, 'Cannot find registry entry: %s', 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages');
|
||||
|
@ -769,6 +835,79 @@ sub refresh_installed_packages ($)
|
|||
read_installed_packages($db);
|
||||
}
|
||||
|
||||
sub read_devices ($$)
|
||||
{
|
||||
my ($devices, $registry) = @_;
|
||||
|
||||
foreach my $busname ($registry->SubKeyNames) {
|
||||
my $bus = $registry->{$busname};
|
||||
next unless defined $bus;
|
||||
foreach my $devname ($bus->SubKeyNames) {
|
||||
my $inst = {
|
||||
Bus => $busname,
|
||||
Device => $devname
|
||||
};
|
||||
my $device = $busname.'\\'.$devname;
|
||||
$$devices{$device} = $inst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub read_present_devices ($)
|
||||
{
|
||||
my ($db) = @_;
|
||||
|
||||
my $devices = {};
|
||||
my $uninst = $Registry->Open('HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\', { Access => 'KEY_READ' });
|
||||
if (! defined $uninst) {
|
||||
print_log('global', ERROR, 'Cannot find registry entry: %s', 'HKLM\\SYSTEM\\CurrentControlSet\\Enum');
|
||||
return undef;
|
||||
}
|
||||
read_devices($devices, $uninst);
|
||||
$$db{Devices} = $devices;
|
||||
return $db;
|
||||
}
|
||||
|
||||
sub read_installed_infs ($)
|
||||
{
|
||||
my ($db) = @_;
|
||||
|
||||
my $directory = $ENV{'SYSTEMROOT'}.'\\Inf';
|
||||
my $infs = {};
|
||||
print_log('global', DEBUG3, 'Scanning directory %s for .INF files', $directory);
|
||||
if (! opendir(DIR, $directory)) {
|
||||
print_log('global', ERROR, 'Cannot read directory %s: %s', $directory, $!);
|
||||
return undef;
|
||||
}
|
||||
while (1) {
|
||||
my $name = readdir(DIR);
|
||||
last unless defined $name;
|
||||
next if $name eq '.' || $name eq '..';
|
||||
next unless $name =~ /\.inf$/io;
|
||||
my $path = $directory.'\\'.$name;
|
||||
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size) = stat($path);
|
||||
if (! defined $size) {
|
||||
print_log('global', INFO, 'Cannot read .INF file size for %s: %s', $path, $!);
|
||||
next;
|
||||
}
|
||||
my $inf = {
|
||||
Path => $path,
|
||||
Filename => $name,
|
||||
Size => $size
|
||||
};
|
||||
$$infs{$path} = $inf;
|
||||
print_log('global', DEBUG3, 'Found .INF file %s (%d bytes)', $path, $size);
|
||||
}
|
||||
closedir(DIR);
|
||||
$$db{INFs} = $infs;
|
||||
return $db;
|
||||
}
|
||||
|
||||
sub check_cfg_drvdef ($$)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub check_cfg_pkgdef ($$)
|
||||
{
|
||||
my ($install, $label) = @_;
|
||||
|
@ -1775,16 +1914,16 @@ sub pkg_check_condition ($$$)
|
|||
return ($condition);
|
||||
}
|
||||
|
||||
sub scan_dir ($$$$);
|
||||
sub scan_package_dir ($$$$);
|
||||
|
||||
sub scan_dir ($$$$)
|
||||
sub scan_package_dir ($$$$)
|
||||
{
|
||||
my ($config, $dir, $maxdepth, $filename) = @_;
|
||||
|
||||
print_log('global', DEBUG1, 'Scanning package directory %s for %d levels', $dir, $maxdepth);
|
||||
|
||||
if (! opendir(DIR, $dir)) {
|
||||
print_log('global', 'ERROR', 'Cannot scan directory %s', $dir);
|
||||
print_log('global', ERROR, 'Cannot scan directory %s', $dir);
|
||||
return 1;
|
||||
}
|
||||
my $subdirs = [];
|
||||
|
@ -1801,7 +1940,7 @@ sub scan_dir ($$$$)
|
|||
|
||||
$maxdepth--;
|
||||
foreach my $path (@$subdirs) {
|
||||
my $error = scan_dir($config, $path, $maxdepth, $filename);
|
||||
my $error = scan_package_dir($config, $path, $maxdepth, $filename);
|
||||
return $error if defined $error;
|
||||
}
|
||||
|
||||
|
@ -1862,12 +2001,82 @@ sub scan_package_dirs ($$)
|
|||
my $vars = get_default_vars($config);
|
||||
foreach my $dir (@$dirs) {
|
||||
my $scandir = substitute_variables($vars, $dir, 1, $basedir, 'global');
|
||||
my $error = scan_dir($config, $scandir, $maxdepth, $filename);
|
||||
my $error = scan_package_dir($config, $scandir, $maxdepth, $filename);
|
||||
return $error if defined $error;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub scan_driver_subdir ($$$$$);
|
||||
|
||||
sub scan_driver_subdir ($$$$$)
|
||||
{
|
||||
my ($config, $drvconfig, $dir, $maxdepth, $filename) = @_;
|
||||
|
||||
print_log('global', DEBUG1, 'Scanning driver directory %s for %d levels', $dir, $maxdepth);
|
||||
|
||||
if (! opendir(DIR, $dir)) {
|
||||
print_log('global', INFO, 'Cannot scan directory %s', $dir);
|
||||
return undef;
|
||||
}
|
||||
my $subdirs = [];
|
||||
my $files = [];
|
||||
while (1) {
|
||||
my $name = readdir(DIR);
|
||||
last unless defined $name;
|
||||
next if $name eq '.' || $name eq '..';
|
||||
my $path = $dir.'\\'.$name;
|
||||
push @$subdirs, $path if $maxdepth > 0 && -d $path;
|
||||
push @$files, $path if $name eq $filename;
|
||||
}
|
||||
closedir(DIR);
|
||||
|
||||
$maxdepth--;
|
||||
foreach my $path (@$subdirs) {
|
||||
my $error = scan_driver_subdir($config, $drvconfig, $path, $maxdepth, $filename);
|
||||
return $error if defined $error;
|
||||
}
|
||||
|
||||
my $drvdefs = $$drvconfig{'driver'};
|
||||
foreach my $path (@$files) {
|
||||
print_log('global', DEBUG1, 'Found driver definition file %s', $path);
|
||||
my $addconfig = parse_cfg_file($path, $driver_cfg_syntax);
|
||||
return 1 unless defined $addconfig;
|
||||
my $adddrvdefs = $$addconfig{'driver'};
|
||||
if (defined $adddrvdefs) {
|
||||
foreach my $key (keys %$adddrvdefs) {
|
||||
my $def = $$adddrvdefs{$key};
|
||||
$drvdefs = $$drvconfig{'driver'} = {} unless defined $drvdefs;
|
||||
if (defined $$drvdefs{$key}) {
|
||||
print_log('global', WARNING, 'Found driver re-definition for %s in file %s', $key, $path);
|
||||
next;
|
||||
}
|
||||
$$def{'definition-directory'} = $dir;
|
||||
$$drvdefs{$key} = $def;
|
||||
print_log('global', DEBUG3, 'Found driver definition for %s (%s) in file %s', $key, $$def{description}, $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub scan_driver_dir ($$$)
|
||||
{
|
||||
my ($config, $basedir, $drvconfig) = @_;
|
||||
|
||||
my $filename = $$drvconfig{'filename'};
|
||||
my $maxdepth = $$drvconfig{'max-depth'};
|
||||
my $dir = $$drvconfig{'driver-directory'};
|
||||
return undef unless defined $filename && defined $dir;
|
||||
|
||||
print_log('global', INFO, 'Scanning driver directory %s', $dir);
|
||||
|
||||
my $vars = get_default_vars($config);
|
||||
my $scandir = substitute_variables($vars, $dir, 1, $basedir, 'global');
|
||||
return scan_driver_subdir($config, $drvconfig, $scandir, $maxdepth, $filename);
|
||||
}
|
||||
|
||||
sub match_package_version_for_processing ($$$)
|
||||
{
|
||||
my ($instver, $op, $ver) = @_;
|
||||
|
@ -2376,6 +2585,141 @@ sub do_modify_user ($$$$$)
|
|||
return 1;
|
||||
}
|
||||
|
||||
sub install_cert ($)
|
||||
{
|
||||
my ($certpath) = @_;
|
||||
|
||||
my $sourcefile = $ENV{systemroot}.'\\System32\\certmgr.exe';
|
||||
my $paramlist = ['-add', $certpath, '-c', '-s', '-r', 'localMachine', 'TrustedPublisher'];
|
||||
my ($error, $exitcode) = run_exe('global', undef, undef, undef, $sourcefile, $paramlist, 0);
|
||||
if (defined $error) {
|
||||
print_log('global', ERROR, 'Error installing certificate %s: %s',
|
||||
$certpath, $error);
|
||||
return 0;
|
||||
}
|
||||
print_log('global', DEBUG1, 'Installed certifiate %s', $certpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub install_pnp_driver ($)
|
||||
{
|
||||
my ($infpath) = @_;
|
||||
|
||||
my $sourcefile = $ENV{systemroot}.'\\System32\\pnputil.exe';
|
||||
my $paramlist = ['-i', '-a', $infpath];
|
||||
my ($error, $exitcode) = run_exe('global', undef, undef, undef, $sourcefile, $paramlist, 0);
|
||||
if (defined $error) {
|
||||
print_log('global', ERROR, 'Error installing extra driver %s: %s',
|
||||
$infpath, $error);
|
||||
return 0;
|
||||
}
|
||||
print_log('global', DEBUG1, 'Installed extra driver %s', $infpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub check_if_driver_matches ($$)
|
||||
{
|
||||
my ($db, $drvdef) = @_;
|
||||
|
||||
my $filter = $$drvdef{'device-filter'};
|
||||
return 0 unless defined $filter;
|
||||
|
||||
my $devices = $$db{Devices};
|
||||
foreach my $devpath (sort keys %$devices) {
|
||||
my $device = $$devices{$devpath};
|
||||
next unless defined $device;
|
||||
my $busname = $$device{Bus};
|
||||
my $devname = $$device{Device};
|
||||
|
||||
my $matches = 0;
|
||||
foreach my $filtrow (@$filter) {
|
||||
my $busfilter = $$filtrow{bus};
|
||||
my $devfilter = $$filtrow{device};
|
||||
next unless defined $busfilter && defined $devfilter;
|
||||
next unless $busname =~ /$busfilter/ && $devname =~ /$devfilter/;
|
||||
$matches = 1;
|
||||
last;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub handle_driver ($$$$$$)
|
||||
{
|
||||
my ($config, $base_directory, $db, $pkg, $counters, $update) = @_;
|
||||
|
||||
my $name = $$pkg{name};
|
||||
my $error = scan_driver_dir($config, $base_directory, $pkg);
|
||||
if (defined $error) {
|
||||
print_log('global', INFO, 'Skipping driver set %s because no driver directory', $name);
|
||||
push @{$$counters{SkipList}}, $name;
|
||||
$$counters{SkipCount}++;
|
||||
return 0;
|
||||
}
|
||||
my $drvdefs = $$pkg{'driver'};
|
||||
if (! defined $drvdefs) {
|
||||
print_log('global', INFO, 'Skipping driver set %s because no driver definition found', $name);
|
||||
push @{$$counters{SkipList}}, $name;
|
||||
$$counters{SkipCount}++;
|
||||
return 0;
|
||||
}
|
||||
foreach my $drvname (sort keys %$drvdefs) {
|
||||
my $drvdef = $$drvdefs{$drvname};
|
||||
my $drvinstname = $name.'/'.$drvname;
|
||||
|
||||
|
||||
my $infpath = $$drvdef{'definition-directory'}.'/'.$$drvdef{'inf-file'};
|
||||
my $certpath = defined $$drvdef{'cert-file'} ? $$drvdef{'definition-directory'}.'/'.$$drvdef{'cert-file'} : undef;
|
||||
if (! -r $infpath) {
|
||||
print_log('global', INFO, 'Skipping driver %s because .INF file %s not found', $drvinstname, $infpath);
|
||||
push @{$$counters{FailList}}, $drvinstname;
|
||||
$$counters{FailCount}++;
|
||||
next;
|
||||
}
|
||||
if (defined $certpath && ! -r $certpath) {
|
||||
print_log('global', INFO, 'Skipping driver %s because cert file %s not found', $drvinstname, $certpath);
|
||||
push @{$$counters{FailList}}, $drvinstname;
|
||||
$$counters{FailCount}++;
|
||||
next;
|
||||
}
|
||||
print_log('global', DEBUG2, 'Checking if driver %s is needed', $drvinstname);
|
||||
my $needed = check_if_driver_matches($db, $drvdef);
|
||||
if (! $needed) {
|
||||
print_log('global', INFO, 'Skipping driver %s because relevant device is not present', $drvinstname);
|
||||
push @{$$counters{SkipList}}, $drvinstname;
|
||||
$$counters{SkipCount}++;
|
||||
next;
|
||||
}
|
||||
# check if present
|
||||
print_log('global', WARNING, 'Driver %s to install: not installed - %s',
|
||||
$drvinstname, $update? 'installing '.$infpath : 'INSTALL');
|
||||
if ($update) {
|
||||
my $rc = 1;
|
||||
if (defined $certpath) {
|
||||
$rc = install_cert($certpath);
|
||||
}
|
||||
if ($rc) {
|
||||
$rc = install_pnp_driver($infpath);
|
||||
}
|
||||
if (! $rc) {
|
||||
push @{$$counters{FailList}}, $drvinstname;
|
||||
$$counters{FailCount}++;
|
||||
}
|
||||
else {
|
||||
push @{$$counters{InstalledList}}, $drvinstname;
|
||||
$$counters{InstalledCount}++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
push @{$$counters{ToInstallList}}, $drvinstname;
|
||||
$$counters{ToInstallCount}++;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub read_mbr_file ($$)
|
||||
{
|
||||
my ($config, $pkg) = @_;
|
||||
|
@ -2692,6 +3036,9 @@ sub handle_pkg ($$$$$$)
|
|||
if (defined $$pkg{'mbr-source-file'}) {
|
||||
return handle_mbr($config, $pkg, $counters, $update);
|
||||
}
|
||||
if (defined $$pkg{'driver-directory'}) {
|
||||
return handle_driver($config, $base_directory, $db, $pkg, $counters, $update);
|
||||
}
|
||||
|
||||
my $pkgdefs = $$config{'package-def'};
|
||||
my $patchdefs = $$config{'patch-def'};
|
||||
|
@ -2772,14 +3119,14 @@ sub handle_pkg ($$$$$$)
|
|||
print_log('global', INFO, 'Ignoring patch set %s patches %s: %s', $name, join(',', @{$$patchdef{kb}}), $error);
|
||||
push @{$$counters{FailList}}, @{$$patchdef{kb}};
|
||||
$$counters{FailCount} += scalar @{$$patchdef{kb}};
|
||||
return;
|
||||
next;
|
||||
}
|
||||
if (defined $condcheck) {
|
||||
if (! $condcheck) {
|
||||
print_log('global', INFO, 'Skipping patch set %s patches %s on false condition', $name, join(',', @{$$patchdef{kb}}));
|
||||
push @{$$counters{SkipList}}, @{$$patchdef{kb}};
|
||||
$$counters{SkipCount} += @{$$patchdef{kb}};
|
||||
return;
|
||||
next;
|
||||
}
|
||||
print_log('global', DEBUG1, 'Considering patch set %s patches %s with true condition', $name, join(',', @{$$patchdef{kb}}));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue