-
Notifications
You must be signed in to change notification settings - Fork 11
/
zetaback_agent.in
executable file
·379 lines (288 loc) · 9.18 KB
/
zetaback_agent.in
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#!/usr/bin/perl
# Copyright (c) 2007 OmniTI Computer Consulting, Inc. All rights reserved.
# For information on licensing see:
# https://labs.omniti.com/zetaback/trunk/LICENSE
use strict;
use Getopt::Long;
use POSIX qw/mkfifo/;
use Data::Dumper;
use vars qw/%conf $version_string
$PREFIX $CONF $LIST $FULL $SNAP $ZFS $BASE $RESTORE $VERSION
$BUG_6343779 $NEEDSFD $DSET/;
$version_string = '1.0.6';
$PREFIX = q^__PREFIX__^;
$CONF = qq^$PREFIX/etc/zetaback_agent.conf^;
$NEEDSFD = ($^O eq 'darwin') ? 1 : 0;
=pod
=head1 NAME
zetaback_agent - client-side component of zetaback.
=head1 SYNOPSIS
zetaback_agent -v
zetaback_agent -l [-c conf]
zetaback_agent -r [-b <timestamp>] [-c conf] [-z zfs]
zetaback -f <timestamp> [-c conf] [-z zfs]
zetaback -i <timestamp> [-c conf] [-z zfs]
zetaback -d <snap> -z <zfs> [-c conf]
=cut
GetOptions(
"c=s" => \$CONF,
"l" => \$LIST,
"r" => \$RESTORE,
"z=s" => \$ZFS,
"d=s" => \$SNAP,
"f=s" => \$FULL,
"i=s" => \$BASE,
"s=s" => \$DSET,
"b=s" => \$BUG_6343779,
"v" => \$VERSION,
);
=pod
=head1 DESCRIPTION
B<zetaback_agent> handles requests from zetaback and performs the requested
operations on a host. Normally B<zetaback_agent> is only called by
zetaback and should never need to be invoked directly.
=head1 OPTIONS
The following options are available:
=over
=item -c <conf>
Use the specified file as the configuration file. The default file, if
none is specified is /usr/local/etc/zetaback_agent.conf. The prefix of this
file may also be specified as an argument to the configure script.
=item -d <snap>
Delete the specified snapshot. Requires the use of -z to specify the
ZFS filesystem.
=item -f <timestamp>
Perform a full backup. The name of the backup will include <timestamp>,
which is provided by the backup server.
=item -i <timestamp>
Perform an incremental backup. The name of the backup will include
<timestamp>, which is provided by the backup server.
=item -s <timestamp>
Perform a dataset backup. The name of the backup will include
<timestamp>, which is provided by the backup server. This requires the -i
option to specify the base dataset the expected by the backup server.
=item -l
List ZFS filesystems.
=item -r
Perform a restore.
=item -b
When performing a restore, if -b is specified, it informs the agent that
the receive command is an incremental based of the full snapshot with the
timestamp specified. The agent will unmount and rollback the filesystem
prior to applying the incremental in order to work around bug 6343779.
=item -v
Print the version number and exit.
=item -z
Specify a ZFS filesystem to backup, restore, or delete.
=back
=cut
if($VERSION) {
print "zetaback_agent: $version_string\n";
exit 0;
}
=pod
=head1 CONFIGURATION
The zetaback_agent configuration file contains a pattern list of ZFS
filesystems to be backed up. The pattern list is a Perl-compatible
regular expression (PCRE). Only one 'pattern=' line is permitted.
The pattern acts as a filter to reduce the list of filesystems to
be backed up. Further excludes from this list are possible by setting
a user property on any filesystem that should not be backed up, even
if it matches the pattern:
zfs set com.omniti.labs.zetaback:exclude=on pool/fs
User properties are available on Solaris 10 8/07 and newer, and on
Solaris Express build 48 and newer.
Once a pattern and/or exclude properties have been configured on a host,
the list of remaining filesystems can be validated by invoking
zetaback_agent with the -l option.
=head2 Excluding inactive boot environments
The zetaback_agent configuration file also has an option to not back up filesystems that are part of an alternate/inactive boot environment. To enable this option, add the following to the configuration file:
exclude_inactive_be=1
=head1 CONFIGURATION EXAMPLES
=head2 All ZFS filesystems
This pattern matches all ZFS filesystems.
pattern=.
=head2 Substring match
This will match anywhere in the name of the ZFS filesystem. This is
helpful for catching all ZFS filesystems in a particular zpool, while
excluding any others.
pattern=zones
=head2 Left-anchored names
This pattern matches all ZFS filesystems whose names begin with 'www'.
pattern=^www
=head2 Specific ZFS filesystems
This pattern matches specific ZFS filesystems.
pattern=(?:data|mirrors|www)
=head2 Combining with property-based exclude
All filesystems in pool 'zones' except 'foo'
pattern=^zones
(At a root shell or with pfexec/sudo):
zfs set com.omniti.labs.zetaback:exclude=on zones/foo
=cut
# Read our config in
$conf{pattern} = '.';
$conf{exclude_inactive_be} = '0';
open(CONF, "<$CONF");
while(<CONF>) { /^\s*([^#](?:\S*)?)\s*=\s*(\S+)/ && ($conf{lc($1)} = $2); }
close(CONF);
sub zfs_agent_remove_snap {
my $target = $ZFS . '@';
die "zfs_agent_remove_snap: insufficient args\n" unless($ZFS && $SNAP);
if($SNAP eq '__zb_incr' or
$SNAP =~ /__zb_full_\d+/ or
$SNAP =~ /__zb_dset_\d+/) {
$target .= $SNAP;
}
else {
die "zfs_agent_remove_snap: illegal snap: $SNAP\n";
}
`__ZFS__ destroy $target`;
}
sub zfs_agent_perform_full {
my $target = $ZFS . '@__zb_full_' . $FULL;
unless($ZFS && $FULL =~ /^\d+$/) {
die "zfs_agent_perform_full: bad fs or snap name\n"
}
`__ZFS__ snapshot $target`;
my @cmd = ("__ZFS__", "send", $target);
if($NEEDSFD) {
fifo_exec(@cmd);
} else {
exec { $cmd[0] } @cmd;
}
exit;
}
sub zfs_agent_perform_incremental {
my $target = $ZFS . '@__zb_incr';
my $base = $ZFS . '@__zb_full_' . $BASE;
unless($ZFS && $BASE) {
die "zfs_agent_perform_incremental: bad args\n"
}
`__ZFS__ snapshot $target`;
my @cmd = ("__ZFS__", "send", "-i", $base, $target);
if($NEEDSFD) {
fifo_exec(@cmd);
} else {
exec { $cmd[0] } @cmd;
}
exit;
}
sub zfs_agent_perform_dataset {
my $target = $ZFS . '@__zb_dset_' . $DSET;
my $base = $ZFS . '@__zb_dset_' . $BASE;
unless($ZFS && $DSET) {
die "zfs_agent_perform_dataset: bad args\n"
}
`__ZFS__ snapshot $target`;
# $BASE (the base snapshot) is optional. If provided, send an incremental
# snapshot
my @cmd;
if ($BASE) {
@cmd = ("__ZFS__", "send", "-i", $base, $target);
} else {
@cmd = ("__ZFS__", "send", $target);
}
if($NEEDSFD) {
fifo_exec(@cmd);
} else {
exec { $cmd[0] } @cmd;
}
exit;
}
sub zfs_agent_list {
my (%zfs, %storageclass);
open(ZFSLIST, "__ZFS__ list -H -t snapshot,filesystem,volume -o name,com.omniti.labs.zetaback:exclude,com.omniti.labs.zetaback:class,org.opensolaris.libbe:parentbe,org.opensolaris.libbe:uuid |");
# Get the UUID (if any) of the current BE, should return blank on systems
# where beadm isn't present
my $currentbe = "";
if($conf{exclude_inactive_be} eq '1') {
$currentbe = (split(/;/,`/sbin/beadm list -H 2>&1 | grep ';N'`))[1];
}
while(<ZFSLIST>) {
chomp;
my @line = split /\t/;
(my $fs = $line[0]) =~ s/\@.+//;
my $excl = $line[1];
my $class = $line[2];
my $parentbe = $line[3];
my $beuuid = $line[4];
if($conf{exclude_inactive_be} eq '1') {
next if ($parentbe ne '-' && $parentbe ne $currentbe);
next if ($beuuid ne '-' && $beuuid ne $currentbe);
}
if(($excl ne "on") && ($fs =~ /$conf{pattern}/)) {
if($line[0] =~ /(\S+)\@([^\@]+)$/) {
$zfs{$1} ||= [];
push @{$zfs{$1}}, $2;
if ($class ne "-" && $class ne "") {
$storageclass{$1} = $class;
}
}
else {
$zfs{$line[0]} ||= [];
if ($class ne "-" && $class ne "") {
$storageclass{$line[0]} = $class;
}
}
}
}
close(ZFSLIST);
foreach my $fs (sort keys %zfs) {
print "$fs [".join(',',@{$zfs{$fs}})."]";
if ($storageclass{$fs} ne "") {
print " {$storageclass{$fs}}";
}
print "\n";
}
}
sub zfs_agent_perform_restore {
unless($ZFS && $RESTORE) {
die "zfs_agent_perform_restore: bad state\n";
}
if($BUG_6343779) {
# Optionally work around Solaris bug: 6343779
my $base = $ZFS . '@__zb_full_' . $BUG_6343779;
`__ZFS__ unmount $ZFS`;
`__ZFS__ rollback $base`;
}
my @cmd = ("__ZFS__", "recv", $ZFS);
exec { $cmd[0] } @cmd;
exit;
}
sub fifo_exec {
my @cmd = @_;
my $rv = -1;
my $fifo = "zetaback_${$}_${FULL}${BASE}.fifo";
mkfifo($fifo, 0600) || die "Could not create fifo: $!";
my $pid = fork();
if($pid == 0) {
close(STDOUT);
open(STDOUT, ">$fifo") || die "Could not open fifo: $!";
exec { $cmd[0] } @cmd;
exit;
}
open(FIFO, "<$fifo");
unlink($fifo);
my $buf;
while(my $len = sysread(FIFO, $buf, 1024*64)) {
syswrite(STDOUT, $buf, $len);
}
waitpid($pid, 0);
}
if($LIST) { zfs_agent_list(); exit; }
if($ZFS && $SNAP) { zfs_agent_remove_snap(); exit; }
if($ZFS && $RESTORE) { zfs_agent_perform_restore(); exit; }
if($ZFS && $FULL) { zfs_agent_perform_full(); exit; }
if($ZFS && $DSET) { zfs_agent_perform_dataset(); exit; }
if($ZFS && $BASE) { zfs_agent_perform_incremental(); exit; }
=pod
=head1 FILES
=over
=item zetaback_agent.conf
The zetaback_agent configuration file. The location of the file can be
specified on the command line with the -c flag. The prefix of this
file may also be specified as an argument to the configure script.
=back
=head1 SEE ALSO
zetaback(1)
=cut