biakus
Newbie | Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Просьба потестировать скрипт, который извлекает из WSUSа последние обновления с учетом заменяемости между ними, раскладывает их по полочками и создает описания: Цитата: #!/usr/bin/perl -w # # Name: WSUSExtractor.pl # # Purpose: Extract latest or all updates from WSUS and generate descriptions. # # Syntax: WSUSExtractor.pl # # Version: 2.0 # # Requirements: Windows 2000+ # ActivePerl 5.8.7 buld 815 or higher from www.ActiveState.com # osql.exe Utility (included in MSDE) # WSUS on MSDE # # Author: biakus # use strict; use locale; use IO::File; use File::Path; use Win32::Registry; use Fcntl; $|=1; my $ppid; my $fpid=open_pid_file("WSUSExtractor.pid"); print $fpid $$; close($fpid); # warn("Starting script (pid=$$)\n"); ########################################################################################### # Tunable params ########################################################################################### my $root = "C:\\WSUS\\FtpRoot"; # your dir for files (volume must be the same at WSUS content folder for NTFS hardlinks) my $is_latest = 1; # Is all updates extract or latest only my $update_langs = " 0,1033,1049 "; # comma separated languageIDs of extracting updates (0-all,1033-en,1049-ru,..%lang) my $descr_lang = "ru"; # short language of titles and descriptions of updates (en,ar ..) my $descfile = "_contents.html"; my $root_url = "ftp://ftp.server.com/microsoft"; my $db_name = "SUSDB"; my $content_dir = ""; # WSUS content folder my $server_name = ""; # WSUS SqlServerName my $tmpfile = "query.sql"; ########################################################################################### my @rec = (); my $col; my $query; my $cmd; my $is_replaced; ########################################################################################### # Huge SQL query temporary save to file ########################################################################################### my $latest_str = ""; if ($is_latest) { $latest_str = qq { AND tbBundleDependency.BundledRevisionID NOT IN ( SELECT tbBundleDependency.BundledRevisionID FROM tbRevisionSupersedesUpdate INNER JOIN tbUpdate ON ( tbUpdate.UpdateID = tbRevisionSupersedesUpdate.SupersededUpdateID ) INNER JOIN tbRevision ON ( tbRevision.LocalUpdateID = tbUpdate.LocalUpdateID ) INNER JOIN tbBundleDependency ON ( tbBundleDependency.RevisionID = tbRevision.RevisionID ) ) }; print "\nStart extracting latest updates\n"; } else { print "\nStart extracting all updates\n"; } open (SQL, ">$tmpfile"); print SQL qq { SELECT C2.CategoryID, C3.CategoryID, RC4.CategoryID, tbRevisionLanguage.LanguageID, tbFile.FileName, tbFile.FileDigest, tbFile.Modified, tbFile.Size, tbPreComputedLocalizedProperty.Title, tbPreComputedLocalizedProperty.Description, tbPreComputedLocalizedProperty.RevisionNumber FROM tbCategory AS C1 INNER JOIN tbCategory AS C2 ON C1.CategoryID = C2.ParentCategoryID INNER JOIN tbCategory AS C3 ON C2.CategoryID = C3.ParentCategoryID INNER JOIN tbCategory AS C4 ON (C4.ParentCategoryID IS NULL AND C4.CategoryID != 7 ) INNER JOIN tbRevisionInCategory AS RC3 ON ( C3.CategoryID = RC3.CategoryID ) INNER JOIN tbRevisionInCategory AS RC4 ON ( RC4.RevisionID = RC3.RevisionID AND RC4.CategoryID = C4.CategoryID ) INNER JOIN tbBundleDependency ON ( RC4.RevisionID = tbBundleDependency.BundledRevisionID $latest_str ) INNER JOIN tbPreComputedLocalizedProperty ON ( tbPreComputedLocalizedProperty.RevisionID = tbBundleDependency.RevisionID ) INNER JOIN tbRevision ON ( tbRevision.RevisionID = RC4.RevisionID AND tbRevision.IsLatestRevision = 1 ) INNER JOIN tbFileForRevision ON ( tbFileForRevision.RevisionID = RC4.RevisionID ) INNER JOIN tbRevisionLanguage ON ( tbRevisionLanguage.RevisionID = tbFileForRevision.RevisionID ) INNER JOIN tbFile ON ( tbFile.FileDigest = tbFileForRevision.FileDigest ) WHERE ( tbRevisionLanguage.Expanded = 0 AND tbRevisionLanguage.LanguageID IN ( $update_langs ) AND tbPreComputedLocalizedProperty.ShortLanguage like '$descr_lang' ) ORDER BY tbFile.Modified }; close(SQL); ########################################################################################### # Find WSUS content folder and SQL server address ########################################################################################### my $reg; $::HKEY_LOCAL_MACHINE->Open("SOFTWARE\\Microsoft\\Update Services\\Server\\Setup", $reg) or die "Can't open registry: $^E"; $reg->QueryValueEx("ContentDir", $col, $content_dir) or die "Cannot find WSUS content folder in registry: $^E"; $content_dir = $content_dir."\\WsusContent"; # print "Here's a dir: $content_dir\n"; $reg->QueryValueEx("SqlServerName", $col, $server_name) or die "Cannot find WSUS content folder in registry: $^E"; $server_name =~ s/%computername%/$ENV{computername}/; # print "Here's a server name: $server_name\n"; ########################################################################################### # Put to hash Microsoft WSUS languages ########################################################################################### my %lang = (); $query= "SELECT LanguageID, ShortLanguage FROM tbLanguage"; $cmd = "osql.exe -w 500 -h-1 -s# -n -E -d $db_name -S $server_name -Q \"$query\" 2>nul |"; open (DAT, $cmd); while(<DAT>) { chomp; # print $_."\n"; if ( @rec = split(/#/) ) { foreach $col (@rec) { if( $col =~ /^\s*(.+?)\s*$/ ) {$col = $1;} } } else { last; } $lang{$rec[0]} = $rec[1]; # print $rec[0]."\t".$rec[1]."\n"; } close(DAT); ########################################################################################### # Put to hash Microsoft WSUS categories ########################################################################################### my %ctg = (); $query= "SELECT CategoryID, Title FROM tbPrecomputedCategoryLocalizedProperty WHERE ( ShortLanguage like 'en') "; $cmd = "osql.exe -w 500 -h-1 -s# -n -E -d $db_name -S $server_name -Q \"$query\" 2>nul |"; open (DAT, $cmd); while(<DAT>) { chomp; if ( @rec = split(/#/) ) { foreach $col (@rec) { if( $col =~ /^\s*(.+?)\s*$/ ) {$col = $1;} } } else { last; } $rec[1] =~ s/\s+|\//_/g; $rec[1] =~ s/\W//g; $ctg{$rec[0]} = $rec[1]; } close(DAT); ########################################################################################### # Processing Microsoft WSUS database and save files with descriptions ########################################################################################### my $srcfile; my $dstfile; my $path; my $counter = 0; my $result; $cmd = "osql.exe -w 5000 -h-1 -s# -n -E -d $db_name -S $server_name -i $tmpfile 2>nul |"; print "\nProcessing..\n"; open (DAT, $cmd); while(<DAT>) { chomp; # print $_."\n"; if ( @rec = split(/#/) ) { foreach $col (@rec) { if( $col =~ /^\s*(.+?)\s*$/ ) {$col = $1;} } } else { last; } if ($rec[4] =~ m/.(\w+)$/ ) { $srcfile = $1; } if ($rec[5] =~ m/^0x(\w{38})(\w{2})$/){ $srcfile = "$content_dir\\$2\\$1$2.$srcfile"; } $path = $root."\\".$ctg{$rec[0]}."\\".$ctg{$rec[1]}."\\".$ctg{$rec[2]}."\\".$lang{$rec[3]}; if (! -e $path) { mkpath($path, 0, 0777) or die "Can't create dir: $^E\n";} $dstfile = $path."\\".$rec[4]; $result = hardlink($srcfile, $dstfile, 1); if ( $result && ! $is_replaced ) { $counter += 1; } if ( $result ) { add2file($path, $rec[6], $rec[4], $rec[7], $rec[8], $rec[9], $rec[10], $root_url."/".$ctg{$rec[0]}."/".$ctg{$rec[1]}."/".$ctg{$rec[2]}."/".$lang{$rec[3]}."/".$rec[4] ); } } close(DAT); print "\nTotal matching updates in database: $counter\n"; unlink($tmpfile) or die "Cannot unlink tmpfile: $^E"; warn "\nSuccess\n"; ########################################################################################### # POSIX NTFS hard link to file ########################################################################################### sub hardlink { my $exist_file=shift; my $new_file=shift; my $is_rewrite = shift; $is_replaced = 0; if(-e $new_file && $is_rewrite) { $is_replaced = 1; unlink ($new_file) or return 0; } link ( $exist_file, $new_file ) or return 0; return 1; } ########################################################################################### # Adding description to file ########################################################################################### sub add2file { my $file_path=shift; my $file = $file_path."\\".$descfile; my $modified = shift; my $name = shift; my $size = shift; my $title = shift; my $desc = shift; my $revnum = shift; my $url = shift; my $header = qq {<html> <head> <title>Description of updates</title> <meta http-equiv="Content-Type" content="text/html; charset=cp866"> </head> <body> <table align=center border cellspacing=0 cellpadding=5> <caption align=up><h2>Описания и ссылки на обновления для данной директории</h2></caption> <tr> <td><h3>Имя Файла</h3></td> <td><h3>Размер файла</h3></td> <td><h3>Название</h3></td> <td><h3>Описание</h3></td> <td><h3>Дата модификации</h3></td> </tr> }; my $footer = qq { </table> <p align="right"><font size="2">©biakus</font></p> </body></html>}; if(-e $file) { sysopen (DESC, "$file", O_WRONLY); seek (DESC, -length($footer)-1, 2); print DESC "<tr>"."\n\t"."<td><a href=".$url.">".$name."</a></td>" ."\n\t"."<td>".dots($size)."</td>" ."\n\t"."<td>".$title."</td>" ."\n\t"."<td>".$desc."</td>" ."\n\t"."<td>".$modified."</td>" ."\n</tr> \n"; print DESC $footer; close(DESC); } else { open (DESC, ">$file"); print DESC $header; print DESC "<tr>"."\n\t"."<td><a href=".$url.">".$name."</a></td>" ."\n\t"."<td>".dots($size)."</td>" ."\n\t"."<td>".$title."</td>" ."\n\t"."<td>".$desc."</td>" ."\n\t"."<td>".$modified."</td>" ."\n</tr> \n"; # FileName, Size, Title, Desc, Modified print DESC $footer; close(DESC); } return 1; } ########################################################################################### # dots for bytes processing ########################################################################################### sub dots { my $tmp = shift; my @tmp=(); unshift (@tmp, $1) while ($tmp =~ s/(\d{1,3})$//g); return join ('.', @tmp); } ########################################################################################### # pid processing ########################################################################################### sub open_pid_file { my $file=shift; if(-e $file) { my $fh=IO::File->new($file) or die "Can't open PID file $file: $^E"; $ppid=<$fh>; die "Invalid PID file" unless $ppid=~ /^(\d+)$/; die "Process already running with PID $ppid" if(kill 0 => $ppid); warn "Removing PID file for defunct process ($ppid).\n"; die "Can't unlink PID file $file" unless -w $file && unlink $file; } $ppid=$$; return IO::File->new($file,O_WRONLY|O_CREAT|O_EXCL,0644) or die "Can't create $file: $^E\n"; } ########################################################################################### END { unlink "WSUSExtractor.pid" or die "Can't unlink PID file: $^E"; # warn("Stoping script (pid=$$)\n"); } __END__ | |