/[psinib]/psinib.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /psinib.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.18 - (show annotations)
Mon Oct 27 18:58:41 2003 UTC (20 years, 6 months ago) by dpavlin
Branch: MAIN
Changes since 1.17: +8 -5 lines
File MIME type: text/plain
spit fatal errors on screen even if quiet (use -q -q to supress all output)

1 #!/usr/bin/perl -w
2 #
3 # psinib - Perl Snapshot Is Not Incremental Backup
4 #
5 # written by Dobrica Pavlinusic <dpavlin@rot13.org> 2003-01-03
6 # released under GPL v2 or later.
7 #
8 # Backup SMB directories using file produced by LinNeighbourhood (or some
9 # other program [vi :-)] which produces file in format:
10 #
11 # smbmount service mountpoint options
12 #
13 #
14 # usage:
15 # $ psinib.pl mountscript
16
17 use strict 'vars';
18 use Data::Dumper;
19 use Net::Ping;
20 use POSIX qw(strftime);
21 use List::Compare;
22 use Filesys::SmbClient;
23 #use Taint;
24 use Fcntl qw(LOCK_EX LOCK_NB);
25 use Digest::MD5;
26 use File::Basename;
27 use Getopt::Long;
28
29 # configuration
30 my $LOG_TIME_FMT = '%Y-%m-%d %H:%M:%S'; # strftime format for logfile
31 my $DIR_TIME_FMT = '%Y%m%d'; # strftime format for backup dir
32
33 # define timeout for ping
34 my $PING_TIMEOUT = 5;
35
36 my $LOG = '/var/log/backup.log'; # add path here...
37 #$LOG = '/tmp/backup.log';
38
39 # store backups in which directory
40 my $BACKUP_DEST = '/backup/isis_backup';
41 #my $BACKUP_DEST = '/tmp/backup/';
42
43 # files to ignore in backup
44 my @ignore = ('.md5sum', '.backupignore', 'backupignore.txt');
45
46 # open log
47 open(L, ">> $LOG") || die "can't open log $LOG: $!";
48 select((select(L), $|=1)[0]); # flush output
49
50 # make a lock on logfile
51
52 my $c = 0;
53 {
54 flock L, LOCK_EX | LOCK_NB and last;
55 sleep 1;
56 redo if ++$c < 10;
57 # no response for 10 sec, bail out
58 xlog("ABORT","can't take lock on $LOG -- another $0 running?");
59 exit 1;
60 }
61
62 # taint path: nmblookup should be there!
63 $ENV{'PATH'} = "/usr/bin:/bin";
64
65 my $use_ping = 1; # default: use syn tcp ping to verify that host is up
66 my $verbose = 1; # default verbosity level
67 my $quiet = 0;
68
69 my $result = GetOptions(
70 "ping!" => \$use_ping, "backupdest!" => \$BACKUP_DEST,
71 "verbose+" => \$verbose, "quiet+" => \$quiet,
72 );
73
74 $verbose -= $quiet;
75
76 my $mounts = shift @ARGV ||
77 'mountscript';
78 # die "usage: $0 mountscript";
79
80
81 my @in_backup; # shares which are backeduped this run
82
83 # init Net::Ping object
84 my $ping;
85 if ($use_ping) {
86 $ping = new Net::Ping->new("syn", 2);
87 # ping will try tcp connect to netbios-ssn (139)
88 $ping->{port_num} = getservbyname("netbios-ssn", "tcp");
89 }
90
91 # do syn ping to cifs port
92 sub host_up {
93 my $ping = shift || return;
94 my $host_ip = shift || xlog("host_up didn't get IP");
95 my $timeout = shift;
96 return 1 if (! $use_ping);
97
98 $ping->ping($host_ip,$timeout);
99 my $return = 0;
100
101 while (my ($host,$rtt,$ip) = $ping->ack) {
102 xlog("","HOST: $host [$ip] ACKed in $rtt seconds");
103 $return = 1 if ($ip eq $host_ip);
104 }
105 return $return;
106 }
107
108 my $backup_ok = 0;
109
110 my $smb;
111 my %smb_atime;
112 my %smb_mtime;
113 my %file_md5;
114
115 open(M, $mounts) || die "can't open $mounts: $!";
116 while(<M>) {
117 chomp;
118 next if !/^\s*smbmount\s/;
119 my (undef,$share,undef,$opt) = split(/\s+/,$_,4);
120
121 my ($user,$passwd,$workgroup,$ip);
122
123 foreach (split(/,/,$opt)) {
124 my ($n,$v) = split(/=/,$_,2);
125 if ($n =~ m/username/i) {
126 if ($v =~ m#^(.+)/(.+)%(.+)$#) {
127 ($user,$passwd,$workgroup) = ($1,$2,$3);
128 } elsif ($v =~ m#^(.+)/(.+)$#) {
129 ($user,$workgroup) = ($1,$2);
130 } elsif ($v =~ m#^(.+)%(.+)$#) {
131 ($user,$passwd) = ($1,$2);
132 } else {
133 $user = $v;
134 }
135 } elsif ($n =~ m#workgroup#i) {
136 $workgroup = $v;
137 } elsif ($n =~ m#ip#i) {
138 $ip = $v;
139 }
140 }
141
142 push @in_backup,$share;
143
144
145 my ($host,$dir,$date_dir) = share2host_dir($share);
146 my $bl = "$BACKUP_DEST/$host/$dir/latest"; # latest backup
147 my $bc = "$BACKUP_DEST/$host/$dir/$date_dir"; # current one
148 my $real_bl;
149 if (-l $bl) {
150 $real_bl=readlink($bl) || die "can't read link $bl: $!";
151 $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
152 if (-l $bc && $real_bl eq $bc) {
153 xlog($share,"allready backuped...");
154 $backup_ok++;
155 next;
156 }
157
158 }
159
160
161 xlog($share,"working on $share...");
162
163 # try to nmblookup IP
164 $ip = get_ip($share) if (! $ip);
165
166 if ($ip) {
167 xlog($share,"IP is $ip");
168 if (host_up($ping, $ip,$PING_TIMEOUT)) {
169 if (snap_share($share,$user,$passwd,$workgroup)) {
170 $backup_ok++;
171 }
172 }
173 }
174 }
175 close(M);
176
177 xlog("","$backup_ok backups completed of total ".($#in_backup+1)." this time (".int($backup_ok*100/($#in_backup+1))." %)");
178
179 1;
180
181 #-------------------------------------------------------------------------
182
183
184 # get IP number from share
185 sub get_ip {
186 my $share = shift;
187
188 my $host = $1 if ($share =~ m#//([^/]+)/#);
189
190 my $ip = `nmblookup $host`;
191 if ($ip =~ m/(\d+\.\d+\.\d+\.\d+)\s$host/i) {
192 return $1;
193 }
194 }
195
196
197 # write entry to screen and log
198 sub xlog {
199 my $share = shift;
200 my $t = strftime $LOG_TIME_FMT, localtime;
201 my $m = shift || '[no log entry]';
202 my $l = shift;
203 $l = 1 if (! defined $l); # default verbosity is 1
204 print STDERR $m,"\n" if ($verbose >= $l);
205 print L "$t $share\t$m\n";
206 }
207
208 # dump warn and dies into log
209 BEGIN { $SIG{'__WARN__'} = sub { xlog('WARN',$_[0],1) ; warn $_[0] } }
210 BEGIN { $SIG{'__DIE__'} = sub { xlog('DIE',$_[0],0) ; die $_[0] } }
211
212
213 # split share name to host, dir and currnet date dir
214 sub share2host_dir {
215 my $share = shift;
216 my ($host,$dir);
217 if ($share =~ m#//([^/]+)/(.+)$#) {
218 ($host,$dir) = ($1,$2);
219 $dir =~ s/\W/_/g;
220 $dir =~ s/^_+//;
221 $dir =~ s/_+$//;
222 } else {
223 xlog($share,"Can't parse share $share into host and directory!",1);
224 return;
225 }
226 return ($host,$dir,strftime $DIR_TIME_FMT, localtime);
227 }
228
229
230 # make a snapshot of a share
231 sub snap_share {
232
233 my $share = shift;
234
235 my %param = ( debug => 0 );
236
237 $param{username} = shift || warn "can't find username for share $share";
238 $param{password} = shift || warn "can't find passwod for share $share";
239 $param{workgroup} = shift || warn "can't find workgroup for share $share";
240
241 my ($host,$dir,$date_dir) = share2host_dir($share);
242
243 # latest backup directory
244 my $bl = "$BACKUP_DEST/$host/$dir/latest";
245 # current backup directory
246 my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";
247
248 my $real_bl;
249 if (-l $bl) {
250 $real_bl=readlink($bl) || die "can't read link $bl: $!";
251 $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
252 undef $real_bl if (! -e $real_bl);
253 }
254 if (! $real_bl) {
255 xlog($share,"no old backup, trying to find last backup,");
256 if (opendir(BL_DIR, "$BACKUP_DEST/$host/$dir")) {
257 my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR);
258 closedir(BL_DIR);
259 $real_bl=pop @bl_dirs;
260 xlog($share,"using $real_bl as latest...");
261 $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
262 if ($real_bl eq $bc) {
263 xlog($share,"latest from today (possible partial backup)");
264 rename $real_bl,$real_bl.".partial" || warn "can't reaname partial backup: $!";
265 $real_bl .= ".partial";
266 }
267 } else {
268 xlog($share,"this is first run...");
269 }
270 }
271
272 if (-l $bc && $real_bl && $real_bl eq $bc) {
273 xlog($share,"allready backuped...");
274 return 1;
275 }
276
277 die "You should really create BACKUP_DEST [$BACKUP_DEST] by hand! " if (!-e $BACKUP_DEST);
278
279 if (! -e "$BACKUP_DEST/$host") {
280 mkdir "$BACKUP_DEST/$host" || die "can't make dir for host $host, $BACKUP_DEST/$host: $!";
281 xlog($share,"created host directory $BACKUP_DEST/$host...");
282 }
283
284 if (! -e "$BACKUP_DEST/$host/$dir") {
285 mkdir "$BACKUP_DEST/$host/$dir" || die "can't make dir for share $share, $BACKUP_DEST/$host/$dir $!";
286 xlog($share,"created dir for this share $BACKUP_DEST/$host/$dir...");
287 }
288
289 mkdir $bc || die "can't make dir for current backup $bc: $!";
290
291 my @dirs = ( "/" );
292 my @smb_dirs = ( "/" );
293
294 my $transfer = 0; # bytes transfered over network
295
296 # this will store all available files and sizes
297 my @files;
298 my %file_size;
299 my %file_atime;
300 my %file_mtime;
301 #my %file_md5;
302 %file_md5 = ();
303
304 my @smb_files;
305 my %smb_size;
306 #my %smb_atime;
307 #my %smb_mtime;
308
309 sub norm_dir {
310 my $foo = shift;
311 my $prefix = shift;
312 $foo =~ s#//+#/#g;
313 $foo =~ s#/+$##g;
314 $foo =~ s#^/+##g;
315 return $prefix.$foo if ($prefix);
316 return $foo;
317 }
318
319 # read local filesystem
320 my $di = 0;
321 while ($di <= $#dirs && $real_bl) {
322 my $d=$dirs[$di++];
323 opendir(DIR,"$real_bl/$d") || warn "opendir($real_bl/$d): $!\n";
324
325 # read .backupignore if exists
326 if (-f "$real_bl/$d/.backupignore") {
327 open(I,"$real_bl/$d/.backupignore");
328 while(<I>) {
329 chomp;
330 push @ignore,norm_dir("$d/$_");
331 }
332 close(I);
333 #print STDERR "ignore: ",join("|",@ignore),"\n";
334 link "$real_bl/$d/.backupignore","$bc/$d/.backupignore" ||
335 warn "can't copy $real_bl/$d/.backupignore to current backup dir: $!\n";
336 }
337
338 # read .md5sum if exists
339 if (-f "$real_bl/$d/.md5sum") {
340 open(I,"$real_bl/$d/.md5sum");
341 while(<I>) {
342 chomp;
343 my ($md5,$f) = split(/\s+/,$_,2);
344 $file_md5{$f}=$md5;
345 }
346 close(I);
347 }
348
349 my @clutter = readdir(DIR);
350 foreach my $f (@clutter) {
351 next if ($f eq '.');
352 next if ($f eq '..');
353 my $pr = norm_dir("$d/$f"); # path relative
354 my $pf = norm_dir("$d/$f","$real_bl/"); # path full
355 if (grep(/^\Q$pr\E$/,@ignore) == 0) {
356 if (-f $pf) {
357 push @files,$pr;
358 $file_size{$pr}=(stat($pf))[7];
359 $file_atime{$pr}=(stat($pf))[8];
360 $file_mtime{$pr}=(stat($pf))[9];
361 } elsif (-d $pf) {
362 push @dirs,$pr;
363 } else {
364 xlog($share,"not file or directory: $pf",0);
365 }
366 } else {
367 xlog($share,"ignored: $pr");
368 }
369 }
370 }
371
372 # local dir always include /
373 xlog($share,($#files+1)." files and ".($#dirs)." dirs on local disk before backup");
374
375 # read smb filesystem
376
377 xlog($share,"smb to $share as $param{username}/$param{workgroup}");
378
379 # FIX: how to aviod creation of ~/.smb/smb.conf ?
380 $smb = new Filesys::SmbClient(%param) || die "SmbClient :$!\n";
381
382 $di = 0;
383 while ($di <= $#smb_dirs) {
384 my $d=$smb_dirs[$di];
385 my $pf = norm_dir($d,"smb:$share/"); # path full
386 my $D = $smb->opendir($pf);
387 if (! $D) {
388 xlog($share,"FATAL: $share [$pf] as $param{username}/$param{workgroup}: $!",0);
389 # remove failing dir
390 delete $smb_dirs[$di];
391 return 0; # failed
392 }
393 $di++;
394
395 my @clutter = $smb->readdir_struct($D);
396 foreach my $item (@clutter) {
397 my $f = $item->[1];
398 next if ($f eq '.');
399 next if ($f eq '..');
400 my $pr = norm_dir("$d/$f"); # path relative
401 my $pf = norm_dir("$d/$f","smb:$share/"); # path full
402 if (grep(/^\Q$pr\E$/,@ignore) == 0) {
403 if ($item->[0] == main::SMBC_FILE) {
404 push @smb_files,$pr;
405 $smb_size{$pr}=($smb->stat($pf))[7];
406 $smb_atime{$pr}=($smb->stat($pf))[10];
407 $smb_mtime{$pr}=($smb->stat($pf))[11];
408 } elsif ($item->[0] == main::SMBC_DIR) {
409 push @smb_dirs,$pr;
410 } else {
411 xlog($share,"not file or directory [".$item->[0]."]: $pf",0);
412 }
413 } else {
414 xlog($share,"smb ignored: $pr");
415 }
416 }
417 }
418
419 xlog($share,($#smb_files+1)." files and ".($#smb_dirs)." dirs on remote share");
420
421 # sync dirs
422 my $lc = List::Compare->new(\@dirs, \@smb_dirs);
423
424 my @dirs2erase = $lc->get_Lonly;
425 my @dirs2create = $lc->get_Ronly;
426 xlog($share,($#dirs2erase+1)." dirs to erase and ".($#dirs2create+1)." dirs to create");
427
428 # create new dirs
429 foreach (sort @smb_dirs) {
430 mkdir "$bc/$_" || warn "mkdir $_: $!\n";
431 }
432
433 # sync files
434 $lc = List::Compare->new(\@files, \@smb_files);
435
436 my @files2erase = $lc->get_Lonly;
437 my @files2create = $lc->get_Ronly;
438 xlog($share,($#files2erase+1)." files to erase and ".($#files2create+1)." files to create");
439
440 sub smb_copy {
441 my $smb = shift;
442
443 my $from = shift;
444 my $to = shift;
445
446
447 my $l = 0;
448
449 foreach my $f (@_) {
450 #print "smb_copy $from/$f -> $to/$f\n";
451 my $md5 = Digest::MD5->new;
452
453 my $fd = $smb->open("$from/$f");
454 if (! $fd) {
455 xlog("WARNING","can't open smb file $from/$f: $!");
456 next;
457 }
458
459 if (! open(F,"> $to/$f")) {
460 xlog("WARNING","can't open new file $to/$f: $!");
461 next;
462 }
463
464 while (defined(my $b=$smb->read($fd,4096))) {
465 print F $b;
466 $l += length($b);
467 $md5->add($b);
468 }
469
470 $smb->close($fd);
471 close(F);
472
473 $file_md5{$f} = $md5->hexdigest;
474
475 # FIX: this fails with -T
476 my ($a,$m) = ($smb->stat("$from/$f"))[10,11];
477 utime $a, $m, "$to/$f" ||
478 warn "can't update utime on $to/$f: $!\n";
479
480 }
481 return $l;
482 }
483
484 # copy new files
485 foreach (@files2create) {
486 $transfer += smb_copy($smb,"smb:$share",$bc,$_);
487 }
488
489 my $size_sync = 0;
490 my $atime_sync = 0;
491 my $mtime_sync = 0;
492 my @sync_files;
493 my @ln_files;
494
495 foreach ($lc->get_intersection) {
496
497 my $f;
498
499 if ($file_size{$_} != $smb_size{$_}) {
500 $f=$_;
501 $size_sync++;
502 }
503 if ($file_atime{$_} != $smb_atime{$_}) {
504 $f=$_;
505 $atime_sync++;
506 }
507 if ($file_mtime{$_} != $smb_mtime{$_}) {
508 $f=$_;
509 $mtime_sync++;
510 }
511
512 if ($f) {
513 push @sync_files, $f;
514 } else {
515 push @ln_files, $_;
516 }
517 }
518
519 xlog($share,($#sync_files+1)." files will be updated (diff: $size_sync size, $atime_sync atime, $mtime_sync mtime), ".($#ln_files+1)." will be linked.");
520
521 foreach (@sync_files) {
522 $transfer += smb_copy($smb,"smb:$share",$bc,$_);
523 }
524
525 xlog($share,"$transfer bytes transfered...");
526
527 foreach (@ln_files) {
528 link "$real_bl/$_","$bc/$_" || warn "link $real_bl/$_ -> $bc/$_: $!\n";
529 }
530
531 # remove files
532 foreach (sort @files2erase) {
533 unlink "$bc/$_" || warn "unlink $_: $!\n";
534 delete $file_md5{$_};
535 }
536
537 # remove not needed dirs (after files)
538 foreach (sort @dirs2erase) {
539 rmdir "$bc/$_" || warn "rmdir $_: $!\n";
540 }
541
542 # remove old .md5sum
543 foreach (sort @dirs) {
544 unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");
545 }
546
547 # erase stale entries in .md5sum
548 my @md5_files = keys %file_md5;
549 $lc = List::Compare->new(\@md5_files, \@smb_files);
550 foreach my $file ($lc->get_Lonly) {
551 xlog("NOTICE","removing stale '$file' from .md5sum");
552 delete $file_md5{$file};
553 }
554
555 # create .md5sum
556 my $last_dir = '';
557 my $md5;
558 foreach my $f (sort { $file_md5{$a} cmp $file_md5{$b} } keys %file_md5) {
559 my $dir = dirname($f);
560 my $file = basename($f);
561 #print "$f -- $dir / $file<--\n";
562 if ($dir ne $last_dir) {
563 close($md5) if ($md5);
564 open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";
565 $last_dir = $dir;
566 #print STDERR "writing $last_dir/.md5sum\n";
567 }
568 print $md5 $file_md5{$f}," $file\n";
569 }
570 close($md5) if ($md5);
571
572 # create leatest link
573 #print "ln -s $bc $real_bl\n";
574 if (-l $bl) {
575 unlink $bl || warn "can't remove old latest symlink $bl: $!\n";
576 }
577 symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";
578
579 # FIX: sanity check -- remove for speedup
580 xlog($share,"failed to create latest symlink $bl -> $bc...") if (readlink($bl) ne $bc || ! -l $bl);
581
582 xlog($share,"backup completed...");
583
584 return 1;
585 }
586 __END__
587 #-------------------------------------------------------------------------
588
589
590 =head1 NAME
591
592 psinib - Perl Snapshot Is Not Incremental Backup
593
594 =head1 SYNOPSIS
595
596 ./psinib.pl [OPTION]... [mount script]
597
598 =head1 DESCRIPTION
599
600 Option can be one of more of following:
601
602 =over 8
603
604 =item C<--backupdest=dir>
605
606 Specify backup destination directory (defaults is /data/
607
608 =item C<--noping>
609
610 Don't use ping to check if host is up (default is ti use tcp syn to cifs
611 port)
612
613 =item C<--verbose -v>
614
615 Increase verbosity level. Defailt is 1 which prints moderate amount of data
616 on STDOUT and STDERR.
617
618 =item C<--quiet -q>
619
620 Decrease verbosity level
621
622 =back
623
624 This script in current version support just backup of Samba (or Micro$oft
625 Winblowz) shares to central disk space. Central disk space is organized in
626 multiple directories named after:
627
628 =over 4
629
630 =item *
631 server which is sharing files to be backed up
632
633 =item *
634 name of share on server
635
636 =item *
637 dated directory named like standard ISO date format (YYYYMMDD).
638
639 =back
640
641 In each dated directory you will find I<snapshot> of all files on
642 exported share on that particular date.
643
644 You can also use symlink I<latest> which will lead you to
645 last completed backup. After that you can use some other backup
646 software to transfer I<snapshot> to tape, CD-ROM or some other media.
647
648 =head2 Design considerations
649
650 Since taking of share snapshot every day requires a lot of disk space and
651 network bandwidth, B<psinib> uses several techniques to keep disk usage and
652 network traffic at acceptable level:
653
654 =over 3
655
656 =item - usage of hard-links to provide same files in each snapshot (as opposed
657 to have multiple copies of same file)
658
659 =item - usage of file size, atime and mtime to find changes of files without
660 transferring whole file over network (just share browsing is transfered
661 over network)
662
663 =item - usage of C<.md5sum> files (compatible with command-line utility
664 C<md5sum>) to keep file between snapshots hard-linked
665
666 =back
667
668 =head1 CONFIGURATION
669
670 This section is not yet written.
671
672 =head1 HACKS, TRICKS, BUGS and LIMITATIONS
673
674 This chapter will have all content that doesn't fit anywhere else.
675
676 =head2 Can snapshots be more frequent than daily?
677
678 There is not real reason why you can't take snapshot more often than
679 once a day. Actually, if you are using B<psinib> to backup Windows
680 workstations you already know that they tend to come-and-go during the day
681 (reboots probably ;-), so running B<psinib> several times a day increases
682 your chance of having up-to-date backup (B<psinib> will not make multiple
683 snapshots for same day, nor will it update snapshot for current day if
684 it already exists).
685
686 However, changing B<psinib> to produce snapshots which are, for example, hourly
687 is a simple change of C<$DIR_TIME_FMT> which is currently set to
688 C<'%Y%m%d'> (see I<strftime> documentation for explanation of that
689 format). If you change that to C<'%Y%m%d-%H> you can have hourly snapshots
690 (if your network is fast enough, that is...). Also, some of messages in
691 program will sound strange, but other than that it should work.
692 I<You have been warned>.
693
694 =head2 Do I really need to share every directory which I want to snapshot?
695
696 Actually, no. Due to usage of C<Filesys::SmbClient> module, you can also
697 specify sub-directory inside your share that you want to backup. This feature
698 is most useful if you want to use administrative shares (but, have in mind
699 that you have to enter your Win administrator password in unencrypted file on
700 disk to do that) like this:
701
702 smbmount //server/c$/WinNT/fonts /mnt -o username=administrator%win
703
704 After that you will get directories with snapshots like:
705
706 server/c_WinNT_fonts/yyyymmdd/....
707
708 =head2 Won't I run out of disk space?
709
710 Of course you will... Snapshots and logfiles will eventually fill-up your disk.
711 However, you can do two things to stop that:
712
713 =head3 Clean snapshort older than x days
714
715 You can add following command to your C<root> crontab:
716
717 find /backup/isis_backup -type d -mindepth 3 -maxdepth 3 -mtime +11 -exec rm -Rf {} \;
718
719 I assume that C</backup/isis_backup> is directory in which are your snapshots
720 and that you don't want to keep snapshots older than 11 days (that's
721 C<-mtime +11> part of command).
722
723 =head3 Rotate your logs
724
725 I will leave that to you. I relay on GNU/Debian's C<logrotate> to do it for me.
726
727 =head2 What are I<YYYYMMDD.partial> directories?
728
729 If there isn't I<latest> symlink in snapshot directory, it's preatty safe to
730 assume that previous backup from that day failed. So, that directory will
731 be renamed to I<YYYYMMDD.partial> and snapshot will be performed again,
732 linking same files (other alternative would be to erase that dir and find
733 second-oldest directory, but this seemed like more correct approach).
734
735 =head2 I can't connect to any share
736
737 Please verify that nmblookup (which is part of samba package) is in /bin or
738 /usr/bin. Also verify that nmblookup returns IP address for your server
739 using:
740
741 $ nmblookup tvhouse
742 querying tvhouse on 192.168.34.255
743 192.168.34.30 tvhouse<00>
744
745 If you don't get any output, your samba might not listen to correct interface
746 (see interfaces in smb.conf).
747
748 =head1 AUTHOR
749
750 Dobrica Pavlinusic <dpavlin@rot13.org>
751
752 L<http:E<sol>E<sol>www.rot13.orgE<sol>~dpavlinE<sol>>
753
754 =head1 LICENSE
755
756 This product is licensed under GNU Public License (GPL) v2 or later.
757
758 =cut

  ViewVC Help
Powered by ViewVC 1.1.26