Rev 1227 | Rev 1253 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1149 | dev | 1 | #!/usr/bin/perl |
2 | # fsbackup - file system backup and synchronization utility. |
||
3 | # |
||
4 | # http://www.opennet.ru/dev/fsbackup/ |
||
5 | # Copyright (c) 2001-2002 by Maxim Chirkov. <mc@tyumen.ru> |
||
6 | # |
||
1153 | dev | 7 | # Ключи: |
8 | # -n - создаем новый архив независимо от состояния хэша. |
||
9 | # -f - full_backup - полный бэкап в архив, без хэша. |
||
10 | # -h - hash - только генерация хэша, без помещения файлов в архив. |
||
11 | # -c - clean - очиска хранилища с инкрементальным бэкапом и создание нового бэкапа. |
||
1206 | dev | 12 | # |
13 | # $Id: fsbackup.pl 1228 2006-03-01 12:04:12Z dev $ |
||
14 | # |
||
1149 | dev | 15 | ############################################# |
1154 | dev | 16 | use constant DB_DEF_CACHE_SIZE => 40960000; # Размер кэша для размежения хэша в памяти |
1149 | dev | 17 | |
18 | use POSIX; |
||
19 | use File::Find; |
||
20 | use Digest::MD5 qw(md5_base64); |
||
21 | use Net::FTP; |
||
22 | use DB_File; |
||
1205 | dev | 23 | use BSD::stat; |
1149 | dev | 24 | |
25 | use constant VERB_SILENT => 0; # Silent mode, suspend all output. |
||
26 | use constant VERB_ERROR => 1; # Output all errors and warnings. |
||
27 | use constant VERB_ALL => 2; # Output all the available data. |
||
28 | |||
1154 | dev | 29 | my $version = "2.0"; |
1149 | dev | 30 | my $list_lines_cnt = 0; |
31 | my $del_lines_cnt = 0; |
||
32 | my $cur_time = time(); |
||
33 | my %active_hash_last; |
||
34 | my %active_hash_new; |
||
35 | my $cfg_new_flag = 0; |
||
36 | my $cfg_clean_flag = 0; |
||
37 | my $config = 0; |
||
1153 | dev | 38 | my $cur_backup_size = 1536; # Размер блока tar |
1149 | dev | 39 | my $backup_file_base; |
40 | my $prog_pgp_filter; |
||
41 | my $prog_gzip_filter; |
||
42 | my $arc_ext; |
||
43 | my $ftp; |
||
44 | my $cur_increment_level; |
||
45 | my $cur_dir; |
||
46 | my $cur_path; |
||
47 | my $cur_file; |
||
48 | my $cur_pathitem; |
||
49 | my $file_fullpath; |
||
50 | my $file_fullpath_md5; |
||
51 | my $key; |
||
52 | my $dbobj_new; |
||
53 | my $dbobj_last; |
||
54 | my $db_hashinfo; |
||
55 | my $db_hashinfo2; |
||
56 | my $file; |
||
57 | my @volume_position=(0); |
||
1153 | dev | 58 | my @fs_path=(); # /dir[/file] - путь к файлу/директории для бэкапа. |
59 | my @fs_notpath=(); # ! - отрицание пути, не помещать в бэкап. Всегда должен быть первым символом. |
||
60 | my @fs_mask=(); # =~ - маска для файла или директории, а не абсолютный путь. Первый или второй символ. |
||
61 | my @fs_filemask=(); # f~ - маска для файла. Первый или второй символ. |
||
62 | my @fs_dirmask=(); # d~ - маска для директории. Первый или второй символ. |
||
63 | my @fs_notmask=(); # =! - "НЕ" маска для файла или директории, а не абсолютный путь. Первый или второй символ. |
||
64 | my @fs_notfilemask=(); # f! - "НЕ" маска для файла. Первый или второй символ. |
||
65 | my @fs_notdirmask=(); # d! - "НЕ" маска для директории. Первый или второй символ. |
||
1149 | dev | 66 | |
1153 | dev | 67 | # ------------- Обработка параметров командной строки |
1149 | dev | 68 | |
69 | if ($ARGV[0] eq "-n" || $ARGV[0] eq "-h" || $ARGV[0] eq "-f" || $ARGV[0] eq "-c"){ |
||
70 | $cfg_new_flag=1; |
||
71 | $config = $ARGV[1]; |
||
72 | } else { |
||
73 | $cfg_new_flag=0; |
||
74 | $config = $ARGV[0]; |
||
75 | } |
||
76 | |||
77 | if ( ! -f $config){ |
||
78 | die "Usage: fsbackup.pl [-n|-f|-h|-c] config_name\n"; |
||
79 | } |
||
80 | |||
81 | require "$config"; |
||
82 | |||
1154 | dev | 83 | $cfg_move_old_backup=1 if(!defined($cfg_move_old_backup)); |
84 | $cfg_exit_on_empty=1 if(!defined($cfg_exit_on_empty)); |
||
85 | |||
1149 | dev | 86 | if ( ! -d $cfg_cache_dir){ |
87 | die "\$cfg_cache_dir ($cfg_cache_dir) not found. Set \$cfg_cache_dir varisble in fsbackup.pl\n"; |
||
88 | } |
||
89 | |||
1153 | dev | 90 | $cfg_time_limit *= 60 * 60 * 24; # Дни в секунды. |
91 | $cfg_size_limit *= 1024; # Килобайты в байты. |
||
92 | $cfg_maximum_archive_size *= 1024; # Килобайты в байты. |
||
1149 | dev | 93 | |
1151 | dev | 94 | chdir($cfg_root_path); |
1149 | dev | 95 | |
96 | if ($ARGV[0] eq "-h"){ |
||
97 | $cfg_backup_style = "hash"; |
||
98 | } |
||
99 | if ($ARGV[0] eq "-f" ){ |
||
100 | $cfg_backup_style = "full_backup"; |
||
101 | } |
||
102 | |||
103 | if ($ARGV[0] eq "-c" ){ |
||
104 | $cfg_clean_flag=1; |
||
105 | } else { |
||
106 | $cfg_clean_flag=0; |
||
107 | } |
||
108 | |||
1153 | dev | 109 | #------------------- Проверяем переменные в файле конфигурации. |
1206 | dev | 110 | if ($cfg_backup_name !~ /^[\w\d\_.]+$/){ |
1149 | dev | 111 | die "Found illegal characters in $cfg_backup_name ($cfg_backup_name)."; |
112 | } |
||
113 | |||
114 | if (! grep {$_ eq $cfg_checksum} ("md5", "timesize")){ |
||
115 | die "Unknown checksum method:\$cfg_checksum=$cfg_checksum (allowed md5 or timesize)\n"; |
||
116 | } |
||
117 | |||
118 | if (! grep {$_ eq $cfg_backup_style} ("backup", "full_backup", "sync", "hash")){ |
||
119 | die "Unknown backup_style:\$cfg_backup_style=$cfg_backup_style\n"; |
||
120 | } |
||
121 | |||
122 | |||
123 | if ($cfg_backup_style eq "full_backup" || $cfg_backup_style eq "hash"){ |
||
124 | $cfg_new_flag=1; |
||
125 | $cfg_clean_flag=1; |
||
126 | } |
||
127 | |||
128 | if (! grep {$_ eq $cfg_type} ("local", "remote_ssh", "remote_ftp")){ |
||
129 | die "Unknown backup target:\$cfg_type=$cfg_type\n"; |
||
130 | } |
||
131 | |||
132 | if (($cfg_type eq "local") && (! -d $cfg_local_path)){ |
||
133 | die "Can't find \$cfg_local_path ($cfg_local_path)"; |
||
134 | } |
||
135 | |||
136 | if ($cfg_backup_style eq "backup"){ |
||
137 | my ($sec,$min,$hour,$mday,$mon,$year) = localtime($cur_time); |
||
138 | $backup_file_base = sprintf ("%s-%4.4d.%2.2d.%2.2d.%2.2d.%2.2d.%2.2d", |
||
139 | $cfg_backup_name,$year+1900,$mon+1,$mday,$hour,$min,$sec); |
||
140 | }else{ |
||
141 | $backup_file_base="$cfg_backup_name"; |
||
142 | } |
||
143 | |||
144 | print "Creating $cfg_type $cfg_backup_style: $cfg_backup_name\n" if ($cfg_verbose == &VERB_ALL); |
||
145 | |||
146 | if ($cfg_pgp_userid ne "" && $prog_pgp ne ""){ |
||
147 | print "PGP: enabled\n" if ($cfg_verbose == &VERB_ALL); |
||
148 | |||
149 | # PGP 2.6 (pgp) |
||
150 | # $prog_pgp_filter="| $prog_pgp -ef $cfg_pgp_userid -z'$cfg_pgp_userid' "; |
||
151 | # PGP 5.0 (pgpe) |
||
152 | # $prog_pgp_filter="| $prog_pgp -f $cfg_pgp_userid"; |
||
153 | # GnuPG (pgp) |
||
154 | $prog_pgp_filter="| $prog_pgp -v --batch -e -r $cfg_pgp_userid"; |
||
155 | } else { |
||
156 | $prog_pgp_filter=""; |
||
157 | } |
||
158 | |||
159 | if ($prog_gzip ne ""){ |
||
160 | $prog_gzip_filter="| $prog_gzip"; |
||
161 | $arc_ext=".gz"; |
||
162 | } else { |
||
163 | $prog_gzip_filter=""; |
||
164 | $arc_ext=""; |
||
165 | |||
166 | } |
||
167 | |||
168 | if (! -d "$cfg_cache_dir/$cfg_backup_name"){ |
||
169 | mkdir("$cfg_cache_dir/$cfg_backup_name", 0700); |
||
170 | } |
||
171 | |||
1153 | dev | 172 | # ---------- Активируем FTP соединение |
1149 | dev | 173 | |
174 | ftp_connect(); |
||
175 | |||
1153 | dev | 176 | #----------- Вычисляем уровень инкрементальности. |
1149 | dev | 177 | if ($cfg_increment_level != 0 && $cfg_backup_style eq "backup"){ |
1154 | dev | 178 | if(open(INCREMENT_LEVEL, "<$cfg_cache_dir/$cfg_backup_name/.increment_level")) { |
179 | $cur_increment_level = <INCREMENT_LEVEL>; |
||
180 | $cur_increment_level++; |
||
181 | close (INCREMENT_LEVEL); |
||
182 | } |
||
183 | else { |
||
184 | print "Can't open increment level file ($cfg_cache_dir/$cfg_backup_name/.increment_level).\n"; |
||
185 | $cur_increment_level = 0; |
||
186 | } |
||
1152 | dev | 187 | |
1154 | dev | 188 | if ($cur_increment_level >= $cfg_increment_level){ |
189 | $cur_increment_level=0; |
||
190 | } |
||
1152 | dev | 191 | |
1154 | dev | 192 | if ($cur_increment_level == 0){ |
1149 | dev | 193 | $cfg_new_flag=1; |
194 | $cfg_clean_flag=1; |
||
195 | } |
||
1154 | dev | 196 | if(open(INCREMENT_LEVEL, ">$cfg_cache_dir/$cfg_backup_name/.increment_level")) { |
197 | print INCREMENT_LEVEL $cur_increment_level; |
||
198 | close (INCREMENT_LEVEL); |
||
199 | } |
||
200 | else { |
||
201 | print "Can't save increment level to file ($cfg_cache_dir/$cfg_backup_name/.increment_level).\n"; |
||
202 | } |
||
1149 | dev | 203 | print "Current increment number: $cur_increment_level\n" if ($cfg_verbose == &VERB_ALL); |
204 | } |
||
205 | ################################################ |
||
1153 | dev | 206 | #----------- Считываем хэш в память. |
1149 | dev | 207 | |
208 | if ( (-f "$cfg_cache_dir/$cfg_backup_name/.hash" || $cfg_type ne "local" ) && $cfg_new_flag == 0){ |
||
1153 | dev | 209 | # Считываем текущий хеш в память. |
1149 | dev | 210 | |
211 | if ( $cfg_type eq "local"){ |
||
212 | rename ("$cfg_cache_dir/$cfg_backup_name/.hash", "$cfg_cache_dir/$cfg_backup_name/.hash.last"); |
||
213 | }elsif ( $cfg_type eq "remote_ssh"){ |
||
214 | system ("$prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat $cfg_remote_path/.hash' > $cfg_cache_dir/$cfg_backup_name/.hash.last") == 0 || print "SSH connection failed: $?\n"; |
||
215 | } elsif ( $cfg_type eq "remote_ftp"){ |
||
216 | unlink ("$cfg_cache_dir/$cfg_backup_name/.hash.last"); |
||
217 | $ftp->get(".hash", "$cfg_cache_dir/$cfg_backup_name/.hash.last")|| print "FTP error, Can't GET .hash\n"; |
||
218 | } |
||
219 | $db_hashinfo = new DB_File::HASHINFO ; |
||
220 | $db_hashinfo->{'cachesize'} = DB_DEF_CACHE_SIZE; |
||
221 | if (! ($dbobj_last = tie(%active_hash_last, "DB_File", "$cfg_cache_dir/$cfg_backup_name/.hash.last", O_RDWR|O_CREAT, 0644, $db_hashinfo ))){ |
||
222 | print "WARNING: Error in hash, creating full backup.\n" if ($cfg_verbose >= &VERB_ERROR); |
||
223 | unlink "$cfg_cache_dir/$cfg_backup_name/.hash.last"; |
||
224 | $dbobj_last = tie(%active_hash_last, "DB_File", "$cfg_cache_dir/$cfg_backup_name/.hash.last", O_RDWR|O_CREAT, 0644, $db_hashinfo )||print "Can't create or open DB File!"; |
||
225 | } |
||
226 | # $dbobj->del($key); |
||
227 | # $dbobj->sync(); |
||
228 | |||
229 | } |
||
230 | |||
1153 | dev | 231 | # Закрываем ftp соединение. Следующий блок может выполняться гораздо дольше |
232 | # чем таймаут ftp. |
||
1149 | dev | 233 | if ( $cfg_type eq "remote_ftp"){ |
234 | $ftp->quit; |
||
235 | } |
||
1153 | dev | 236 | #Создаем новый хеш. |
1149 | dev | 237 | unlink("$cfg_cache_dir/$cfg_backup_name/.hash"); |
238 | $db_hashinfo2 = new DB_File::HASHINFO ; |
||
239 | $db_hashinfo2->{'cachesize'} = 100000; |
||
240 | $dbobj_new = tie(%active_hash_new, "DB_File", "$cfg_cache_dir/$cfg_backup_name/.hash", O_RDWR|O_CREAT, 0644, $db_hashinfo2) || print "Can't create or open DB File!\n"; |
||
241 | |||
1153 | dev | 242 | # Создаем список файлов для помещения в архив. |
1149 | dev | 243 | open (LIST, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list")|| print "Can't create list file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list).\n"; |
244 | flock (LIST, 2); |
||
245 | |||
1153 | dev | 246 | # Создаем список директорий в архиве. |
1149 | dev | 247 | open (DIRS, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir")|| print "Can't create list file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir).\n"; |
248 | flock (DIRS, 2); |
||
249 | |||
1154 | dev | 250 | open (META, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.meta")|| print "Can't create meta file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.meta).\n"; |
251 | print META "fsbackup $version\n"; |
||
252 | print META "increment level: $cur_increment_level\n"; |
||
253 | close(META); |
||
254 | |||
1153 | dev | 255 | # Считываем список подлежащих бэкапу директорий в память. |
1151 | dev | 256 | |
1149 | dev | 257 | while(<DATA>){ |
258 | chomp; |
||
259 | $cur_path = $_; |
||
260 | if ($cur_path =~ /^\!(.*)$/){ # ! |
||
261 | push @fs_notpath, $1; |
||
262 | |||
263 | } elsif ($cur_path =~ /^\=\~(.*)$/){ # =~ |
||
264 | push @fs_mask, $1; |
||
265 | |||
266 | } elsif ($cur_path =~ /^f\~(.*)$/){ # f~ |
||
267 | push @fs_filemask, $1; |
||
268 | |||
269 | } elsif ($cur_path =~ /^d\~(.*)$/){ # d~ |
||
270 | push @fs_dirmask, $1; |
||
271 | |||
272 | } elsif ($cur_path =~ /^\=\!(.*)$/){ # =! |
||
273 | push @fs_notmask, $1; |
||
274 | |||
275 | } elsif ($cur_path =~ /^f\!(.*)$/){ # f! |
||
276 | push @fs_notfilemask, $1; |
||
277 | |||
278 | } elsif ($cur_path =~ /^d\!(.*)$/){ # d! |
||
279 | push @fs_notdirmask, $1; |
||
280 | |||
281 | } elsif ($cur_path =~ /^#/ || $cur_path =~ /^\s*$/){ # comment |
||
282 | next; |
||
283 | |||
284 | } elsif ($cur_path =~ /[\/\w]+/) { # /dir[/file] |
||
1151 | dev | 285 | push @fs_path, $cur_path; |
1149 | dev | 286 | |
287 | } else { |
||
288 | print STDERR "Syntax error: $cur_path, ingnored.\n" if ($cfg_verbose >= &VERB_ALL); |
||
289 | } |
||
290 | } |
||
291 | |||
292 | #-------------------------------------------------------------------- |
||
1153 | dev | 293 | # Последовательно просматририваем весь список директорий отмеченных для бэкапа |
1149 | dev | 294 | |
295 | |||
296 | foreach $cur_pathitem (@fs_path){ |
||
297 | print "Adding $cur_pathitem....\n" if ($cfg_verbose == &VERB_ALL); |
||
298 | find (\&add_to_backup, $cur_pathitem); |
||
299 | print "done\n" if ($cfg_verbose == &VERB_ALL); |
||
300 | } |
||
301 | close (LIST); |
||
302 | close (DIRS); |
||
303 | #------------ |
||
1153 | dev | 304 | # Составляем список удаленных файлов. |
1149 | dev | 305 | |
306 | open (DEL, ">$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del")|| print "Can't create list file ($cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del).\n"; |
||
307 | flock (DEL, 2); |
||
308 | if ($cfg_backup_style ne "hash"){ |
||
309 | while(($file, $key)= each(%active_hash_last)){ |
||
310 | $file =~ s/\'/\'\\\'\'/g; |
||
311 | $file =~ s/^\/(.*)$/$1/; |
||
312 | print DEL "rm -rf '$file'\n"; |
||
313 | $del_lines_cnt++; |
||
314 | } |
||
315 | } |
||
316 | close(DEL); |
||
317 | |||
1153 | dev | 318 | # Записываем хэш на диск. |
1149 | dev | 319 | $dbobj_new->sync(); |
320 | untie %active_hash_new; |
||
321 | untie %active_hash_last; |
||
322 | |||
1153 | dev | 323 | chdir ("/"); # Переходим в корень, так как все пути у нас без корневого / |
1149 | dev | 324 | |
1153 | dev | 325 | # Активируем FTP соединение второй раз. |
1149 | dev | 326 | ftp_connect(); |
327 | |||
328 | #------------ |
||
1153 | dev | 329 | # Если только обновляем хэш, то записываем его и выходим. |
1149 | dev | 330 | |
1153 | dev | 331 | if ($cfg_backup_style eq "hash"){ # Только создать хэшь без архивирования. |
1149 | dev | 332 | |
333 | if ( $cfg_type eq "local"){ |
||
334 | system( "cp -f $cfg_cache_dir/$cfg_backup_name/.hash $cfg_local_path/.hash") == 0 || print "Local FS copy hash failed: $?"; |
||
335 | } elsif ( $cfg_type eq "remote_ssh"){ |
||
336 | system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.hash'") == 0 || print "SSH connection failed (copy hash): $?\n"; |
||
337 | } elsif ( $cfg_type eq "remote_ftp"){ |
||
338 | $ftp->delete(".hash"); |
||
339 | $ftp->put("$cfg_cache_dir/$cfg_backup_name/.hash", ".hash")|| print "Can't upload .hash to remote server via FTP\n"; |
||
340 | } |
||
341 | exit (0); |
||
342 | } |
||
343 | |||
344 | #------------ |
||
1153 | dev | 345 | # Архивируем и передаем в хранилище. |
1149 | dev | 346 | |
1154 | dev | 347 | if ($cfg_exit_on_empty == 1 && $list_lines_cnt == 0 && $del_lines_cnt == 0){ |
348 | print "$cfg_exit_on_empty\n"; |
||
1149 | dev | 349 | print "WARNING: Nothing to backup.\n" if ($cfg_verbose >= &VERB_ALL); |
350 | exit; |
||
351 | } |
||
352 | if ( $cfg_type eq "local"){ |
||
353 | |||
354 | print "Storing local backup...\n" if ($cfg_verbose == &VERB_ALL); |
||
355 | if ($cfg_backup_style eq "sync"){ |
||
1153 | dev | 356 | if ($cfg_clean_flag == 1){ # Удалить старые копии |
1151 | dev | 357 | print "WARNING: If you really shure to delete $cfg_local_path before sync operatioun uncomment line 'system( \"find \$cfg_local_path -not -path '\$cfg_local_path' -maxdepth 1 -exec \$prog_rm -rf \{\} \\;\");'" if ($cfg_verbose >= &VERB_ALL); |
358 | # system( "find $cfg_local_path -not -path '$cfg_local_path' -maxdepth 1 -exec $prog_rm -rf \{\} \\;"); |
||
1149 | dev | 359 | } |
360 | |||
361 | system( "cd $cfg_local_path; sh $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del"); |
||
362 | system( "$prog_tar -c -f - -T $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list| $prog_tar -xf - -C $cfg_local_path") == 0 || print "Local FS sync failed (tar|untar): $?\n"; |
||
363 | system( "cd $cfg_local_path; sh $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir"); |
||
364 | system( "cp -f $cfg_cache_dir/$cfg_backup_name/.hash $cfg_local_path/$backup_file_base.hash") == 0 || print "Local FS copy failed: $?\n"; |
||
365 | |||
366 | } else { |
||
1153 | dev | 367 | if ($cfg_clean_flag == 1){ # Удалить старые копии |
1149 | dev | 368 | if ($cfg_save_old_backup == 0){ |
369 | system( "$prog_rm -f $cfg_local_path/*"); |
||
1154 | dev | 370 | } elsif($cfg_move_old_backup == 1) { |
1149 | dev | 371 | if (! -d "$cfg_local_path/OLD"){ |
372 | system( "mkdir $cfg_local_path/OLD"); |
||
373 | } |
||
374 | system( "$prog_rm -f $cfg_local_path/OLD/*"); |
||
375 | system( "mv -f $cfg_local_path/$cfg_backup_name* $cfg_local_path/OLD/"); |
||
376 | # system( "$prog_rm -f $cfg_local_path/*"); |
||
377 | } |
||
378 | } |
||
1154 | dev | 379 | system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.meta $cfg_local_path/$backup_file_base.meta") == 0 || print "Local FS .meta copy failed: $?\n"; |
1149 | dev | 380 | system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list $cfg_local_path/$backup_file_base.list") == 0 || print "Local FS .list copy failed: $?\n"; |
381 | system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir $cfg_local_path/$backup_file_base.dir") == 0 || print "Local FS .dir copy failed: $?\n"; |
||
382 | system( "cp -f $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del $cfg_local_path/$backup_file_base.del") == 0 || print "Local FS .del copy failed: $?\n"; |
||
1154 | dev | 383 | #system( "cp -f $cfg_cache_dir/$cfg_backup_name/.hash $cfg_local_path/$backup_file_base.hash") == 0 || print "Local FS .hash copy failed: $?\n"; |
1153 | dev | 384 | # Обрабатываем разбиение на тома |
1149 | dev | 385 | for ($arc_block_level=0; $arc_block_level <= $#volume_position; $arc_block_level++){ |
1154 | dev | 386 | my $tmp_list_file = create_tmp_list($arc_block_level, $volume_position[$arc_block_level], $volume_position[$arc_block_level+1], "$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list"); |
1149 | dev | 387 | system( "$prog_tar -c -f - -T $tmp_list_file $prog_gzip_filter $prog_pgp_filter > $cfg_local_path/$backup_file_base-$arc_block_level.tar${arc_ext}") == 0 || print "Local FS tar backup failed: $?\n"; |
388 | } |
||
389 | } |
||
390 | |||
391 | } elsif ( $cfg_type eq "remote_ssh"){ |
||
392 | print "Storing remote ssh backup...\n" if ($cfg_verbose == &VERB_ALL); |
||
393 | if ($cfg_backup_style eq "sync"){ |
||
1153 | dev | 394 | if ($cfg_clean_flag == 1){ # Удалить старые копии |
1151 | dev | 395 | system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host find $cfg_remote_path -not -path '$cfg_remote_path' -maxdepth 1 -exec rm -rf \{\} \\;"); |
1149 | dev | 396 | } |
397 | system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.hash'") == 0 || print "SSH connection failed (store .hash): $?\n"; |
||
398 | system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.del'") == 0 || print "SSH connection failed (store .hash): $?\n"; |
||
399 | system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.dir'") == 0 || print "SSH connection failed (store .hash): $?\n"; |
||
400 | system("$prog_ssh -l $cfg_remote_login $cfg_remote_host '(cd $cfg_remote_path; sh .del)'"); |
||
401 | system( "$prog_tar -c -f - -T $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list $prog_gzip_filter| $prog_ssh -l $cfg_remote_login $cfg_remote_host tar -xf - -C $cfg_remote_path") == 0 || print "SSH connection failed (tar): $?\n";; |
||
402 | system("$prog_ssh -l $cfg_remote_login $cfg_remote_host '(cd $cfg_remote_path; sh .dir)'"); |
||
403 | |||
404 | |||
405 | open (DEL, "<$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del"); |
||
406 | flock (DEL, 1); |
||
407 | while(<DEL>){ |
||
408 | chomp; |
||
409 | $cur_file = $_; |
||
410 | $cur_file =~ s/\'/\'\\\'\'/g; |
||
411 | system("$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f '$cfg_remote_path/$cur_file'"); |
||
412 | } |
||
413 | close(DEL); |
||
414 | } else { |
||
1153 | dev | 415 | if ($cfg_clean_flag == 1){ # Удалить старые копии |
1149 | dev | 416 | |
417 | if ($cfg_save_old_backup == 0){ |
||
418 | system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f $cfg_remote_path/*"); |
||
419 | } else { |
||
420 | system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host '(if [ ! -d $cfg_remote_path/OLD ]; then mkdir $cfg_remote_path/OLD; fi)'"); |
||
421 | system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f $cfg_remote_path/OLD/*"); |
||
422 | system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host mv -f $cfg_remote_path/$cfg_backup_name* $cfg_remote_path/OLD/"); |
||
423 | # system( "$prog_ssh -l $cfg_remote_login $cfg_remote_host rm -f $cfg_remote_path/*"); |
||
424 | } |
||
425 | } |
||
426 | system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.list'") == 0 || print "SSH connection failed (copy .list): $?\n"; |
||
427 | system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.dir'") == 0 || print "SSH connection failed (copy .dir): $?\n"; |
||
428 | system( "cat $cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.del'") == 0 || print "SSH connection failed (copy .del): $?\n"; |
||
429 | system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base.hash'") == 0 || print "SSH connection failed (copy .hash): $?\n"; |
||
430 | system( "cat $cfg_cache_dir/$cfg_backup_name/.hash | $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/.hash'") == 0 || print "SSH connection failed (cache .hash): $?\n"; |
||
1153 | dev | 431 | # Обрабатываем разбиение на тома |
1149 | dev | 432 | for ($arc_block_level=0; $arc_block_level <= $#volume_position; $arc_block_level++){ |
1154 | dev | 433 | my $tmp_list_file = create_tmp_list($arc_block_level, $volume_position[$arc_block_level], $volume_position[$arc_block_level+1], "$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list"); |
1149 | dev | 434 | system( "$prog_tar -c -f - -T $tmp_list_file $prog_gzip_filter $prog_pgp_filter| $prog_ssh -l $cfg_remote_login $cfg_remote_host 'cat - > $cfg_remote_path/$backup_file_base-$arc_block_level.tar${arc_ext}'") == 0 || print "SSH connection failed (tar): $?\n"; |
435 | } |
||
436 | } |
||
437 | } elsif ( $cfg_type eq "remote_ftp"){ |
||
438 | print "Storing remote ftp backup...\n" if ($cfg_verbose == &VERB_ALL); |
||
439 | |||
440 | if ($cfg_backup_style eq "sync"){ |
||
441 | print "WARNING: Backup style 'sync' only allowed for local and remote_ssh storage.\n" if ($cfg_verbose >= &VERB_ALL); |
||
442 | } else { |
||
1153 | dev | 443 | if ($cfg_clean_flag == 1){ # Удалить старые копии |
1149 | dev | 444 | if ($cfg_save_old_backup == 0){ |
445 | foreach $cur_dir ($ftp->ls()){ |
||
446 | $ftp->delete($cur_dir); |
||
447 | } |
||
448 | } else { |
||
449 | $ftp->mkdir("$cfg_remote_path/OLD"); |
||
450 | $ftp->cwd("$cfg_remote_path/OLD"); |
||
451 | foreach $cur_dir ($ftp->ls()){ |
||
452 | $ftp->delete($cur_dir); |
||
453 | } |
||
454 | $ftp->cwd("$cfg_remote_path"); |
||
455 | foreach $cur_dir ($ftp->ls()){ |
||
456 | if ($cur_dir =~ /$cfg_backup_name/){ |
||
457 | $ftp->rename($cur_dir,"$cfg_remote_path/OLD/$cur_dir"); |
||
458 | } |
||
459 | } |
||
460 | foreach $cur_dir ($ftp->ls()){ |
||
461 | $ftp->delete($cur_dir); |
||
462 | } |
||
463 | } |
||
464 | } |
||
465 | $ftp->delete("$backup_file_base.list"); |
||
466 | $ftp->put("$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list", "$backup_file_base.list") || print "Can't PUT .list file to remote FTP server\n"; |
||
467 | $ftp->delete("$backup_file_base.dir"); |
||
468 | $ftp->put("$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.dir", "$backup_file_base.dir")|| print "Can't PUT .dir file to remote FTP server\n"; |
||
469 | $ftp->delete("$backup_file_base.del"); |
||
470 | $ftp->put("$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.del", "$backup_file_base.del")|| print "Can't PUT .del file to remote FTP server\n"; |
||
471 | $ftp->delete("$backup_file_base.hash"); |
||
472 | $ftp->put("$cfg_cache_dir/$cfg_backup_name/.hash", "$backup_file_base.hash")|| print "Can't PUT old .hash file to remote FTP server\n"; |
||
473 | $ftp->delete(".hash"); |
||
474 | $ftp->put("$cfg_cache_dir/$cfg_backup_name/.hash", ".hash")|| print "Can't PUT new .hash file to remote FTP server\n"; |
||
1153 | dev | 475 | # Обрабатываем разбиение на тома |
1149 | dev | 476 | for ($arc_block_level=0; $arc_block_level <= $#volume_position; $arc_block_level++){ |
1154 | dev | 477 | my $tmp_list_file = create_tmp_list($arc_block_level, $volume_position[$arc_block_level], $volume_position[$arc_block_level+1], "$cfg_cache_dir/$cfg_backup_name/$cfg_backup_name.list"); |
1149 | dev | 478 | $ftp->delete("$backup_file_base-$arc_block_level.tar${arc_ext}"); |
479 | open (TAR,"$prog_tar -c -f - -T $tmp_list_file $prog_gzip_filter $prog_pgp_filter|")|| print "tar failed: $?\n"; |
||
480 | flock(TAR,1); |
||
481 | $ftp->put(*TAR, "$backup_file_base-$arc_block_level.tar${arc_ext}")|| print "Can't store backup archive to remote FTP server.\n"; |
||
482 | close(TAR); |
||
483 | } |
||
484 | $ftp->quit; |
||
485 | } |
||
486 | } |
||
487 | |||
488 | if ( $cfg_type eq "remote_ftp"){ |
||
489 | $ftp->quit; |
||
490 | } |
||
491 | print "***** Backup successful complete.\n" if ($cfg_verbose == &VERB_ALL); |
||
492 | exit (0); |
||
493 | |||
494 | |||
495 | ######################################## |
||
496 | sub add_to_backup{ |
||
497 | my($file_name, $file_dir, $md5_checksum_stat, $checksum_stat); |
||
1205 | dev | 498 | my($tmp, $stat_mode, $stat_uid, $stat_gid, $stat_size, $stat_mtime, $stat_time, $stat_flags); |
1149 | dev | 499 | |
500 | $file_name = $_; |
||
501 | $file_fullpath = $File::Find::name; |
||
502 | $file_dir = $File::Find::dir; |
||
503 | my $file_fullpath_esc = $file_fullpath; |
||
504 | $file_fullpath_esc =~ s/\'/\'\\\'\'/g; |
||
505 | |||
1153 | dev | 506 | # Создаем список директорий |
1151 | dev | 507 | if ((-d $file_fullpath) && (! -l $file_fullpath)){ |
1149 | dev | 508 | if (check_path($file_dir, $file_name) == 1){ |
509 | if ($cfg_backup_style ne "hash"){ |
||
1228 | dev | 510 | ($tmp, $tmp, $stat_mode, $tmp, $stat_uid, $stat_gid, $tmp, $stat_size, $tmp, $stat_mtime, $stat_time, $tmp, $tmp, $tmp, $tmp, $tmp, $stat_flags, $tmp) = lstat($file_fullpath); |
511 | $stat_mode = sprintf ("%04o", $stat_mode & 07777); |
||
512 | $file_fullpath_esc =~ s/^\/(.*)$/$1/; |
||
513 | my ($sec,$min,$hour,$mday,$mon,$year) = localtime($stat_time); |
||
514 | $stat_time = sprintf ("%4.4d%2.2d%2.2d%2.2d%2.2d.%2.2d", |
||
515 | $year+1900,$mon+1,$mday,$hour,$min,$sec); |
||
516 | print DIRS "mkdir -p '$file_fullpath_esc'\n"; |
||
517 | print DIRS "chmod $stat_mode '$file_fullpath_esc'\n"; |
||
518 | print DIRS "chown $stat_uid:$stat_gid '$file_fullpath_esc'\n"; |
||
519 | print DIRS "touch -t $stat_time '$file_fullpath_esc'\n"; |
||
520 | if($stat_flags) { |
||
521 | print DIRS "chflags " |
||
522 | . ($stat_flags & UF_NODUMP ? "nodump," : "") |
||
523 | . ($stat_flags & UF_IMMUTABLE ? "uchg," : "") |
||
524 | . ($stat_flags & UF_APPEND ? "uappnd," : "") |
||
525 | . ($stat_flags & UF_OPAQUE ? "opaque," : "") |
||
526 | . ($stat_flags & UF_NOUNLINK ? "uunlnk," : "") |
||
527 | . ($stat_flags & SF_ARCHIVED ? "arch," : "") |
||
528 | . ($stat_flags & SF_IMMUTABLE ? "schg," : "") |
||
529 | . ($stat_flags & SF_APPEND ? "sappnd," : "") |
||
530 | . ($stat_flags & SF_NOUNLINK ? "sunlnk," : "") |
||
531 | . " '$file_fullpath_esc'\n"; |
||
532 | } |
||
533 | |||
1205 | dev | 534 | unless($stat_flags & UF_NODUMP) { |
535 | $cur_backup_size += int(length($file_fullpath)/100.0 + 1)*512; |
||
536 | if ($cfg_maximum_archive_size > 0 && $cur_backup_size + 10240 >= $cfg_maximum_archive_size){ |
||
537 | my $old_val = $cur_backup_size - $stat_size - int(length($file_fullpath)/100.0 + 1)*512; |
||
538 | my $tmp_pos= $#volume_position+1; |
||
539 | print "Volume $tmp_pos Done. Size: $old_val\n" if ($cfg_verbose == &VERB_ALL); |
||
540 | $cur_backup_size = $stat_size + int(length($file_fullpath)/100.0 + 1)*512 + 1536; |
||
541 | push @volume_position, $list_lines_cnt; |
||
542 | } |
||
543 | $active_hash_new{$file_fullpath} = "D"; |
||
544 | check_update($file_fullpath, "D", $file_fullpath, $stat_size); |
||
1149 | dev | 545 | } |
1205 | dev | 546 | else { |
547 | push @fs_notpath, "$file_dir/$file_name"; # if nodump - skip all subitems |
||
548 | if ($cfg_stopdir_prune == 1){ |
||
549 | $File::Find::prune = 1; |
||
550 | return; |
||
551 | } |
||
552 | } |
||
1149 | dev | 553 | } |
554 | } else { |
||
555 | if ($cfg_stopdir_prune == 1){ |
||
556 | $File::Find::prune = 1; |
||
557 | return; |
||
558 | } |
||
559 | } |
||
1205 | dev | 560 | # Работаем с файлами |
561 | } elsif ((-f $file_fullpath) || (-l $file_fullpath)){ |
||
562 | if (check_path($file_dir, $file_name) == 1){ |
||
1227 | dev | 563 | ($tmp, $tmp, $stat_mode, $tmp, $stat_uid, $stat_gid, $tmp, $stat_size, $tmp, $stat_mtime, $stat_time, $tmp, $tmp, $tmp, $tmp, $tmp, $stat_flags, $tmp) = lstat($file_fullpath); |
1205 | dev | 564 | unless($stat_flags & UF_NODUMP) { |
565 | $checksum_stat= md5_base64("$stat_mtime/$stat_size/$stat_mode/$stat_uid/$stat_gid"); |
||
566 | # $file_fullpath_md5 = md5_base64($file_fullpath); |
||
567 | $file_fullpath_md5 = $file_fullpath; |
||
568 | if ($cfg_time_limit != 0 && $cur_time - $cfg_time_limit > $stat_mtime){ |
||
569 | print "Time limit: $cur_time - $cfg_time_limit > $stat_mtime, file $file_fullpath ignored.\n" if ($cfg_verbose == &VERB_ALL); |
||
570 | next; |
||
571 | } |
||
572 | if ($cfg_size_limit != 0 && $cfg_size_limit < $stat_size){ |
||
573 | print "Size limit: $cfg_size_limit < $stat_size, file $file_fullpath ignored.\n" if ($cfg_verbose == &VERB_ALL); |
||
574 | next; |
||
575 | } |
||
1149 | dev | 576 | |
1205 | dev | 577 | if (($cfg_checksum eq "md5") && (! -l $file_fullpath)){ |
578 | ($md5_checksum_stat, $tmp) = split(/\s+/, `$prog_md5sum '$file_fullpath_esc'`); |
||
579 | $active_hash_new{$file_fullpath_md5} = "$checksum_stat/$md5_checksum_stat"; |
||
580 | check_update($file_fullpath, "$checksum_stat/$md5_checksum_stat", $file_fullpath_md5, $stat_size); |
||
581 | } else { |
||
582 | $active_hash_new{$file_fullpath} = $checksum_stat; |
||
583 | check_update($file_fullpath, $checksum_stat, $file_fullpath, $stat_size); |
||
584 | } |
||
585 | } |
||
586 | } |
||
587 | } |
||
1149 | dev | 588 | } |
589 | |||
590 | ############################################### |
||
1153 | dev | 591 | # Проверяем изменился ли файл или нет, если да апдейтим лог. |
1149 | dev | 592 | sub check_update{ |
593 | my ($file, $checksum, $filesum, $stat_size) = @_; |
||
594 | |||
1154 | dev | 595 | if (($active_hash_last{$filesum} ne $checksum) && ($checksum ne "D")){ |
1149 | dev | 596 | if ($cfg_backup_style ne "hash"){ |
597 | $file =~ s/^\/(.*)$/$1/; |
||
598 | print LIST "$file\n"; |
||
599 | |||
1153 | dev | 600 | # Обрабатываем случай разбиения гиганских архивов. |
1149 | dev | 601 | if (-l "/$file"){ |
602 | $stat_size = 0; |
||
603 | } |
||
604 | $cur_backup_size += $stat_size + int(length($file)/100.0 + 1)*512; |
||
605 | # print "$cur_backup_size:$stat_size:$file\n"; |
||
606 | if ($cfg_maximum_archive_size > 0 && $cur_backup_size + 10240 >= $cfg_maximum_archive_size){ |
||
607 | my $old_val = $cur_backup_size - $stat_size - int(length($file)/100.0 + 1)*512; |
||
608 | my $tmp_pos= $#volume_position+1; |
||
609 | print "Volume $tmp_pos Done. Size: $old_val\n" if ($cfg_verbose == &VERB_ALL); |
||
610 | $cur_backup_size = $stat_size + int(length($file)/100.0 + 1)*512 + 1536; |
||
611 | push @volume_position, $list_lines_cnt; |
||
612 | } |
||
613 | |||
614 | } |
||
615 | $list_lines_cnt++; |
||
616 | } |
||
1154 | dev | 617 | if(($active_hash_last{$filesum} eq "D") && ($checksum ne "D") |
618 | || ($active_hash_last{$filesum} ne "D") && ($checksum eq "D")) |
||
619 | { |
||
620 | # if old entry was a directory and now it's file or link or vice versa, leave it in hash |
||
621 | # to add it later to the delete list |
||
1149 | dev | 622 | } |
1154 | dev | 623 | else { |
624 | delete $active_hash_last{$filesum}; |
||
625 | if (defined $dbobj_last){ |
||
626 | $dbobj_last->del($filesum); |
||
627 | } |
||
628 | } |
||
1149 | dev | 629 | } |
630 | |||
631 | ############################################### |
||
1153 | dev | 632 | # 0 - не добавлять файл |
633 | # 1 - добавть файл |
||
1149 | dev | 634 | |
635 | sub check_path { |
||
636 | my ($dir_name, $file_name) = @_; |
||
637 | my ($item, $path); |
||
638 | |||
639 | $path = "$dir_name/$file_name"; |
||
640 | |||
1151 | dev | 641 | |
642 | |||
1149 | dev | 643 | foreach $item (@fs_notmask){ |
644 | if ($path =~ /$item/){ |
||
645 | return 0; |
||
646 | } |
||
647 | } |
||
648 | |||
649 | foreach $item (@fs_notfilemask){ |
||
650 | if ($file_name =~ /$item/){ |
||
651 | return 0; |
||
652 | } |
||
653 | } |
||
654 | |||
655 | foreach $item (@fs_filemask){ |
||
656 | if ($file_name =~ /$item/){ |
||
657 | return 1; |
||
658 | } |
||
659 | } |
||
660 | |||
661 | foreach $item (@fs_notdirmask){ |
||
662 | if ($dir_name =~ /$item/){ |
||
663 | return 0; |
||
664 | } |
||
665 | } |
||
666 | |||
667 | |||
668 | foreach $item (@fs_mask){ |
||
669 | if ($path =~ /$item/){ |
||
670 | return 1; |
||
671 | } |
||
672 | } |
||
673 | |||
674 | foreach $item (@fs_dirmask){ |
||
675 | if ($dir_name =~ /$item/){ |
||
676 | return 1; |
||
677 | } |
||
678 | } |
||
679 | |||
680 | |||
681 | foreach $item (@fs_notpath){ |
||
682 | if (($dir_name eq $item) || ($path eq $item) || ($dir_name =~ /^$item\//)){ |
||
683 | return 0; |
||
684 | } |
||
685 | } |
||
686 | |||
687 | return 1; |
||
688 | } |
||
689 | ############################################### |
||
1153 | dev | 690 | # Устанавливаем соединение с удаленным сервером по FTP. |
1149 | dev | 691 | |
692 | sub ftp_connect{ |
||
693 | if ( $cfg_type eq "remote_ftp"){ |
||
1151 | dev | 694 | $ftp = Net::FTP->new($cfg_remote_host, Timeout => 30, Debug => 0) || die "Can't connect to ftp server.\n"; |
1149 | dev | 695 | $ftp->login($cfg_remote_login, $cfg_remote_password) || die "Can't login to ftp server.\n"; |
696 | $ftp->cwd($cfg_remote_path) || die "Path $cfg_remote_path not found on ftp server.\n"; |
||
697 | $ftp->binary(); |
||
698 | } |
||
699 | } |
||
700 | ############################################### |
||
1153 | dev | 701 | # Содание списка файлов для помещения в определенный том многотомного архива. |
1149 | dev | 702 | |
1154 | dev | 703 | sub create_tmp_list{ |
1149 | dev | 704 | my ($arc_block_level, $position1, $position2, $full_list_path) = @_; |
705 | my ($tmp_list_path, $pos_counter); |
||
706 | |||
707 | if ($arc_block_level == 0 && $position1 == 0 && $position2 eq ''){ |
||
708 | $tmp_list_path = $full_list_path; |
||
709 | } else { |
||
710 | $pos_counter = 0; |
||
711 | $tmp_list_path = "$full_list_path.$arc_block_level"; |
||
712 | open(FULL_LIST, "<$full_list_path")|| die "Can't open full list $full_list_path\n"; |
||
713 | flock(FULL_LIST, 1); |
||
714 | open(TMP_LIST, ">$tmp_list_path")|| die "Can't create temp list $tmp_list_path\n"; |
||
715 | flock(TMP_LIST, 2); |
||
716 | while(<FULL_LIST>){ |
||
717 | if (($pos_counter >= $position1) && ($pos_counter < $position2 || $position2 eq '')){ |
||
718 | print TMP_LIST $_; |
||
719 | } |
||
720 | $pos_counter++; |
||
721 | } |
||
722 | close(TMP_LIST); |
||
723 | close(FULL_LIST); |
||
724 | } |
||
725 | return $tmp_list_path; |
||
726 | } |
||
727 | ############################################### |
||
728 | ############################################### |
||
729 | |||
730 | __END__ |
||
731 | |||
732 | =head1 NAME |
||
733 | |||
734 | fsbackup - file system backup and synchronization utility. |
||
735 | |||
736 | =head1 SYNOPSIS |
||
737 | |||
738 | fsbackup.pl [options] <configuration file> |
||
739 | |||
740 | =head1 DESCRIPTION |
||
741 | |||
742 | C<fsbackup.pl> is a incremental backup creation utility. |
||
743 | C<fsbackup.pl> support backup compression and encryption. Backup can be stored |
||
744 | on local file system and on remote host stored over SSH or FTP. Some addition |
||
745 | scripts allow backups SQL tables from PostgreSQL and MySQL (C<pgsql_backup.sh> |
||
746 | and C<mysql_backup.sh>)), save system configuration files and list of installed |
||
747 | packages (C<sysbackup.sh>). |
||
748 | Backuped with C<fsbackup.pl> files can be recovered by script C<fsrestore.sh>, |
||
749 | backuped with C<sysbackup.sh> system packeges can be reinstalled by C<sysrestore.sh> |
||
750 | |||
751 | =head1 OPTIONS |
||
752 | |||
753 | The following command-line options can be used with C<fsbackup.pl>: |
||
754 | |||
755 | =over |
||
756 | |||
757 | =item C<-n> |
||
758 | |||
759 | Create new backup without checking files in previously stored hash. |
||
760 | |||
761 | =item C<-f> |
||
762 | |||
763 | Create full backup, like as C<-n> option. |
||
764 | |||
765 | =item C<-h> |
||
766 | |||
767 | Only rebuild hash, no storing files in backup archive. |
||
768 | |||
769 | =item C<-c> |
||
770 | |||
771 | Clean incremental backup storage and create new full backup without checking |
||
772 | $cfg_increment_level config parameter. |
||
773 | |||
774 | =head1 ADDITION SCRIPTS |
||
775 | |||
776 | =item C<create_backup.sh> |
||
777 | |||
778 | Backup planner running from C<crontab>. For example: |
||
779 | |||
1154 | dev | 780 | 18 4 * * * /root/backup/fsbackup/create_backup.sh |
1149 | dev | 781 | |
782 | =item C<install.pl> |
||
783 | |||
784 | Script to install fsbackup package and some required perl modules. |
||
785 | |||
786 | =item C<fsbackup.pl> |
||
787 | |||
788 | File system backup utility. |
||
789 | |||
790 | =item C<cfg_example> |
||
791 | |||
792 | Example of configuration file. |
||
793 | |||
794 | =item C<scripts/pgsql_backup.sh> |
||
795 | |||
796 | =item C<scripts/mysql_backup.sh> |
||
797 | |||
798 | Script for backup SQL tables from PostreSQL and MySQL. |
||
799 | |||
800 | =item C<scripts/sysbackup.sh> |
||
801 | |||
802 | Script for store system configuration files and information about installed |
||
803 | packages. |
||
804 | |||
805 | =item C<scripts/fsrestore.sh> |
||
806 | |||
807 | Script for restore files backuped by C<fsbackup.pl>. |
||
808 | |||
809 | =item C<scripts/sysrestore.sh> |
||
810 | |||
811 | Script for reinstall packages stored by C<sysbackup.sh>. |
||
812 | |||
813 | |||
814 | =head1 CONFIGURATION FILE |
||
815 | |||
816 | =item B<$cfg_backup_name> = 'test_host' |
||
817 | |||
818 | Name of backup, single word. |
||
819 | |||
1154 | dev | 820 | =item B<$cfg_cache_dir> = '/root/backup/fsbackup/cache' |
1149 | dev | 821 | |
822 | Path of internal cache directory for local backup method. |
||
823 | |||
824 | =item B<$prog_md5sum> = 'md5sum -b' |
||
825 | |||
826 | =item B<$prog_tar> = 'tar' |
||
827 | |||
828 | =item B<$prog_ssh> = 'ssh' |
||
829 | |||
830 | =item B<$prog_rm> = 'rm' |
||
831 | |||
832 | =item B<$prog_gzip> = 'gzip' |
||
833 | |||
834 | =item B<$prog_pgp> = 'gpg' |
||
835 | |||
836 | Full path of some external program running from C<fsbackup.pl>. |
||
837 | B<$prog_gzip = ''> - not use compression, B<$prog_pgp = ''> - not use |
||
838 | encryption. |
||
839 | |||
840 | =item B<$cfg_checksum> = 'timesize' |
||
841 | |||
842 | File checksum method: |
||
843 | |||
844 | timesize - checksum of file attributes (default, best speed) |
||
845 | |||
846 | md5 - checksum of file attributes + MD5 checksum of file content. |
||
847 | |||
848 | =item B<$cfg_backup_style> = 'backup' |
||
849 | |||
850 | Backup style: |
||
851 | |||
852 | backup - incremental backup (copy only new and changed files). |
||
853 | |||
854 | full_backup - full backup (copy all files). |
||
855 | |||
856 | sync - file tree synchronization. |
||
857 | |||
858 | hash - hash creation without storing archive (spying for new or changed files). |
||
859 | |||
860 | =item B<$cfg_increment_level> = 7 |
||
861 | |||
862 | Incremental level (after how many incremental copy make full refresh of backup) |
||
863 | |||
864 | =item B<$cfg_type> = 'remote_ssh' |
||
865 | |||
866 | Type of backup storage: |
||
867 | |||
868 | local - store backup on local file system. |
||
869 | remote_ssh - store backup on remote host over SSH connection. |
||
870 | remote_ftp - store backup on remote FTP server. |
||
871 | |||
872 | |||
873 | =item B<$cfg_remote_host> = 'backup-server.test.ru' |
||
874 | |||
875 | =item B<$cfg_remote_login> = 'backup_login' |
||
876 | |||
877 | =item B<$cfg_remote_path> = '/home/backup_login/backup' |
||
878 | |||
879 | Connection parameters for remote_ssh storage type. |
||
880 | |||
881 | =item B<$cfg_remote_password> = 'Test1234' |
||
882 | |||
883 | Password of remote login for remote_ftp storage type. |
||
884 | |||
885 | =item B<$cfg_local_path> = '/var/backup/' |
||
886 | |||
887 | Path of directory to store backup on local file system for local storage type. |
||
888 | |||
889 | =item B<$cfg_time_limit> = 0 |
||
890 | |||
891 | Limit of file creation time in days. If not 0, don't backup files created or |
||
892 | modified later then $cfg_time_limit (days). |
||
893 | |||
894 | =item B<$cfg_size_limit> = 0 |
||
895 | |||
896 | Limit of maximum file size. If not 0, don't backup files witch size more then |
||
897 | $cfg_time_limit kilobytes. |
||
898 | |||
899 | =item B<$cfg_root_path> = '/' |
||
900 | |||
901 | Root path for initial chdir. |
||
902 | |||
903 | =item B<$cfg_pgp_userid> = '' |
||
904 | |||
905 | Name of user in public key ring with public key will be used for PGP encryption. |
||
906 | Not use encryption if not set. |
||
907 | |||
908 | =item B<$cfg_verbose> = 3 |
||
909 | |||
910 | Verbose level. |
||
911 | |||
912 | |||
913 | 1 - Output errors and warnings. |
||
914 | 2 - Output all the available data. |
||
915 | |||
916 | =item B<$cfg_save_old_backup> = 1 |
||
917 | |||
918 | Save previous backup to OLD directory before rotation or before storing full backup. |
||
919 | |||
920 | |||
921 | 1 - save old backup. |
||
922 | |||
923 | =item B<$cfg_maximum_archive_size> = 0 |
||
924 | |||
925 | Size of maximum size (in KiloBytes) of single unpacked archive file (0 - unlimited file size). |
||
926 | |||
927 | =item B<$cfg_stopdir_prune> = 0 |
||
928 | |||
929 | Recursive review of the prohibited directories. |
||
930 | |||
931 | 1 - not use a recursive entrance to directory prohibited for backup (speed is increased, reduces flexibility of customization). |
||
932 | |||
933 | =item B<__DATA__> - list of backuped path and regexp mask. |
||
934 | |||
935 | /dir[/file] - backup file or directory. |
||
936 | !/dir[/file] - NOT include this file or directory to backup. |
||
937 | # - ignore this line. |
||
938 | |||
939 | Mask: |
||
940 | |||
941 | =~ - regexp mask for include file or directory to backup. |
||
942 | f~ - regexp file mask for include file to backup. |
||
943 | d~ - regexp directory mask for include directory to backup. |
||
944 | =! - regexp mask for NOT include file or directory to backup. |
||
945 | f! - regexp file mask for NOT include file to backup. |
||
946 | d! - regexp directory mask for NOT include directory to backup. |
||
947 | |||
948 | |||
949 | Operation priority: |
||
950 | |||
951 | 1. =! |
||
952 | 2. f! |
||
953 | 3. f~ |
||
954 | 4. d! |
||
955 | 5. =~ |
||
956 | 6. d~ |
||
957 | 7. !path |
||
958 | 8. path |
||
959 | |||
960 | |||
961 | =head1 COPYRIGHT |
||
962 | |||
963 | Copyright (c) 2001 by Maxim Chirkov <mc@tyumen.ru> |
||
964 | http://www.opennet.ru/dev/fsbackup/ |
||
965 | |||
966 | =head1 BUGS |
||
967 | |||
968 | Look TODO file. |
||
969 | |||
970 | =head1 AUTHORS |
||
971 | |||
972 | Maxim Chirkov <mc@tyumen.ru> |
||
973 | |||
974 | =cut |