102680591Sperseant#!/usr/pkg/bin/perl 2ebaf3982Sperseant# 3*11a6dbe7Smartin# $NetBSD: check-all,v 1.5 2008/04/30 13:10:52 martin Exp $ 4ebaf3982Sperseant# 5ebaf3982Sperseant# Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc. 6ebaf3982Sperseant# All rights reserved. 7ebaf3982Sperseant# 8ebaf3982Sperseant# This code is derived from software contributed to The NetBSD Foundation 9ebaf3982Sperseant# by Konrad E. Schroder <perseant@hhhh.org>. 10ebaf3982Sperseant# 11ebaf3982Sperseant# Redistribution and use in source and binary forms, with or without 12ebaf3982Sperseant# modification, are permitted provided that the following conditions 13ebaf3982Sperseant# are met: 14ebaf3982Sperseant# 1. Redistributions of source code must retain the above copyright 15ebaf3982Sperseant# notice, this list of conditions and the following disclaimer. 16ebaf3982Sperseant# 2. Redistributions in binary form must reproduce the above copyright 17ebaf3982Sperseant# notice, this list of conditions and the following disclaimer in the 18ebaf3982Sperseant# documentation and/or other materials provided with the distribution. 19ebaf3982Sperseant# 20ebaf3982Sperseant# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21ebaf3982Sperseant# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22ebaf3982Sperseant# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23ebaf3982Sperseant# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24ebaf3982Sperseant# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25ebaf3982Sperseant# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26ebaf3982Sperseant# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27ebaf3982Sperseant# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28ebaf3982Sperseant# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29ebaf3982Sperseant# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30ebaf3982Sperseant# POSSIBILITY OF SUCH DAMAGE. 31ebaf3982Sperseant# 3202680591Sperseant 3302680591Sperseant# 3402680591Sperseant# Use dumplfs to find all locations of the Ifile inode on a given disk. 3502680591Sperseant# Order these by serial number and call fsck_lfs on the raw disk for each. 36ebaf3982Sperseant# If any fsck gives errors (any line of all capital letters, with a few 37ebaf3982Sperseant# exceptions) print an error code with the daddr of the failing Ifile inode 38ebaf3982Sperseant# location. 3902680591Sperseant# 4002680591Sperseant 417cd0266aSperseant$| = 1; 4202680591Sperseant$rdev = $ARGV[0]; 437cd0266aSperseant$gfile = $ARGV[1]; 447cd0266aSperseant$wfile = $ARGV[2]; 457cd0266aSperseant$sstart = $ARGV[3]; 46b8eed869Sperseant$test_rfw = 1; # $ARGV[4]; 4702680591Sperseant$rollid = 0; 4802680591Sperseantopen(DUMPLFS, "dumplfs $rdev |"); 4902680591Sperseant 5002680591Sperseant# Look for "roll_id" so we don't use garbage 5102680591Sperseantwhile (<DUMPLFS>) { 527cd0266aSperseant if ($ssize == 0 && m/ssize *([0-9]*)/) { 537cd0266aSperseant $ssize = $1; 547cd0266aSperseant } 557cd0266aSperseant if ($fsize == 0 && m/fsize *([0-9]*)/) { 567cd0266aSperseant $fsize = $1; 577cd0266aSperseant } 5802680591Sperseant if (m/roll_id *([x0-9a-f]*)/) { 5902680591Sperseant $rollid = $1; 6002680591Sperseant last; 6102680591Sperseant } 6202680591Sperseant} 6302680591Sperseant 6402680591Sperseant# Now look for inodes and segment summaries. Build a hash table of these 6502680591Sperseant# based on serial number. Ignore any with serial numbers lower than $sstart. 6602680591Sperseant 6702680591Sperseant%iloc = (); 687cd0266aSperseant%snloc = (); 697cd0266aSperseant%sumloc = (); 707cd0266aSperseantprint "Reading segments:"; 7102680591Sperseantwhile (<DUMPLFS>) { 7202680591Sperseant if (m/roll_id *([0-9a-f]*)/) { 7302680591Sperseant # print "rollid $1\n"; 7402680591Sperseant if ("0x$1" ne $rollid) { 7502680591Sperseant # Skip the rest of this segment 76ebaf3982Sperseant print "{skip bad rollid 0x$1}"; 7702680591Sperseant while(<DUMPLFS>) { 7802680591Sperseant last if m/SEGMENT/; 7902680591Sperseant } 807cd0266aSperseant # Fall through 8102680591Sperseant } 8202680591Sperseant } 837cd0266aSperseant if (m/roll_id.*serial *([0-9]*)/) { 8402680591Sperseant $serno = $1; 857cd0266aSperseant $snloc{$serno} = $segnum; 867cd0266aSperseant $sumloc{$serno} = $sumloc; 87ebaf3982Sperseant print "($serno)"; 8802680591Sperseant if ($serno < $sstart) { 8902680591Sperseant # Skip the rest of this partial segment 90ebaf3982Sperseant #print "{skip bad serno $serno}"; 9102680591Sperseant while(<DUMPLFS>) { 927cd0266aSperseant last if m/Segment Summary/ || 937cd0266aSperseant m/SEGMENT/; 9402680591Sperseant } 957cd0266aSperseant # Fall through 9602680591Sperseant } 9702680591Sperseant } 987cd0266aSperseant if (m/Segment Summary Info at 0x([0-9a-f]*)/) { 997cd0266aSperseant $sumloc = $1; 100ebaf3982Sperseant next; 1017cd0266aSperseant } 10202680591Sperseant if (m/0x([0-9a-f]*)/) { 10302680591Sperseant foreach $ss (split "0x", $_) { 10402680591Sperseant if ($ss =~ m/^([0-9a-f][0-9a-f]*)/) { 10502680591Sperseant # print "iblk 0x$1\n"; 10602680591Sperseant $daddr = $1; 10702680591Sperseant if (m/[^0-9]1v1/) { 10802680591Sperseant # print "** ifblk 0x$daddr\n"; 10902680591Sperseant $iloc{$serno} = $daddr; 1107cd0266aSperseant $lastaddr = $daddr; 11102680591Sperseant } 11202680591Sperseant } 11302680591Sperseant } 11402680591Sperseant } 1157cd0266aSperseant if (m/SEGMENT *([0-9]*)/) { 1167cd0266aSperseant $segnum = $1; 117ebaf3982Sperseant print "[$segnum]"; 11802680591Sperseant } 11902680591Sperseant} 1207cd0266aSperseantprint "\n"; 12102680591Sperseantclose(DUMPLFS); 12202680591Sperseant 123ebaf3982Sperseant# Complain about missing partial-segments 124ebaf3982Sperseantfor ($i = $sstart; $i < $serno; ++$i) { 125ebaf3982Sperseant if (hex $sumloc{$i} == 0 && $i > 0) { 126ebaf3982Sperseant print "Oops, couldn't find pseg $i\n"; 127ebaf3982Sperseant } 128ebaf3982Sperseant} 129ebaf3982Sperseant 1307cd0266aSperseant# If there were no checkpoints, print *something* 1317cd0266aSperseantif ($#iloc == 0) { 1327cd0266aSperseant print "0 $sstart 0\n"; 1337cd0266aSperseant exit 0; 1347cd0266aSperseant} 1357cd0266aSperseant 13602680591Sperseant# 13702680591Sperseant# Now fsck each checkpoint in turn, beginning with $sstart. 1387cd0266aSperseant# Because the log wraps we will have to reconstruct the filesystem image 1397cd0266aSperseant# as it existed at each checkpoint before running fsck. 1407cd0266aSperseant# 14102680591Sperseant# Look for lines containing only caps or "!", but ignore known 14202680591Sperseant# false positives. 14302680591Sperseant# 14402680591Sperseant$error = 0; 1457cd0266aSperseant$lastgood = $sstart - 1; 14602680591Sperseantopen(LOG, ">>check-all.log"); 1477cd0266aSperseantprint "Available checkpoints:"; 1487cd0266aSperseantprint LOG "Available checkpoints:"; 14902680591Sperseantforeach $k (sort { $a <=> $b } keys %iloc) { 15002680591Sperseant $a = $iloc{$k}; 1517cd0266aSperseant print " $a"; 1527cd0266aSperseant print LOG " $a"; 1537cd0266aSperseant} 1547cd0266aSperseantprint "\n"; 1557cd0266aSperseantprint LOG "\n"; 1567cd0266aSperseant 157ebaf3982Sperseant# 158ebaf3982Sperseant# Copy the partial segments $_[0]--$_[1] from the raw device onto 159ebaf3982Sperseant# the working file. Return the next partial-segment serial number 160ebaf3982Sperseant# after the last one we copied (usually $_[1] + 1, except in case of 161ebaf3982Sperseant# an error). 162ebaf3982Sperseant# 163ebaf3982Sperseantsub copypseg 164ebaf3982Sperseant{ 165ebaf3982Sperseant my ($blstart, $blstop, $segstop, $cmd); 166ebaf3982Sperseant my ($totalstart, $totalstop); 167ebaf3982Sperseant 168ebaf3982Sperseant $totalstart = 0; 169ebaf3982Sperseant $totalstop = 0; 170ebaf3982Sperseant for ($i = $_[0]; $i <= $_[1]; ++$i) { 171ebaf3982Sperseant $blstart = hex $sumloc{$i}; 172ebaf3982Sperseant last if $blstart <= 0; 173ebaf3982Sperseant $totalstart = $blstart if $totalstart == 0; 174ebaf3982Sperseant $blstop = hex $sumloc{$i + 1}; 175ebaf3982Sperseant $segstop = ((int ($blstart / $fps)) + 1) * $fps; 176ebaf3982Sperseant if ($segstop < $blstop || $blstop < $blstart) { 177ebaf3982Sperseant #print "Adjusting $blstop -> $segstop\n"; 178ebaf3982Sperseant $blstop = $segstop; 179ebaf3982Sperseant } 180ebaf3982Sperseant $totalstop = $blstop; 181ebaf3982Sperseant 182b8eed869Sperseant print "pseg $i: write blocks ", hex $blstart, "-", hex ($blstop - 1), "\n"; 183ebaf3982Sperseant $blstart = $blstop; 184ebaf3982Sperseant } 185ebaf3982Sperseant $cmd = "dd if=$rdev of=$wfile bs=$fsize seek=$totalstart " . 186ebaf3982Sperseant "skip=$totalstart conv=notrunc count=" . 187ebaf3982Sperseant ($totalstop - $totalstart); 188ebaf3982Sperseant# print "$cmd\n"; 189ebaf3982Sperseant system("$cmd >/dev/null 2>&1"); 190ebaf3982Sperseant 191ebaf3982Sperseant return $i; 192ebaf3982Sperseant} 193ebaf3982Sperseant 1947cd0266aSperseantprint "Recreating filesystem image as of $sstart:\n"; 1957cd0266aSperseantif ($sstart == 0) { 1967cd0266aSperseant $cmd = "dd if=$rdev of=$wfile bs=1m conv=swab,oldebcdic"; # garbage 1977cd0266aSperseant} else { 1987cd0266aSperseant $cmd = "dd if=$gfile of=$wfile bs=1m"; 1997cd0266aSperseant} 2007cd0266aSperseantprint "$cmd\n"; 2017cd0266aSperseantsystem("$cmd >/dev/null 2>&1"); 2027cd0266aSperseant 203ebaf3982Sperseantprint "Copying over first superblock\n"; 204ebaf3982Sperseantsystem("dd if=$rdev of=$wfile bs=8k count=2 conv=notrunc >/dev/null 2>&1"); 205ebaf3982Sperseant 206b8eed869Sperseantsub test_fsck 207b8eed869Sperseant{ 208b8eed869Sperseant my $a = $_[0]; 209b8eed869Sperseant my $flags = $_[1]; 210b8eed869Sperseant my $printit = $_[2]; 211b8eed869Sperseant my $output = ""; 2127cd0266aSperseant 213b8eed869Sperseant $flags = "-n -f -i 0x$a $wfile" unless $flags; 2147cd0266aSperseant 215b8eed869Sperseant $cmd = "fsck_lfs $flags"; 2167cd0266aSperseant print "$cmd\n"; 2177cd0266aSperseant print LOG "$cmd\n"; 2187cd0266aSperseant open(FSCK, "$cmd 2>&1 |"); 21902680591Sperseant while(<FSCK>) { 22002680591Sperseant print LOG; 221b8eed869Sperseant $rline = "$_"; 22202680591Sperseant chomp; 22302680591Sperseant 22402680591Sperseant # Known false positives (mismatch between sb and ifile, 22502680591Sperseant # which should be expected given we're using an arbitrarily 226ebaf3982Sperseant # old version of the ifile) 22702680591Sperseant if (m/AVAIL GIVEN/ || 22802680591Sperseant m/BFREE GIVEN/ || 22902680591Sperseant m/DMETA GIVEN/ || 2307cd0266aSperseant m/NCLEAN GIVEN/ || 2317cd0266aSperseant m/FREE BUT NOT ON FREE LIST/ || # UNWRITTEN inodes OK 232b8eed869Sperseant m/FILE SYSTEM WAS MODIFIED/ || 2337cd0266aSperseant m/FREE LIST HEAD IN SUPERBLOCK/ ) { 23402680591Sperseant next; 23502680591Sperseant } 23602680591Sperseant 23702680591Sperseant # Fsck reports errors in ALL CAPS 23802680591Sperseant # But don't count hex numbers as "lowercase". 239b8eed869Sperseant $oline = "$_"; 24002680591Sperseant s/0x[0-9a-f]*//g; 24102680591Sperseant if (m/[A-Z]/ && ! m/[a-z]/) { 24202680591Sperseant $error = 1; 2437cd0266aSperseant $errsn = $k; 244b8eed869Sperseant $errstr = "1 $k 0x$a $oline"; 245b8eed869Sperseant # last; 24602680591Sperseant } 247b8eed869Sperseant 248b8eed869Sperseant # Log everything we get, except for some things we 249b8eed869Sperseant # will see every single time. 250b8eed869Sperseant if (m/checkpoint invalid/ || 251b8eed869Sperseant m/skipping free list check/ || 252b8eed869Sperseant m/expect discrepancies/) { 253b8eed869Sperseant next; 254b8eed869Sperseant } 255b8eed869Sperseant $output .= $rline; 25602680591Sperseant } 25702680591Sperseant close(FSCK); 258b8eed869Sperseant 259b8eed869Sperseant if ($? != 0) { 260b8eed869Sperseant $error = 1; 261b8eed869Sperseant $errsn = $k; 262b8eed869Sperseant $errstr = "1 $k 0x$a <" . (hex $?) . ">"; 263b8eed869Sperseant } 264b8eed869Sperseant 265b8eed869Sperseant if ($error || $printit) { 266b8eed869Sperseant print $output; 267b8eed869Sperseant } 268b8eed869Sperseant} 269b8eed869Sperseant 270b8eed869Sperseant$blstart = 0; 271b8eed869Sperseant$fps = $ssize / $fsize; 272b8eed869Sperseant$oind = ($sstart ? $sstart : 1); 273b8eed869SperseantBIGLOOP: foreach $k (sort { $a <=> $b } keys %iloc) { 274b8eed869Sperseant $a = $iloc{$k}; 275b8eed869Sperseant 276b8eed869Sperseant if (hex($a) > hex($lastaddr)) { 277b8eed869Sperseant print "Skipping out-of-place checkpoint $k at $a\n"; 278b8eed869Sperseant next; 279b8eed869Sperseant } 280b8eed869Sperseant 281b8eed869Sperseant if ($test_rfw && $iloc{$oind - 1}) { 282b8eed869Sperseant for ($tk = $oind; $tk < $k; $tk++) { 283b8eed869Sperseant print "Test roll-forward agent at non-checkpoint pseg $tk\n"; 284b8eed869Sperseant print LOG "Test roll-forward agent at non-checkpoint pseg $tk\n"; 285b8eed869Sperseant ©pseg($oind, $tk); 286b8eed869Sperseant # Add -d flag here for verbose debugging info 287b8eed869Sperseant $flags = "-p -f -i 0x" . $iloc{$oind - 1} . " $wfile"; 288b8eed869Sperseant &test_fsck($iloc{$oind - 1}, $flags, 1); 289b8eed869Sperseant last BIGLOOP if $error; 290b8eed869Sperseant 291b8eed869Sperseant # note lack of -i flag, since the roll-forward 292b8eed869Sperseant # will have rewritten the superblocks. 293b8eed869Sperseant &test_fsck($iloc{$oind - 1}, "-n -f $wfile", 0); 294b8eed869Sperseant last BIGLOOP if $error; 295b8eed869Sperseant } 296b8eed869Sperseant } 297b8eed869Sperseant 298b8eed869Sperseant print "Recreate fs state at checkpoint pseg $k (from " . ($oind - 1) . 299b8eed869Sperseant ")\n"; 300b8eed869Sperseant $oind = ©pseg($oind, $k); 301b8eed869Sperseant 302b8eed869Sperseant &test_fsck($a, "", 0); 303b8eed869Sperseant 30402680591Sperseant last if $error; 3057cd0266aSperseant $lastgood = $k; # record last good serial number 3067cd0266aSperseant} 307b8eed869Sperseant 308b8eed869Sperseantif ($errstr) { 309b8eed869Sperseant print "$errstr\n"; 310b8eed869Sperseant exit 0; 311b8eed869Sperseant} 312b8eed869Sperseant 313ebaf3982Sperseantif (!$errstr) { 314ebaf3982Sperseant print "Bring filesystem state up to log wrap\n"; 315ebaf3982Sperseant $lastgood = ©pseg($oind, 100000000000) - 1; 3167cd0266aSperseant 3177cd0266aSperseant print "Copying this good image to $gfile\n"; 3187cd0266aSperseant system("dd bs=1m if=$rdev of=$gfile >/dev/null 2>&1"); 319ebaf3982Sperseant print "0 $lastgood 0x$a\n"; 3207cd0266aSperseant exit 0; 32102680591Sperseant} 32202680591Sperseant 3237cd0266aSperseant# 324ebaf3982Sperseant# Ifile write-checking paranoia. 325ebaf3982Sperseant# 3267cd0266aSperseant# If we found an error, try to find which blocks of the Ifile inode changed 3277cd0266aSperseant# between the last good checkpoint and this checkpoint; and which blocks 3287cd0266aSperseant# *should* have changed. This means (1) which segments were written; and 3297cd0266aSperseant# (2) which inodes were written. The 0 block of the Ifile should always 3307cd0266aSperseant# have changed since lfs_avail is always in flux. 3317cd0266aSperseant# 3327cd0266aSperseant 3337cd0266aSperseant$cmd = "dumplfs"; 3347cd0266aSperseant$oseg = -1; 3357cd0266aSperseant%iblk = (); 3367cd0266aSperseant%iblk_done = (); 3377cd0266aSperseant%why = (); 3387cd0266aSperseant$iblk{0} = 1; 3397cd0266aSperseantfor ($i = $lastgood + 1; $i <= $errsn; $i++) { 3407cd0266aSperseant if ($oseg != $snloc{$i}) { 3417cd0266aSperseant $oseg = 0 + $snloc{$i}; 3427cd0266aSperseant $cmd .= " -s$oseg"; 34302680591Sperseant } 3447cd0266aSperseant} 3457cd0266aSperseant$cmd .= " $rdev"; 3467cd0266aSperseant 3477cd0266aSperseantopen(DUMPLFS, "$cmd |"); 3487cd0266aSperseantwhile(<DUMPLFS>) { 3497cd0266aSperseant if (m/ifpb *([0-9]*)/) { 3507cd0266aSperseant $ifpb = $1; 3517cd0266aSperseant } 3527cd0266aSperseant if (m/sepb *([0-9]*)/) { 3537cd0266aSperseant $sepb = $1; 3547cd0266aSperseant } 3557cd0266aSperseant if (m/cleansz *([0-9]*)/) { 3567cd0266aSperseant $cleansz = $1; 3577cd0266aSperseant } 3587cd0266aSperseant if (m/segtabsz *([0-9]*)/) { 3597cd0266aSperseant $segtabsz = $1; 3607cd0266aSperseant } 3617cd0266aSperseant last if m/SEGMENT/; 3627cd0266aSperseant} 3637cd0266aSperseantwhile(<DUMPLFS>) { 3647cd0266aSperseant chomp; 3657cd0266aSperseant 3667cd0266aSperseant # Skip over partial segments outside our range of interest 3677cd0266aSperseant if (m/roll_id.*serial *([0-9]*)/) { 3687cd0266aSperseant $serno = $1; 3697cd0266aSperseant if ($serno <= $lastgood || $serno > $errsn) { 3707cd0266aSperseant # Skip the rest of this partial segment 3717cd0266aSperseant while(<DUMPLFS>) { 3727cd0266aSperseant last if m/Segment Summary/ || m/SEGMENT/; 3737cd0266aSperseant } 3747cd0266aSperseant next; 3757cd0266aSperseant } 3767cd0266aSperseant } 3777cd0266aSperseant 3787cd0266aSperseant # Look for inodes 3797cd0266aSperseant if (m/Inode addresses/) { 3807cd0266aSperseant s/^[^{]*{/ /o; 3817cd0266aSperseant s/}[^{]*$/ /o; 3827cd0266aSperseant s/}[^{]*{/,/og; 3837cd0266aSperseant s/v[0-9]*//og; 3847cd0266aSperseant @ilist = split(','); 3857cd0266aSperseant foreach $i (@ilist) { 3867cd0266aSperseant $i =~ s/ *//og; 3877cd0266aSperseant next if $i == 1; 3887cd0266aSperseant $iaddr = $cleansz + $segtabsz + int ($i / $ifpb); 3897cd0266aSperseant $iblk{$iaddr} = 1; 3907cd0266aSperseant $why{$iaddr} .= " $i"; 3917cd0266aSperseant } 3927cd0266aSperseant } 3937cd0266aSperseant 3947cd0266aSperseant # Look for Ifile blocks actually written 3957cd0266aSperseant if (m/FINFO for inode: ([0-9]*) version/) { 3967cd0266aSperseant $i = $1; 3977cd0266aSperseant $inoblkmode = ($i == 1); 3987cd0266aSperseant } 3997cd0266aSperseant if ($inoblkmode && m/^[-\t 0-9]*$/) { 4007cd0266aSperseant s/\t/ /og; 4017cd0266aSperseant s/^ *//o; 4027cd0266aSperseant s/ *$//o; 4037cd0266aSperseant @bn = split(' '); 4047cd0266aSperseant foreach $b (@bn) { 4057cd0266aSperseant $iblk_done{$b} = 1; 4067cd0266aSperseant } 4077cd0266aSperseant } 4087cd0266aSperseant} 4097cd0266aSperseantclose(DUMPLFS); 4107cd0266aSperseant 4117cd0266aSperseant# Report found and missing Ifile blocks 4127cd0266aSperseantprint "Ifile blocks found:"; 4137cd0266aSperseantforeach $b (sort { $a <=> $b } keys %iblk) { 4147cd0266aSperseant if ($iblk_done{$b} == 1) { 4157cd0266aSperseant print " $b"; 4167cd0266aSperseant } 4177cd0266aSperseant} 4187cd0266aSperseantprint "\n"; 4197cd0266aSperseant 4207cd0266aSperseantprint "Ifile blocks missing:"; 4217cd0266aSperseantforeach $b (sort { $a <=> $b } keys %iblk) { 4227cd0266aSperseant if ($iblk_done{$b} == 0) { 4237cd0266aSperseant $why{$b} =~ s/^ *//o; 4247cd0266aSperseant print " $b ($why{$b})"; 4257cd0266aSperseant } 4267cd0266aSperseant} 4277cd0266aSperseantprint "\n"; 4287cd0266aSperseant 4297cd0266aSperseantprint "$errstr\n"; 4307cd0266aSperseantexit 0; 431