1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
#!/usr/bin/perl
# SPDX-License-Identifier: 0BSD OR MIT-0
use warnings;
use strict;
use File::Temp qw(tempfile);
use Getopt::Std;
use POSIX;
sub wail { warn "nspatch: @_\n"; }
sub fail { die "nspatch: @_\n"; }
sub fale { die "nspatch: @_: $!\n"; }
sub version {
while (<DATA>) {
print if m{^=head1 VERSION} ... m{^=head1 }
and not m{^=head1 };
}
exit;
}
sub usage {
print STDERR <<EOF;
usage: nspatch [patch-opts] -- [diff-opts] -- [update-opts]
Runs `nsdiff [diff-opts] | nsupdate [update-opts]`.
Output is suppressed unless there is an error. The script
retries if it fails because of a concurrent update.
nspatch options:
-h display full documentation
-V display version information
-r count retry count (default 2)
-v turn on verbose output
EOF
exit 1;
}
my %opt;
usage unless getopts '-hVrv', \%opt;
version if $opt{V};
exec "perldoc -oterm -F $0" if $opt{h};
$opt{r} //= 2;
usage unless $opt{r} =~ m{^\d+$};
usage unless 1 == grep m{^--$}, @ARGV;
my @nsdiff = ('nsdiff', '-v', $opt{v} ? 'qr' : '');
while (@ARGV) {
my $arg = shift;
last if $arg eq '--';
push @nsdiff, $arg;
}
my @nsupdate = ('nsupdate', @ARGV);
sub slurp ($) {
my $f = shift;
open my $h, '<', $f or fale "open $f";
undef $/;
return <$h>;
}
sub dupout ($$) {
no strict 'refs';
my ($dst,$src) = @_;
open $dst, '>&', $src or fale 'dup';
}
sub mktmp {
return tempfile('nspatch.XXXXXXXXXX',
TMPDIR => 1, UNLINK => 1);
}
sub tmpf {
my $dup = shift;
my ($fh,$name) = mktmp;
dupout $dup, $fh;
return $name;
}
dupout 'XSTDOUT', 'STDOUT';
dupout 'XSTDERR', 'STDERR';
sub runtmp {
wail "running @_" if $opt{v};
my $out = tmpf 'STDOUT';
my $err = tmpf 'STDERR';
my $x = system @_;
dupout 'STDOUT', 'XSTDOUT';
dupout 'STDERR', 'XSTDERR';
return ($out, $err, $x);
}
my ($dashh,$dash) = mktmp;
print $dashh "----\n";
close $dashh;
RETRY: for (;;) {
my ($diffout,$differr,$diffx) = runtmp @nsdiff;
system 'cat', $dash, $diffout, $dash, $differr, $dash
if $opt{v} or ($diffx != 0 && $diffx != 256);
exit 0 if $diffx == 0;
fail "bad exit status from @nsdiff" if $diffx != 256;
open STDIN, '<', $diffout or fale 'open';
my ($upout,$uperr,$upx) = runtmp @nsupdate;
system 'cat', $dash, $upout, $dash, $uperr, $dash if $opt{v};
exit 0 if $upx == 0;
if (slurp($uperr) eq "update failed: NXRRSET\n" and $opt{r}--) {
wail "trying again" if $opt{v};
next RETRY;
} else {
system 'cat', $dash, $diffout, $dash, $differr,
$dash, $upout, $dash, $uperr, $dash unless $opt{v};
fail "bad exit status from @nsupdate";
}
}
__END__
=head1 NAME
nspatch - run `nsdiff | nsupdate` with error handling
=head1 SYNOPSIS
nspatch [B<-hVv>] [B<-r> I<count>]
-- [nsdiff options] -- [nsupdate options]
=head1 DESCRIPTION
The B<nspatch> utility runs `C<nsdiff | nsupdate>` and checks that
both programs complete successfully. It suppresses their output unless
there is an error, in a manner suitable for running from B<cron>.
The B<nsupdate> script produced by B<nsdiff> includes a prerequisite
check to detect and fail if there is a concurrent update. These
failures are detected by B<nspatch> which retries the update.
Rather than using a pipe, B<nspatch> uses temporary files to store the
output of B<nsdiff> and B<nsupdate>.
=head1 OPTIONS
=over
=item B<-h>
Display this documentation.
=item B<-V>
Display version information.
=item B<-r> I<count>
If the update fails because of a concurrent update, B<nspatch> will
retry up to I<count> times. The default retry I<count> is 2.
=item B<-v>
Turn on verbose mode, so the output from B<nsdiff> and B<nsupdate> is
printed even if they are successful. (By default it is suppressed.)
The verbose option is passed on to B<nsdiff>. If B<nspatch> is not
given the B<-v> option, it passes the B<-v ''> option to B<nsdiff>. If
B<nspatch> is given the B<-v> option, it passes the B<-v 'qr'> option
to B<nsdiff>.
=back
=head1 EXIT STATUS
The B<nspatch> utility returns 0 if no change is required or if the
update is successful, or 1 if there is an error.
=head1 ENVIRONMENT
=over
=item C<TMPDIR>
Location for temporary files.
=back
=head1 VERSION
This is nspatch-1.85 <https://dotat.at/prog/nsdiff/>
Written by Tony Finch <fanf2@cam.ac.uk> <dot@dotat.at>
at Cambridge University Information Services.
You may do anything with this. It has no warranty.
=head1 SEE ALSO
nsdiff(1), nsupdate(1), cron(8)
=cut
|