File: nspatch

package info (click to toggle)
nsdiff 1.85-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 128 kB
  • sloc: perl: 409; sh: 5; makefile: 3
file content (191 lines) | stat: -rwxr-xr-x 4,538 bytes parent folder | download
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