r41873 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r41872‎ | r41873 | r41874 >
Date:00:33, 9 October 2008
Author:river
Status:old
Tags:
Comment:
base-specs/curl: bump version to avoid useless download during build
TSnicstat, TSpca: new specs
Modified paths:
  • /trunk/tools/ts-specs/TSnicstat.spec (added) (history)
  • /trunk/tools/ts-specs/TSpca.spec (added) (history)
  • /trunk/tools/ts-specs/base-specs/curl.spec (modified) (history)
  • /trunk/tools/ts-specs/ext-sources/pca (added) (history)

Diff [purge]

Index: trunk/tools/ts-specs/base-specs/curl.spec
@@ -5,7 +5,7 @@
66 #
77 Name: TScurl
88 Summary: curl - Get a file from FTP or HTTP server.
9 -Version: 7.18.2
 9+Version: 7.19.0
1010 URL: http://curl.haxx.se/
1111 Source: http://curl.haxx.se/download/curl-%{version}.tar.bz2
1212 SUNW_BaseDir: %{_basedir}
Index: trunk/tools/ts-specs/TSpca.spec
@@ -0,0 +1,35 @@
 2+%include Solaris.inc
 3+
 4+Name: TSpca
 5+Summary: Patch Check Advanced
 6+Version: 20080911-01
 7+Source1: pca
 8+
 9+SUNW_BaseDir: %{_basedir}
 10+BuildRoot: %{_tmppath}/%{name}-%{version}-build
 11+%include default-depend.inc
 12+
 13+# Requires:
 14+
 15+%prep
 16+%setup -q -T -c -n %name-%version
 17+cp %SOURCE1 .
 18+
 19+%build
 20+
 21+%install
 22+rm -rf $RPM_BUILD_ROOT
 23+mkdir -p $RPM_BUILD_ROOT%{_bindir}
 24+cp pca $RPM_BUILD_ROOT%{_bindir}
 25+
 26+%clean
 27+rm -rf $RPM_BUILD_ROOT
 28+
 29+%files
 30+%defattr (-, root, bin)
 31+%dir %attr (0755, root, bin) %{_bindir}
 32+%attr (0755, root, bin) %{_bindir}/pca
 33+
 34+%changelog
 35+* Thu Oct 9 2008 - river@wikimedia.org
 36+- initial spec
Index: trunk/tools/ts-specs/TSnicstat.spec
@@ -0,0 +1,37 @@
 2+%include Solaris.inc
 3+
 4+Name: TSnicstat
 5+Summary: iostat(1m)-like utility for network interfaces
 6+Version: 1
 7+Source1: http://blogs.sun.com/roller/resources/timc/nicstat.c
 8+
 9+SUNW_BaseDir: %{_basedir}
 10+BuildRoot: %{_tmppath}/%{name}-%{version}-build
 11+%include default-depend.inc
 12+
 13+# Requires:
 14+
 15+%prep
 16+%setup -q -T -c -n %name-%version
 17+cp %SOURCE1 .
 18+
 19+%build
 20+
 21+cc nicstat.c -o nicstat -lkstat -lrt -lsocket -lgen
 22+
 23+%install
 24+rm -rf $RPM_BUILD_ROOT
 25+mkdir -p $RPM_BUILD_ROOT%{_bindir}
 26+cp nicstat $RPM_BUILD_ROOT%{_bindir}
 27+
 28+%clean
 29+rm -rf $RPM_BUILD_ROOT
 30+
 31+%files
 32+%defattr (-, root, bin)
 33+%dir %attr (0755, root, bin) %{_bindir}
 34+%{_bindir}/nicstat
 35+
 36+%changelog
 37+* Thu Oct 9 2008 - river@wikimedia.org
 38+- initial spec
Index: trunk/tools/ts-specs/ext-sources/pca
@@ -0,0 +1,3757 @@
 2+#!/bin/sh
 3+#! -*- perl -*-
 4+eval 'exec perl -x -w $0 ${1+"$@"}'
 5+ if 0;
 6+
 7+# PCA - Patch Check Advanced
 8+# Analyze, download and install patches for Sun Solaris
 9+#
 10+# Author : Martin Paul <martin@par.univie.ac.at>
 11+# Home : http://www.par.univie.ac.at/solaris/pca/
 12+my $version='20080911-01';
 13+
 14+use strict;
 15+use Config;
 16+
 17+# Default paths
 18+my $unzip= '/usr/bin/unzip';
 19+my $showrev= '/usr/bin/showrev';
 20+my $pkginfo= '/usr/bin/pkginfo';
 21+my $pkgchk= '/usr/sbin/pkgchk';
 22+my $uncompress= '/usr/bin/uncompress';
 23+my $tar= '/usr/sbin/tar';
 24+my $uname= '/usr/bin/uname';
 25+my $pager= '/usr/bin/more';
 26+
 27+# Supported options, format is:
 28+# Long name, short name, argument type, argument text, default value, help
 29+my @options=(
 30+ "list|l|||0|List patches",
 31+ "listhtml|L|||0|List patches, produce HTML output",
 32+ "download|d|||0|Download patches",
 33+ "install|i|||0|Install patches",
 34+ "pretend|I|||0|Pretend to install patches",
 35+ "readme|r|||0|Display patch READMEs",
 36+ "getxref|x|||0|Download patch xref file",
 37+ "xrefdir|X|s|DIR|/var/tmp|Location of patch xref file",
 38+ "nocheckxref|y|||0|Do not check for updated patch xref file",
 39+ "xrefown||||0|Give write permissions on xref file to user only",
 40+ "nocache||||0|Tell proxy to not cache xref file",
 41+ "patchdir|P|s|DIR|.|Patch download directory",
 42+ "askauth|a|||0|Ask for Sun Online Account data interactively",
 43+ "user||s|USER||Sun Online Account user name",
 44+ "passwd||s|PASS||Sun Online Account password",
 45+ "localurl||s|URL||DEPRECATED",
 46+ "patchurl||s|URL||Local URL for patches and READMEs",
 47+ "xrefurl||s|URL||Local URL for patchdiag.xref",
 48+ "stop||s@|ID||Stop after patch ID",
 49+ "ignore||s@|ID||Ignore patch ID",
 50+ "rec||s@|ID||Set Recommended flag on patch ID",
 51+ "sec||s@|ID||Set Security flag on patch ID",
 52+ "pattern|p|s|REGEX||List only patches whose synopsis matches REGEX",
 53+ "noreboot|n|||0|Install only patches which do not require a reboot",
 54+ "minage||i|DAYS|0|List only patches which are at least DAYS old",
 55+ "maxage||i|DAYS|0|List only patches which are at most DAYS old",
 56+ "syslog||s|TYPE||Log successful patch installs to syslog facility TYPE",
 57+ "nobackup|k|s@|ID||Do not back up files to be patched for patch ID",
 58+ "backdir|B|s|DIR||Saves patch backout data to DIR",
 59+ "safe|s|||0|Check locally modified files for safe patch installation",
 60+ "currentzone|G|||0|Make patchadd install patches in the current zone only",
 61+ "patchadd||s|FILE|/usr/sbin/patchadd|Path to patchadd command",
 62+ "noheader|H|||0|Don't display descriptive headers",
 63+ "format||s|FORMAT|%p %i %e %c %r%s%b %a %y|Set output format to FORMAT",
 64+ "fromfiles|f|s|DIR||Read uname/showrev/pkginfo output from files in DIR",
 65+ "dltries||i|NUM|1|Try downloads from Sun download server NUM times",
 66+ "force|F|||0|Force local caching proxy to download from Sun server",
 67+ "root|R|s|DIR||Alternative root directory",
 68+ "wget||s|FILE|/usr/sfw/bin/wget /usr/local/bin/wget /opt/csw/bin/wget /usr/bin/wget|Path to wget command",
 69+ "wgetproxy||s|URL||Default proxy for wget",
 70+ "logger||s|FILE|/usr/bin/logger|Path to logger command",
 71+ "threads|t|i|NUM|0|Use NUM download threads" . ($Config{useithreads} ? '' : ' (DISABLED, Perl lacks ithreads)'),
 72+ "update||s|TYPE|never|Update pca (TYPE is never, check, now or auto)",
 73+ "pcaurl||s|URL|http://www.par.univie.ac.at/solaris/pca/stable/|URL for pca update",
 74+ "ssprot||s|PROT|http|Protocol used to access sunsolve (http or https)",
 75+ "sshost||s|HOST|sunsolve.sun.com|HOST name or IP address for sunsolve",
 76+ "cffile||s@|FILE||Read FILE as additional configuration file",
 77+ "debug|V|||0|Print debug information",
 78+ "help|h|||0|Display this help",
 79+ "man|m|||0|Display manual page",
 80+ "version|v|||0|Display version information",
 81+ "operands||||missing|ENVFILE",
 82+ "tmpdir||||/tmp|INTERNAL",
 83+ "proxy||||0|INTERNAL",
 84+ "pforce||||0|INTERNAL",
 85+ "dbgfile||||/tmp/pca-proxy-debug.txt|INTERNAL"
 86+);
 87+
 88+# Modules
 89+use Getopt::Long;
 90+use Time::Local;
 91+use Cwd;
 92+use File::Path;
 93+use Fcntl;
 94+use File::Basename;
 95+use File::Copy;
 96+
 97+# Variable declarations
 98+my (%o, %input, %p, %pkgs, %u, %c, %locks, %rloop);
 99+my (@plist, @slist, @rlist);
 100+my $conf_dbg=''; my %conf_read;
 101+my $dlfile='';
 102+my $sttyset=0;
 103+my $patchxdir='';
 104+my $currenttime=time();
 105+my $queue;
 106+my %a;
 107+my $download_start;
 108+my $stopreached=0;
 109+my $wgetv='';
 110+
 111+# Force flush to stdout right after every print command without "\n"
 112+$|= 1;
 113+
 114+# Set signal handler
 115+$SIG{HUP} = 'IGNORE';
 116+$SIG{TERM} = $SIG{INT} = $SIG{QUIT} = \&handler;
 117+
 118+# Main
 119+#
 120+parse_args();
 121+check_prerequisites();
 122+import_threads();
 123+process_patch_exceptions();
 124+process_patch_flags();
 125+update();
 126+
 127+$o{proxy} && proxy();
 128+
 129+expand_operands();
 130+
 131+if ($o{readme} && ("@slist" =~ /^(\d{6}-\d{2} *)+$/)) {
 132+ foreach my $pp (@slist) {
 133+ my ($id, $rev)= split (/-/, $pp);
 134+ init_patch ($id);
 135+ $p{$id}{prev}=$rev;
 136+ push (@plist, $id);
 137+ }
 138+ do_patch_list();
 139+ exit 0;
 140+}
 141+
 142+get_current_xref();
 143+if (!$o{list} && !$o{download} && !$o{install} && !$o{readme}) { exit 0 }
 144+
 145+get_uname();
 146+get_installed_packages();
 147+get_installed_patches();
 148+get_current_patches();
 149+create_patch_list();
 150+print_header();
 151+do_patch_list();
 152+print_footer();
 153+
 154+exit 0;
 155+
 156+# Functions
 157+
 158+sub download_patch_worker {
 159+ my $pp;
 160+ { # signal the download loop that we're ready...
 161+ lock $download_start;
 162+ if (!defined($download_start)) { # if we haven't already
 163+ $download_start = 0;
 164+ dbg ("Broadcasting download worker ready");
 165+ cond_broadcast(\$download_start);
 166+ }
 167+ }
 168+ { # wait for the download loop to tell us to start
 169+ lock $download_start;
 170+ dbg ("Waiting for download loop broadcast");
 171+ cond_wait(\$download_start) until $download_start == 1;
 172+ }
 173+ while (defined($pp = $queue->dequeue())) {
 174+ my ($id, $rev) = split /-/, $pp;
 175+ BEGIN { $^W = 0 }
 176+ $p{$id}{worker_tid} = threads->self->tid();
 177+ BEGIN { $^W = 1 }
 178+ $p{$id}{worker_done} = 0;
 179+ dbg ("Worker " . $p{$id}{worker_tid} . " downloading " . $pp);
 180+ download_patch($pp);
 181+ $p{$id}{worker_done} = 1;
 182+ }
 183+ $queue->enqueue(undef);
 184+}
 185+
 186+sub buffer_worker_out {
 187+ my $id = shift;
 188+ my $cat = shift;
 189+
 190+ if (!$o{threads}) {
 191+ return (out ($cat, @_));
 192+ }
 193+
 194+ my $ar = &share([]);
 195+ @{$ar} = ($cat, @_);
 196+ lock $p{$id};
 197+ push @{$p{$id}{output}}, $ar;
 198+}
 199+
 200+sub unbuffer_worker_out {
 201+ my $id = $_[0];
 202+ lock $p{$id};
 203+ while (my $ar = shift @{$p{$id}{output}}) {
 204+ if (@{$ar}) {
 205+ my $cat = shift @{$ar};
 206+ out ($cat, @{$ar});
 207+ }
 208+ }
 209+}
 210+
 211+sub do_patch_list {
 212+ (@plist) || return;
 213+
 214+ my @workers;
 215+ for (my $i = 0; $i < $o{threads}; $i ++) {
 216+ dbg ("Creating worker");
 217+ BEGIN { $^W = 0 }
 218+ push @workers, threads->new(\&download_patch_worker);
 219+ BEGIN { $^W = 1 }
 220+ }
 221+
 222+ # Counters
 223+ $c{current}=0;
 224+ $c{total}=$#plist+1;
 225+ $c{dl}=$c{skipdl}=$c{faildl}=$c{inst}=$c{skipinst}=$c{failinst}=0;
 226+ $c{p_ci}=$c{p_bi}=$c{p_c}=$c{p_b}=0;
 227+
 228+ foreach my $id (@plist) {
 229+ $c{current}++;
 230+
 231+ # Add revision to patch id
 232+ my $pp="";
 233+ ($p{$id}{irev} ne "00") && ($pp="$id-$p{$id}{irev}");
 234+ ($p{$id}{crev} ne "00") && ($pp="$id-$p{$id}{crev}");
 235+ ($p{$id}{prev} ne "00") && ($pp="$id-$p{$id}{prev}");
 236+ $pp || err ("Unknown patch-id $id");
 237+ # Remember revised patch id for later use
 238+ $p{$id}{pp} = $pp;
 239+ if ($o{threads}) {
 240+ $queue->enqueue($pp);
 241+ }
 242+ }
 243+
 244+ if ($o{threads}) {
 245+ $queue->enqueue(undef);
 246+ }
 247+
 248+ $c{current}=0;
 249+
 250+ foreach my $id (@plist) {
 251+ $c{current}++;
 252+
 253+ my $pp = $p{$id}{pp};
 254+
 255+ if ($o{list} || $o{download} || $o{install}) {
 256+ print_patch ($id);
 257+ }
 258+ if ($o{download} || $o{install}) {
 259+ out ('info', "\nDownloading $pp ($c{current}/$c{total})");
 260+ if ($o{threads}) {
 261+ { # wait for the first download worker to tell us it's ready...
 262+ lock $download_start;
 263+ if (!defined($download_start)) { # if it hasn't already
 264+ dbg ("Waiting for download worker broadcast");
 265+ cond_wait(\$download_start) until (defined($download_start) && $download_start == 0);
 266+ }
 267+ }
 268+ { # signal download workers to start...
 269+ lock $download_start;
 270+ if(defined($download_start) && $download_start != 1) { # if we haven't already
 271+ $download_start=1;
 272+ dbg ("Broadcasting download loop ready");
 273+ cond_broadcast(\$download_start);
 274+ }
 275+ }
 276+ while(!defined $p{$id}{worker_done} || !$p{$id}{worker_done}) {
 277+ unbuffer_worker_out ($id);
 278+ sleep 1;
 279+ }
 280+ unbuffer_worker_out ($id);
 281+ }
 282+ else {
 283+ download_patch($pp);
 284+ }
 285+ }
 286+ if ($o{install}) {
 287+ out ('info', "\nInstalling $pp ($c{current}/$c{total})");
 288+ install_patch($pp);
 289+ if (-x '/var/run/nopatch') {
 290+ `/var/run/nopatch`;
 291+ last
 292+ }
 293+ }
 294+ if ($o{readme}) {
 295+ my $rtmp=get_readme ($pp);
 296+ ($rtmp) && (push (@rlist, $rtmp));
 297+ }
 298+ ($o{download} || $o{install}) && out ('info', '-' x 78);
 299+ }
 300+
 301+ foreach my $worker (@workers) {
 302+ dbg ("Waiting for worker");
 303+ $worker->join();
 304+ }
 305+
 306+ if ($o{download} || $o{install}) {
 307+ printf "Download Summary: %d total, %d successful, ", $c{total}, $c{dl};
 308+ printf "%d skipped, %d failed\n", $c{skipdl}, $c{faildl};
 309+ }
 310+ if ($o{install}) {
 311+ printf "Install Summary : %d total, %d successful, ", $c{total}, $c{inst};
 312+ printf "%d skipped, %d failed\n", $c{skipinst}, $c{failinst};
 313+
 314+ if ($c{p_ci}) { out ('info', "\nReconfiguration reboot required (init 6)") }
 315+ elsif ($c{p_bi}) { out ('info', "\nReboot required (init 6)") }
 316+ elsif ($c{p_c }) { out ('info', "\nReconfiguration reboot recommended (init 6)") }
 317+ elsif ($c{p_b }) { out ('info', "\nReboot recommended (init 6)") }
 318+ }
 319+ if ($o{readme} && (@rlist)) {
 320+ system ("$pager @rlist");
 321+ unlink (@rlist);
 322+ }
 323+}
 324+
 325+sub expand_operands {
 326+ my @tlist=@ARGV; my $again=1; my %fc;
 327+
 328+ while ($again) {
 329+ $again=0; @slist=();
 330+ foreach my $s (@tlist) {
 331+ if ($s =~ /^(missingr?s?|installedr?s?|allr?s?|totalr?s?|unbundledr?s?|badr?s?)$/) {
 332+ push (@slist, $s);
 333+ } elsif ($s =~ /^(mr?s?|ir?s?|ar?s?|tr?s?|ur?s?|br?s?)$/) {
 334+ push (@slist, $s);
 335+ } elsif ($s =~ /^(\d{6}|\d{6}-\d{2})$/) {
 336+ push (@slist, $s);
 337+ } elsif ($s =~ /(\d{6}-\d{2})\.(zip|jar|tar\.Z|tar)$/) {
 338+ push (@slist, $1);
 339+ } elsif (-f $s) {
 340+ if ($fc{$s}) { err ("Recursive file inclusion: $s") } else { $fc{$s}=1 }
 341+ open (LIST, "<$s") || err ("Can't open $s ($!)");
 342+ while (<LIST>) {
 343+ chomp;
 344+ next unless $_;
 345+ push (@slist, (split (/ /, $_))[0]);
 346+ $again=1;
 347+ }
 348+ } else {
 349+ err ("Unknown operand: $s");
 350+ }
 351+ }
 352+ @tlist=@slist;
 353+ }
 354+ dbg ("Expanded patch list: @slist");
 355+}
 356+
 357+sub create_patch_list {
 358+ # Put patches for patch utilities at the top of the list
 359+ my @putil= (106125,106126,107171,107172,108987,108988,112951,114194,119254,119255);
 360+ foreach my $id (@putil) { init_patch($id) }
 361+
 362+ foreach my $id (@putil, sort keys %p) {
 363+ add_patch_list ($id,0);
 364+ }
 365+}
 366+
 367+sub add_patch_list {
 368+ my $id=$_[0];
 369+ my $type=$_[1];
 370+
 371+ # Ignore patches which have been listed already.
 372+ ($p{$id}{listed}) && return (0);
 373+
 374+ $type=match_patch_list($id,$type);
 375+ $type || return (0);
 376+
 377+ if ($id eq "125547") {
 378+ $p{$id}{requires}="122660-10";
 379+ $p{"122660"}{obs}=0; $p{"122660"}{obsoletedby}="";
 380+ $p{"124204"}{obs}=0; $p{"124204"}{obsoletedby}="";
 381+ $p{"118731"}{obs}=0; $p{"118731"}{obsoletedby}="";
 382+ }
 383+ if ($id eq "125548") {
 384+ $p{$id}{requires}="122661-08";
 385+ $p{"122661"}{obs}=0; $p{"122661"}{obsoletedby}="";
 386+ $p{"124205"}{obs}=0; $p{"124205"}{obsoletedby}="";
 387+ }
 388+
 389+ if ($p{$id}{requires} ne '') {
 390+ REQ: foreach my $r (split (/;/, $p{$id}{requires})) {
 391+ # Fix required patches which were never released
 392+ ($r eq "125077-02") && ($r="120011-09"); # 119757
 393+ ($r eq "125078-02") && ($r="120012-10"); # 119758
 394+ ($r eq "125486-01") && ($r="120011-14"); # 126206
 395+ ($r eq "125487-01") && ($r="120012-14"); # 126207
 396+ ($r eq "126677-02") && ($r="124628-03"); # 119534, 124630
 397+ ($r eq "126678-02") && ($r="124629-03"); # 119535, 124631
 398+ ($r eq "114431-03") && ($r="117172-17"); # 116473
 399+
 400+ my ($r_id, $r_rev)= split (/-/, $r);
 401+
 402+ # If a required patch has been obsoleted by another patch, we
 403+ # continue with the patch that obsoleted it.
 404+ while ($p{$r_id}{obsoletedby} ne '') {
 405+ my ($oby_id, $oby_rev)= split (/-/, $p{$r_id}{obsoletedby});
 406+ dbg ("$r_id-$r_rev required by $id: obsolete, replaced with $oby_id-$oby_rev");
 407+ ($r_id, $r_rev)= ($oby_id, $oby_rev);
 408+ }
 409+ # Check if patch requires itself
 410+ if ($r_id eq $id) {
 411+ dbg ("$r_id-$r_rev required by $id: patch requires itself");
 412+ next;
 413+ }
 414+ # Check if the required patch is in our database. Normally we should
 415+ # stop with an error here, but maybe information in patchdiag.xref
 416+ # is wrong and the patch will install without the missing required patch.
 417+ if ($p{$r_id}{crev} eq "00") {
 418+ dbg ("$r_id-$r_rev required by $id: unknown patch");
 419+ next;
 420+ }
 421+ # Ignore patches already in our list.
 422+ if ($p{$r_id}{listed}) {
 423+ dbg ("$r_id-$r_rev required by $id: already listed");
 424+ next;
 425+ }
 426+ # Ignore patches already installed.
 427+ if ($p{$r_id}{irev} ge $r_rev) {
 428+ dbg ("$r_id-$r_rev required by $id: already installed");
 429+ next;
 430+ }
 431+ # Check for circular patch dependencies.
 432+ if ($p{$r_id}{requires} ne '') {
 433+ foreach my $s (split (/;/, $p{$r_id}{requires})) {
 434+ (my $s_id, my $s_rev)= split (/-/, $s);
 435+ if (exists $rloop{"$r_id:$s_id"}) {
 436+ dbg ("$r_id-$r_rev required by $id: Circular patch dependency");
 437+ next REQ;
 438+ }
 439+ $rloop{"$r_id:$s_id"} = 1;
 440+ }
 441+ }
 442+
 443+ dbg ("$r_id-$r_rev required by $id");
 444+ if (!add_patch_list($r_id,$type)) {
 445+ dbg ("$r_id-$r_rev required by $id: does not match");
 446+ }
 447+ }
 448+ }
 449+ $p{$id}{listed}=1;
 450+ push (@plist, $id);
 451+ return (1);
 452+}
 453+
 454+sub match_patch_list {
 455+ my $id=$_[0];
 456+ my $type=$_[1];
 457+ my $found;
 458+
 459+ S: foreach my $s (@slist) {
 460+ # Complete patch id with revision (123456-78)
 461+ if ($s =~ /\d{6}-\d{2}/) {
 462+ my ($s_id,$s_rev)= split(/-/,$s);
 463+ if ($id eq $s_id) {
 464+ if ($p{$id}{ignore} eq "00") { next }
 465+ if ($p{$id}{ignore} ge $s_rev) { next }
 466+ $p{$id}{prev}=$s_rev;
 467+ return (1);
 468+ }
 469+ }
 470+ # Incomplete patch id (123456)
 471+ if ($s =~ /\d{6}/) {
 472+ if ($id eq $s) {
 473+ if ($p{$id}{ignore} eq "00") { next }
 474+ if ($p{$id}{ignore} eq $p{$id}{crev}) { next }
 475+ return (2);
 476+ }
 477+ if ($type == 2) { return (2); }
 478+ }
 479+ # installed or all
 480+ if (($s =~ /^i/) || ($s =~ /^a/)) {
 481+ if (!check_rs($s,$id)) { next; }
 482+
 483+ # Check if patch is installed.
 484+ if ($p{$id}{irev} ne '00') { return (3); }
 485+ }
 486+ # unbundled
 487+ if ($s =~ /^u/) {
 488+ # Check if patch is Unbundled and has an empy packages list.
 489+ if (!(($p{$id}{os} eq "Unbundled") && ($p{$id}{pkgs} eq ""))) { next; }
 490+
 491+ # Ignore obsolete and bad patches
 492+ if ($p{$id}{obs} || $p{$id}{bad}) { next; }
 493+
 494+ if (!check_rs($s,$id)) { next; }
 495+
 496+ return (4);
 497+ }
 498+ # missing or all
 499+ if (($s =~ /^m/) || ($s =~ /^a/)) {
 500+ # Ignore obsolete and bad patches
 501+ if ($p{$id}{obs} || $p{$id}{bad}) { next; }
 502+
 503+ # Ignore patches which are installed in the current or higher revision
 504+ if ($p{$id}{irev} ge $p{$id}{crev}) { next; }
 505+
 506+ # Ignore patches for foreign architectures.
 507+ $found=0;
 508+ foreach my $j (split (/\;/, $p{$id}{archs})) {
 509+ if (($j eq $u{arch}) || ($j eq "all") || ($j eq "Solaris") || ($j eq "$u{arch}.$u{model}")) {
 510+ $found=1; last;
 511+ }
 512+ }
 513+ if (!$found) { next; }
 514+
 515+ # Ignore patches for packages that are not installed.
 516+ $found=0;
 517+ foreach my $j (split (/\;/, $p{$id}{pkgs})) {
 518+ my ($package, $version)= split (/:/, $j);
 519+ if ($pkgs{$package} && ($pkgs{$package} =~ /<$version>/)) {
 520+ $found=1; last;
 521+ }
 522+ }
 523+ if (!$found) { next; }
 524+
 525+ if (!patch_apply_check($id)) { next; }
 526+ if (!check_rs($s,$id) && ($type != 5)) { next; }
 527+
 528+ return (5);
 529+ }
 530+ # Total set of patches
 531+ if ($s =~ /^t/) {
 532+ if ($p{$id}{crev} eq "00") { next; }
 533+ if (!check_rs($s,$id)) { next; }
 534+
 535+ return (6);
 536+ }
 537+ # Installed bad patches
 538+ if ($s =~ /^b/) {
 539+ if (!$p{$id}{ibad}) { next; }
 540+
 541+ # Check if bad patch has been obsoleted by an installed patch
 542+ my $oby_id= $id; my $oby_rev;
 543+ while ($p{$oby_id}{obsoletedby} ne '') {
 544+ ($oby_id, $oby_rev)= split (/-/, $p{$oby_id}{obsoletedby});
 545+ if ($p{$oby_id}{irev} ge $oby_rev) { next S; }
 546+ }
 547+ if (!check_rs($s,$id)) { next; }
 548+
 549+ return (7);
 550+ }
 551+ }
 552+ return (0);
 553+}
 554+
 555+sub check_rs {
 556+ my $s=$_[0]; my $id=$_[1];
 557+
 558+ # Check that a "stop" patch hasn't been seen
 559+ if ($stopreached) { return(0); }
 560+
 561+ # Check that this isn't a "stop" patch
 562+ if ($p{$id}{stop} eq "00") { $stopreached = 1; }
 563+ if ($p{$id}{stop} eq $p{$id}{crev}) { $stopreached = 1; }
 564+
 565+ # Check for R/S flags
 566+ if ($s =~ /rs$/) {
 567+ if (!($p{$id}{rec} || $p{$id}{recf} || $p{$id}{sec} || $p{$id}{secf})) { return(0); }
 568+ } else {
 569+ if (($s =~ /r$/) && (!$p{$id}{rec}) && (!$p{$id}{recf})) { return(0); }
 570+ if (($s =~ /s$/) && (!$p{$id}{sec}) && (!$p{$id}{secf})) { return(0); }
 571+ }
 572+ # Ignore patches in the ignore list.
 573+ if ($p{$id}{ignore} eq "00") { return 0 }
 574+ if ($p{$id}{ignore} eq $p{$id}{crev}) { return 0 }
 575+
 576+ # Check for minage, maxage and pattern
 577+ if (($o{minage}) && (calculateage($p{$id}{reldate}) < $o{minage})) { return(0); }
 578+ if (($o{maxage}) && (calculateage($p{$id}{reldate}) > $o{maxage})) { return(0); }
 579+ if ($o{pattern}) {
 580+ if ($o{pattern} =~ /^!/) {
 581+ my $pattern= substr ($o{pattern}, 1);
 582+ if ($p{$id}{synopsis} =~ /$pattern/) { return (0) }
 583+ } else {
 584+ if ($p{$id}{synopsis} !~ /$o{pattern}/) { return(0) }
 585+ }
 586+ }
 587+
 588+ return(1);
 589+}
 590+
 591+sub download_patch {
 592+ my $pp=$_[0];
 593+ my ($id, $rev)= split (/-/, $pp);
 594+
 595+ lock_free($o{patchdir}, "download.$pp", 300) || err ("Another instance of pca is downloading $pp to $o{patchdir} right now");
 596+
 597+ # Check if patch exists
 598+ if (-d "$o{patchdir}/$pp") {
 599+ buffer_worker_out ($id, 'info', "Skipped (directory exists)"); $c{skipdl}++; return;
 600+ }
 601+ foreach my $ext ('zip','jar','tar.Z','tar') {
 602+ if (-f "$o{patchdir}/$pp.$ext") {
 603+ if (-s "$o{patchdir}/$pp.$ext") {
 604+ buffer_worker_out ($id, 'info', "Skipped (file exists)"); $c{skipdl}++; return;
 605+ }
 606+ unlink "$o{patchdir}/$pp.$ext";
 607+ }
 608+ }
 609+
 610+ # Remember if we downloaded the patch for install only
 611+ $o{download} || ($p{$id}{dfori}=1);
 612+
 613+ (-w $o{patchdir}) || err ("Can't write to patch download directory $o{patchdir}");
 614+
 615+ lock_create($o{patchdir}, "download.$pp", 1) || err ("Another instance of pca is downloading $pp to $o{patchdir} right now");
 616+
 617+ # Try to get patch from local patch server
 618+ if ($o{patchurl} =~ /^file:/) {
 619+ buffer_worker_out ($id, 'info', "Trying $o{patchurl}");
 620+ my $path=$o{patchurl}; $path =~ s/^file://;
 621+ foreach my $ext ('zip','jar','tar.Z','tar') {
 622+ (-r "$path/$pp.$ext") && copy ("$path/$pp.$ext", "$o{patchdir}/$pp.$ext");
 623+ if (-s "$o{patchdir}/$pp.$ext") {
 624+ buffer_worker_out ($id, 'info', "Done"); $c{dl}++; goto DONE;
 625+ }
 626+ unlink "$o{patchdir}/$pp.$ext";
 627+ }
 628+ buffer_worker_out ($id, 'info', "Failed");
 629+ }
 630+ # Without wget we can't download the patch
 631+ if (!$o{wget}) {
 632+ buffer_worker_out ($id, 'info', "Failed (can't find wget executable)"); goto FAIL;
 633+ }
 634+ # Try to get patch from local patch server with wget
 635+ if ($o{patchurl} =~ /^http:|^https:|^ftp:/) {
 636+ buffer_worker_out ($id, 'info', "Trying $o{patchurl}");
 637+ if ($o{patchurl} =~ /^http.*pca-proxy\.cgi/) {
 638+ if (download("patch", $pp, "local", "$o{patchdir}/$pp.tmp", "")) {
 639+ my $type=filetype("$o{patchdir}/$pp.tmp");
 640+ if ($type ne 'unknown') {
 641+ rename ("$o{patchdir}/$pp.tmp", "$o{patchdir}/$pp.$type");
 642+ buffer_worker_out ($id, 'info', "Done"); $c{dl}++; goto DONE;
 643+ } else {
 644+ buffer_worker_out ($id, 'info', "Failed (unknown file type)");
 645+ }
 646+ } else {
 647+ buffer_worker_out ($id, 'info', "Failed");
 648+ }
 649+ unlink "$o{patchdir}/$pp.tmp";
 650+ } elsif ($o{patchurl} =~ /^http:|^https:|^ftp:/) {
 651+ foreach my $ext ('zip','jar','tar.Z','tar') {
 652+ if (download("patch", "$pp.$ext", "local", "$o{patchdir}/$pp.$ext", "")) {
 653+ buffer_worker_out ($id, 'info', "Done"); $c{dl}++; goto DONE;
 654+ }
 655+ unlink "$o{patchdir}/$pp.$ext";
 656+ }
 657+ buffer_worker_out ($id, 'info', "Failed");
 658+ }
 659+ }
 660+ # Try download from restricted patch server, if the user provided
 661+ # Sun Online Account data
 662+ if (checksoa()) {
 663+ my $try=1;
 664+ while ($try <= $o{dltries} ) {
 665+ buffer_worker_out ($id, 'info', "Trying $o{ssprot}://$o{sshost}/ ($try/$o{dltries})");
 666+ if (download("patch", $pp, "sunsolve", "$o{patchdir}/$pp.tmp", "")) {
 667+ my $type=filetype("$o{patchdir}/$pp.tmp");
 668+ if ($type ne 'unknown') {
 669+ rename ("$o{patchdir}/$pp.tmp", "$o{patchdir}/$pp.$type");
 670+ buffer_worker_out ($id, 'info', "Done"); $c{dl}++; goto DONE;
 671+ } else {
 672+ buffer_worker_out ($id, 'info', "Failed (unknown file type)");
 673+ }
 674+ } else {
 675+ buffer_worker_out ($id, 'info', "Failed");
 676+ }
 677+ unlink "$o{patchdir}/$pp.tmp";
 678+ $try++; if ($try <= $o{dltries}) { sleep ($try*2) }
 679+ }
 680+ } else {
 681+ buffer_worker_out ($id, 'info', "Failed (no Sun Online Account data)");
 682+ }
 683+FAIL:
 684+ buffer_worker_out ($id, 'info', "Failed (patch not found)");
 685+ $c{faildl}++;
 686+DONE:
 687+ lock_remove($o{patchdir}, "download.$pp");
 688+}
 689+
 690+sub download {
 691+ my $what=$_[0]; my $pp=$_[1]; my $src=$_[2]; my $dstf=$_[3]; my $dstd=$_[4];
 692+
 693+ my %urls=(
 694+ "pca:pcaurl", "$o{pcaurl}pca",
 695+ "xref:local", "$o{xrefurl}patchdiag.xref",
 696+ "xref:sunsolve", "$o{ssprot}://$o{sshost}/patchdiag.xref",
 697+ "readme:local", "$o{patchurl}README.$pp",
 698+ "readme:sunsolve", "$o{ssprot}://$o{sshost}/pdownload.do?target=$pp&method=r",
 699+ "patch:local", "$o{patchurl}$pp",
 700+ "patch:sunsolve", "$o{ssprot}://$o{sshost}/pdownload.do?target=$pp&method=h"
 701+ );
 702+ my $cmd="$o{wget}"; my $url;
 703+
 704+ if (($o{debug}) && ($o{threads} > 1)) { $cmd .= " -nv" }
 705+ if (!$o{debug}) { $cmd .= " -q" }
 706+
 707+ $url=$urls{"$what:$src"};
 708+ if (($o{force}) && ($url =~ /pca-proxy\.cgi/)) { $url .= ":force" }
 709+ $cmd .= " \"$url\"";
 710+
 711+ if ($o{wgetproxy}) { $cmd .= " --execute http_proxy=$o{wgetproxy}" }
 712+ if (($what eq "xref") && ($src eq "sunsolve") && ($o{nocache})) { $cmd .= " --execute cache=off" }
 713+ if ((($url =~ /https:\/\/www.par.univie.ac.at\//) || ($url =~ /https:\/\/sunsolve.sun.com\//)) && ($wgetv >= 11000)) { $cmd .= " --ca-certificate=$0" }
 714+ if (($what eq "patch") && ($url =~ /pca-proxy\.cgi/)) { $cmd .= " --timeout 3600" }
 715+ if ((($what eq "patch") || ($what eq "readme")) && ($src eq "sunsolve")) {
 716+ $cmd .= " --header=\"Authorization: Basic " . base64("$a{user}:$a{passwd}") . "\"";
 717+ }
 718+
 719+ if ($dstf) {
 720+ $cmd .= " -O $dstf";
 721+ if ($what eq "patch") { $p{$pp}{dlfile}="$dstf" } else { $dlfile="$dstf" }
 722+ }
 723+ if ($dstd) {
 724+ $cmd = "cd $dstd; " . $cmd . " -N";
 725+ }
 726+ $o{debug} && $o{proxy} && ($cmd .= " >>$o{dbgfile} 2>&1");
 727+ dbg ($cmd); `$cmd`;
 728+ if ($what eq "patch") { $p{$pp}{dlfile}="" } else { $dlfile="" }
 729+
 730+ if (!$? && ($dstd)) { return 1 }
 731+ if (!$? && ($dstf) && (-s $dstf)) { return 1 }
 732+ return 0;
 733+}
 734+
 735+sub filetype {
 736+ my $fname=$_[0]; my $buffer;
 737+
 738+ open (F, "< $fname") || err ("Can't open $fname ($!)");
 739+ read F, $buffer, 1024;
 740+ close (F);
 741+
 742+ if (substr($buffer, 257, 5) eq "ustar") { return ('tar') }
 743+ if (substr($buffer, 0, 2) eq "\037\235") { return ('tar.Z') }
 744+ if (substr($buffer, 0, 4) eq "PK\003\004") {
 745+ `$unzip -l $fname META-INF/manifest.mf 2>&1`;
 746+ if ($?) { return ('zip') } else { return ('jar') }
 747+ }
 748+
 749+ dbg ("Unknown Filetype: $fname");
 750+ return ('unknown');
 751+}
 752+
 753+sub install_patch {
 754+ my $pp=$_[0];
 755+ my ($id, $rev)= split (/-/, $pp);
 756+ my $output;
 757+ my $dfile='';
 758+ my $opt='';
 759+
 760+ $patchxdir= "$o{tmpdir}/pca." . time() . $$;
 761+ mkdir $patchxdir,0755 || err ("Can't create temporary directory $patchxdir ($!)");
 762+
 763+ if (-d "$o{patchdir}/$pp") {
 764+ $?= !symlink ("$o{patchdir}/$pp", "$patchxdir/$pp");
 765+ } elsif (-f "$o{patchdir}/$pp.zip") {
 766+ out ('info', "Unzipping patch");
 767+ `$unzip -n $o{patchdir}/$pp.zip -d $patchxdir </dev/null 2>&1`;
 768+ $p{$id}{dfori} && ($dfile= "$o{patchdir}/$pp.zip")
 769+ } elsif (-f "$o{patchdir}/$pp.jar") {
 770+ out ('info', "Unjarring patch");
 771+ `$unzip -n $o{patchdir}/$pp.jar -d $patchxdir </dev/null 2>&1`;
 772+ $p{$id}{dfori} && ($dfile= "$o{patchdir}/$pp.jar")
 773+ } elsif (-f "$o{patchdir}/$pp.tar.Z") {
 774+ out ('info', "Uncompressing patch");
 775+ `cd $patchxdir; $uncompress -c $o{patchdir}/$pp.tar.Z | $tar xf -`;
 776+ $p{$id}{dfori} && ($dfile= "$o{patchdir}/$pp.tar.Z")
 777+ } elsif (-f "$o{patchdir}/$pp.tar") {
 778+ out ('info', "Untarring patch");
 779+ `cd $patchxdir; $tar xf $o{patchdir}/$pp.tar`;
 780+ $p{$id}{dfori} && ($dfile= "$o{patchdir}/$pp.tar")
 781+ } else {
 782+ out ('info', "Failed (missing patch file)");
 783+ rmdir $patchxdir; $patchxdir="";
 784+ $c{failinst}++; return;
 785+ }
 786+ if (($?) || (! -d "$patchxdir/$pp")) {
 787+ out ('info', "Failed");
 788+ rmtree ($patchxdir); $patchxdir="";
 789+ $c{failinst}++; return;
 790+ }
 791+ my $readme= "$patchxdir/$pp/README.$pp";
 792+ my $patchinfo= "$patchxdir/$pp/patchinfo";
 793+
 794+ if ($o{safe}) {
 795+ out ('info', "Checking files for safe patch installation");
 796+ if (!verify_files($id, $readme)) {
 797+ rmtree ($patchxdir); $patchxdir="";
 798+ $c{failinst}++; return;
 799+ }
 800+ }
 801+
 802+ # Do we need a reboot?
 803+ my $p_b=0; my $p_bi=0; my $p_c=0; my $p_ci=0;
 804+ if (-f $patchinfo) {
 805+ open(PATCHINFO,$patchinfo) || err ("Can't open $patchinfo ($!)");
 806+ dbg ("Checking for reboot/reconfig in patchinfo");
 807+ while (<PATCHINFO>) {
 808+ if (/PATCH_PROPERTIES=.*reconfigimmediate/) { $p_ci=1; last }
 809+ if (/PATCH_PROPERTIES=.*rebootimmediate/) { $p_bi=1; last }
 810+ if (/PATCH_PROPERTIES=.*reconfigafter/) { $p_c=1; last }
 811+ if (/PATCH_PROPERTIES=.*rebootafter/) { $p_b=1; last }
 812+ }
 813+ close PATCHINFO;
 814+ } elsif (-f $readme) {
 815+ open(README,$readme) || err ("Can't open $readme ($!)");
 816+ dbg ("Checking for reboot/reconfig in README");
 817+ while(<README>) {
 818+ if (/Reconfig.*immediate.*after.*install/) { $p_ci=1; last }
 819+ if (/Reboot.*immediate.*after.*install/) { $p_bi=1; last }
 820+ if (/Reconfig.*after.*install/) { $p_c=1; last }
 821+ if (/Reboot.*after.*install/) { $p_b=1; last }
 822+ }
 823+ close README;
 824+ }
 825+
 826+ # If the patchadd command doesn't exist, try installpatch, which
 827+ # comes with patches for Solaris <= 2.5.1.
 828+ (-x $o{patchadd}) || ($o{patchadd}="$patchxdir/$pp/installpatch");
 829+ (-x $o{patchadd}) || err ("Can't execute patchadd/installpatch");
 830+
 831+ # Sun Studio 11 patches on Solaris 10 must be installed with -G
 832+ # Patches 119254-34 and 119255-34 fix this in patchadd
 833+ my ($major, $minor) = split (/\./, $u{osrel});
 834+ if (($minor == 10) && ($p{$id}{synopsis} =~ /^Sun Studio 11/) && (!$o{currentzone})) {
 835+ if (!($p{119254}{irev} ge '34') && !($p{119255}{irev} ge '34')) {
 836+ dbg ("Adding -G to patchadd for Sun Studio 11 on Solaris 10");
 837+ $opt .= "-G ";
 838+ }
 839+ }
 840+ # Ignore currentzone option on Solaris <= 9
 841+ ($minor <= 9) && ($o{currentzone}=0);
 842+
 843+ if ($o{noreboot} && ($p_ci || $p_bi || $p_c || $p_b)) {
 844+ out ('info', "Skipping patchadd (noreboot)"); $c{skipinst}++;
 845+ } elsif ($o{pretend}) {
 846+ out ('info', "Skipping patchadd (pretend)"); $c{skipinst}++;
 847+ } else {
 848+ lock_create($o{tmpdir}, "install", 1) || err ("Another instance of pca is installing patches right now");
 849+ $o{currentzone} && ($opt .= "-G ");
 850+ if (($o{nobackup} =~ /all/) || ($p{$id}{nobackup} eq "00") || ($p{$id}{nobackup} eq $p{$id}{crev})) { $opt .= "-d " }
 851+ $o{backdir} && ($opt .= "-B $o{backdir} ");
 852+ out ('info', "Running patchadd");
 853+ dbg ("Starting patchadd at " . localtime);
 854+ dbg ("$o{patchadd} $o{root} $opt $patchxdir/$pp");
 855+ $SIG{INT}='IGNORE';
 856+ $output=`$o{patchadd} $o{root} $opt $patchxdir/$pp </dev/null 2>&1`;
 857+ $SIG{INT}=\&handler;
 858+ my $rc=$?;
 859+ lock_remove($o{tmpdir}, "install");
 860+ if ($rc) {
 861+ out ('info', "\n$output");
 862+ $rc /= 256; out ('info', "Failed (exit code $rc)\n$!");
 863+ rmtree ($patchxdir); $patchxdir="";
 864+ log_msg("Failed to install patch $pp ($p{$id}{synopsis}) rc=$rc");
 865+ $c{failinst}++; return;
 866+ }
 867+ dbg ("\n$output");
 868+ out ('info', "Done"); $c{inst}++;
 869+ $dfile && unlink ($dfile);
 870+ log_msg("Installed patch $pp ($p{$id}{synopsis})");
 871+ if ($p_ci || $p_c) {
 872+ my $r=''; if ($o{root}) { $r=$o{root}; $r =~ s/-R // }
 873+ if (! -f "$r/reconfigure") {
 874+ out ('info', "Creating $r/reconfigure");
 875+ open (F, ">$r/reconfigure"); close (F);
 876+ }
 877+ }
 878+ }
 879+ if ($p_ci) { out ('info', "Reconfig required"); $o{noreboot} || $c{p_ci}++ }
 880+ elsif ($p_bi) { out ('info', "Reboot required"); $o{noreboot} || $c{p_bi}++ }
 881+ elsif ($p_c ) { out ('info', "Reconfig recommended"); $o{noreboot} || $c{p_c}++ }
 882+ elsif ($p_b ) { out ('info', "Reboot recommended"); $o{noreboot} || $c{p_b}++ }
 883+ rmtree ($patchxdir); $patchxdir="";
 884+}
 885+
 886+sub proxy {
 887+ my $f=$o{proxy};
 888+ my $odir;
 889+
 890+ dbg ("Requested file: $f");
 891+ if ($f =~ /^patchdiag.xref$/) {
 892+ $o{xrefown}=1; $odir=$o{xrefdir};
 893+ if ($o{pforce}) { unlink ("$odir/$f") }
 894+ get_current_xref();
 895+ }
 896+ if ($f =~ /^README\.\d{6}-\d{2}$/) {
 897+ my $pp=$f; $pp =~ s/^.*(\d{6}-\d{2}).*$/$1/; $odir=$o{patchdir};
 898+ if ($o{pforce}) { unlink ("$odir/$f") }
 899+ if (! -f "$odir/$f") {
 900+ my $rtmp=get_readme ($pp);
 901+ if ($rtmp) {
 902+ copy ($rtmp, "$odir/$f");
 903+ unlink ($rtmp);
 904+ }
 905+ }
 906+ }
 907+ # Deprecated
 908+ if ($f =~ /^\d{6}-\d{2}\.(zip|jar|tar|tar\.Z)$/) {
 909+ my $pp=$f; $pp =~ s/^.*(\d{6}-\d{2}).*$/$1/; $odir=$o{patchdir};
 910+ if ($o{pforce}) { unlink ("$odir/$f") }
 911+ download_patch($pp);
 912+ }
 913+ if ($f =~ /^\d{6}-\d{2}$/) {
 914+ $odir=$o{patchdir};
 915+ if ($o{pforce}) { unlink ("$odir/$f.zip", "$odir/$f.jar", "$odir/$f.tar", "$odir/$f.tar.Z") }
 916+ download_patch($f);
 917+ (-f "$odir/$f.zip") && ($f="$f.zip");
 918+ (-f "$odir/$f.jar") && ($f="$f.jar");
 919+ (-f "$odir/$f.tar") && ($f="$f.tar");
 920+ (-f "$odir/$f.tar.Z") && ($f="$f.tar.Z");
 921+ }
 922+ if ($f =~ /^pca$/) {
 923+ $odir=$o{patchdir};
 924+ if ($o{pforce}) { unlink ("$odir/$f") }
 925+ if ($o{pcaurl} =~ /^http.*pca-proxy\.cgi/) {
 926+ download('pca', '', 'pcaurl', "$odir/pca", "");
 927+ } else {
 928+ download('pca', '', 'pcaurl', "", $odir);
 929+ }
 930+ }
 931+
 932+ if (-f "$odir/$f") {
 933+ ($f =~ /patchdiag.xref/) && print "Content-type: text/plain\n";
 934+ ($f =~ /README/) && print "Content-type: text/plain\n";
 935+ ($f =~ /\.zip$/) && print "Content-type: application/zip\n";
 936+ ($f =~ /\.jar$/) && print "Content-type: application/zip\n";
 937+ ($f =~ /\.tar$/) && print "Content-type: application/x-tar\n";
 938+ ($f =~ /\.tar.Z$/) && print "Content-type: application/x-compress\n";
 939+ ($f =~ /^pca$/) && print "Content-type: text/plain\n";
 940+
 941+ my $size=(stat("$odir/$f"))[7];
 942+ print "Content-length: $size\n\n";
 943+
 944+ open (F, "<$odir/$f"); read (F, my $content, $size); close (F);
 945+ print $content;
 946+ } else {
 947+ err ("$f not found");
 948+ }
 949+ exit 0;
 950+}
 951+
 952+sub checksoa {
 953+ lock %a; # prevent simultaneous access to checksoa() if multithreaded
 954+ if(!$a{transcribed}) {
 955+ $a{user}=$o{user};
 956+ $a{passwd}=$o{passwd};
 957+ $a{askauth}=$o{askauth};
 958+ $a{transcribed}=1;
 959+ }
 960+ # Check if we already asked for user/passwd
 961+ if ($a{askauth} eq 'asked') {
 962+ if ($a{user} && $a{passwd}) { return (1) } else { return (0) }
 963+ }
 964+ # If askauth is set, ask for user
 965+ my $uask=0;
 966+ if ($a{askauth}) {
 967+ print "\nPlease enter Sun Online Account User: ";
 968+ chomp($a{user} = <STDIN>);
 969+ $a{askauth}= 'asked'; $uask=1;
 970+ }
 971+ # If askauth or user (but not passwd) is set, ask for passwd
 972+ if ($a{askauth} || ($a{user} && !$a{passwd})) {
 973+ system "stty -echo"; $sttyset=1;
 974+ $uask || print "\n";
 975+ print "Please enter Sun Online Account Password";
 976+ if ($a{askauth}) { print ": " } else { print " for $a{user}: " }
 977+ chomp($a{passwd} = <STDIN>);
 978+ print "\n\n";
 979+ system "stty echo"; $sttyset=0;
 980+ $a{askauth}= 'asked';
 981+ }
 982+ if ($a{user} && $a{passwd}) { return (1) } else { return (0) }
 983+}
 984+
 985+sub check_prerequisites {
 986+ # Must be root to install patches
 987+ if ($o{install} && ($< != 0) && !$o{pretend}) {
 988+ err ("You must be root to install patches");
 989+ }
 990+ if ($o{install} && $o{safe} && !$o{proxy} && ($< != 0)) {
 991+ err ("You must be root to use safe mode");
 992+ }
 993+
 994+ # Set umask (esp. for patchxdir)
 995+ umask (0022);
 996+
 997+ # Check for wget executable
 998+ my $found='';
 999+ foreach my $i (split (/ /, $o{wget})) {
 1000+ if (-x $i) {
 1001+ $found= $i;
 1002+ open (W, "$found --version |"); my $v=<W>; close (W); chomp $v;
 1003+ $v =~ s/^GNU Wget ([\d\.]*).*$/$1/; my ($v1, $v2, $v3) = split (/\./, $v);
 1004+ $wgetv=$v1*10000 + $v2*100; $v3 && ($wgetv += $v3);
 1005+ dbg ("Using $found ($v, $wgetv)");
 1006+ last;
 1007+ }
 1008+ }
 1009+ $o{wget}=$found;
 1010+
 1011+ # Get patchdiag.xref location
 1012+ $input{xref}="$o{xrefdir}/patchdiag.xref";
 1013+
 1014+ # Check patch download directory
 1015+ (-d $o{patchdir}) || err ("Can't find patch directory $o{patchdir}");
 1016+
 1017+ # Check for pager
 1018+ $ENV{PAGER} && ($pager=$ENV{PAGER});
 1019+
 1020+ # Check for valid prefix in $fromfiles and set input files/commands
 1021+ if ($o{fromfiles}) {
 1022+ if (-f "$o{fromfiles}/sysconfig/uname-a.out") {
 1023+ $input{pkginfo}= "<$o{fromfiles}/patch+pkg/pkginfo-l.out";
 1024+ $input{showrev}= "<$o{fromfiles}/patch+pkg/showrev-p.out";
 1025+ $input{uname} = "<$o{fromfiles}/sysconfig/uname-a.out";
 1026+ } elsif (-f "$o{fromfiles}uname.out") {
 1027+ $input{pkginfo}= "<$o{fromfiles}pkginfo.out";
 1028+ $input{showrev}= "<$o{fromfiles}showrev.out";
 1029+ $input{uname} = "<$o{fromfiles}uname.out";
 1030+ } elsif (-f "$o{fromfiles}/uname.out") {
 1031+ $input{pkginfo}= "<$o{fromfiles}/pkginfo.out";
 1032+ $input{showrev}= "<$o{fromfiles}/showrev.out";
 1033+ $input{uname} = "<$o{fromfiles}/uname.out";
 1034+ } else {
 1035+ err ("Can't find pkginfo/showrev/uname output with prefix $o{fromfiles}");
 1036+ }
 1037+ dbg ("Using $o{fromfiles} as prefix to read .out files");
 1038+ } else {
 1039+ $input{pkginfo}= "$pkginfo -x $o{root} |";
 1040+ $input{uname} = "$uname -a |";
 1041+ }
 1042+
 1043+ # Set default locale for forks
 1044+ $ENV{LC_ALL}='C';
 1045+
 1046+ # Make sure that any request to thread is sane
 1047+ if (!$Config{useithreads} || $o{threads} < 2 || !($o{download} || $o{install})) {
 1048+ dbg ("Prerequisites for threads not met, setting threads to 0");
 1049+ $o{threads} = 0;
 1050+ }
 1051+}
 1052+
 1053+sub import_threads {
 1054+ return unless ($o{threads} > 1);
 1055+
 1056+ eval { require threads; require threads::shared; require Thread::Queue; };
 1057+ if (!$@) {
 1058+ dbg ("Thread modules passed require, importing");
 1059+ BEGIN { $^W = 0 }
 1060+ threads->import;
 1061+ BEGIN { $^W = 1 }
 1062+ threads::shared->import;
 1063+ Thread::Queue->import;
 1064+ $queue = new Thread::Queue;
 1065+ share(\%a);
 1066+ share(\%c);
 1067+ share(\$download_start);
 1068+ share(\%locks);
 1069+ }
 1070+ else {
 1071+ dbg ("Thread modules did not pass require");
 1072+ $o{threads} = 0;
 1073+ }
 1074+}
 1075+
 1076+sub verify_files {
 1077+ my $id=$_[0]; my $readme=$_[1]; my @files=(); my %wl;
 1078+
 1079+ # All
 1080+ $wl{all}="/etc/name_to_major /etc/driver_aliases /etc/driver_classes /etc/minor_perm /etc/security/exec_attr";
 1081+ # 7/SPARC
 1082+ $wl{106541}="/etc/devlink.tab /etc/rmmount.conf /etc/syslog.conf /etc/vold.conf";
 1083+ $wl{106857}="/usr/openwin/share/locale/C/props/basic_setting";
 1084+ $wl{106978}="/etc/nsswitch.conf";
 1085+ $wl{107589}="/etc/default/kbd";
 1086+ $wl{107684}="/etc/inet/services /etc/mail/main.cf /etc/mail/subsidiary.cf";
 1087+ $wl{107738}="/usr/openwin/lib/locale/compose.dir /usr/openwin/lib/locale/locale.alias /usr/openwin/lib/locale/locale.dir";
 1088+ $wl{108800}="/etc/inet/inetd.conf /etc/init.d/cachefs.daemon";
 1089+ # 8/SPARC
 1090+ $wl{108725}="/kernel/drv/st.conf";
 1091+ $wl{108968}="/etc/rmmount.conf /etc/vold.conf";
 1092+ $wl{108999}="/etc/pam.conf";
 1093+ $wl{109077}="/etc/security/auth_attr /etc/security/prof_attr";
 1094+ $wl{109134}="/etc/security/auth_attr /etc/security/prof_attr";
 1095+ $wl{109695}="/etc/smartcard/opencard.properties";
 1096+ $wl{109766}="/usr/openwin/lib/locale/ja/X11/fonts/TT/fonts.alias";
 1097+ $wl{109887}="/etc/smartcard/ocf.classpath";
 1098+ $wl{110369}="/etc/iu.ap";
 1099+ $wl{110386}="/etc/security/auth_attr /etc/security/prof_attr";
 1100+ $wl{110615}="/etc/mail/main.cf /etc/mail/subsidiary.cf";
 1101+ $wl{110896}="/etc/inet/inetd.conf";
 1102+ $wl{112438}="/etc/devlink.tab";
 1103+ $wl{112663}="/usr/openwin/server/etc/OWconfig";
 1104+ $wl{114542}="/usr/openwin/lib/X11/fonts/TrueType/ttmap/ttmaps.dir /usr/openwin/lib/X11/fonts/encodings/encodings.dir";
 1105+ $wl{116973}="/etc/apache/mime.types";
 1106+ $wl{117518}="/usr/openwin/lib/X11/fonts/F3bitmaps/fonts.dir";
 1107+ $wl{128624}="/etc/default/login /etc/nsswitch.conf /etc/pam.conf";
 1108+ # 9/SPARC
 1109+ $wl{112233}="/etc/iu.ap";
 1110+ $wl{112874}="/etc/name_to_sysnum /etc/security/crypt.conf /etc/security/policy.conf";
 1111+ $wl{112908}="/etc/krb5/krb5.conf";
 1112+ $wl{112954}="/kernel/drv/uata.conf";
 1113+ $wl{113073}="/etc/inet/inetd.conf";
 1114+ $wl{113085}="/usr/openwin/lib/X11/fonts/TrueType/ttmap/ttmaps.dir /usr/openwin/lib/X11/fonts/encodings/encodings.dir";
 1115+ $wl{113096}="/usr/openwin/server/etc/OWconfig";
 1116+ $wl{113277}="/kernel/drv/st.conf /kernel/drv/sd.conf";
 1117+ $wl{113471}="/usr/bin/cputrack";
 1118+ $wl{113575}="/etc/mail/main.cf /etc/mail/subsidiary.cf";
 1119+ $wl{114320}="/usr/openwin/server/etc/OWconfig";
 1120+ $wl{114352}="/etc/inet/inetd.conf";
 1121+ $wl{123184}="/usr/openwin/lib/X11/fonts/TrueType/ttmap/ttmaps.dir /usr/openwin/lib/X11/fonts/encodings/encodings.dir";
 1122+ # 9/x86
 1123+ $wl{114137}="/etc/mail/main.cf /etc/mail/subsidiary.cf";
 1124+ $wl{114353}="/etc/inet/inetd.conf";
 1125+ $wl{115168}="/etc/krb5/krb5.conf";
 1126+ $wl{122300}="/etc/rc0.d/K05volmgt /etc/rc1.d/K05volmgt /etc/rc2.d/K05volmgt /etc/rc3.d/S81volmgt /etc/rcS.d/K05volmgt /etc/security/audit_class /etc/security/audit_event";
 1127+ # 10/SPARC
 1128+ $wl{116298}="/usr/bin/wscompile /usr/bin/wsdeploy";
 1129+ $wl{118666}="/etc/.java/.systemPrefs/.system.lock /etc/.java/.systemPrefs/.systemRootModFile";
 1130+ $wl{118717}="/usr/openwin/server/etc/OWconfig";
 1131+ $wl{118833}="/etc/logindevperm /etc/security/prof_attr /etc/vold.conf /etc/security/auth_attr";
 1132+ $wl{119090}="/etc/ima.conf /kernel/drv/iscsi.conf";
 1133+ $wl{119130}="/kernel/drv/fp.conf /kernel/drv/qlc.conf";
 1134+ $wl{119252}="/etc/default/kbd /etc/default/nfs";
 1135+ $wl{119313}="/etc/security/auth_attr /etc/security/prof_attr";
 1136+ $wl{119757}="/etc/inet/services";
 1137+ $wl{120011}="/etc/inet/hosts /etc/inet/ipnodes /etc/inet/services /etc/nscd.conf /etc/security/auth_attr /etc/security/prof_attr /etc/default/dhcpagent /etc/security/device_policy /etc/iu.ap";
 1138+ $wl{120185}="/opt/staroffice8/share/config/javasettingsunopkginstall.xml";
 1139+ $wl{120222}="/kernel/drv/emlxs.conf";
 1140+ $wl{120346}="/etc/hba.conf";
 1141+ $wl{120410}="/etc/gtk-2.0/gtk.immodules /etc/sparcv9/gtk-2.0/gtk.immodules";
 1142+ $wl{120460}="/etc/gtk-2.0/gtk.immodules /etc/sparcv9/gtk-2.0/gtk.immodules";
 1143+ $wl{121430}="/etc/default/lu";
 1144+ $wl{122212}="/etc/gconf/gconf.xml.defaults/apps/panel/default_setup/general/%gconf.xml";
 1145+ $wl{124393}="/etc/security/auth_attr /etc/security/prof_attr";
 1146+ $wl{125166}="/kernel/drv/qlc.conf";
 1147+ $wl{127127}="/etc/krb5/krb5.conf /etc/logadm.conf /etc/pam.conf /etc/security/audit_warn /etc/security/auth_attr /etc/security/prof_attr /etc/shadow /etc/user_attr /kernel/drv/mpt.conf";
 1148+ $wl{127755}="/etc/logadm.conf";
 1149+ $wl{137093}="/etc/logindevperm";
 1150+ $wl{137274}="/etc/mnttab";
 1151+ # 10/x86
 1152+ $wl{118855}="/etc/logindevperm /etc/security/prof_attr /etc/vold.conf /lib/libc.so.1 /etc/security/device_policy /etc/ipf/pfil.ap /boot/solaris/devicedb/master";
 1153+ $wl{119091}="/etc/ima.conf /kernel/drv/iscsi.conf";
 1154+ $wl{119131}="/kernel/drv/fp.conf /kernel/drv/qlc.conf";
 1155+ $wl{119253}="/etc/default/kbd /etc/default/nfs";
 1156+ $wl{119314}="/etc/security/auth_attr /etc/security/prof_attr";
 1157+ $wl{119758}="/etc/inet/services";
 1158+ $wl{120012}="/boot/solaris/bootenv.rc /boot/solaris/devicedb/master /etc/default/dhcpagent /etc/inet/hosts /etc/inet/ipnodes /etc/inet/services /etc/nscd.conf /etc/security/auth_attr /etc/security/device_policy /etc/security/prof_attr";
 1159+ $wl{120186}="/opt/staroffice8/share/config/javasettingsunopkginstall.xml";
 1160+ $wl{120223}="/kernel/drv/emlxs.conf";
 1161+ $wl{120273}="/etc/sma/snmp/snmpd.conf";
 1162+ $wl{120347}="/etc/hba.conf";
 1163+ $wl{120411}="/etc/gtk-2.0/gtk.immodules /etc/amd64/gtk-2.0/gtk.immodules";
 1164+ $wl{120461}="/etc/gtk-2.0/gtk.immodules /etc/amd64/gtk-2.0/gtk.immodules";
 1165+ $wl{121431}="/etc/default/lu";
 1166+ $wl{122213}="/etc/gconf/gconf.xml.defaults/apps/panel/default_setup/general/%gconf.xml";
 1167+ $wl{124394}="/etc/security/auth_attr /etc/security/prof_attr";
 1168+ $wl{125165}="/kernel/drv/qlc.conf";
 1169+ $wl{125216}="/etc/wgetrc";
 1170+ $wl{127128}="/etc/krb5/krb5.conf /etc/logadm.conf /etc/pam.conf /etc/security/audit_warn /etc/security/auth_attr /etc/security/prof_attr /etc/shadow /etc/user_attr /kernel/drv/mpt.conf";
 1171+ $wl{127756}="/etc/logadm.conf";
 1172+ $wl{128307}="/etc/security/device_policy";
 1173+ $wl{137094}="/etc/logindevperm";
 1174+ $wl{137112}="/lib/libc.so.1";
 1175+ $wl{137275}="/etc/mnttab";
 1176+
 1177+ (-f $readme) || return (1);
 1178+ open (README, "<$readme") || err ("Can't open $readme ($!)");
 1179+
 1180+ FILE: while (<README>) {
 1181+ next if ($_ !~ /Files included with this patch:/);
 1182+ LINE: while (<README>) {
 1183+ chomp;
 1184+ next if (/^$/);
 1185+ last FILE if (! /\//);
 1186+ next if (/ELF/); # Ignore files with "ELF" in pathname - pkgchk bug
 1187+ s/\s+\(deleted\)//;
 1188+ s/\s+\<deleted\>//;
 1189+ s/\(deleted\)$//;
 1190+ s/\(deleted file\)$//;
 1191+ s/^\s+//;
 1192+ s/^/\// unless /^\//;
 1193+
 1194+ foreach my $i (split (/ /, $wl{all})) { ($_ eq $i) && next LINE; }
 1195+ if ($wl{$id}) {
 1196+ foreach my $i (split (/ /, $wl{$id})) { ($_ eq $i) && next LINE; }
 1197+ }
 1198+ push (@files, $_);
 1199+ }
 1200+ }
 1201+ close (README);
 1202+ dbg ("Number of files to check: ", $#files+1);
 1203+ ($#files == -1) && return (1);
 1204+
 1205+ # pkgchk has a limit of 1024 pathnames
 1206+ my @tfiles=@files; my $out='';
 1207+ while ($#tfiles != -1) {
 1208+ my $fc=$#tfiles;
 1209+ ($fc >= 1023) && ($fc=1023);
 1210+ my $pfile="$o{tmpdir}/pca.pkgchk." . time() . $$;
 1211+ open (PFILE, ">$pfile") || err ("Can't open $pfile ($!)");
 1212+ foreach my $f (@tfiles[0..$fc]) { print PFILE "$f\n" }
 1213+ close PFILE;
 1214+ $out .= `$pkgchk $o{root} -q -i $pfile 2>&1`;
 1215+ unlink $pfile;
 1216+ for (0..1023) { shift @tfiles; }
 1217+ }
 1218+ ($out) || return (1);
 1219+
 1220+ if ($out =~ /file size |file cksum |pathname |pkgchk: ERROR/) {
 1221+ print "failed file verification:\n\n$out";
 1222+ return (0);
 1223+ }
 1224+ return (1);
 1225+}
 1226+
 1227+sub patch_apply_check {
 1228+ my $id=$_[0];
 1229+
 1230+ if ($id =~ /113039|113040|113041|113042|113043/) {
 1231+ if (!$pkgs{"SUNWsan"}) { return (0) }
 1232+ }
 1233+
 1234+ if ($id eq "114045") {
 1235+ if ((exists $p{114049}) && ($p{114049}{irev} gt '03')) { return (0) }
 1236+ }
 1237+
 1238+ if (($id =~ /114046|119209/) && ($u{osrel} ne "5.8")) { return (0) }
 1239+ if (($id =~ /114049|114050/) && ($u{osrel} ne "5.9")) { return (0) }
 1240+ if (($id =~ /119211|119212/) && ($u{osrel} ne "5.9")) { return (0) }
 1241+
 1242+ if ($id eq "114790") {
 1243+ if (!$pkgs{"SUNWdcar"} || $pkgs{"SUNWdcar"} !~ "<1.1.0,REV=2002.05.29.15.02>") { return (0) }
 1244+ if (!$pkgs{"SUNWcrypr"} || $pkgs{"SUNWcrypr"} !~ "<1.1.0,REV=2002.05.29.15.00>") { return (0) }
 1245+ }
 1246+
 1247+ if (($id =~ /117765|117766/) && ($u{osrel} ne "5.8")) { return (0) }
 1248+ if (($id =~ /117767|117768/) && ($u{osrel} ne "5.9")) { return (0) }
 1249+
 1250+ if ($id eq "113332") {
 1251+ if (($pkgs{"SUNWhea"}) || ($pkgs{"SUNWmdb"})) { return (1) }
 1252+ if (($u{model} eq 'sun4u') || ($u{model} eq 'sun4us')) { return (1) }
 1253+ return (0);
 1254+ }
 1255+
 1256+ if ($id =~ /115010|116478/) {
 1257+ if (($pkgs{"SUNWhea"}) || ($pkgs{"SUNWmdb"})) { return (1) }
 1258+ if ($u{model} eq 'sun4u') { return (1) }
 1259+ return (0);
 1260+ }
 1261+
 1262+ if ($id =~ /109077|109078/) {
 1263+ if ((!$pkgs{"SUNWdhcm"}) && (!$pkgs{"SUNWdhcsu"})) { return (0) }
 1264+ if ($pkgs{"SUNWj3rt"}) { return (1) }
 1265+ return (0);
 1266+ }
 1267+
 1268+ if ($id =~ /118739|116706/) {
 1269+ if (!$pkgs{"SUNWtsr"} || $pkgs{"SUNWtsr"} !~ "<2.5.0,REV=2003.04.03.21.27>") { return (0) }
 1270+ }
 1271+ if ($id =~ /118740|116707/) {
 1272+ if (!$pkgs{"SUNWtsr"} || $pkgs{"SUNWtsr"} !~ "<2.5.0,REV=2003.04.03.19.26>") { return (0) }
 1273+ }
 1274+ if ($id eq "118741") {
 1275+ if (!$pkgs{"SUNWtsr"} || $pkgs{"SUNWtsr"} !~ "<2.5.0,REV=2003.11.11.23.55>") { return (0) }
 1276+ }
 1277+ if ($id eq "118742") {
 1278+ if (!$pkgs{"SUNWtsr"} || $pkgs{"SUNWtsr"} !~ "<2.5.0,REV=2003.11.11.20.36>") { return (0) }
 1279+ }
 1280+
 1281+ if ($id eq "110692") {
 1282+ if ((exists $p{108806}) && ($p{108806}{irev} ge '01')) { return (0) }
 1283+ if ((exists $p{108806}) && ($p{108806}{crev} ge '01')) { return (0) }
 1284+ }
 1285+
 1286+ if ($id eq "111412") {
 1287+ if (!$pkgs{"SUNWmdi"} || $pkgs{"SUNWmdi"} !~ "<11.8.0,REV=2001.01.19.01.02>") { return (0) }
 1288+ if (!$pkgs{"SUNWsan"}) { return (0) }
 1289+ }
 1290+ if ($id =~ /111095|111096|111413/) {
 1291+ if (!$pkgs{"SUNWsan"}) { return (0) }
 1292+ }
 1293+ if ($id eq "111097") {
 1294+ if (!$pkgs{"SUNWsan"}) { return (0) }
 1295+ if (!$pkgs{"SUNWqlc"}) { return (0) }
 1296+ }
 1297+
 1298+ if ($id eq "111656") {
 1299+ if (!((exists $p{109460}) && ($p{109460}{irev} eq '05'))) { return (0) }
 1300+ }
 1301+ if ($id eq "111658") {
 1302+ if (!((exists $p{107469}) && ($p{107469}{irev} eq '08'))) { return (0) }
 1303+ }
 1304+ if ($id eq "111079") {
 1305+ if (!((exists $p{105375}) && ($p{105375}{irev} eq '26'))) { return (0) }
 1306+ }
 1307+
 1308+ if ($id eq "107474") {
 1309+ if ((exists $p{107292}) && ($p{107292}{irev} ge '02')) { return (1) }
 1310+ return (0);
 1311+ }
 1312+
 1313+ if ($id eq "106533") {
 1314+ if ($u{platform} ne 'SUNW,UltraSPARC-IIi-cEngine') { return (0) }
 1315+ }
 1316+ if ($id eq "106629") {
 1317+ if ($u{platform} ne 'CYRS,Superserver-6400') { return (0) }
 1318+ }
 1319+ if ($id eq "112780") {
 1320+ if (!($u{model} eq 'sun4u')) { return (0) }
 1321+ }
 1322+ if ($id eq "112327") {
 1323+ if (($u{osrel} ne "5.6") && ($u{osrel} ne "5.7")) { return (0) }
 1324+ }
 1325+
 1326+ if ($id =~ /11464[456789]|11465[0123]|11481[67]|11578[01]|11752[01]/) {
 1327+ if ($u{osrel} ne "5.8") { return (0) }
 1328+ }
 1329+ if ($id =~ /11468[6789]|11469[012345]|11481[89]|11578[23]|11752[67]/) {
 1330+ if ($u{osrel} ne "5.9") { return (0) }
 1331+ }
 1332+
 1333+ if ($id eq "111891") {
 1334+ if (!$pkgs{"SUNWutr"} || $pkgs{"SUNWutr"} !~ "<1.3_12.c,REV=2001.07.16.20.52>") { return (0) }
 1335+ }
 1336+
 1337+ if (($id eq "114255") && ($u{arch} ne "sparc")) { return (0) }
 1338+ if (($id eq "114256") && ($u{arch} ne "i386")) { return (0) }
 1339+
 1340+ if (($id eq "115328") && ($u{osrel} ne "5.8")) { return (0) }
 1341+ if (($id eq "115342") && ($u{osrel} ne "5.9")) { return (0) }
 1342+ if (($id eq "115343") && ($u{osrel} ne "5.9")) { return (0) }
 1343+ if (($id eq "119346") && ($u{osrel} ne "5.10")) { return (0) }
 1344+
 1345+ if (($id eq "115766") && ($u{arch} ne "sparc")) { return (0) }
 1346+ if (($id eq "120091") && ($u{arch} ne "i386")) { return (0) }
 1347+ if (($id eq "120879") && ($u{arch} ne "sparc")) { return (0) }
 1348+ if (($id eq "120880") && ($u{arch} ne "i386")) { return (0) }
 1349+ if (($id eq "120954") && ($u{arch} ne "sparc")) { return (0) }
 1350+ if (($id eq "120955") && ($u{arch} ne "i386")) { return (0) }
 1351+
 1352+ if (($id =~ /115835|115836/) && (!$pkgs{"SUNWgscr"})) { return (0) }
 1353+
 1354+ if (($id eq "119300") && ($u{osrel} ne "5.8")) { return (0) }
 1355+ if (($id eq "119301") && ($u{osrel} ne "5.9")) { return (0) }
 1356+ if (($id eq "119302") && ($u{osrel} ne "5.10")) { return (0) }
 1357+
 1358+ if ($id =~ /109357/) {
 1359+ if ((exists $p{109778}) && ($p{109778}{irev} ge '08')) { return (0) }
 1360+ if ((exists $p{109778}) && ($p{109778}{crev} ge '08')) { return (0) }
 1361+ }
 1362+ if (($id eq "113434") && (!$pkgs{"SUNWwbsup"})) { return (0) }
 1363+
 1364+ if (($id eq "109700") && ($u{osrel} ne "5.6")) { return (0) }
 1365+ if (($id eq "109701") && ($u{osrel} ne "5.7")) { return (0) }
 1366+ if (($id eq "111248") && ($u{osrel} ne "5.6")) { return (0) }
 1367+ if (($id eq "111249") && ($u{osrel} ne "5.7")) { return (0) }
 1368+ if (($id eq "111250") && ($u{osrel} ne "5.8")) { return (0) }
 1369+ if (($id eq "115548") && ($u{osrel} ne "5.9")) { return (0) }
 1370+
 1371+ if (($id eq "108553") && ($u{osrel} ne "5.8")) { return (0) }
 1372+ if (($id eq "108834") && (($u{osrel} ne "5.5.1") || ($u{osrel} ne "5.6") || ($u{osrel} ne "5.7"))) { return (0) }
 1373+ if (($id eq "112125") && (($u{osrel} ne "5.6") || ($u{osrel} ne "5.7"))) { return (0) }
 1374+ if (($id eq "112126") && (($u{osrel} ne "5.8") || ($u{osrel} ne "5.9"))) { return (0) }
 1375+
 1376+ if (($id eq "123200") && ($u{osrel} ne "5.8")) { return (0) }
 1377+ if (($id eq "123201") && ($u{osrel} ne "5.9")) { return (0) }
 1378+ if (($id eq "123202") && ($u{osrel} ne "5.10")) { return (0) }
 1379+
 1380+ if (($id eq "119527") && ($u{arch} ne "sparc")) { return (0) }
 1381+ if (($id eq "119528") && ($u{arch} ne "i386")) { return (0) }
 1382+ if (($id eq "119530") && ($u{arch} ne "sparc")) { return (0) }
 1383+ if (($id eq "119531") && ($u{arch} ne "i386")) { return (0) }
 1384+ if (($id eq "119325") && ($u{arch} ne "sparc")) { return (0) }
 1385+ if (($id eq "119326") && ($u{arch} ne "i386")) { return (0) }
 1386+
 1387+ if (($id eq "127498") && ($u{osrel} ne "5.8")) { return (0) }
 1388+ if (($id eq "127499") && ($u{osrel} ne "5.8")) { return (0) }
 1389+
 1390+ if (($id eq "136986") && ($u{osrel} ne "5.8")) { return (0) }
 1391+ if (($id eq "136987") && ($u{osrel} ne "5.8")) { return (0) }
 1392+ if (($id eq "125950") && ($u{osrel} ne "5.9")) { return (0) }
 1393+ if (($id eq "125951") && ($u{osrel} ne "5.9")) { return (0) }
 1394+ if (($id eq "125952") && ($u{osrel} ne "5.10")) { return (0) }
 1395+ if (($id eq "125953") && ($u{osrel} ne "5.10")) { return (0) }
 1396+
 1397+ if (($id eq "121430") && ($p{121430}{crev} ge '16') && (!$pkgs{"SUNWlucfg"})) { return (0) }
 1398+ if (($id eq "121431") && ($p{121431}{crev} ge '17') && (!$pkgs{"SUNWlucfg"})) { return (0) }
 1399+ if (($id eq "121428") && ($p{121428}{crev} ge '08')) {
 1400+ if (!((exists $p{121430}) && ($p{121430}{irev} ge '16'))) { return (0) }
 1401+ }
 1402+ if (($id eq "121429") && ($p{121429}{crev} ge '08')) {
 1403+ if (!((exists $p{121431}) && ($p{121431}{irev} ge '16'))) { return (0) }
 1404+ }
 1405+
 1406+ if (($id eq "125276") && ($u{arch} ne "sparc")) { return (0) }
 1407+ if (($id eq "125277") && (($u{osrel} ne "5.9") || ($u{arch} ne "i386"))) { return (0) }
 1408+ if (($id eq "125278") && (($u{osrel} ne "5.10") || ($u{arch} ne "i386"))) { return (0) }
 1409+
 1410+ if (($id eq "123827") && ($u{osrel} ne "5.8")) { return (0) }
 1411+ if (($id eq "123828") && ($u{osrel} ne "5.9")) { return (0) }
 1412+ if (($id eq "123829") && ($u{osrel} ne "5.10")) { return (0) }
 1413+
 1414+ if (($id eq "125760") && ($u{osrel} ne "5.8")) { return (0) }
 1415+ if (($id eq "125761") && ($u{osrel} ne "5.9")) { return (0) }
 1416+ if (($id eq "125762") && ($u{osrel} ne "5.10")) { return (0) }
 1417+
 1418+ if ($id =~ /120971|120972|120973|122803|122804|122805|126507|126508|126506/) {
 1419+ if (!($pkgs{"SUNWsamfsr"} || $pkgs{"SUNWsamfsu"})) { return (0) }
 1420+ }
 1421+ if ($id =~ /120974|120975|120976|122806|122807|122808|126511|126512|126510/) {
 1422+ if (!($pkgs{"SUNWqfsr"} || $pkgs{"SUNWqfsu"})) { return (0) }
 1423+ }
 1424+
 1425+ if (($id eq "106514") && ($u{arch} ne "sparc")) { return (0) }
 1426+ if (($id eq "106515") && ($u{arch} ne "i386")) { return (0) }
 1427+ if (($id eq "108049") && ($u{arch} ne "sparc")) { return (0) }
 1428+ if (($id eq "108050") && ($u{arch} ne "i386")) { return (0) }
 1429+ if (($id eq "108879") && ($u{arch} ne "sparc")) { return (0) }
 1430+ if (($id eq "108881") && ($u{arch} ne "i386")) { return (0) }
 1431+ if (($id eq "109120") && ($u{arch} ne "sparc")) { return (0) }
 1432+ if (($id eq "109121") && ($u{arch} ne "i386")) { return (0) }
 1433+ if (($id eq "109413") && ($u{arch} ne "sparc")) { return (0) }
 1434+ if (($id eq "109414") && ($u{arch} ne "i386")) { return (0) }
 1435+ if (($id eq "117784") && ($u{arch} ne "sparc")) { return (0) }
 1436+ if (($id eq "117785") && ($u{arch} ne "i386")) { return (0) }
 1437+ if (($id eq "118195") && ($u{arch} ne "sparc")) { return (0) }
 1438+ if (($id eq "118196") && ($u{arch} ne "i386")) { return (0) }
 1439+ if (($id eq "118263") && ($u{arch} ne "sparc")) { return (0) }
 1440+ if (($id eq "118264") && ($u{arch} ne "i386")) { return (0) }
 1441+ if (($id eq "118950") && ($u{arch} ne "sparc")) { return (0) }
 1442+ if (($id eq "118951") && ($u{arch} ne "i386")) { return (0) }
 1443+ if (($id eq "123254") && ($u{arch} ne "sparc")) { return (0) }
 1444+ if (($id eq "124590") && ($u{arch} ne "i386")) { return (0) }
 1445+
 1446+ if (($id eq "124480") && (($u{osrel} ne "5.9") || ($u{arch} ne "sparc"))) { return (0) }
 1447+ if (($id eq "124481") && (($u{osrel} ne "5.10") || ($u{arch} ne "sparc"))) { return (0) }
 1448+ if (($id eq "124482") && (($u{osrel} ne "5.10") || ($u{arch} ne "i386"))) { return (0) }
 1449+
 1450+ if (($id eq "126356") && ($u{arch} ne "sparc")) { return (0) }
 1451+ if (($id eq "126357") && ($u{arch} ne "i386")) { return (0) }
 1452+
 1453+ if (($id eq "117429") && ($u{osrel} ne "5.9")) { return (0) }
 1454+ if (($id eq "118386") && ($u{osrel} ne "5.6")) { return (0) }
 1455+ if (($id eq "118387") && ($u{osrel} ne "5.7")) { return (0) }
 1456+ if (($id eq "118388") && ($u{osrel} ne "5.8")) { return (0) }
 1457+ if (($id eq "118389") && ($u{osrel} ne "5.9")) { return (0) }
 1458+ if (($id eq "118828") && ($u{osrel} ne "5.8")) { return (0) }
 1459+ if (($id eq "118829") && ($u{osrel} ne "5.9")) { return (0) }
 1460+ if (($id eq "118836") && ($u{osrel} ne "5.6")) { return (0) }
 1461+ if (($id eq "118837") && ($u{osrel} ne "5.7")) { return (0) }
 1462+ if (($id eq "118838") && ($u{osrel} ne "5.8")) { return (0) }
 1463+ if (($id eq "118839") && ($u{osrel} ne "5.9")) { return (0) }
 1464+ if (($id eq "118840") && ($u{osrel} ne "5.9")) { return (0) }
 1465+ if (($id eq "120376") && ($u{osrel} ne "5.6")) { return (0) }
 1466+ if (($id eq "120377") && ($u{osrel} ne "5.7")) { return (0) }
 1467+ if (($id eq "120378") && ($u{osrel} ne "5.8")) { return (0) }
 1468+ if (($id eq "120379") && ($u{osrel} ne "5.9")) { return (0) }
 1469+ if (($id eq "124689") && ($u{osrel} ne "5.8")) { return (0) }
 1470+ if (($id eq "124690") && ($u{osrel} ne "5.9")) { return (0) }
 1471+
 1472+ if (($id eq "116338") && (!$pkgs{"SUNWxwplt"} || $pkgs{"SUNWxwplt"} !~ "<3.8.1800,REV=0.99.03.23>")) { return (0) }
 1473+ if (($id eq "116339") && (!$pkgs{"SUNWxwplt"} || $pkgs{"SUNWxwplt"} !~ "<3.8.1800,REV=0.99.03.23>")) { return (0) }
 1474+ if (($id eq "118383") && (!$pkgs{"SUNWxwplt"} || $pkgs{"SUNWxwplt"} !~ "<3.8.1800,REV=0.99.03.23>")) { return (0) }
 1475+ if (($id eq "118384") && (!$pkgs{"SUNWxwplt"} || $pkgs{"SUNWxwplt"} !~ "<3.8.1800,REV=0.99.03.23>")) { return (0) }
 1476+ if (($id eq "125698") && (!$pkgs{"SUNWxwplt"} || $pkgs{"SUNWxwplt"} !~ "<3.8.1800,REV=0.99.03.23>")) { return (0) }
 1477+ if (($id eq "125699") && (!$pkgs{"SUNWxwplt"} || $pkgs{"SUNWxwplt"} !~ "<3.8.1800,REV=0.99.03.23>")) { return (0) }
 1478+
 1479+ if (($id eq "111857") && ($u{osrel} ne "5.8")) { return (0) }
 1480+ if (($id eq "114176") && ($u{osrel} ne "5.9")) { return (0) }
 1481+
 1482+ if (($id eq "119380") && ($u{osrel} ne "5.8")) { return (0) }
 1483+ if (($id eq "119381") && ($u{osrel} ne "5.9") && ($u{osrel} ne "5.10")) { return (0) }
 1484+
 1485+ if (($id eq "125445") && ($u{osrel} ne "5.9")) { return (0) }
 1486+ if (($id eq "125446") && ($u{osrel} ne "5.10")) { return (0) }
 1487+
 1488+ if (($id eq "127553") && ($u{arch} ne "sparc")) { return (0) }
 1489+ if (($id eq "127554") && ($u{arch} ne "i386")) { return (0) }
 1490+
 1491+ if (($id eq "121708") && ($u{osrel} ne "5.8")) { return (0) }
 1492+ if (($id eq "121709") && ($u{osrel} ne "5.9")) { return (0) }
 1493+ if (($id eq "121710") && ($u{osrel} ne "5.10")) { return (0) }
 1494+
 1495+ if (($id eq "125848") && ($u{osrel} ne "5.8")) { return (0) }
 1496+ if (($id eq "125849") && ($u{osrel} ne "5.9")) { return (0) }
 1497+ if (($id eq "125850") && ($u{osrel} ne "5.10")) { return (0) }
 1498+
 1499+ return (1);
 1500+}
 1501+
 1502+sub get_uname {
 1503+ # Get information about host
 1504+ open(UNAME, $input{uname}) || err ("Can't open $input{uname} ($!)");
 1505+ $_=<UNAME>;
 1506+ $_ || err ("Empty uname output");
 1507+ chomp;
 1508+ close UNAME;
 1509+
 1510+ ($u{osname}, $u{hostname}, $u{osrel}, $u{osversion}, $u{model}, $u{arch}, $u{platform})= split (/ /, $_);
 1511+ ($u{osname} && $u{hostname} && $u{osrel} && $u{osversion} && $u{model} && $u{arch} && $u{platform}) || err ("Can't parse ouput from $input{uname}:\n $_");
 1512+}
 1513+
 1514+sub get_installed_packages {
 1515+ my $package;
 1516+
 1517+ # Read pkginfo
 1518+ open(PKGINFO, $input{pkginfo}) || err ("Can't open $input{pkginfo} ($!)");
 1519+ if ($input{pkginfo} =~ /pkginfo-l.out/) {
 1520+ while(<PKGINFO>) {
 1521+ if (/\s+PKGINST:\s+(\S+)$/) { $package = $1; }
 1522+ if (/\s+VERSION:\s+(\S+)$/) { $pkgs{$package} .= "<$1>"; }
 1523+ }
 1524+ } else {
 1525+ while(<PKGINFO>) {
 1526+ ($_ =~ /^(\S+) /) || err ("Can't parse output from $input{pkginfo}:\n $_");
 1527+ $package=$1;
 1528+ # Removing trailing .2/.3/... (multiple versions of same package)
 1529+ $package =~ s/\..*//;
 1530+ $_= <PKGINFO>;
 1531+ ($_ =~ / (\S+)$/) || err ("Can't parse output from $input{pkginfo}:\n $_");
 1532+ $pkgs{$package} .= "<$1>";
 1533+ }
 1534+ }
 1535+ close(PKGINFO);
 1536+}
 1537+
 1538+sub get_installed_patches {
 1539+ my $list='';
 1540+ my $done=0;
 1541+
 1542+ my $showrev_cmd=$showrev; ( -x $showrev_cmd) || ($showrev_cmd='');
 1543+ my $patchadd_cmd=$o{patchadd}; ( -x $patchadd_cmd) || ($patchadd_cmd='');
 1544+
 1545+ # On Solaris <= 8, showrev doesn't support -R. Use patchadd instead.
 1546+ my ($major, $minor) = split (/\./, $u{osrel});
 1547+ if (($minor <= 8) && $o{root}) { $showrev_cmd='' }
 1548+
 1549+ if ($o{fromfiles}) {
 1550+ dbg ("Reading from $input{showrev}");
 1551+ open(SHOWREV, $input{showrev}) || err ("Can't open $input{showrev} ($!)");
 1552+ $/=""; $list= <SHOWREV>; $/="\n";
 1553+ close SHOWREV;
 1554+ $done=1;
 1555+ } else {
 1556+ foreach my $cmd ($showrev_cmd, $patchadd_cmd) {
 1557+ next unless $cmd;
 1558+ $input{showrev}="$cmd -p $o{root} 2>/dev/null";
 1559+ dbg ("Reading from $input{showrev}");
 1560+ $list=`$input{showrev}`;
 1561+ if (!$?) { $done=1; last } else { dbg ("Failed: $list") }
 1562+ }
 1563+ }
 1564+ $done || err ("Couldn't get list of installed patches");
 1565+
 1566+ $list || ($list= "No patches are installed\n");
 1567+ my @list= split(/\n/, $list);
 1568+
 1569+ foreach my $i (sort @list) {
 1570+ # Known formats of patch IDs:
 1571+ # 123456-78 : Regular Sun
 1572+ # IDR123456-78 : Unsupported (pre-release) Sun
 1573+ # 123-45 : EMC
 1574+ # CKPSP123456-78 : Checkpoint
 1575+ # CPFWSP410002-01: Checkpoint
 1576+ # KDE20060107-01 : KDE
 1577+ # IDCE32-02 : DCE
 1578+ # DP550001-05 : HP Data Protector
 1579+ # DP550011-1 : HP Data Protector
 1580+ # PSE400SOL023 : Citrix
 1581+ # ME113SB222 : Citrix
 1582+ # Q995801-01 : SUNWluxop
 1583+ # T000000-01 : Terix DST patch
 1584+ if (
 1585+ ($i =~ /^Patch:\s+(\d{3,6})-(\d{2}).*/) ||
 1586+ ($i =~ /^Patch:\s+IDR(\d{6})-(\d{2}).*/) ||
 1587+ ($i =~ /^Patch:\s+CKPSP(\d{6})-(\d{2}).*/) ||
 1588+ ($i =~ /^Patch:\s+CPFWSP(\d{6})-(\d{2}).*/) ||
 1589+ ($i =~ /^Patch:\s+KDE(\d{8})-(\d{2}).*/) ||
 1590+ ($i =~ /^Patch:\s+IDCE(\d{2})-(\d{2}).*/) ||
 1591+ ($i =~ /^Patch:\s+DP(\d{6})-(\d{2}).*/) ||
 1592+ ($i =~ /^Patch:\s+DP(\d{6})-(\d{1}).*/) ||
 1593+ ($i =~ /^Patch:\s+PSE(\d{3})SOL(\d{3}).*/) ||
 1594+ ($i =~ /^Patch:\s+ME(\d{3})SB(\d{3}).*/) ||
 1595+ ($i =~ /^Patch:\s+Q(\d{6})-(\d{2}).*/) ||
 1596+ ($i =~ /^Patch:\s+T(\d{6})-(\d{2}).*/)
 1597+ ) {
 1598+ my ($id, $rev)=($1,$2);
 1599+ init_patch($id);
 1600+ $p{$id}{irev}= $rev;
 1601+ if ($i =~ / Obsoletes: ([-0-9, ]*) /) {
 1602+ for my $j (split (/,* /, $1)) {
 1603+ my ($oid, $orev) = split (/-/, $j);
 1604+ ($id eq $oid) && next;
 1605+ init_patch($oid);
 1606+ $p{$oid}{iobsoletedby}="$id-$rev";
 1607+ #dbg ("$oid-$orev obsoleted by $id-$rev");
 1608+ }
 1609+ }
 1610+ if ($i =~ / Incompatibles: ([-0-9, ]*) /) {
 1611+ for my $j (split (/,* /, $1)) {
 1612+ my ($iid, $irev) = split (/-/, $j);
 1613+ init_patch($iid);
 1614+ #dbg ("$iid-$irev incompatible with $id-$rev");
 1615+ }
 1616+ }
 1617+ next;
 1618+ }
 1619+ next if ($i =~ "No patches are installed");
 1620+ next if ($i =~ "No patches installed");
 1621+ next if ($i =~ /^$/);
 1622+ print ("WARNING: Can't parse output from $input{showrev}:\n $i\n");
 1623+ }
 1624+}
 1625+
 1626+sub get_current_xref {
 1627+ # Download most recent patchdiag.xref, if requested
 1628+
 1629+ return if ($o{nocheckxref});
 1630+
 1631+ lock_free($o{xrefdir}, "xref", 60) || err ("Another instance of pca is downloading $input{xref} right now");
 1632+
 1633+ # Remove possibly left-over size zero file
 1634+ if (-z $input{xref}) { unlink ($input{xref}) }
 1635+
 1636+ # Check for existing and up to date local copy of xref file
 1637+ if ((-f $input{xref}) && (!$o{getxref})) {
 1638+ my $interval=10800; # 3 hours
 1639+ my $current=(stat($input{xref}))[9];
 1640+ my $now=time();
 1641+ my $age=$now-$current;
 1642+ dbg ("xref now : " . localtime($now));
 1643+ dbg ("xref current: " . localtime($current));
 1644+ dbg ("xref age : " . $age);
 1645+ if ($age < $interval) {
 1646+ dbg ("Local file $input{xref} is up to date");
 1647+ return;
 1648+ }
 1649+ }
 1650+ out ('info', "Downloading xref file to $input{xref}");
 1651+
 1652+ # Check if we can write to xref directory
 1653+ if (! -w $o{xrefdir}) {
 1654+ my $msg="Can't write to xref download directory $o{xrefdir}";
 1655+ if ($o{getxref} || (! -f $input{xref})) {
 1656+ err ($msg)
 1657+ } else { out ('info', $msg); return }
 1658+ }
 1659+ # Check if we can write to xref file
 1660+ if ((-f $input{xref}) && (! -w $input{xref})) {
 1661+ my $msg="Can't write to $input{xref}";
 1662+ if ($o{getxref}) {
 1663+ err ($msg)
 1664+ } else { out ('info', $msg); return }
 1665+ }
 1666+
 1667+ lock_create($o{xrefdir}, "xref", 1) || err ("Another instance of pca is downloading $input{xref} right now");
 1668+ (-s $input{xref}) && rename ("$input{xref}", "$input{xref}.tmp");
 1669+
 1670+ if ($o{xrefurl} =~ /^file:/) {
 1671+ out ('info', "Trying $o{xrefurl}");
 1672+ my $path=$o{xrefurl}; $path =~ s/^file://;
 1673+ (-r "$path/patchdiag.xref") && copy ("$path/patchdiag.xref", $input{xref});
 1674+ if (-s $input{xref}) { goto DONE } else { out ('info', "Failed") }
 1675+ }
 1676+ # Without wget we can't download patchdiag.xref
 1677+ if (!$o{wget}) {
 1678+ out ('info', "Failed (can't find wget executable)"); goto FAIL;
 1679+ }
 1680+ if ($o{xrefurl} =~ /^http:|^https:|^ftp/) {
 1681+ out ('info', "Trying $o{xrefurl}");
 1682+ if (download("xref", "", "local", $input{xref}, "")) { goto DONE } else { out ('info', "Failed") }
 1683+ }
 1684+ my $try=1; my $ret=0;
 1685+ while ($try <= $o{dltries} ) {
 1686+ out ('info', "Trying $o{ssprot}://$o{sshost}/patchdiag.xref ($try/$o{dltries})");
 1687+ $ret=download("xref", "", "sunsolve", $input{xref}, "");
 1688+ last if ($ret);
 1689+ $try++; if ($try <= $o{dltries}) { sleep ($try*2) }
 1690+ }
 1691+ if ($ret) { goto DONE } else { out ('info', "Failed") }
 1692+
 1693+FAIL:
 1694+ out ('info', "Failed (patchdiag.xref not found)");
 1695+DONE:
 1696+ # If we have a backup copy and the download failed, use the backup
 1697+ if (-s "$input{xref}.tmp" && ! -s $input{xref}) {
 1698+ rename ("$input{xref}.tmp", "$input{xref}")
 1699+ }
 1700+ # If we have a backup copy, check if the downloaded file is newer
 1701+ if (-s "$input{xref}.tmp") {
 1702+ my $odate; my $ndate; my $oage=-1; my $nage=-1;
 1703+ open (XREF, "<$input{xref}.tmp");
 1704+ while (<XREF>) {
 1705+ if ($_ =~ /PATCHDIAG TOOL CROSS-REFERENCE FILE AS OF (.*) /) {
 1706+ $oage= calculateage ($odate=$1); dbg ("old xref age: $oage"); last;
 1707+ }
 1708+ }
 1709+ close XREF;
 1710+ open (XREF, "<$input{xref}");
 1711+ while (<XREF>) {
 1712+ if ($_ =~ /PATCHDIAG TOOL CROSS-REFERENCE FILE AS OF (.*) /) {
 1713+ $nage= calculateage ($ndate=$1); dbg ("new xref age: $nage"); last;
 1714+ }
 1715+ }
 1716+ close XREF;
 1717+ if ((($oage == -1) && ($nage != -1)) || (($oage != -1) && ($nage != -1) && ($nage <= $oage))) {
 1718+ unlink ("$input{xref}.tmp")
 1719+ } else {
 1720+ out ('info', "Downloaded file ($ndate) older than local file ($odate)");
 1721+ rename ("$input{xref}.tmp", "$input{xref}")
 1722+ }
 1723+ }
 1724+ lock_remove($o{xrefdir}, "xref");
 1725+
 1726+ if (-s $input{xref}) {
 1727+ my $now=time(); utime $now, $now, $input{xref};
 1728+ if ($o{xrefown} || ($o{xrefdir} =~ /\/home\//)) {
 1729+ chmod 0644, $input{xref};
 1730+ } else {
 1731+ chmod 0666, $input{xref};
 1732+ }
 1733+ return;
 1734+ }
 1735+ (-z $input{xref}) && unlink ($input{xref});
 1736+}
 1737+
 1738+sub get_current_patches {
 1739+ # Read patchdiag.xref
 1740+ #
 1741+ (-z $input{xref}) && err ("Empty file $input{xref}");
 1742+ open(XREF, "<$input{xref}") || err ("Can't open xref file $input{xref} ($!)");
 1743+ while (<XREF>) {
 1744+ if ($_ =~ /PATCHDIAG TOOL CROSS-REFERENCE FILE AS OF (.*) /) {
 1745+ out ('info', "Using $input{xref} from $1"); last;
 1746+ }
 1747+ }
 1748+ $/=""; my $xref= <XREF>; $/="\n";
 1749+ close XREF;
 1750+
 1751+ if (!$xref) { err ("Corrupt file $input{xref}") }
 1752+
 1753+ my @xref= split( /\n/, $xref );
 1754+
 1755+ # Build our patch information table from the xref file.
 1756+ # patchdiag.xref is sorted, so if multiple revisions of a patch are listed,
 1757+ # the one with the highest revision comes last.
 1758+ #
 1759+ foreach my $i (sort @xref) {
 1760+ # Ignore comment lines and HTML tags
 1761+ next if (($i =~ /^##/) || ($i =~ /^</));
 1762+ if ($i !~ /^\d{6}\|\d{2}\|.*\|.\|.\|.\|..\|.*\|.*\|.*\|.*$/) { err ("Can't parse input from $input{xref}:\n $i") }
 1763+
 1764+ my ($id, $crev, $reldate, $rFlag, $sFlag, $oFlag, $byFlag, $os,
 1765+ $archs, $pkgs, $synopsis )= split( /\|/, $i);
 1766+
 1767+ init_patch($id);
 1768+
 1769+ # If an installed patch revision is marked bad, note this.
 1770+ if (($p{$id}{irev} eq $crev) && ($byFlag =~ ".B")) {
 1771+ $p{$id}{ibad}= 1;
 1772+ dbg ("Bad patch installed: $id-$p{$id}{irev}");
 1773+ }
 1774+
 1775+ # If a patch revision is obsoleted or bad, use either the highest
 1776+ # non-obsoleted revision, or the highest obsoleted revision if all
 1777+ # revisions are obsoleted or bad.
 1778+ #
 1779+ if ($p{$id}{crev} ne "00") {
 1780+ if (($oFlag eq "O") || ($byFlag =~ ".B")) {
 1781+ if (!$p{$id}{obs} && !$p{$id}{bad}) { next; }
 1782+ }
 1783+ }
 1784+
 1785+ $p{$id}{crev}=$crev;
 1786+ if ($reldate ne '') { $p{$id}{reldate}=$reldate; }
 1787+ $p{$id}{rec}=0; if ($rFlag eq 'R' ) { $p{$id}{rec}=1; }
 1788+ $p{$id}{sec}=0; if ($sFlag eq 'S' ) { $p{$id}{sec}=1; }
 1789+ $p{$id}{obs}=0; if ($oFlag eq 'O' ) { $p{$id}{obs}=1; }
 1790+ $p{$id}{bad}=0; if ($byFlag =~ ".B") { $p{$id}{bad}=1; }
 1791+ $p{$id}{y2k}=0; if ($byFlag =~ "Y.") { $p{$id}{y2k}=1; }
 1792+ $p{$id}{os}=$os;
 1793+ $p{$id}{synopsis}=$synopsis;
 1794+
 1795+ # If a patch is obsoleted by another patch, note it.
 1796+ # There are (at least) two forms, one with a patch revision
 1797+ # and one without. We check for both.
 1798+ #
 1799+ if ($p{$id}{obs}) {
 1800+ if ($synopsis =~ /Obsoleted by[ :]*(\d{6})-(\d{2})/) {
 1801+ if ($id ne $1) {
 1802+ $p{$id}{obsoletedby}="$1-$2";
 1803+ #dbg ("$id-$crev obsoleted by $p{$id}{obsoletedby}");
 1804+ }
 1805+ }
 1806+ if ($synopsis =~ /OBSOLETED by (\d{6})/) {
 1807+ if ($id ne $1) {
 1808+ $p{$id}{obsoletedby}="$1-01";
 1809+ #dbg ("$id-$crev obsoleted by $p{$id}{obsoletedby}");
 1810+ }
 1811+ }
 1812+ }
 1813+
 1814+ # Patches might be obsoleted by installed patches, which are not
 1815+ # (yet) listed in patchdiag.xref.
 1816+ #
 1817+ if (($p{$id}{iobsoletedby}) && (!$p{$id}{obs})) {
 1818+ $p{$id}{obs}=1;
 1819+ $p{$id}{obsoletedby}=$p{$id}{iobsoletedby};
 1820+ }
 1821+
 1822+ # Patch requires are coded into the archs field - separate them.
 1823+ $p{$id}{archs}='';
 1824+ $p{$id}{requires}='';
 1825+ foreach my $r (split /\;/, $archs) {
 1826+ if ($r =~ /^\d{6}-\d{2}/) {
 1827+ $p{$id}{requires} .= "$r;";
 1828+ # We run init_patch here for required patches because they might
 1829+ # be missing in the xref file, and would be uninitialized later.
 1830+ my ($r_id, $r_rev)= split (/-/, $r);
 1831+ init_patch($r_id);
 1832+ } else {
 1833+ $p{$id}{archs} .= "$r;";
 1834+ }
 1835+ }
 1836+ # Patch incompatibilities are coded into the pkgs field
 1837+ $p{$id}{pkgs}='';
 1838+ foreach my $r (split /\;/, $pkgs) {
 1839+ if ($r =~ /^\d{6}-\d{2}/) {
 1840+ my ($r_id, $r_rev)= split (/-/, $r);
 1841+ init_patch($r_id);
 1842+ #dbg ("$r_id-$r_rev incompatible with $id-$crev");
 1843+ } else {
 1844+ $p{$id}{pkgs} .= "$r;";
 1845+ }
 1846+ }
 1847+ }
 1848+}
 1849+
 1850+sub init_patch {
 1851+ my $id=$_[0];
 1852+
 1853+ # Every patch should be initialized only once.
 1854+ return if ($p{$id}{init});
 1855+
 1856+ if($o{threads}) {
 1857+ $p{$id}=&share({});
 1858+ $p{$id}{output} = &share([]);
 1859+ }
 1860+
 1861+ $p{$id}{irev}= $p{$id}{crev}= $p{$id}{prev}= '00';
 1862+ $p{$id}{synopsis}= 'NOT FOUND IN CROSS REFERENCE FILE!';
 1863+ $p{$id}{rec}= $p{$id}{sec}= $p{$id}{obs}= $p{$id}{bad}= $p{$id}{y2k}= 0;
 1864+ $p{$id}{recf}= $p{$id}{secf}= 0;
 1865+ $p{$id}{os}= '';
 1866+ $p{$id}{pkgs}= '';
 1867+ $p{$id}{stop}= '';
 1868+ $p{$id}{ignore}= '';
 1869+ $p{$id}{nobackup}= '';
 1870+ $p{$id}{reldate}= 'Jan/01/71';
 1871+ $p{$id}{obsoletedby}= '';
 1872+ $p{$id}{iobsoletedby}= '';
 1873+ $p{$id}{archs}= '';
 1874+ $p{$id}{requires}= '';
 1875+ $p{$id}{listed}= 0;
 1876+ $p{$id}{ibad}= 0;
 1877+ $p{$id}{init}= 1;
 1878+ $p{$id}{dfori}= 0;
 1879+ $p{$id}{dloutput}= '';
 1880+ $p{$id}{dlfile}= '';
 1881+}
 1882+
 1883+sub print_patch {
 1884+ my $id=$_[0];
 1885+ my ($char, $h_char, $irev, $crev, $rec, $sec, $bad, $age, $synopsis);
 1886+
 1887+ if ($p{$id}{irev} lt $p{$id}{crev}) { $char='<'; $h_char='&lt;'; }
 1888+ if ($p{$id}{irev} eq $p{$id}{crev}) { $char='='; $h_char='='; }
 1889+ if ($p{$id}{irev} gt $p{$id}{crev}) { $char='>'; $h_char='&gt;'; }
 1890+
 1891+ $irev= $p{$id}{irev}; if ($irev eq "00") { $irev= '--' };
 1892+ $crev= $p{$id}{crev}; if ($crev eq "00") { $crev= '--' };
 1893+
 1894+ $rec='-'; if ($p{$id}{recf}) { $rec='r'; }; if ($p{$id}{rec}) { $rec='R'; }
 1895+ $sec='-'; if ($p{$id}{secf}) { $sec='s'; }; if ($p{$id}{sec}) { $sec='S'; }
 1896+ $bad='-';
 1897+ if (($p{$id}{irev} eq "00") && ($p{$id}{bad} )) { $bad='B' }
 1898+ if (($p{$id}{irev} ne "00") && ($p{$id}{ibad})) { $bad='B' }
 1899+
 1900+ $synopsis= $p{$id}{synopsis};
 1901+
 1902+ $age=calculateage($p{$id}{reldate});
 1903+ if ($age > 999) { $age=999; }
 1904+
 1905+ if (!$o{listhtml}) {
 1906+ my $out=$o{format};
 1907+ $id=sprintf ("%6d", $id); $out =~ s/%p/$id/;
 1908+ $out =~ s/%i/$irev/; $out =~ s/%e/$char/; $out =~ s/%c/$crev/;
 1909+ $out =~ s/%r/$rec/; $out =~ s/%s/$sec/; $out =~ s/%b/$bad/;
 1910+ $age=sprintf ("%3d", $age); $out =~ s/%a/$age/;
 1911+ $out =~ s/%y/$synopsis/;
 1912+ my $n=sprintf ("%3d", $c{current}); $out =~ s/%n/$n/;
 1913+ my $t=sprintf ("%3d", $c{total}); $out =~ s/%t/$t/;
 1914+ print "$out\n";
 1915+ } else {
 1916+ # The patch download link will only work for patches in zip format,
 1917+ # there is no way to determine if it's in zip or tar.Z here.
 1918+ #
 1919+ $synopsis =~ s/\&/\&amp;/;
 1920+ printf "<tr>";
 1921+ if ($o{patchurl} =~ /pca-proxy\.cgi/) {
 1922+ printf "<td><a href=\"$o{patchurl}$id-$crev.zip\">%6d</a>", $id;
 1923+ } else {
 1924+ printf "<td><a href=\"$o{ssprot}://$o{sshost}/pdownload.do?target=$id-$crev&method=h\">%6d</a>", $id;
 1925+ }
 1926+ printf "<td>%2s<td>%1s<td>%2s<td>%1s%1s%1s<td align=right>%3s", $irev, $h_char, $crev, $rec, $sec, $bad, $age;
 1927+ if ($o{patchurl} =~ /pca-proxy\.cgi/) {
 1928+ printf "<td><a href=\"$o{patchurl}README.$id-$crev\">%s</a></tr>\n", $synopsis;
 1929+ } else {
 1930+ printf "<td><a href=\"$o{ssprot}://$o{sshost}/search/document.do?assetkey=$id\">%s</a></tr>\n", $synopsis;
 1931+ }
 1932+ }
 1933+}
 1934+
 1935+sub print_header {
 1936+ if (!$o{listhtml} && !$o{noheader} && !$o{readme}) {
 1937+ print "Host: $u{hostname} ($u{osname} $u{osrel}/$u{osversion}/$u{arch}/$u{model})\n";
 1938+ if ($o{root}) { my $r=$o{root}; $r =~ s/-R //; print "Root: $r\n" }
 1939+ print "List: @slist\n\n";
 1940+ (@plist) || return;
 1941+
 1942+ my $hdr= my $sep=$o{format};
 1943+ $hdr =~ s/%p/Patch /; $hdr =~ s/%i/IR/; $hdr =~ s/%e/ /;
 1944+ $hdr =~ s/%c/CR/; $hdr =~ s/%r/R/; $hdr =~ s/%s/S/;
 1945+ $hdr =~ s/%b/B/; $hdr =~ s/%a/Age/; $hdr =~ s/%y/Synopsis/;
 1946+ $hdr =~ s/%n/Cnt/; $hdr =~ s/%t/Tot/;
 1947+ $sep =~ s/%p/------/; $sep =~ s/%i/--/; $sep =~ s/%e/-/; $sep =~ s/%c/--/;
 1948+ $sep =~ s/%r/-/; $sep =~ s/%s/-/; $sep =~ s/%b/-/; $sep =~ s/%a/---/;
 1949+ $sep =~ s/%n/---/; $sep =~ s/%t/---/;
 1950+ if ($sep =~ /%y/) {
 1951+ my $ysep = '-' x (78 - (length ($sep)-2)); $sep =~ s/%y/$ysep/;
 1952+ }
 1953+ print "$hdr\n$sep\n";
 1954+ }
 1955+ if ($o{listhtml}) {
 1956+ print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"";
 1957+ print "\n \"http://www.w3.org/TR/html4/loose.dtd\">\n";
 1958+ print "<html>\n<head>\n";
 1959+ print "<title>PCA report for $u{hostname}</title>\n";
 1960+ print "</head>\n<body>\n";
 1961+ print "<h2>Host: $u{hostname} ($u{osname} $u{osrel}/$u{osversion}/$u{arch}/$u{model})<br>\n";
 1962+ if ($o{root}) { my $r=$o{root}; $r =~ s/-R //; print "Root: $r<br>\n" }
 1963+ print "List: @slist</h2>\n<table>\n";
 1964+ (@plist) || return;
 1965+
 1966+ print "<tr><th>Patch</th>";
 1967+ print "<th><span title='Installed Revision'>IR</span></th><th></th>";
 1968+ print "<th><span title='Current Revision'>CR</span></th>";
 1969+ print "<th><span title='Recommended/Security/Bad Status'>RSB</span></th>";
 1970+ print "<th>Age</th>";
 1971+ print "<th>Synopsis</th></tr>\n";
 1972+ }
 1973+}
 1974+
 1975+sub print_footer {
 1976+ if ($o{listhtml}) {
 1977+ print "</table>\n</body>\n</html>\n";
 1978+ }
 1979+}
 1980+
 1981+sub get_readme {
 1982+ my $pp=$_[0];
 1983+ my ($id, $rev)= split (/-/, $pp);
 1984+
 1985+ my $rfile="$o{tmpdir}/README.$pp" . time() . $$;
 1986+ $p{$id}{dlfile}=$rfile;
 1987+ out ('stderr', "Downloading README for $pp");
 1988+
 1989+ # If patch is available in unzipped format, use its README
 1990+ if (-f "$o{patchdir}/$pp/README.$pp") {
 1991+ out ('stderr', "Trying $o{patchdir}/$pp");
 1992+ copy ("$o{patchdir}/$pp/README.$pp", $rfile);
 1993+ if (-s $rfile) { goto DONE } else { out ('stderr', "Failed") }
 1994+ }
 1995+ # If we have the zip/jar file, extract README from there. This doesn't work
 1996+ # for tar/tar.Z files, as Sun's tar cannot extract files to stdout.
 1997+ foreach my $ext ('zip', 'jar') {
 1998+ if ((-f "$o{patchdir}/$pp.$ext") && !$o{pforce}) {
 1999+ out ('stderr', "Trying $o{patchdir}/$pp.$ext");
 2000+ `$unzip -p $o{patchdir}/$pp.$ext $pp/README.$pp >$rfile 2>/dev/null`;
 2001+ if (!$? && (-s $rfile)) { goto DONE } else { out ('stderr', "Failed") }
 2002+ }
 2003+ }
 2004+ # Get README from local patch server
 2005+ if ($o{patchurl} =~ /^file:/) {
 2006+ out ('stderr', "Trying $o{patchurl}");
 2007+ my $path=$o{patchurl}; $path =~ s/^file://;
 2008+ (-r "$path/README.$pp") && copy ("$path/README.$pp", $rfile);
 2009+ if (-s $rfile) { goto DONE } else { out ('stderr', "Failed") }
 2010+ }
 2011+ # Without wget we can't download the README
 2012+ if (!$o{wget}) {
 2013+ out ('info', "Failed (can't find wget executable)"); goto FAIL;
 2014+ }
 2015+ if ($o{patchurl} =~ /^http:|^https:|^ftp/) {
 2016+ out ('stderr', "Trying $o{patchurl}");
 2017+ if (download("readme", $pp, "local", $rfile, "")) { goto DONE } else { out ('stderr', "Failed") }
 2018+ }
 2019+ # Try download from restricted patch server, if the user provided
 2020+ # Sun Online Account data
 2021+ if (checksoa()) {
 2022+ my $try=1; my $ret=0;
 2023+ while ($try <= $o{dltries}) {
 2024+ out ('stderr', "Trying $o{ssprot}://$o{sshost}/ ($try/$o{dltries})");
 2025+ $ret=download("readme", $pp, "sunsolve", $rfile, "");
 2026+ last if $ret;
 2027+ $try++; if ($try <= $o{dltries}) { sleep ($try*2) }
 2028+ }
 2029+ if ($ret) { goto DONE } else { out ('stderr', "Failed") }
 2030+ } else {
 2031+ out ('stderr', "Failed (no Sun Online Account data)");
 2032+ }
 2033+FAIL:
 2034+ out ('info', "Failed (README not found)");
 2035+DONE:
 2036+ $p{$id}{dlfile}="";
 2037+
 2038+ if (-s $rfile) {
 2039+ out ('stderr', "Done");
 2040+ return ($rfile);
 2041+ }
 2042+ unlink ($rfile);
 2043+ return ();
 2044+}
 2045+
 2046+sub update {
 2047+ if ($o{update} eq "never") {
 2048+ dbg ("Never update");
 2049+ return;
 2050+ }
 2051+ if (($o{update} ne "auto") && $o{proxy}) {
 2052+ dbg ("update option $o{update} not supported in proxy mode");
 2053+ return;
 2054+ }
 2055+
 2056+ # If we can't write to pca, update won't work.
 2057+ if (($o{update} eq 'now') || ($o{update} eq 'auto')) {
 2058+ if (! -w $0) { err ("Update option unavailable: Can't write to $0") }
 2059+ }
 2060+
 2061+ if ($o{update} eq 'auto') {
 2062+ dbg ("Auto update");
 2063+ my $interval=86400; # One day
 2064+ my $current=(stat($0))[9];
 2065+ my $now=time();
 2066+ my $age=$now-$current;
 2067+ dbg ("pca now : " . localtime($now));
 2068+ dbg ("pca current: " . localtime($current));
 2069+ dbg ("pca age : " . $age);
 2070+ if ($age < $interval) {
 2071+ dbg ("age lower than interval");
 2072+ return;
 2073+ }
 2074+ }
 2075+
 2076+ my $udir= "$o{tmpdir}/pca." . time() . $$;
 2077+ mkdir $udir,0755 || err ("Can't create temporary directory $udir ($!)");
 2078+ my $ufile= "$udir/pca";
 2079+ dbg ("ufile: $ufile");
 2080+
 2081+ # Copy pca to temporary directory, keeping the timestamps.
 2082+ my ($atime, $mtime) = (stat($0))[8..9];
 2083+ copy ($0, $ufile);
 2084+ utime $atime, $mtime, $ufile;
 2085+
 2086+ my $ret;
 2087+ if ($o{pcaurl} =~ /^http.*pca-proxy\.cgi/) {
 2088+ $ret=download('pca', '', 'pcaurl', "$udir/pca", "");
 2089+ } else {
 2090+ $ret=download('pca', '', 'pcaurl', "", $udir);
 2091+ }
 2092+ $ret || err ("Could not get pca from $o{pcaurl}");
 2093+
 2094+ my $newv;
 2095+ open (F, "<$ufile");
 2096+ while (<F>) {
 2097+ if ($_ =~ /^my \$version=\'(\d{8}-\d{2})\'/) {
 2098+ $newv=$1;
 2099+ dbg ("Old version: $version");
 2100+ dbg ("New version: $newv");
 2101+ last;
 2102+ }
 2103+ }
 2104+ close (F);
 2105+
 2106+ my $updated=0;
 2107+ if ($newv eq $version) {
 2108+ if ($o{update} eq "auto") {
 2109+ my $now=time(); utime $now, $now, $0;
 2110+ } else {
 2111+ out ('info', "No new version available");
 2112+ }
 2113+ } else {
 2114+ out ('info', "New version available: $newv (current version: $version)");
 2115+ if (!$o{proxy}) {
 2116+ out ('info', "\nChanges:\n");
 2117+ open (F, "<$ufile");
 2118+ while (<F>) {
 2119+ if ($_ =~ /=head2 Version $newv/) {
 2120+ out ('info', "Version $newv");
 2121+ while (<F>) {
 2122+ if ($_ =~ /=head2 Version (\d{8}-\d{2})/) {
 2123+ ($1 le $version) && last;
 2124+ }
 2125+ $_ =~ s/=head2 //; chomp;
 2126+ out ('info', "$_");
 2127+ }
 2128+ }
 2129+ }
 2130+ close (F);
 2131+ }
 2132+ if ($o{update} ne "check") {
 2133+ out ('info', "Update");
 2134+ dbg ("Copy $ufile to $0");
 2135+ if (!copy ($ufile, $0)) {
 2136+ out ('info', "Failed ($!)");
 2137+ } else {
 2138+ out ('info', "Done");
 2139+ }
 2140+ $updated=1;
 2141+ }
 2142+ }
 2143+
 2144+ unlink ($ufile);
 2145+ rmdir ($udir);
 2146+
 2147+ if (($o{update} eq "auto") && !$updated) { return }
 2148+ if ($o{proxy}) { return }
 2149+ exit 0;
 2150+}
 2151+
 2152+sub out {
 2153+ my $cat= shift;
 2154+
 2155+ if ($o{proxy}) {
 2156+ if ($cat eq 'error') {
 2157+ print "Content-type: text/plain\n";
 2158+ print "Status: 500 @_\n\n";
 2159+ print "Internal Error: @_\n";
 2160+ return;
 2161+ }
 2162+ if ($o{debug}) {
 2163+ open (F, ">> $o{dbgfile}");
 2164+ my $now= localtime;
 2165+ print F $now, ": ", @_, "\n";
 2166+ close (F);
 2167+ return
 2168+ }
 2169+ return;
 2170+ }
 2171+ if ($cat eq 'debug' ) {
 2172+ if ($o{debug}) { print "@_\n" }
 2173+ return
 2174+ }
 2175+ if ($o{noheader}) { return }
 2176+ if ($cat eq 'stderr') { print STDERR @_, "\n"; return }
 2177+ if ($cat eq 'info' ) { print @_, "\n"; return }
 2178+ if ($cat eq 'error' ) { print STDERR "\nERROR: ", @_, "\n"; return }
 2179+}
 2180+
 2181+sub dbg {
 2182+ $o{debug} || return;
 2183+ out ('debug', @_);
 2184+}
 2185+
 2186+sub calculateage {
 2187+ my ($tmonth, $day, $year)=split(/\//, $_[0]);
 2188+ my %months=("Jan",0,"Feb",1,"Mar",2,"Apr",3,"May",4,"Jun",5,"Jul",6,"Aug",7,"Sep",8,"Oct",9,"Nov",10,"Dec",11);
 2189+ my $month=$months{$tmonth};
 2190+
 2191+ return (int(($currenttime-timelocal(0,0,0,$day,$month,$year))/86400));
 2192+}
 2193+
 2194+sub lock_create {
 2195+ my $lockd=$_[0]; my $tag=$_[1]; my $maxretry=$_[2];
 2196+ my $lockf="$lockd/.pcaLock.$tag";
 2197+
 2198+ lock_free ($lockd, $tag, $maxretry) || return (0);
 2199+
 2200+ unlink "$lockf";
 2201+ sysopen (LOCKF, $lockf, O_RDWR|O_CREAT|O_EXCL) || err ("Can't write $lockf ($!)");
 2202+ print LOCKF "$$\n";
 2203+ close LOCKF;
 2204+ chmod 0666, $lockf;
 2205+ $locks{$tag}=$lockf;
 2206+ return (1);
 2207+}
 2208+
 2209+sub lock_free {
 2210+ my $lockd=$_[0]; my $tag=$_[1]; my $maxretry=$_[2];
 2211+ my $lockf="$lockd/.pcaLock.$tag";
 2212+
 2213+ my $retry=0;
 2214+ while ($retry < $maxretry) {
 2215+ if (-s "$lockf") {
 2216+ open (LOCKF, "<$lockf");
 2217+ chomp(my $pid = <LOCKF>);
 2218+ close LOCKF;
 2219+ if (kill (0, $pid) || ($! eq "Not owner")) {
 2220+ dbg ("Locking $lockf failed");
 2221+ $retry++;
 2222+ ($retry < $maxretry) && sleep (1);
 2223+ next;
 2224+ }
 2225+ }
 2226+ return (1);
 2227+ }
 2228+ return (0);
 2229+}
 2230+
 2231+sub lock_remove {
 2232+ my $lockd=$_[0];
 2233+ my $tag=$_[1];
 2234+
 2235+ unlink "$locks{$tag}";
 2236+ $locks{$tag}='';
 2237+}
 2238+
 2239+sub log_msg {
 2240+ ($o{syslog}) && system("$o{logger} -t pca -p $o{syslog}.info \"@_\"");
 2241+}
 2242+
 2243+sub err {
 2244+ out ('error', @_);
 2245+ cleanup();
 2246+ exit 1;
 2247+}
 2248+
 2249+sub handler {
 2250+ err ("Caught a SIG@_");
 2251+}
 2252+
 2253+sub cleanup {
 2254+ dbg ("Cleanup");
 2255+ if ($dlfile) {
 2256+ dbg ("Removing $dlfile");
 2257+ unlink "$dlfile";
 2258+ }
 2259+ if (($input{xref}) && (-s "$input{xref}.tmp")) {
 2260+ dbg ("Putting back copy of patchdiag.xref file");
 2261+ rename ("$input{xref}.tmp", "$input{xref}");
 2262+ }
 2263+ foreach my $id (keys %p) {
 2264+ if ($p{$id}{dlfile}) {
 2265+ dbg ("Removing $p{$id}{dlfile}");
 2266+ unlink "$p{$id}{dlfile}";
 2267+ }
 2268+ }
 2269+ if (@rlist) {
 2270+ dbg ("Removing @rlist");
 2271+ unlink (@rlist);
 2272+ }
 2273+ $patchxdir && rmtree ($patchxdir);
 2274+ foreach my $tag (keys %locks) {
 2275+ if ($locks{$tag}) {
 2276+ dbg ("Removing $locks{$tag}");
 2277+ unlink "$locks{$tag}";
 2278+ }
 2279+ }
 2280+ $sttyset && system "stty echo";
 2281+}
 2282+
 2283+sub base64 {
 2284+ # From MIME-Base64-Perl-1.00
 2285+
 2286+ my $res = pack("u", $_[0]);
 2287+ # Remove first character of each line, remove newlines
 2288+ $res =~ s/^.//mg; $res =~ s/\n//g;
 2289+
 2290+ $res =~ tr|` -_|AA-Za-z0-9+/|;
 2291+ # fix padding at the end
 2292+ my $padding = (3 - length($_[0]) % 3) % 3;
 2293+ $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
 2294+ return $res;
 2295+}
 2296+
 2297+sub parse_args {
 2298+ # Get internal defaults
 2299+ foreach my $opt (@options) {
 2300+ my ($long, $short, $arg, $argtxt, $default, $help)=split (/\|/, $opt);
 2301+ if ($long eq 'cffile') {
 2302+ map { read_cffile($_) } split (/ /, $default);
 2303+ } elsif ($arg =~ /@/) {
 2304+ @{$o{$long}}=split (/ /, $default);
 2305+ } else {
 2306+ $o{$long}=$default;
 2307+ }
 2308+ }
 2309+ (basename($0) eq "pca-proxy.cgi") && ($o{proxy}=1);
 2310+ (basename($0) =~ "debug") && ($o{debug}=1);
 2311+ $o{proxy} && ($o{xrefdir} = getcwd());
 2312+
 2313+ # Get defaults from optional configuration file(s)
 2314+ my @conf=();
 2315+ push (@conf, dirname($0)."/pca.conf");
 2316+ push (@conf, dirname($0)."/../etc/pca.conf");
 2317+ push (@conf, "/etc/pca.conf");
 2318+ $ENV{HOME} && push (@conf, $ENV{HOME}."/.pca");
 2319+ push (@conf, "pca.conf");
 2320+ if ($o{proxy}) {
 2321+ push (@conf, dirname($0)."/../etc/pca-proxy.conf");
 2322+ push (@conf, "/etc/pca-proxy.conf");
 2323+ push (@conf, "pca-proxy.conf");
 2324+ }
 2325+ foreach my $i (@conf) { read_cffile ($i) }
 2326+
 2327+ # Get defaults from optional environment variables (PCA_*)
 2328+ foreach my $opt (@options) {
 2329+ my ($long, $short, $arg, $argtxt, $default, $help)=split (/\|/, $opt);
 2330+ next if ($help eq "INTERNAL");
 2331+ my $env=uc("PCA_$long");
 2332+ next unless ($ENV{$env});
 2333+ if ($long eq 'cffile') {
 2334+ map { read_cffile($_) } split (/ /, $ENV{$env});
 2335+ } elsif ($arg =~ /@/) {
 2336+ push (@{$o{$long}}, split (/ /, $ENV{$env}))
 2337+ } else {
 2338+ $o{$long}=$ENV{$env}
 2339+ }
 2340+ }
 2341+ ($ENV{TMPDIR}) && (-d $ENV{TMPDIR}) && ($o{tmpdir}= $ENV{TMPDIR});
 2342+
 2343+ # Proxy mode ?
 2344+ if ($o{proxy}) {
 2345+ if ($#ARGV != 0) { err ("Missing argument") }
 2346+ if ($ARGV[0] =~ /:force/) { $ARGV[0] =~ s/:force//; $o{pforce}=1 }
 2347+ if ((($ARGV[0] !~ /^patchdiag.xref$/) &&
 2348+ ($ARGV[0] !~ /^\d{6}-\d{2}\.(zip|jar|tar|tar\.Z)$/) &&
 2349+ ($ARGV[0] !~ /^\d{6}-\d{2}$/) &&
 2350+ ($ARGV[0] !~ /^README\.\d{6}-\d{2}$/) &&
 2351+ ($ARGV[0] !~ /^pca$/))) {
 2352+ err ("Illegal argument");
 2353+ }
 2354+ $o{proxy}=$ARGV[0];
 2355+ } else {
 2356+ # Get command line options
 2357+ my @olist=();
 2358+ foreach my $opt (@options) {
 2359+ my ($long, $short, $arg, $argtxt, $default, $help)=split (/\|/, $opt);
 2360+ next if (($help eq "INTERNAL") || ($help eq "ENVFILE"));
 2361+ $short && ($long="$long|$short");
 2362+ if ($arg) { $long .= "=$arg" } elsif ($Getopt::Long::VERSION >= 2.32) { $long .= "!" }
 2363+ push (@olist, $long);
 2364+ }
 2365+ Getopt::Long::config ("bundling", "no_ignore_case");
 2366+ $o{cffile}=sub { read_cffile ($_[1]) };
 2367+ GetOptions (\%o, @olist) || usage() && exit 1;
 2368+ delete $o{cffile};
 2369+ }
 2370+ if ($o{help}) { usage(); exit 0 }
 2371+ if ($o{man}) { man(); exit 0 }
 2372+ if ($o{version}) { version(); exit 0 }
 2373+
 2374+ $o{listhtml} && ($o{list}=1);
 2375+ $o{pretend} && ($o{install}=1);
 2376+ $o{root} && ($o{root}="-R $o{root}");
 2377+ if (!$o{patchurl} && $o{localurl}) { $o{patchurl}=$o{localurl} }
 2378+ if (!$o{xrefurl} && $o{localurl}) { $o{xrefurl}=$o{localurl} }
 2379+ if ($o{patchurl} =~ /\.cgi$/) { $o{patchurl} .= "?" }
 2380+ if ($o{xrefurl} =~ /\.cgi$/) { $o{xrefurl} .= "?" }
 2381+ if ($o{pcaurl} =~ /\.cgi$/) { $o{pcaurl} .= "?" }
 2382+ if ($o{patchurl} =~ /[^\/\?]$/) { $o{patchurl} .= "/" }
 2383+ if ($o{xrefurl} =~ /[^\/\?]$/) { $o{xrefurl} .= "/" }
 2384+ if ($o{pcaurl} =~ /[^\/\?]$/) { $o{pcaurl} .= "/" }
 2385+ ($o{patchdir} !~ /^(\/|\w:\/)/) && ($o{patchdir}= getcwd()."/$o{patchdir}");
 2386+ ($o{xrefdir} !~ /^(\/|\w:\/)/) && ($o{xrefdir}= getcwd()."/$o{xrefdir}");
 2387+
 2388+ # Set defaults
 2389+ $o{operands} && !@ARGV && (@ARGV=(split (/\s+/, $o{operands})));
 2390+ ($o{download} || $o{install} || $o{readme} || $o{getxref} || $o{proxy}) || ($o{list}=1);
 2391+
 2392+ # Show all options and configuration files in debug output
 2393+ foreach my $opt (@options) {
 2394+ my ($long, $short, $arg, $argtxt, $default, $help)=split (/\|/, $opt);
 2395+ if ($long eq 'cffile') {
 2396+ } elsif ($arg =~ /@/) {
 2397+ next unless (@{$o{$long}});
 2398+ dbg ("Option $long: @{$o{$long}}");
 2399+ } else {
 2400+ next if ($o{$long} eq $default);
 2401+ dbg ("Option $long: $o{$long}")
 2402+ }
 2403+ }
 2404+ dbg ("ARGV: @ARGV");
 2405+ dbg ("Version: $version");
 2406+ ($conf_dbg) && dbg ("Config files: $conf_dbg");
 2407+
 2408+ if ($o{update} !~ /^(never|check|now|auto)$/) {
 2409+ err ("Invalid TYPE specified with --update: $o{update}");
 2410+ }
 2411+}
 2412+
 2413+sub process_patch_exceptions {
 2414+ foreach my $i ('stop', 'ignore', 'nobackup') {
 2415+ foreach my $j (@{$o{$i}}) {
 2416+ #dbg ("$i: $j");
 2417+ if ($j =~ /^all$/) { $o{$i}='all'; last }
 2418+ if ($j =~ /^(\d{6})$/) { init_patch($1); ($p{$1}{$i}= "00"); next }
 2419+ if ($j =~ /^(\d{6})-(\d{2})$/) { init_patch($1); ($p{$1}{$i}= $2); next }
 2420+ err ("Invalid patch ID with --$i: $j")
 2421+ }
 2422+ }
 2423+}
 2424+
 2425+sub process_patch_flags {
 2426+ foreach my $i ('rec', 'sec') {
 2427+ foreach my $j (@{$o{$i}}) {
 2428+ #dbg ("$i: $j");
 2429+ if ($j =~ /^(\d{6})(-\d{2})*$/) { init_patch($1); ($p{$1}{"${i}f"}= 1); next }
 2430+ err ("Invalid patch ID with --$i: $j")
 2431+ }
 2432+ }
 2433+}
 2434+
 2435+sub read_cffile {
 2436+ if ($conf_read{$_[0]}) { return } else { $conf_read{$_[0]}=1 }
 2437+ local *CONF;
 2438+ open (CONF, "<$_[0]") || return;
 2439+ $conf_dbg .= "$_[0] "; dbg ("Reading cffile $_[0]");
 2440+ while (<CONF>) {
 2441+ chomp;
 2442+ s/^#.*$//; s/\s+#.*$//; s/^\s*//; s/\s*$//;
 2443+ next if /^$/;
 2444+
 2445+ # Deprecated
 2446+ if (/(\d{6})\s+ignore/) { push (@{$o{ignore}}, $1); next }
 2447+ if (/(\d{6}-\d{2})\s+ignore/) { push (@{$o{ignore}}, $1); next }
 2448+ if (/(\d{6})\s+\+rec/) { push (@{$o{rec}}, $1); next }
 2449+ if (/(\d{6})\s+\+sec/) { push (@{$o{sec}}, $1); next }
 2450+
 2451+ next unless (/(\w+)\s*=\s*(.+)/); my ($name, $value)=($1,$2);
 2452+ foreach my $opt (@options) {
 2453+ my ($long, $short, $arg, $argtxt, $default, $help)=split (/\|/, $opt);
 2454+ next if ($help eq "INTERNAL");
 2455+ next unless ($name eq $long);
 2456+ if ($long eq 'cffile') {
 2457+ map { read_cffile($_) } split (/ /, $value);
 2458+ } elsif ($arg =~ /@/) {
 2459+ push (@{$o{$long}}, split (/ /, $value))
 2460+ } else {
 2461+ $o{$long}=$value
 2462+ }
 2463+ last
 2464+ }
 2465+ }
 2466+}
 2467+
 2468+sub usage {
 2469+ print<<EOT
 2470+Usage: $0 [OPTION] .. [OPERAND] ..
 2471+
 2472+Operands:
 2473+ patch group: missing, installed, all, total, unbundled, bad
 2474+ Add r, s or rs at the end to list Recommended,
 2475+ Security or Recommended/Security patches only.
 2476+ patch ID: 123456, 123456-78
 2477+ patch file: 123456-78.zip, 123456-78.jar, 123456-78.tar.Z
 2478+ file name: patchlist.txt
 2479+
 2480+Options:
 2481+
 2482+EOT
 2483+;
 2484+
 2485+ foreach my $opt (@options) {
 2486+ my ($long, $short, $arg, $argtxt, $default, $help)=split (/\|/, $opt);
 2487+ next if (($help eq "INTERNAL") || ($help eq "ENVFILE") || ($help eq "DEPRECATED"));
 2488+ $short && ($short="-$short,");
 2489+ $argtxt && ($long="$long=$argtxt");
 2490+ printf " %3s --%-15s%s\n", $short, $long, $help;
 2491+ }
 2492+ return 1;
 2493+}
 2494+
 2495+sub man {
 2496+ eval "use Pod::Usage";
 2497+ if ($@) { err ('Required module Pod::Usage not found') }
 2498+
 2499+ # Change uid to something secure as pod2usage does not run as root.
 2500+ # This snippet is taken from perldoc. See the comments there.
 2501+ my $id= eval { getpwnam("nobody") };
 2502+ $id= eval { getpwnam("nouser") } unless defined $id;
 2503+ $id= -2 unless defined $id;
 2504+ eval {
 2505+ $< = $id; # real uid
 2506+ $> = $id; # effective uid
 2507+ $< = $id; # real uid
 2508+ $> = $id; # effective uid
 2509+ };
 2510+ pod2usage( -exitstatus => 0, -verbose => 2 );
 2511+}
 2512+
 2513+sub version {
 2514+ print "pca $version\n";
 2515+}
 2516+
 2517+__END__
 2518+
 2519+=head1 NAME
 2520+
 2521+pca - analyze, download and install patches for Sun Solaris
 2522+
 2523+=head1 SYNOPSIS
 2524+
 2525+pca [OPTION] .. [OPERAND] ..
 2526+
 2527+=head1 DESCRIPTION
 2528+
 2529+pca is a perl script which generates lists of installed and missing
 2530+patches for Sun Solaris systems and optionally downloads and installs
 2531+patches. By default, if run without any option or operand, pca
 2532+shows a list of all patches which are not installed in their most recent
 2533+revision.
 2534+
 2535+The output of the pkginfo, showrev and uname commands is used to gather
 2536+information about the system. Sun offers a patch cross-reference file
 2537+called patchdiag.xref which contains information about all available patches.
 2538+This file is downloaded by pca automatically to /var/tmp/ and kept up-to-date.
 2539+If the file
 2540+exists and is not writable, pca uses it and won't try to update it.
 2541+
 2542+=head1 SAMPLE OUTPUT
 2543+
 2544+Here's some sample output from I<pca -l all>, which shows a list
 2545+of all installed and missing patches:
 2546+
 2547+ Host: myhost (SunOS 5.9/Generic_117171-09/sparc/sun4u)
 2548+ List: all
 2549+
 2550+ Patch IR CR RSB Age Synopsis
 2551+ ------ -- - -- --- --- --------------------------------------------------
 2552+ 112785 42 < 43 RS- 18 X11 6.6.1: Xsun patch
 2553+ 112787 01 = 01 --- 999 X11 6.6.1: twm patch
 2554+ 112807 10 = 10 RS- 9 CDE 1.5: dtlogin patch
 2555+ 113039 -- < 06 --- 76 SAN 4.4.1: Sun StorEdge Traffic Manager patch
 2556+ 113040 -- < 08 R-- 77 SAN 4.4.1: fctl/fp/fcp driver patch
 2557+ 113477 02 > -- --- 999 NOT FOUND IN CROSS REFERENCE FILE!
 2558+ 117114 -- < 02 --- 4 CDE 1.5: sdtwebclient patch
 2559+
 2560+The first column (I<Patch>) contains the patch number, followed by the
 2561+installed revision (I<IR>) and the current revision
 2562+(I<CR>), with one of E<lt>, E<gt>, or = between
 2563+them, which tells whether the installed patch revision is lower, equal or
 2564+higher than the current revision of the patch. The I<RSB> column lists
 2565+the Recommended/Security/Bad flag of the patch. I<Age> shows the
 2566+number of days since the patch was released, and
 2567+I<Synopsis> shows a short description of the patch.
 2568+
 2569+On this system, revision 42 of patch 112785 is installed, but a newer
 2570+revision (43) is available. This patch is marked Recommended/Security.
 2571+Patch 112787 is installed in revision 01, which is the most
 2572+recent revision of the patch.
 2573+112807 is marked Recommended/Security, and it's up-to-date.
 2574+
 2575+Patches 113039, 113040 and 117114 are not installed
 2576+at all, and 113040 is marked Recommended.
 2577+
 2578+113477 is installed, but not listed in the cross-reference file.
 2579+New Solaris update releases often have patches pre-installed which
 2580+are not yet listed in patchdiag.xref.
 2581+
 2582+Often one patch requires other patches to be installed before
 2583+it can be installed. pca resolves these dependencies,
 2584+and lists patches in their correct order. This can be seen
 2585+in the patch list when a greater patch number is shown
 2586+before a lower one, or when a patch which is not marked R/S is shown
 2587+on the list of missing R/S patches.
 2588+
 2589+=head1 OPTIONS
 2590+
 2591+=over 5
 2592+
 2593+=item -l, --list
 2594+
 2595+List patches. See OPERANDS on how to specify which patches
 2596+are listed.
 2597+
 2598+=item -L, --listhtml
 2599+
 2600+Like I<-l>, but generates output in HTML format, including
 2601+links to patch READMEs and downloads. If I<patchurl> is set and
 2602+points at a local patch proxy, the links in HTML output will point there,
 2603+too.
 2604+
 2605+=item -d, --download
 2606+
 2607+Download patches. See OPERANDS on how to specify which patches
 2608+are downloaded. Patches are placed in the current directory or
 2609+in I<patchdir>, if set. A Sun Online Account is required.
 2610+
 2611+=item -i, --install
 2612+
 2613+Download and install patches. See OPERANDS on how to specify
 2614+which patches are installed. Requires pca to be run as root. Downloaded
 2615+patches are removed after successful installation, unless I<--download>
 2616+is used, too.
 2617+
 2618+=item -I, --pretend
 2619+
 2620+Like I<-i>, but only pretend to install patches. Can be used
 2621+to find out if any of the patches require a reboot.
 2622+
 2623+=item -r, --readme
 2624+
 2625+Display patch READMEs. See OPERANDS on how to specify
 2626+which READMEs are displayed.
 2627+The patch README is
 2628+extracted from a previously downloaded patch file or downloaded
 2629+directly from Sun, if Sun Online Account data is provided.
 2630+
 2631+=item -x, --getxref
 2632+
 2633+Download most recent patch cross-reference file.
 2634+If the file does not exist or is older than 3 hours, pca tries to
 2635+download it on its own before anything else.
 2636+
 2637+=item -X, --xrefdir=DIR
 2638+
 2639+Set location of the cross-reference file. The default is
 2640+I</var/tmp> (in proxy mode, the default is the current directory).
 2641+By default, patchdiag.xref is writable for all users. If the I<xrefown>
 2642+option is set, or the I<xrefdir> option contains I</home>,
 2643+the cross reference file will be
 2644+writable by the current user only.
 2645+
 2646+=item -y, --nocheckxref
 2647+
 2648+Do not check for updated patch cross-reference file. Use this option
 2649+to maintain a global baseline patch set.
 2650+
 2651+=item --xrefown
 2652+
 2653+If set, patchdiag.xref will be writable for the current
 2654+user only.
 2655+
 2656+=item --nocache
 2657+
 2658+If a proxy is used to access the Internet, this option advises
 2659+it to not cache patchdiag.xref. Useful if the proxy can't be trusted
 2660+to always return an up-to-date version of the file.
 2661+
 2662+=item -P, --patchdir=DIR
 2663+
 2664+Set directory to which patches are downloaded. The default is
 2665+the current working directory.
 2666+
 2667+=item -a, --askauth
 2668+
 2669+Ask for Sun Online Account data interactively when needed.
 2670+
 2671+=item --user=USER
 2672+
 2673+Login name for Sun Online Account authentication.
 2674+
 2675+=item --passwd=PASS
 2676+
 2677+Password for Sun Online Account authentication.
 2678+
 2679+=item --patchurl=URL
 2680+
 2681+If set, pca tries to download patches and READMEs from
 2682+this URL first. Any URL starting with file:/, ftp://, http:// or
 2683+https:// can be used. See LOCAL PATCH SERVER for more information.
 2684+
 2685+=item --xrefurl=URL
 2686+
 2687+If set, pca tries to download the patchdiag.xref file from
 2688+this URL first. Any URL starting with file:/, ftp://, http:// or
 2689+https:// can be used. See LOCAL PATCH SERVER for more information.
 2690+
 2691+=item --localurl=URL
 2692+
 2693+Deprecated. Use I<patchurl> and I<xrefurl> instead.
 2694+If set, pca tries to download patches, READMEs and patchdiag.xref from
 2695+this URL first. Any URL starting with file:/, ftp://, http:// or
 2696+https:// can be used.
 2697+
 2698+=item --stop=ID
 2699+
 2700+Stop after patch ID. When the specified patch ID is reached during
 2701+listing, downloading or installing patches all operations are stopped.
 2702+
 2703+=item --ignore=ID
 2704+
 2705+Ignore patch ID. The patch will not be listed, downloaded
 2706+or installed unless it is required by another patch. Specify a patch ID
 2707+without revision (I<123456>) to ignore any revision of patch 123456.
 2708+Specify I<123456-78> to ignore only revision 78 of patch 123456; newer
 2709+revisions will not be ignored.
 2710+
 2711+=item --rec=ID
 2712+
 2713+Set Recommended flag on patch ID. Useful to add single patches
 2714+to the set of recommended patches. The patch will be marked with a lowercase
 2715+I<r> in pca's output.
 2716+
 2717+=item --sec=ID
 2718+
 2719+Set Security flag on patch ID. Useful to add single patches
 2720+to the set of security patches. The patch will be marked with a lowercase
 2721+I<s> in pca's output.
 2722+
 2723+=item -p, --pattern=REGEX
 2724+
 2725+List only patches whose synopsis matches the search pattern
 2726+REGEX. This can be a simple string like I<mail> or a regular
 2727+expression like I<"[kK]ernel">. If the pattern starts with a
 2728+'!', only patches which do not match the pattern are shown.
 2729+
 2730+=item -n, --noreboot
 2731+
 2732+Install only patches that don't require a reboot after
 2733+installation.
 2734+
 2735+=item --minage=DAYS
 2736+
 2737+List only patches which are at least DAYS old.
 2738+
 2739+=item --maxage=DAYS
 2740+
 2741+List only patches which are at most DAYS old.
 2742+
 2743+=item --syslog=TYPE
 2744+
 2745+Syslog facility (eg. user or local0) to log successful patch
 2746+installs to.
 2747+
 2748+=item -k, --nobackup=ID
 2749+
 2750+Do not back up files to be patched for patch ID. This works by running
 2751+patchadd with its I<-d> option. Patches can not be backed out if this
 2752+option is used. Specify a patch ID with or without a revision or the
 2753+special ID "all" to not back up files for any patch.
 2754+
 2755+=item -B, --backdir=DIR
 2756+
 2757+Saves patch backout data to DIR. This works by running patchadd
 2758+with its I<-B> option.
 2759+
 2760+=item -s, --safe
 2761+
 2762+Safe patch installation. Checks all files for local modifications
 2763+before installing a patch. A patch will not be installed if files with
 2764+local modifications would be overwritten.
 2765+
 2766+=item -G, --currentzone
 2767+
 2768+Make patchadd modify packages in the current zone only. This
 2769+works by running patchadd with its I<-G> option. This option works
 2770+on Solaris 10 or newer only.
 2771+
 2772+=item --patchadd=FILE
 2773+
 2774+Path to an alternative patchadd command.
 2775+
 2776+=item -H, --noheader
 2777+
 2778+Don't display descriptive headers and other information, just one line
 2779+per patch. Useful if re-using pca's output in own scripts.
 2780+
 2781+=item --format=FORMAT
 2782+
 2783+Set output format to FORMAT. The default format is
 2784+"%p %i %e %c %r%s%b %a %y". Use I<%p> for the patch number,
 2785+I<%i> for the installed revision, I<%e> for information whether
 2786+the installed revision is lower, equal or higher than the current revision
 2787+(I<%c>). Use I<%r>, I<%s> and I<%b> for the Recommended,
 2788+Security and Bad flag, I<%a> for the age and I<%y> for the
 2789+Synopsis. Use I<%n> as a patch counter and I<%t> for the total
 2790+number of patches.
 2791+Example: With the format string "%p-%c %y" pca shows patches
 2792+in the same format as smpatch. Use of this option in combination with
 2793+I<--listhtml> is unsupported.
 2794+
 2795+=item -f, --fromfiles=DIR
 2796+
 2797+Read uname/showrev/pkginfo output from files in the specified
 2798+directory, where DIR can also be a file name prefix. See
 2799+CREATING PATCH REPORTS FOR REMOTE MACHINES for details.
 2800+
 2801+=item --dltries=NUM
 2802+
 2803+Try downloads from Sun's download server NUM times. The
 2804+default is 1. Can be raised to reduce failed patch downloads when
 2805+Sun's patch download server is unresponsive.
 2806+
 2807+=item -F, --force
 2808+
 2809+Force local caching proxy to download patchdiag.xref, patches
 2810+and patch READMEs from Sun's download server, even if the file is already
 2811+in the cache. Useful to download updated patch READMEs for bad
 2812+patches.
 2813+
 2814+=item -R, --root=DIR
 2815+
 2816+Set alternative root directory. This can be useful for Live Upgrade,
 2817+to analyze patches in an alternate root environment or to point pca at
 2818+the mini-root of a jumpstart install server.
 2819+
 2820+=item --wget=FILE
 2821+
 2822+Path to the wget command.
 2823+
 2824+=item --wgetproxy=URL
 2825+
 2826+Default proxy for wget.
 2827+
 2828+=item --logger=FILE
 2829+
 2830+Path to (alternative) logger command.
 2831+
 2832+=item -t, --threads=NUM
 2833+
 2834+Number of concurrent download threads. See THREADS for details.
 2835+
 2836+=item --update=TYPE
 2837+
 2838+Check for available updates for pca itself. TYPE can be I<never>, I<check>,
 2839+I<now> or I<auto>. See UPDATE PCA for more information.
 2840+
 2841+=item --pcaurl=URL
 2842+
 2843+Set the URL which is used by I<update> to check for new versions of pca.
 2844+See UPDATE PCA for more information.
 2845+
 2846+=item --ssprot=PROT
 2847+
 2848+Use PROT as the protocol to access the Sun download server. The default
 2849+is I<http>. The only reasonable alternative value is I<https>; wget must
 2850+support HTTPS for this to work.
 2851+
 2852+=item --sshost=HOST
 2853+
 2854+Use HOST as the hostname or IP address of the Sun download server.
 2855+The default is I<sunsolve.sun.com>.
 2856+
 2857+=item --cffile=FILE
 2858+
 2859+Read FILE as additional configuration file. Use I<cffile=FILE> in a
 2860+configuration file to include FILE.
 2861+
 2862+=item -V, --debug
 2863+
 2864+Show debug output. This includes output generated by patchadd. When
 2865+running in proxy mode, debug output will be written to the file
 2866+/tmp/pca-proxy-debug.txt.
 2867+
 2868+=item -h, --help
 2869+
 2870+Print help on command line options.
 2871+
 2872+=item -m, --man
 2873+
 2874+Print manual page. This requires the Pod::Usage module.
 2875+
 2876+=item -v, --version
 2877+
 2878+Print version information.
 2879+
 2880+=back
 2881+
 2882+If no option is specified, the I<-l> option to list patches is used.
 2883+
 2884+=head1 OPERANDS
 2885+
 2886+The operands determine which patches are listed (I<-l>),
 2887+downloaded (I<-d>), installed (I<-i>) or whose READMEs
 2888+are displayed (I<-r>). Multiple operands
 2889+can be specified. Supported operands
 2890+are I<patch group> (missing, installed, all, total, unbundled, bad),
 2891+I<patch ID> with or without revision
 2892+(123456-78 or 123456), I<patch file> (123456-78.zip) and
 2893+I<file name> (patchlist.txt).
 2894+
 2895+The patch groups can be used to specify all missing patches (I<missing>),
 2896+all installed patches (I<installed>), both installed and missing patches
 2897+(I<all>), all patches listed in patchdiag.xref (I<total>),
 2898+patches not associated with a software package (I<unbundled>)
 2899+or installed patches which are marked Bad (I<bad>).
 2900+By adding I<r>, I<s> or I<rs> to any of
 2901+the patch groups, only patches from the patch group which are marked
 2902+Recommended, Security or either Recommended or Security are specified.
 2903+Examples are I<missings> for all missing Security patches, or
 2904+I<allrs> for all Recommended/Security patches.
 2905+Patch groups can be shortened by using the first letter of the patch
 2906+group plus optional r/s/rs
 2907+(e.g. I<ms> for missings or I<ars> for allrs).
 2908+
 2909+Patch IDs like I<123456-78> or I<123456> are used to specify
 2910+single patches. If no revision (I<-78>) is specified, patch dependencies
 2911+will be resolved. If the name of a patch file like
 2912+I<123456-78.zip> is specified, it has the same effect as using
 2913+I<123456-78>. This can be useful to install a list of already
 2914+downloaded patches with I<pca -i *.zip>.
 2915+
 2916+If a I<file name> is specified, the file is read and its
 2917+contents are added to the list of operands line-by-line. A file
 2918+can contain other file names. If the file name is equal to a valid
 2919+patch group name it will not be read.
 2920+
 2921+The patch list can be limited to patches whose synopsis line contains
 2922+a search pattern by using any patch group in combination with the
 2923+I<--pattern=REGEX> option.
 2924+A command like I<pca -p mail> shows any missing patch containing
 2925+the I<mail> keyword in its description.
 2926+If the search pattern contains whitespace or special characters, enclose
 2927+it in quotation marks:
 2928+I<pca -p "Sun Studio" -l total> shows patches for all versions of
 2929+Sun Studio. If the pattern starts with '!', the patch list is
 2930+limited to patches which do not match the pattern.
 2931+
 2932+If no operands are specified, the I<missing> operand is used.
 2933+
 2934+=head1 CONFIGURATION
 2935+
 2936+The behaviour of pca can be configured by setting any option either
 2937+in a configuration file, as an environment variable with the I<PCA_>
 2938+prefix or on the command line. See OPTIONS for a complete list; only
 2939+the long names can be used in configuration files and for environment
 2940+variables.
 2941+
 2942+At first, the configuration files are read. pca reads I<pca.conf>
 2943+in the directory where pca is installed, I<../etc/pca.conf> of the
 2944+directory where it is installed, I</etc/pca.conf>, I<$HOME/.pca>
 2945+and I<pca.conf> in the current
 2946+directory, in this order. In proxy mode the files I<../etc/pca-proxy.conf>,
 2947+I</etc/pca-proxy.conf> and
 2948+I<pca-proxy.conf> in the current directory are read, too.
 2949+Options are set by specifying
 2950+I<option=value> in the file. Example: To set the path of the wget command,
 2951+use I<wget=/opt/bin/wget>. To enable debug output, use
 2952+I<debug=1>.
 2953+
 2954+Then, all environment variables matching I<PCA_OPTION> are
 2955+read. Example:
 2956+To set the patch download directory, set I<PCA_PATCHDIR> to
 2957+I</some/dir/>. To set the I<noheader> option, set I<PCA_NOHEADER>
 2958+to I<1>.
 2959+
 2960+At last, the command line options are read. Example: To set the location
 2961+of the patch xref file, use I<-X /tmp> or I<--xrefdir=/tmp>.
 2962+To set the option for safe patch installation, use I<-s> or I<--safe>.
 2963+
 2964+All boolean options (i.e. those which do not take an argument) can
 2965+be negated on the command line by specifying I<--no-option> to override
 2966+settings from configuration files. Version 2.32 or newer of the
 2967+Getopt::Long module is required. Example: If I<noreboot=1> is set in
 2968+I<pca.conf> it can be overridden with I<--no-noreboot>.
 2969+
 2970+The I<operands> option can only be used in configuration files and as
 2971+an environment variable. It sets the default OPERANDS.
 2972+
 2973+In a configuration file, everything after a I<#> character is
 2974+regarded as a comment and ignored.
 2975+
 2976+=head1 PATCH DOWNLOAD AND INSTALLATION
 2977+
 2978+The I<-d> option downloads patches to the current directory, I<-i>
 2979+downloads and installs them.
 2980+The download option can be used as a regular user. The external command
 2981+wget is used for the actual download. If it can't be found in the
 2982+default paths, set the I<wget> option to contain the path to the
 2983+wget command.
 2984+The install option
 2985+requires pca to be run as root. It uses I<patchadd> to
 2986+install the patches.
 2987+Using I<-I> instead of I<-i>
 2988+pretends to install patches, but does not actually install any patch.
 2989+
 2990+The patches are downloaded from Sun's patch download server.
 2991+To download patches from Sun, a Sun Online Account (SOA) is required.
 2992+A free SOA will grant access to security and driver patches only.
 2993+To access all patches, you need to buy a Sun Service Plan and
 2994+connect it to your SOA.
 2995+Set the two options I<user> and I<passwd> to contain
 2996+the SOA user name and password. If I<user> is set, but
 2997+I<passwd> is not, pca will ask for the password.
 2998+Alternatively, when run with the I<askauth> option, pca
 2999+asks for Sun Online Account data interactively.
 3000+
 3001+As pca analyzes the information in the cross-reference file to resolve
 3002+patch dependencies, it lists and installs patches in the correct order.
 3003+
 3004+For some patches, a (reconfiguration) reboot is
 3005+recommended or required after installation. The /reconfigure file
 3006+is created as needed and a message is shown in the summary.
 3007+When the I<install> or I<pretend> option
 3008+is combined with the I<noreboot> option, only patches which do
 3009+not require a reboot are installed. This information is contained
 3010+in the patch distribution file. Therefore, even if I<noreboot>
 3011+is specified, the patches are downloaded anyway; only the installation
 3012+is skipped.
 3013+
 3014+I<patchadd> normally keeps a backup of all files it modifies.
 3015+Using the I<--nobackup=ID> option with pca instructs
 3016+I<patchadd>
 3017+to not keep backup copies of replaced files (see the I<-d> option in
 3018+patchadd's man page).
 3019+
 3020+Sometimes a patch re-delivers
 3021+and silently overwrites files which have been modified locally. pca
 3022+tries to overcome this issue with its safe patch installation mode.
 3023+Before installing a patch, pca checks all files listed in the patch
 3024+README for local modifications. If any modified file is in danger
 3025+of being overwritten, a warning is shown and the patch is skipped.
 3026+Safe mode is off by default, and can be turned on by using I<-s> or
 3027+I<--safe> in
 3028+combination with I<-i> (install patches) or I<-I> (pretend to install
 3029+patches). You must be root to use I<-s>. Running I<pca -s -I> is a
 3030+safe way of identifying problematic patches without actually installing them.
 3031+
 3032+In rare cases, patches modify or replace files without updating the
 3033+checksum in the package database used by pca. Installing a more recent
 3034+revision of the same patch will fail although no local modifications
 3035+have been made to a file. Patch installation can be forced by not
 3036+using the I<safe> option.
 3037+
 3038+Running I<pca -si missingrs> on a regular basis
 3039+is enough to keep the system at the recommended patch level.
 3040+This is quite comfortable and works without problems in many cases.
 3041+As some patches require special handling,
 3042+it's recommended to read the README of every patch before
 3043+installation. pca's I<-L> option for HTML output and the
 3044+I<--readme> option to display patch READMEs are handy for that.
 3045+
 3046+Sometimes installing a patch might fail because of inconsistencies
 3047+in the patchdiag.xref file, the patch README and the
 3048+information contained inside the patch. Often this gets fixed in a new
 3049+version of patchdiag.xref or in a new revision of the patch.
 3050+Either try again a few days later or take a look at the
 3051+I<Notes> section on the pca web site, where some problematic
 3052+patches are listed.
 3053+
 3054+=head1 LOCAL PATCH SERVER
 3055+
 3056+On a local network, it might be useful to have a local patch
 3057+server.
 3058+There are two ways to set up a local patch server for pca, using the
 3059+I<patchurl> and I<xrefurl> options.
 3060+URLs specified with these options are always accessed first when
 3061+downloading patches, patch READMEs or patchdiag.xref.
 3062+Only if a file can't be found
 3063+there, pca falls back to Sun's patch server. Like this, files
 3064+are downloaded from Sun's patch server only once when installing
 3065+patches on multiple machines.
 3066+
 3067+Create the local patch repository by copying downloaded patch files
 3068+(e.g. 123456-78.zip), patch READMEs (e.g. README.123456-78) and/or
 3069+patchdiag.xref to a
 3070+directory which is available via NFS or on a local web server.
 3071+Point pca at it by setting the I<patchurl> and/or I<xrefurl>
 3072+options to the URL
 3073+(e.g. file:/patches/ or http://www.my.org/patches/).
 3074+
 3075+The more advanced method uses pca to work as a local caching proxy for itself.
 3076+Create a directory in the document root of the local web server, and
 3077+link or copy pca there under the name I<pca-proxy.cgi>. Make sure that
 3078+the directory (or the directories specified with the I<xrefdir> and I<patchdir>
 3079+options) are owned and writable by the user under which CGI scripts run,
 3080+as patches, patch READMEs and patchdiag.xref will be stored there.
 3081+Verify that the
 3082+web server is configured to run CGI scripts (for apache, check the ExecCGI
 3083+and AddHandler options in httpd.conf).
 3084+Create a pca.conf
 3085+file in the same directory to specify Sun Online Account data. On the client,
 3086+point
 3087+pca at the caching proxy by setting the I<patchurl> and I<xrefurl>
 3088+options to e.g.
 3089+http://www.my.org/patches/pca-proxy.cgi.
 3090+
 3091+In proxy mode, if a patch or
 3092+patch README exists in the local cache directory, it is delivered
 3093+immediately. If it doesn't, the file is downloaded from Sun's
 3094+patch server, put into the cache, and delivered. For patchdiag.xref,
 3095+I<pca-proxy.cgi> will always make sure that it has a recent
 3096+version of this file and deliver it from its cache.
 3097+
 3098+With a local caching proxy in place, client systems running pca and
 3099+using this proxy do not need direct access to the Internet at all.
 3100+
 3101+Example setup of a local caching proxy on a vanilla Solaris 10 system:
 3102+
 3103+ # mkdir /var/tmp/pca
 3104+ # chown webservd:webservd /var/tmp/pca
 3105+
 3106+This is where patches, READMEs and patchdiag.xref will be stored by
 3107+the proxy. Now put the CGI script in place and create a configuration
 3108+file:
 3109+
 3110+ # cd /var/apache2/cgi-bin
 3111+ # cp /usr/local/bin/pca pca-proxy.cgi
 3112+ # chmod 555 pca-proxy.cgi
 3113+ # cat > /etc/pca-proxy.conf
 3114+ xrefdir=/var/tmp/pca
 3115+ patchdir=/var/tmp/pca
 3116+ user=XXXXXX
 3117+ passwd=YYYYYY
 3118+ ^D
 3119+ # chown webservd:webservd /etc/pca-proxy.conf
 3120+ # chmod 600 /etc/pca-proxy.conf
 3121+
 3122+If the apache2 server is not running yet, create I</etc/apache2/httpd.conf>
 3123+and enable the server with I<svcadm>:
 3124+
 3125+ # svcadm enable svc:/network/http:apache2
 3126+
 3127+Test the caching proxy on a client:
 3128+
 3129+ ./pca -X . --xrefurl=http://server.domain/cgi-bin/pca-proxy.cgi
 3130+ --patchurl=http://server.domain/cgi-bin/pca-proxy.cgi -d 126306-01
 3131+
 3132+The patchdiag.xref and 126301-01.zip will be downloaded by the proxy and
 3133+stored in I</var/tmp/pca/> on the server, and both files will be delivered
 3134+to the client. If it doesn't work, add I<debug=1> to the pca.conf file
 3135+and look at /tmp/pca-proxy-debug.txt and /var/apache2/logs/ for details.
 3136+
 3137+When downloading large patches through the proxy, you must ensure that
 3138+the web server does not kill I<pca-proxy.cgi> before it has completed
 3139+the download from SunSolve. Apache has a I<Timeout> option with a default
 3140+value of 300 seconds. Raise that to 1800 seconds to avoid problems.
 3141+
 3142+For large setups, you can build a cascade of local caching proxies by
 3143+pointing one proxy at another proxy by setting I<xrefurl> and I<patchurl>
 3144+to point at the master proxy in the slave proxies' pca.conf.
 3145+
 3146+As pca uses the wget command to download patches from the patch server,
 3147+make sure that any specially required option is set in I</etc/wgetrc> or
 3148+I<$HOME/.wgetrc>. Example: When running the local patch server on a HTTPS
 3149+server with a self-signed certificate, I<check-certificate=off> should
 3150+be specified in I<wgetrc> on the client.
 3151+
 3152+=head1 UNBUNDLED PATCHES
 3153+
 3154+Usually a patch is related to one or more software packages installed
 3155+on a system. Apart from that, there are unbundled patches. They
 3156+provide firmware updates for machines, disks, or tape drives and fixes
 3157+for software which doesn't come in package format. Currently there is
 3158+no way to automatically determine if such patches actually apply to a
 3159+system.
 3160+
 3161+The I<unbundled> operand specifies this type of patches.
 3162+At first, I<pca -l unbundled> will show a long list of patches.
 3163+To reduce this list
 3164+to the interesting ones, unnecessary patches can be ignored by using
 3165+the I<ignore> option in a pca configuration file.
 3166+For patches you are absolutely not interested in, use an
 3167+entry like I<ignore=123456> in the configuration file;
 3168+this patch will never be shown again, even if a newer revision of
 3169+the patch appears. Patches that are installed in their current revision
 3170+should be put with this revision into the configuration file
 3171+(e.g. I<ignore=123456-78>).
 3172+The patch will show up again when a newer revision is released.
 3173+
 3174+Example: Patch 106121-18 contains the most recent PROM firmware for
 3175+Ultra 5/10 workstations. As it's installed on all systems, I put
 3176+I<ignore=106121-18> into the configuration file.
 3177+When a new revision of the patch
 3178+is released, it will show up in I<pca -l unbundled> again.
 3179+Patch 118324 is the PROM firmware patch for the Sun Fire V440. As I
 3180+don't have such a machine, I put I<ignore=118324> into the
 3181+configuration file to ignore this patch completely.
 3182+
 3183+All that pca can do is to notify of new unbundled patches or patch
 3184+revisions. It's on you to decide whether a patch is needed by checking
 3185+its README file, and to install it by following the instructions in the
 3186+README. Unbundled patches cannot be installed by patchadd or pca.
 3187+
 3188+=head1 CREATING PATCH REPORTS FOR REMOTE MACHINES
 3189+
 3190+pca can create a patch report or download patches for a
 3191+system which cannot run pca directly, like stripped-down
 3192+systems without perl or an Internet connection. On such systems,
 3193+run:
 3194+
 3195+ uname -a > uname.out
 3196+ showrev -p > showrev.out
 3197+ pkginfo -x > pkginfo.out
 3198+
 3199+On systems with a minimal core installation of Solaris, the I<showrev>
 3200+command might not be available. Use I<patchadd -p E<gt> showrev.out>
 3201+instead.
 3202+
 3203+Copy the resulting I<*.out> files to a system where pca is
 3204+installed. Use the I<-f DIR> or I<--fromfiles=DIR> option to
 3205+point pca at the location of the input files.
 3206+The argument to I<-f> can be a directory
 3207+or a file name prefix like I<myhost_>.
 3208+This allows collecting I<*.out> files for multiple systems
 3209+and telling pca which ones to read.
 3210+
 3211+If Sun Explorer is used to collect information about Sun systems, a
 3212+directory containing Sun Explorer output can be used as the argument
 3213+to I<-f> as well.
 3214+
 3215+Other options can be used in combination
 3216+with I<-f>. Example: I<pca -f . -d missing> downloads all missing
 3217+patches for the remote system.
 3218+
 3219+=head1 ZONES
 3220+
 3221+pca can be run both in the global zone or any non-global zone. Patches
 3222+installed in the global zone are usually installed in all non-global zones,
 3223+too. It's recommended to install patches in the global zone first,
 3224+and then run pca in all non-global zones to check for additionally
 3225+needed patches. This is necessary if packages have been added to or
 3226+removed from just the global or any non-global zone.
 3227+
 3228+When pca is run with the I<-G> option, this option is handed through
 3229+to patchadd, which will install patches in the current zone only. See the
 3230+man page for patchadd for further details.
 3231+
 3232+=head1 THREADS
 3233+
 3234+If pca is run with the I<--threads=NUM> option, in conjunction with the
 3235+download I<-d> or install I<-i> options, pca will begin downloading multiple
 3236+patches in parallel, up to NUM patches at once. Patches will still be
 3237+installed one at a time, in the appropriate order.
 3238+
 3239+The perl version used to run pca must support threading, otherwise requests
 3240+to use threading will be silently ignored. The /usr/bin/perl which comes with
 3241+Solaris and perl binaries compiled with the default settings do not
 3242+support threading. In that case, the output of I<--help> will indicate that
 3243+threads have been disabled.
 3244+
 3245+=head1 UPDATE PCA
 3246+
 3247+Changes to the patch infrastructure by Sun and problems with
 3248+single patches often make updates to pca necessary. To ease that procedure,
 3249+the I<update=TYPE> option can be used. The default is type I<never> - pca
 3250+will never
 3251+check for updates. Use the I<check> type to contact the pca webpage and
 3252+check for available updates. Using I<now> will not only check, but also
 3253+download and install the updated version of pca.
 3254+
 3255+With I<auto>, pca will check for updates automatically once per day,
 3256+keeping itself up to date without user intervention. Unlike I<check>
 3257+and I<now> which are for interactive usage, this type is best used
 3258+in a configuration file.
 3259+
 3260+The default URL to check for updates is
 3261+http://www.par.univie.ac.at/solaris/pca/stable/
 3262+(official release). It can be set with the I<pcaurl=URL> option.
 3263+Set it to
 3264+http://www.par.univie.ac.at/solaris/pca/develop/
 3265+to check for and update to new development versions of pca.
 3266+You can set I<pcaurl> to point at a local URL
 3267+to distribute whatever version in your local network.
 3268+If set to point at a local caching proxy, the proxy will check
 3269+for updates automatically, keep a local copy of the pca script
 3270+in I<patchdir> and deliver it to the client.
 3271+
 3272+Set I<update=auto> in the configuration file for pca in proxy mode
 3273+(I<pca-proxy.cgi>) to make it keep itself up-to-date.
 3274+
 3275+=head1 JUMPSTART
 3276+
 3277+You can use pca to install patches in the finish script of a jumpstart
 3278+install server. Perl is included in the OS image which is booted over
 3279+the network for installation starting with Solaris 8. As the machine
 3280+will probably not have an Internet connection during installation, you can
 3281+either pre-download all necessary patches into a directory accessible
 3282+via NFS, or set up a local caching proxy. If you use any http or ftp
 3283+url for I<xrefurl> or I<patchurl>, you must put a copy of wget into
 3284+the directory that contains your finish script and pca, and use the
 3285+I<wget> option to point pca at it.
 3286+
 3287+Set I<patchdir> and I<xrefdir> (unless you use I<nocheckxref>) to /tmp
 3288+to avoid problems with non-writable directories. As the OS which gets
 3289+installed during jumpstart is mounted at /a, use the I<root> option to
 3290+instruct pca to install patches there.
 3291+
 3292+=head1 EXAMPLES
 3293+
 3294+List all missing patches. This is
 3295+the same as running pca without any arguments:
 3296+
 3297+ pca -l missing
 3298+
 3299+List all installed security patches:
 3300+
 3301+ pca -l installeds
 3302+
 3303+Display the README for the current revision of patch 116532:
 3304+
 3305+ pca --readme 116532
 3306+
 3307+Show all installed patches which are marked Bad. You should read the
 3308+patch README to find out how to handle a specific bad patch:
 3309+
 3310+ pca -l bad
 3311+
 3312+Download multiple explicitly specified patches after asking for
 3313+Sun Online Account data:
 3314+
 3315+ pca --askauth -d 121308-02 122032
 3316+
 3317+Download and install all missing patches which do not require to reboot
 3318+the system in safe mode:
 3319+
 3320+ pca --noreboot --safe --install
 3321+
 3322+Download all missing patches for a remote system. Output from I<uname -a>,
 3323+I<showrev -p> and I<pkginfo -x> has been saved to /tmp/myhost_uname.out
 3324+etc. before:
 3325+
 3326+ pca -f /tmp/myhost_ -d missing
 3327+
 3328+Check for a new version of pca and install it:
 3329+
 3330+ pca --update now
 3331+
 3332+A sample configuration file:
 3333+
 3334+ # Sun Online Account
 3335+ user=myuser
 3336+ passwd=secret
 3337+ # Other
 3338+ patchurl=http://www.my.org/patches/pca-proxy.cgi
 3339+ xrefurl=http://www.my.org/patches/pca-proxy.cgi
 3340+ syslog=user
 3341+ safe=1
 3342+
 3343+=head1 ENVIRONMENT VARIABLES
 3344+
 3345+All environment variables with the I<PCA_> prefix are evaluated
 3346+as options; see CONFIGURATION for details. Furthermore, these environment
 3347+variables are used by pca:
 3348+
 3349+=over 5
 3350+
 3351+=item PAGER
 3352+
 3353+Path to the command which is used to display patch README
 3354+files
 3355+
 3356+=back
 3357+
 3358+=over 5
 3359+
 3360+=item TMPDIR
 3361+
 3362+During patch installation, patches are extracted under this
 3363+directory
 3364+
 3365+=back
 3366+
 3367+=head1 AUTHORS
 3368+
 3369+Martin Paul E<lt>martin@par.univie.ac.atE<gt>
 3370+
 3371+Thanks to everybody who contributed code or provided feedback:
 3372+
 3373+Andrew Brooks, Bruce Riddle, Damian Hole, Peter Van Eynde,
 3374+Richard Whelan, Eugene MacDougal, Peter Schmitz, Fredrik Lundholm,
 3375+Dan W. Early, Markus Reger, Constantijn Sikkel, Stephen P. Potter,
 3376+Fletcher Cocquyt, Timothy J. Howard, Thomas Bluhm, Frank Doeschner,
 3377+Loris Serena, Marion Biallowons, Ricky Chew, Martin R. Korczak,
 3378+Imad Soltani, Scott Lucas, Anders Grund, Bernd Senf, Chris Zappala,
 3379+Ashley Krelle, Mike Patnode, Mats Larsson, Thomas Maier-Komor,
 3380+Willi Burmeister, Stefaan A. Eeckels, Ian Collins, Leptonux,
 3381+Joseph Millman, Guenter Zaehle, Frank Fejes, Mark Jeffery,
 3382+Alberto da Silva, Mauricio Tavares, Kurt Rabitsch, Jeff Wieland,
 3383+Frank Bertels, Steve Meier, Dan Lorenzini, Gerard Henry, Laurent Blume,
 3384+Sean Berry, George Moberly, Erik Nordenberg, Mark Ashley, Jim Prescott,
 3385+Christian Pelissier, Hugues Sapin, Colin A. White, Dale T. Long,
 3386+Christophe Kalt, Bruno Delbono, Nan Liu, Frank Cusack,
 3387+Marlon Sanchez-Nunez, Jois Diwakar, Toni Viemero, Jens Larsson,
 3388+Gordon D. Gregory, Luis Catacora, Erik Larson, Tim Longo, Mike Borkowski,
 3389+Nicolas Goy, William Bonnet, Dave Love, Thomas Brandstetter, Daniel Kunkel,
 3390+Gregor Longariva, Miroslav Zubcic, Tim Bradshaw, Chris Quenelle,
 3391+Christopher Odenbach, Andy Fiddaman, Peter Sundstrom, Andreas F. Borchert,
 3392+Jonah Simandjuntak, Damian Lilley, Chris Ridd, Albert Lee, James Lick,
 3393+John Douglass, Andres A. Flores Concepcion, Chris Reece, Toni Viemero,
 3394+Timothy Meader, John D. Groenveld, Ceri Davies, Martin Wismer,
 3395+Laszlo Kiss, Mike Moya, Leon Koll, Shawn Boots, Mike Wallace,
 3396+Robert P. McGraw, Peter Arnold, Matt Kolb, Mike Shackelford, John Dzubera,
 3397+Donald Teed, Asif Iqbal, Stephen Nash, Jason Loretz, Bryan Howard, Roman,
 3398+Jonathan Hoefker, Daniel Trinkle, Ron Halstead, Rob Fisher, Chris Coffey,
 3399+Travis Freeland, Hans-Werner Jouy, Gary Mills, Craig Bell, Mick Russom,
 3400+Brian King, Ashley Rowland, Guillermo Castellini, Bryan D. Moorehead,
 3401+Mark Scheufele, Corey Becker, David Robson, Kevin Maguire, Mike Wallace,
 3402+Marcos Della, Frank Sperber, Horst Scheuermann, Adrian Ulrich, Steve Fox,
 3403+David Collodel, Jeremiah Johnson, Erik Schubert, David Sullivan,
 3404+Tom Francen, Matthew Scala, Richard Mayebo, Gerald Sinkiewicz,
 3405+David Montag, Steve Forman, Jeffrey King, Gerry Van Trieste,
 3406+Chris Denneen, Greg Barry, Paul Armstrong, Andreas Fineske,
 3407+Eric Kissinger, Torsten Peter, Yevgeniy Averin, Sean Walmsley,
 3408+Alexander Skwar, Jeffrey King, Jones Olatunji, Richard Skelton,
 3409+Kjetil Torgrim Homme, Brian McNamara, Gerry Sinkiewicz, Kazuyuki Sato,
 3410+Mayuresh Kshirsagar, Mauro Mozzarelli, Judy Illeman Gaukel, Petri Kunnari,
 3411+William Pool, Steven Faulconer, Rono Jacob, Will Green, Martial Rioux,
 3412+Zafar Pravaiz, Romeo Theriault, Fredrich Maney, Ben Szoko, Pietari Hyvarinen,
 3413+Roman Pestka, Juergen Mengeling, David S. Bryant, Maciek S., Alexander
 3414+Sverdlov, David Coronel, David Groce, Jeff Woolsey, Thomas Marshall,
 3415+Allen Eastwood, Mike Busse, Martin Bellenberg, Dennis Clarke,
 3416+Dominique Frise, Mark Hopkins, Enda O'Connor, Victor Feng, Peter Englmaier,
 3417+Paul B. Henson, Gerry Haskins, Jeff A. Earickson, Stuart Anderson,
 3418+Dagobert Michelsen, Simon Bellwood, Ateeq Altaf, Andrew Berry, Julian Davies,
 3419+Con Petsoglou, Uwe Wolfram, Micah Cowan, Dan Shaw, Paul Moore, Neal A. Lucier,
 3420+Eric Bourgi, Sergiusz Pawlowicz, Paul Van Bommel, Matt Banks, Ray Cromwell,
 3421+Jan Holzhueter, Liam Carey, Alex Docauer, Christopher S. Chan, Philip Kime,
 3422+Michael Schmarck, Kevin L. Bliss, Thomas Bleek, Albert White, Ron Helzer,
 3423+Sergei Haramundanis, Steven M. Christensen, Felix Schattschneider,
 3424+Rajiv G Gunja, Jeremy Simpson, Jesse Caldwell, Amy Rich.
 3425+
 3426+=head1 MAILING LISTS
 3427+
 3428+Two mailing lists are available:
 3429+
 3430+=over 5
 3431+
 3432+=item pca-news
 3433+
 3434+This is a one-way list for announcements of new versions and news.
 3435+To join, send an empty message to E<lt>pca-news-join@lists.univie.ac.atE<gt>.
 3436+
 3437+=back
 3438+
 3439+=over 5
 3440+
 3441+=item pca
 3442+
 3443+This is a discussion and support list. Messages from pca-news will be
 3444+posted to this list as well. Posting to the list is allowed for
 3445+non-subscribers. To join, send an empty message to
 3446+E<lt>pca-join@lists.univie.ac.atE<gt>. To post to the list, send your
 3447+message to E<lt>pca@lists.univie.ac.atE<gt>.
 3448+
 3449+=back
 3450+
 3451+=head1 SEE ALSO
 3452+
 3453+=over 5
 3454+
 3455+=item pca web site:
 3456+
 3457+http://www.par.univie.ac.at/solaris/pca/
 3458+
 3459+=back
 3460+
 3461+=head1 CHANGES
 3462+
 3463+=head2 Version 20080911-01
 3464+
 3465+ * Remove check for patches which are only partly installed
 3466+
 3467+=head2 Version 20080909-02
 3468+
 3469+ * False positive with partly installed patches: add 120011, 127127
 3470+
 3471+=head2 Version 20080909-01
 3472+
 3473+ * Check for patches which are only partly installed
 3474+ * Fix bug with missing patches with multiple versions of the same package
 3475+ * Log failed patch install to syslog
 3476+ * Fix minage option being one day off
 3477+ * Include and use VeriSign certificate for HTTPS downloads from SunSolve
 3478+ * Include and use CyberTrust certificate for HTTPS downloads from pca home
 3479+ * Simplify patch download and pca update code
 3480+ * Simplify patch README download code
 3481+ * Simplify patchdiag.xref download code
 3482+ * Add some debug code
 3483+ * Fix coding error
 3484+ * Code cleanup
 3485+ * Documentation: Better describe what the noheader option does
 3486+ * Fix required patches which were never released: 125486, 125487, 114431
 3487+ * Fix required patches which were never released: 126677, 126678
 3488+ * Apply check: modify 119313, 119314
 3489+ * Update list of contributors
 3490+
 3491+Today pca turns 5 years; version 1.0 was published first on 2003/09/09.
 3492+
 3493+Thanks to all of you for the large amount of positive feedback I've
 3494+received during the last years!
 3495+
 3496+=head2 Version 20080729-01
 3497+
 3498+ * Workaround for 120011/120012 requiring obsolete patches 122660/122661
 3499+ * Fix a bug when using current version of wget with https
 3500+ * Enhance checks for empty or corrupt patchdiag.xref file
 3501+ * New default URL setting for pcaurl
 3502+ * Apply check: add 121708, 121709, 121710, 125848, 125849, 125850
 3503+ * Update list of contributors
 3504+
 3505+=head2 Version 20080626-01
 3506+
 3507+ * New option to list patches with a maximum age (--maxage=DAYS)
 3508+ * Create DIR/reconfigure (not /reconfigure) when using --root=DIR
 3509+ * Don't show ignorable error message from showrev
 3510+ * Fix required patches which were never released: 125077, 125078
 3511+ * Whitelist: add 120185, 120186
 3512+ * Whitelist: remove obsolete patches
 3513+ * Apply check: add 137112, 119252, 119253
 3514+ * Apply check: add 136987, 136986
 3515+ * Apply check: add 127553, 127554
 3516+ * Apply check: remove obsolete patches
 3517+ * Update list of contributors
 3518+
 3519+=head2 Version 20080519-01
 3520+
 3521+ * Fix a bug where pca fails to run when threads option is used
 3522+ * Use locally existing patches in jar format
 3523+ * Read configuration file immediately when cffile option is seen
 3524+ * Enhance performance with hundreds of patch IDs as operands
 3525+ * Fix hanging unzip when no diskspace is left
 3526+ * Show timestamp in debug mode when patchadd is started
 3527+ * Remove duplicate debug message
 3528+ * Documentation: Add note about possible requirement of wgetrc settings
 3529+ * Whitelist: add 137093, 137094
 3530+ * Apply check: fix 119381
 3531+ * Apply check: add 125445, 125446
 3532+ * Cosmetic changes
 3533+ * Update list of contributors
 3534+
 3535+=head2 Version 20080507-01
 3536+
 3537+ * Add option for concurrent patch downloads (--threads=NUM)
 3538+ * New option to set sunsolve access protocol to HTTPS (--ssprot=PROT)
 3539+ * Ignore HTML tags in patchdiag.xref
 3540+ * Honor wgetproxy option for non-SunSolve URLs as well
 3541+ * Read ../etc/pca-proxy.conf in proxy mode
 3542+ * Fix wget version detection under Red Hat
 3543+ * Make safe option work with ill-formatted README files
 3544+ * Whitelist: add 137274, 137275
 3545+ * Whitelist: add 127755, 127756, 128307
 3546+ * Whitelist: add 127127, 127128
 3547+ * Apply check: add 117429, 118386, 118387, 118388, 118389
 3548+ * Apply check: add 118828, 118829, 118836, 118837, 118838, 118839, 118840
 3549+ * Apply check: add 120376, 120377, 120378, 120379, 124689, 124690
 3550+ * Apply check: add 116338, 116339, 118383, 118384, 125698, 125699
 3551+ * Apply check: add 111857, 114176
 3552+ * Apply check: add 119380, 119381
 3553+ * Update list of contributors
 3554+
 3555+=head2 Version 20080311-01
 3556+
 3557+ * Try to download patchdiag.xref from alternative sunsolve URL
 3558+ * Add hack to make pca work with wget v1.11
 3559+ * Add new format specifiers for patch count (%n) and total patches (%t)
 3560+ * Show info header even if no patches are listed
 3561+ * Fix bug with pca not finding its configuration file in some cases
 3562+ * Fix warning about use of uninitialized value
 3563+ * Documentation: Add information about mailing lists
 3564+ * Whitelist: add 127718
 3565+ * Apply check: add 127719
 3566+ * Update list of contributors
 3567+
 3568+=head2 Version 20080131-01
 3569+
 3570+ * New option to display man page (-m/--man)
 3571+ * Make patch utilities patches appear at the top of the list
 3572+ * Create /reconfigure after patch installation if needed
 3573+ * Show correct reboot command in patch installation summary (init 6)
 3574+ * Allow specifying negative patterns with the pattern option
 3575+ * Honore ignore options even if patch IDs are specified explicitely
 3576+ * Make update option work with pcaurl set to a local caching proxy
 3577+ * Make auto update work with pca in proxy mode
 3578+ * Documentation format fixes
 3579+ * Whitelist: add 127718
 3580+ * Whitelist: remove obsolete patch
 3581+ * Apply check: add 126356, 126357
 3582+ * Apply check: add 106514, 106515, 108049, 108050, 108879, 108881
 3583+ * Apply check: add 109120, 109121, 109413, 109414, 117010, 117011
 3584+ * Apply check: add 117784, 117785, 118195, 118196, 118263, 118264
 3585+ * Apply check: add 118950, 118951, 123254, 124590
 3586+ * Apply check: add 124480, 124481, 124482
 3587+ * Update list of contributors
 3588+
 3589+=head2 Version 20080109-01
 3590+
 3591+ * Read /etc/pca-proxy.conf and ./pca-proxy.conf in proxy mode
 3592+ * New option to specify additional configuration files (--cffile=FILE)
 3593+ * Allow negating boolean options on the command line with --no-option
 3594+ * Documentation: Add information about the Timeout option in Apache
 3595+ * Whitelist: add 125082, 125890, 128624
 3596+ * Whitelist: remove obsolete patch
 3597+ * Apply check: add SAM-FS and QFS patches
 3598+
 3599+Use /etc/pca-proxy.conf or pca-proxy.conf in the same directory as
 3600+the CGI script from now on. For downward compatibility pca-proxy.cgi
 3601+will still read pca.conf in the standard locations, but this is
 3602+deprecated.
 3603+
 3604+=head2 Version 20071214-01
 3605+
 3606+ * Enhance check for circular patch dependencies
 3607+ * Enhance error handling
 3608+ * Update list of contributors
 3609+ * Whitelist: add 118717, 127111, 127112
 3610+
 3611+=head2 Version 20071115-01
 3612+
 3613+ * Streamline patch downloads from local caching proxy
 3614+ * More verbose output during downloads and patch installation
 3615+ * Complete code redesign of all download functions
 3616+ * Try to download patchdiag.xref from sunsolve without SOA data
 3617+ * Always keep backup copy of patchdiag.xref before trying download
 3618+ * Use downloaded copy of patchdiag.xref if date is equal to backup copy
 3619+ * Always check if downloaded copy of xref file is newer than local copy
 3620+ * Restore backup copy of patchdiag.xref if pca is interrupted
 3621+ * Correctly handle SOA password with # sign in configuration file
 3622+ * Add trailing slash to download URL if missing
 3623+ * Show wget version in debug output
 3624+ * Fix bug when installing patch using previously unpacked patch directory
 3625+ * Change implementation of file type recognition
 3626+ * Fix perl warning
 3627+ * Update list of contributors
 3628+
 3629+Attention: There has been a change in the protocol used to download
 3630+patches from the local caching proxy. Make sure to update both pca
 3631+and pca-proxy.cgi.
 3632+
 3633+The output format of pca has been changed to give more detailed information
 3634+about download sources and patch installation.
 3635+
 3636+SunSolve has changed in a way that download of the patchdiag.xref file
 3637+is possible without a Sun Online Account again. pca now tries both with
 3638+and without SOA.
 3639+
 3640+=head2 Version 20071023-01
 3641+
 3642+ * Change nobackup option to accept a patch ID or "all" as argument
 3643+ * Preserve local copy of patchdiag.xref if sunsolve copy is older
 3644+ * New option to set sunsolve hostname or IP address (--sshost=HOST)
 3645+ * Honor setting of nocheckxref option in proxy mode
 3646+ * Detect absolute path names for xrefdir/patchdir under Windows
 3647+ * Use "--execute cache=off" instead of "--cache=off" with nocache
 3648+ * Show patchadd command in debug output
 3649+ * Fix debug message shown when safe option is used
 3650+ * Remove temporary entry for 126509 which was missing from xref
 3651+ * Apply check: add 123202
 3652+ * Apply check: add 123827, 123828, 123829
 3653+ * Apply check: add 125760, 125761, 125762
 3654+
 3655+The nobackup option requires an argument now. Instead of --nobackup
 3656+use --nobackup=all, or specify selected (e.g. large) patches in
 3657+multiple nobackup options.
 3658+
 3659+SunSolve is sometimes delivering out-of-date versions of the
 3660+patchdiag.xref file. pca now checks the date in the header and
 3661+will not overwrite a more recent local copy with an outdated file
 3662+from SunSolve.
 3663+
 3664+=head2 Version 20071011-02
 3665+
 3666+ * A Sun Online Account is needed to access patchdiag.xref
 3667+ * Modify askauth to ask for authentication data when needed
 3668+ * Use same URL for patch README in HTML output with and without SOA
 3669+ * Fix bug with update showing old list of changes
 3670+ * Fix typo in reconfiguration message
 3671+ * Documentation: Clarify information about ignore option
 3672+ * Whitelist: add 121429, 119757, 119758, 125367, 125368
 3673+ * Whitelist: modify 121428
 3674+
 3675+With a recent change to SunSolve, a Sun Online Account is now required
 3676+to allow pca downloading the patchdiag.xref file. The askauth option
 3677+has been modified to ask for the Sun Online Account in that case, too.
 3678+
 3679+=head2 Version 20071005-03
 3680+
 3681+ * Add documentation about using pca in a jumpstart finish script
 3682+ * Whitelist: modify 120011
 3683+ * Fix small bug with installing Sun Studio 11 patches on Solaris 10
 3684+
 3685+=head2 Version 20071002-01
 3686+
 3687+ * New option to allow pca to update itself (--update=TYPE)
 3688+ * Code cleanup and performance enhancements
 3689+ * Deliver files in proxy mode directly without HTTP redirect
 3690+ * Write debug output to file in proxy mode
 3691+ * Fix a problem with timelocal when running pca under Windows
 3692+ * Fix obscure bug with --safe option and pathnames with commas
 3693+ * Add option to stop after a specified patch ID (--stop=ID)
 3694+ * Allow patch proxy to download files from another patch proxy
 3695+ * Revert to old mechanism of specifying user and password with wget
 3696+ * Include documentation in pca
 3697+ * Handle relative path names in xrefdir correctly
 3698+ * Whitelist: add 120011, 125369, 120012, 125216, 125370
 3699+ * Whitelist: modify 118833
 3700+ * Whitelist: remove obsolete patches
 3701+ * Apply check: add 125950, 125951, 125952, 125953
 3702+ * Apply check: add 121430, 121431, 121428
 3703+ * Apply check: add 125276, 125277, 125278
 3704+ * Apply check: remove obsolete patches
 3705+
 3706+There is a new version scheme, consisting of the date in ISO format plus a
 3707+serial number. The same scheme is used for official and development releases.
 3708+A new option has been added (update=TYPE) which allows pca to check for and
 3709+install new versions of itself. See UPDATE PCA in the documentation. The
 3710+documentation is now included in POD format in pca. Use
 3711+I</usr/perl5/bin/perldoc pca> to view it.
 3712+
 3713+There are some enhancements to pca in proxy mode. You can setup a cascade
 3714+of local proxies by pointing one proxy at another. The xrefdir and patchdir
 3715+are honored now in proxy mode, so you can keep the cache directory out of
 3716+the document root of your web server and use an existing cgi-bin directory.
 3717+Debugging a proxy is simplified; when the debug option is set, debug output
 3718+will be written to a file.
 3719+
 3720+=cut
 3721+
 3722+# O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority - G2
 3723+-----BEGIN CERTIFICATE-----
 3724+MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
 3725+BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
 3726+c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
 3727+MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
 3728+emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
 3729+DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
 3730+FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
 3731+UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
 3732+YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
 3733+MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
 3734+AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
 3735+pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
 3736+13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
 3737+AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
 3738+U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
 3739+F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
 3740+oJ2daZH9
 3741+-----END CERTIFICATE-----
 3742+
 3743+# O=GTE Corporation, CN=GTE CyberTrust Global Root
 3744+-----BEGIN CERTIFICATE-----
 3745+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
 3746+VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
 3747+bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
 3748+b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
 3749+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
 3750+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
 3751+b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
 3752+iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
 3753+r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
 3754+04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
 3755+GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
 3756+3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
 3757+lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
 3758+-----END CERTIFICATE-----

Status & tagging log