| 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__
 
 
 
 | 
 
 |