Handle hostnames with upper-case letters
[webmin.git] / fsdump / linux-lib.pl
1 # linux-lib.pl
2
3 local $out = &backquote_command("dump -v 2>&1", 1);
4 if ($out =~ /dump\s+([0-9\.]+)b(\d+)/) {
5         $dump_version = "$1$2";
6         }
7 else {
8         $out = &backquote_command("dump --version 2>&1", 1);
9         if ($out =~ /dump\s+([0-9\.]+)b(\d+)/) {
10                 $dump_version = "$1$2";
11                 }
12         }
13
14 $supports_tar = 1;
15
16 # supported_filesystems()
17 # Returns a list of filesystem types on which dumping is supported
18 sub supported_filesystems
19 {
20 local @rv;
21 push(@rv, "ext2", "ext3", "ext4") if (&has_command("dump"));
22 push(@rv, "xfs") if (&has_command("xfsdump"));
23 return @rv;
24 }
25
26 # multiple_directory_support(fs)
27 # Returns 1 if some filesystem dump supports multiple directories
28 sub multiple_directory_support
29 {
30 return $_[0] eq "tar";
31 }
32
33 # dump_form(&dump)
34 sub dump_form
35 {
36 # Display destination options
37 print &ui_table_row(&hlink($text{'dump_dest'}, "dest"),
38    &ui_radio("mode", $_[0]->{'host'} ? 1 : 0,
39         [ [ 0, $text{'dump_file'}." ".
40                &ui_textbox("file", $_[0]->{'file'}, 50).
41                " ".&file_chooser_button("file")."<br>" ],
42           [ 1, &text('dump_host',
43                      &ui_textbox("host", $_[0]->{'host'}, 15),
44                      &ui_textbox("huser", $_[0]->{'huser'}, 8),
45                      &ui_textbox("hfile", $_[0]->{'hfile'}, 20)) ] ]), 3);
46
47 if ($_[0]->{'fs'} ne 'xfs') {
48         # Display remote target options
49         print &ui_table_row(&hlink($text{'dump_rsh'},"rsh"),
50                       &rsh_command_input("rsh_def", "rsh", $_[0]->{'rsh'}), 3);
51
52         # Password option for SSH
53         print &ui_table_row(&hlink($text{'dump_pass2'},"pass2"),
54                       &ui_password("pass", $_[0]->{'pass'}, 20), 3);
55         }
56 }
57
58 sub dump_options_form
59 {
60 local ($dump, $tds) = @_;
61 if ($_[0]->{'fs'} eq 'tar') {
62         # Display gnutar options
63         print &ui_table_row(&hlink($text{'dump_label'},"label"),
64                             &ui_textbox("label", $_[0]->{'label'}, 15),
65                             1, $tds);
66
67         print &ui_table_row(&hlink($text{'dump_blocks'},"blocks"),
68                             &ui_opt_textbox("blocks", $_[0]->{'blocks'}, 8,
69                                             $text{'dump_auto'})." kB",
70                             3, $tds);
71
72         print &ui_table_row(&hlink($text{'dump_exclude'}, "exclude"),
73                             &ui_textbox("exclude", $_[0]->{'exclude'}, 50),
74                             3, $tds);
75
76         print &ui_table_row(&hlink($text{'dump_gzip'},"gzip"),
77                             &ui_select("gzip", int($_[0]->{'gzip'}),
78                                 [ [ 0, $text{'no'} ],
79                                   [ 1, $text{'dump_gzip1'} ],
80                                   [ 2, $text{'dump_gzip2'} ] ]), 1, $tds);
81
82         print &ui_table_row(&hlink($text{'dump_multi'},"multi"),
83                             &ui_yesno_radio("multi", int($_[0]->{'multi'})),
84                             1, $tds);
85
86         print &ui_table_row(&hlink($text{'dump_links'},"links"),
87                             &ui_yesno_radio("links", int($_[0]->{'links'})),
88                             1, $tds);
89
90         print &ui_table_row(&hlink($text{'dump_xdev'},"xdev"),
91                             &ui_yesno_radio("xdev", int($_[0]->{'xdev'})),
92                             1, $tds);
93
94         print &ui_table_row(&hlink($text{'dump_notape'},"notape"),
95                             &ui_radio("notape", int($_[0]->{'notape'}),
96                                   [ [ 0, $text{'yes'} ], [ 1, $text{'no'} ] ]),
97                             1, $tds);
98
99         print &ui_table_row(&hlink($text{'dump_update2'},"tarupdate"),
100                             &ui_yesno_radio("update", int($_[0]->{'update'})),
101                             1, $tds);
102
103         print &ui_table_row(&hlink($text{'dump_ignoreread'},"ignoreread"),
104                             &ui_yesno_radio("ignoreread",
105                                             int($_[0]->{'ignoreread'})),
106                             1, $tds);
107         }
108 elsif ($_[0]->{'fs'} eq 'xfs') {
109         # Display xfs dump options
110         print &ui_table_row(&hlink($text{'dump_level'},"level"),
111                             &ui_select("level", int($_[0]->{'level'}),
112                                 [ map { [ $_, $text{'dump_level_'.$_} ] }
113                                       (0 .. 9) ]), 1, $tds);
114
115         print &ui_table_row(&hlink($text{'dump_label'},"label"),
116                             &ui_textbox("label", $_[0]->{'label'}, 15),
117                             1, $tds);
118
119         print &ui_table_row(&hlink($text{'dump_max'},"max"),
120             &ui_opt_textbox("max", $_[0]->{'max'}, 8,
121                             $text{'dump_unlimited'})." kB", 1, $tds);
122
123         print &ui_table_row(&hlink($text{'dump_attribs'},"attribs"),
124                             &ui_yesno_radio("attribs", int($_[0]->{'attribs'})),
125                             1, $tds);
126
127         print &ui_table_row(&hlink($text{'dump_over'},"over"),
128                             &ui_yesno_radio("over", int($_[0]->{'over'})),
129                             1, $tds);
130
131         print &ui_table_row(&hlink($text{'dump_invent'},"invent"),
132                             &ui_radio("noinvent", int($_[0]->{'noinvent'}),
133                               [ [ 0, $text{'yes'} ], [ 1, $text{'no'} ] ]),
134                             1, $tds);
135
136         print &ui_table_row(&hlink($text{'dump_overwrite'},"overwrite"),
137                     &ui_yesno_radio("overwrite", int($_[0]->{'overwrite'})),
138                     1, $tds);
139
140         print &ui_table_row(&hlink($text{'dump_erase'},"erase"),
141                             &ui_yesno_radio("erase", int($_[0]->{'erase'})),
142                             1, $tds);
143
144         print &ui_table_row(&hlink($text{'dump_bsize'},"bsize"),
145             &ui_opt_textbox("bsize", $_[0]->{'bsize'}, 8,
146                             $text{'default'})." kB", 1, $tds);
147         }
148 else {
149         # Display ext2/3 filesystem dump options
150         print &ui_table_row(&hlink($text{'dump_update'},"update"),
151                             &ui_yesno_radio("update", int($_[0]->{'update'})),
152                             1, $tds);
153
154         print &ui_table_row(&hlink($text{'dump_multi'},"multi"),
155                             &ui_yesno_radio("multi", int($_[0]->{'multi'})),
156                             1, $tds);
157
158         print &ui_table_row(&hlink($text{'dump_level'},"level"),
159                             &ui_select("level", int($_[0]->{'level'}),
160                                 [ map { [ $_, $text{'dump_level_'.$_} ] }
161                                       (0 .. 9) ]), 1, $tds);
162
163         print &ui_table_row(&hlink($text{'dump_label'},"label"),
164                             &ui_textbox("label", $_[0]->{'label'}, 15),
165                             1, $tds);
166
167         print &ui_table_row(&hlink($text{'dump_blocks'},"blocks"),
168                             &ui_opt_textbox("blocks", $_[0]->{'blocks'}, 8,
169                                             $text{'dump_auto'})." kB",
170                             3, $tds);
171
172         print &ui_table_row(&hlink($text{'dump_bsize'},"bsize"),
173             &ui_opt_textbox("bsize", $_[0]->{'bsize'}, 8,
174                             $text{'default'})." kB", 1, $tds);
175
176         print &ui_table_row(&hlink($text{'dump_honour'},"honour"),
177                             &ui_yesno_radio("honour", int($_[0]->{'honour'})),
178                             1, $tds);
179
180         if ($dump_version >= 0.424) {
181                 print &ui_table_row(&hlink($text{'dump_comp'},"comp"),
182                         &ui_opt_textbox("comp", $_[0]->{'comp'}, 4,
183                                         $text{'no'}, $text{'dump_complvl'}),
184                         3, $tds);
185                 }
186         }
187
188 # Re-mount option
189 print &ui_table_row(&hlink($text{'dump_remount'},"remount"),
190         &ui_yesno_radio("remount", int($_[0]->{'remount'})), 1, $tds);
191
192 if ($_[0]->{'fs'} eq 'tar') {
193         # rmt path option
194         print &ui_table_row(&hlink($text{'dump_rmt'},"rmt"),
195                 &ui_opt_textbox("rmt", $_[0]->{'rmt'}, 30, $text{'default'}),
196                 3, $tds);
197         }
198 }
199
200 # parse_dump(&dump)
201 sub parse_dump
202 {
203 # Parse destination options
204 if ($in{'mode'} == 0) {
205         $in{'file'} =~ /\S/ || &error($text{'dump_efile'});
206         $_[0]->{'file'} = $in{'file'};
207         delete($_[0]->{'host'});
208         delete($_[0]->{'huser'});
209         delete($_[0]->{'hfile'});
210         }
211 else {
212         &to_ipaddress($in{'host'}) ||
213             &to_ip6address($in{'host'}) ||
214                 &error($text{'dump_ehost'});
215         $_[0]->{'host'} = $in{'host'};
216         $in{'huser'} =~ /^\S*$/ || &error($text{'dump_ehuser'});
217         $in{'huser'} =~ /\@/ && &error($text{'dump_ehuser2'});
218         $_[0]->{'huser'} = $in{'huser'};
219         $in{'hfile'} || &error($text{'dump_ehfile'});
220         $_[0]->{'hfile'} = $in{'hfile'};
221         delete($_[0]->{'file'});
222         }
223
224 if ($_[0]->{'fs'} eq 'tar') {
225         # Parse tar options
226         $_[0]->{'rsh'} = &rsh_command_parse("rsh_def", "rsh");
227         $_[0]->{'pass'} = $in{'pass'};
228         $in{'label'} =~ /^\S*$/ && length($in{'label'}) < 16 ||
229                 &error($text{'dump_elabel'});
230         $_[0]->{'label'} = $in{'label'};
231         if ($in{'blocks_def'}) {
232                 delete($_[0]->{'blocks'});
233                 }
234         else {
235                 $in{'blocks'} =~ /^\d+$/ || &error($text{'dump_eblocks'});
236                 $_[0]->{'blocks'} = $in{'blocks'};
237                 $in{'gzip'} && &error($text{'dump_egzip'});
238                 }
239         $_[0]->{'exclude'} = $in{'exclude'};
240         $_[0]->{'gzip'} = $in{'gzip'};
241         $_[0]->{'multi'} = $in{'multi'};
242         $_[0]->{'links'} = $in{'links'};
243         $_[0]->{'xdev'} = $in{'xdev'};
244         if ($in{'update'} && $in{'rsh_def'} == 3) {
245                 # Cannot append via FTP
246                 &error($text{'dump_eftpupdate'});
247                 }
248         $_[0]->{'update'} = $in{'update'};
249         $_[0]->{'ignoreread'} = $in{'ignoreread'};
250         if ($in{'gzip'} && $in{'update'}) {
251                 &error($text{'dump_egzip3'});
252                 }
253         if ($in{'multi'}) {
254                 !-c $in{'file'} && !-b $in{'file'} ||
255                         &error($text{'dump_emulti'});
256                 $in{'gzip'} && &error($text{'dump_egzip2'});
257                 $in{'mode'} == 0 || &error($text{'dump_emulti2'});
258                 }
259         $_[0]->{'notape'} = $in{'notape'};
260         if ($in{'rmt_def'}) {
261                 delete($_[0]->{'rmt'});
262                 }
263         else {
264                 $in{'rmt'} =~ /^\S+$/ || &error($text{'dump_ermt'});
265                 $_[0]->{'rmt'} = $in{'rmt'};
266                 }
267         }
268 elsif ($_[0]->{'fs'} eq 'xfs') {
269         # Parse xfs options
270         &is_mount_point($in{'dir'}) || &error($text{'dump_emp'});
271         $in{'label'} =~ /^\S*$/ && length($in{'label'}) < 256 ||
272                 &error($text{'dump_elabel2'});
273         $_[0]->{'label'} = $in{'label'};
274         $_[0]->{'level'} = $in{'level'};
275         if ($in{'max_def'}) {
276                 delete($_[0]->{'max'});
277                 }
278         else {
279                 $in{'max'} =~ /^\d+$/ || &error($text{'dump_emax'});
280                 $_[0]->{'max'} = $in{'max'};
281                 }
282         $_[0]->{'noattribs'} = $in{'noattribs'};
283         $_[0]->{'over'} = $in{'over'};
284         $_[0]->{'noinvent'} = $in{'noinvent'};
285         $_[0]->{'overwrite'} = $in{'overwrite'};
286         $_[0]->{'erase'} = $in{'erase'};
287         if ($in{'bsize_def'}) {
288                 delete($_[0]->{'bsize'});
289                 }
290         else {
291                 $in{'bsize'} =~ /^\d+$/ || &error($text{'dump_ebsize'});
292                 $_[0]->{'bsize'} = $in{'bsize'};
293                 }
294         }
295 else {
296         # Parse ext2/3 dump options
297         $_[0]->{'rsh'} = &rsh_command_parse("rsh_def", "rsh");
298         $_[0]->{'pass'} = $in{'pass'};
299         if ($in{'update'}) {
300                 &is_mount_point($in{'dir'}) || &error($text{'dump_eupdatedir'});
301                 }
302         $_[0]->{'update'} = $in{'update'};
303         $_[0]->{'multi'} = $in{'multi'};
304         if ($in{'level'} > 0) {
305                 &is_mount_point($in{'dir'}) || &error($text{'dump_eleveldir'});
306                 }
307         $_[0]->{'level'} = $in{'level'};
308         $in{'label'} =~ /^\S*$/ && length($in{'label'}) < 16 ||
309                 &error($text{'dump_elabel'});
310         $_[0]->{'label'} = $in{'label'};
311         if ($in{'blocks_def'}) {
312                 delete($_[0]->{'blocks'});
313                 }
314         else {
315                 $in{'blocks'} =~ /^\d+$/ || &error($text{'dump_eblocks'});
316                 $_[0]->{'blocks'} = $in{'blocks'};
317                 }
318         if ($in{'bsize_def'}) {
319                 delete($_[0]->{'bsize'});
320                 }
321         else {
322                 $in{'bsize'} =~ /^\d+$/ || &error($text{'dump_ebsize'});
323                 $_[0]->{'bsize'} = $in{'bsize'};
324                 }
325         $_[0]->{'honour'} = $in{'honour'};
326         if ($in{'comp_def'} || !defined($in{'comp'})) {
327                 delete($_[0]->{'comp'});
328                 }
329         else {
330                 $in{'comp'} =~ /^[1-9]\d*$/ || &error($text{'dump_ecomp'});
331                 $_[0]->{'comp'} = $in{'comp'};
332                 }
333         }
334 $_[0]->{'remount'} = $in{'remount'};
335 }
336
337 # execute_dump(&dump, filehandle, escape, background-mode, [time])
338 # Executes a dump and displays the output
339 sub execute_dump
340 {
341 local $fh = $_[1];
342 local ($cmd);
343 local ($flag, $hfile) = &dump_flag($_[0], $_[4]);
344 local $tapecmd = $_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar' ? $multi_cmd :
345                  $_[0]->{'notape'} ? undef :
346                  $_[0]->{'multi'} ? undef :
347                  $_[3] && !$config{'nonewtape'} ? $newtape_cmd : $notape_cmd;
348 local @dirs = $_[0]->{'tabs'} ? split(/\t+/, $_[0]->{'dir'})
349                               : split(/\s+/, $_[0]->{'dir'});
350 if ($_[0]->{'fs'} eq 'tar') {
351         # tar format backup
352         $cmd = "tar ".($_[0]->{'update'} ? "-u" : "-c")." ".$flag;
353         $cmd .= " -V '$_[0]->{'label'}'" if ($_[0]->{'label'});
354         $cmd .= " -L $_[0]->{'blocks'}" if ($_[0]->{'blocks'});
355         $cmd .= " -z" if ($_[0]->{'gzip'} == 1);
356         $cmd .= " --bzip" if ($_[0]->{'gzip'} == 2);
357         $cmd .= " -M" if ($_[0]->{'multi'});
358         $cmd .= " -h" if ($_[0]->{'links'});
359         $cmd .= " --one-file-system" if ($_[0]->{'xdev'});
360         $cmd .= " -F \"$tapecmd $_[0]->{'id'}\""
361                 if (!$_[0]->{'gzip'} && $tapecmd);
362         $cmd .= " --rsh-command=".quotemeta($_[0]->{'rsh'})
363                 if ($_[0]->{'rsh'} && $_[0]->{'host'});
364         $cmd .= " --rmt-command=".quotemeta($_[0]->{'rmt'})
365                 if ($_[0]->{'rmt'});
366         $cmd .= " --ignore-failed-read" if ($_[0]->{'ignoreread'});
367         if ($_[0]->{'exclude'}) {
368                 foreach my $e (&split_quoted_string($_[0]->{'exclude'})) {
369                         $cmd .= " --exclude ".quotemeta($e);
370                         }
371                 }
372         $cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
373         $cmd .= " ".join(" ", map { "'$_'" } @dirs);
374         }
375 elsif ($_[0]->{'fs'} eq 'xfs') {
376         # xfs backup
377         $cmd = "xfsdump -l $_[0]->{'level'}";
378         $cmd .= $flag;
379         $cmd .= " -L '$_[0]->{'label'}'" if ($_[0]->{'label'});
380         $cmd .= " -M '$_[0]->{'label'}'" if ($_[0]->{'label'});
381         $cmd .= " -z '$_[0]->{'max'}'" if ($_[0]->{'max'});
382         $cmd .= " -A" if ($_[0]->{'noattribs'});
383         $cmd .= " -F" if ($_[0]->{'over'});
384         $cmd .= " -J" if ($_[0]->{'noinvent'});
385         $cmd .= " -o" if ($_[0]->{'overwrite'});
386         $cmd .= " -E -F" if ($_[0]->{'erase'});
387         $cmd .= " -b $_[0]->{'bsize'}" if ($_[0]->{'bsize'});
388         $cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
389         $cmd .= " ".join(" ", map { "'$_'" } @dirs);
390         }
391 else {
392         # ext2/3 backup
393         $cmd = "dump -$_[0]->{'level'}";
394         $cmd .= $flag;
395         $cmd .= " -u" if ($_[0]->{'update'});
396         $cmd .= " -M" if ($_[0]->{'multi'});
397         $cmd .= " -L '$_[0]->{'label'}'" if ($_[0]->{'label'});
398         $cmd .= " -B $_[0]->{'blocks'}" if ($_[0]->{'blocks'});
399         $cmd .= " -b $_[0]->{'bsize'}" if ($_[0]->{'bsize'});
400         $cmd .= " -h0" if ($_[0]->{'honour'});
401         $cmd .= " -j$_[0]->{'comp'}" if ($_[0]->{'comp'});
402         $cmd .= " -F \"$tapecmd $_[0]->{'id'}\"" if ($tapecmd);
403         $cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
404         $cmd .= " '$_[0]->{'dir'}'";
405         if ($_[0]->{'rsh'}) {
406                 $cmd = "RSH=\"$_[0]->{'rsh'}\" RMT=\"touch '$hfile'; /etc/rmt\" $cmd";
407                 }
408         else {
409                 $cmd = "RMT=\"touch '$hfile'; /etc/rmt\" $cmd";
410                 }
411         }
412
413 &system_logged("sync");
414 sleep(1);
415
416 # Remount with noatime, if needed
417 if ($_[0]->{'remount'}) {
418         local @fs = &directory_filesystem($dirs[0]);
419         &mount::parse_options($fs[2], $fs[3]);
420         $mount::options{'noatime'} = '';
421         $fs[3] = &mount::join_options($fs[2]);
422         local $err = &mount::remount_dir(@fs);
423         if ($err) {
424                 $err =~ s/<[^>]*>//g;
425                 print $fh "Failed to re-mount with noatime option : $err\n";
426                 return 0;
427                 }
428         }
429
430 # Run the command, which may call SSH to do a remote login
431 $ENV{'DUMP_PASSWORD'} = $_[0]->{'pass'};
432 local $got = &run_ssh_command($cmd, $fh, $_[2], $_[0]->{'pass'});
433 if ($_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar') {
434         # Run multi-file switch command one last time
435         &execute_command("$multi_cmd $_[0]->{'id'} >/dev/null 2>&1");
436         }
437
438 # Remount with atime option
439 if ($_[0]->{'remount'}) {
440         local @fs = &directory_filesystem($dirs[0]);
441         &mount::parse_options($fs[2], $fs[3]);
442         delete($mount::options{'noatime'});
443         $mount::options{'atime'} = '';
444         $fs[3] = &mount::join_options($fs[2]);
445         local $err = &mount::remount_dir(@fs);
446         }
447
448 return $got ? 0 : 1;
449 }
450
451 # dump_flag(&dump, at-time)
452 # Given a dump, returns the -f flag and server-side file
453 sub dump_flag
454 {
455 local ($flag, $hfile);
456 if ($_[0]->{'huser'}) {
457         $hfile = &date_subs($_[0]->{'hfile'}, $_[1]);
458         $flag = " -f '$_[0]->{'huser'}\@$_[0]->{'host'}:$hfile'";
459         }
460 elsif ($_[0]->{'host'}) {
461         $hfile = &date_subs($_[0]->{'hfile'}, $_[1]);
462         $flag = " -f '$_[0]->{'host'}:$hfile'";
463         }
464 else {
465         $flag = " -f '".&date_subs($_[0]->{'file'}, $_[1])."'";
466         }
467 return ($flag, $hfile);
468 }
469
470 # verify_dump(&dump, filehandle, escape, background-mode, [time])
471 # Verifies a dump, returning 1 if OK and 0 if not
472 sub verify_dump
473 {
474 # Build verify command
475 local $fh = $_[1];
476 local $vcmd;
477 local ($flag, $hfile) = &dump_flag($_[0], $_[4]);
478 if ($_[0]->{'fs'} eq "tar") {
479         $vcmd = "tar -t -v";
480         $vcmd .= " -z" if ($_[0]->{'gzip'} == 1);
481         $vcmd .= " --bzip" if ($_[0]->{'gzip'} == 2);
482         $vcmd .= " -M" if ($_[0]->{'multi'});
483         }
484 elsif ($_[0]->{'fs'} eq "xfs") {
485         $vcmd = "xfsrestore -t";
486         }
487 else {
488         $vcmd = "restore -t";
489         $vcmd .= " -M" if ($_[0]->{'multi'});
490         }
491 $vcmd .= $flag;
492 if ($_[0]->{'fs'} eq "tar") {
493         $vcmd .= " --rsh-command=$_[0]->{'rsh'}"
494                 if ($_[0]->{'rsh'} && $_[0]->{'host'});
495         }
496 elsif ($_[0]->{'fs'} ne "xfs") {
497         if ($_[0]->{'rsh'}) {
498                 $vcmd = "RSH=\"$_[0]->{'rsh'}\" RMT=\"touch '$hfile'; /etc/rmt\" $vcmd";
499                 }
500         else {
501                 $vcmd = "RMT=\"touch '$hfile'; /etc/rmt\" $vcmd";
502                 }
503         }
504
505 # Run it
506 $vcmd .= " >/dev/null";
507 local $vgot = &run_ssh_command($vcmd, $fh, $_[2], $_[0]->{'pass'});
508 return $vgot ? 0 : 1;
509 }
510
511 # dump_dest(&dump)
512 sub dump_dest
513 {
514 if ($_[0]->{'file'}) {
515         return "<tt>".&html_escape($_[0]->{'file'})."</tt>";
516         }
517 else {
518         return "<tt>".&html_escape("$_[0]->{'host'}:$_[0]->{'hfile'}")."</tt>";
519         }
520 }
521
522 # missing_restore_command(filesystem)
523 sub missing_restore_command
524 {
525 local $cmd = $_[0] eq 'xfs' ? 'xfsrestore' : 'restore';
526 return &has_command($cmd) ? undef : $cmd;
527 }
528
529 # restore_form(filesystem, [&dump], &tds)
530 sub restore_form
531 {
532 local ($fs, $dump, $tds) = @_;
533
534 # Restore from
535 print &ui_table_row(&hlink($text{'restore_src'}, "rsrc"),
536    &ui_radio("mode", $_[1]->{'host'} ? 1 : 0,
537         [ [ 0, $text{'dump_file'}." ".
538                &ui_textbox("file", $_[1]->{'file'}, 50).
539                " ".&file_chooser_button("file")."<br>" ],
540           [ 1, &text('dump_host',
541                      &ui_textbox("host", $_[1]->{'host'}, 15),
542                      &ui_textbox("huser", $_[1]->{'huser'}, 8),
543                      &ui_textbox("hfile", $_[1]->{'hfile'}, 20)) ] ]), 3, $tds);
544
545 if ($_[0] eq 'tar') {
546         # tar restore options
547         print &ui_table_row(&hlink($text{'restore_rsh'},"rrsh"),
548                       &rsh_command_input("rsh_def", "rsh", $_[1]->{'rsh'}),
549                       3, $tds);
550
551         # Password option for SSH
552         print &ui_table_row(&hlink($text{'dump_pass2'},"passs"),
553                       &ui_password("pass", $_[1]->{'pass'}, 20),
554                       3, $tds);
555
556         # Files to restore
557         print &ui_table_row(&hlink($text{'restore_files'},"rfiles"),
558                       &ui_opt_textbox("files", undef, 40, $text{'restore_all'},
559                                       $text{'restore_sel'}), 3, $tds);
560
561         # Target dir
562         print &ui_table_row(&hlink($text{'restore_dir'},"rdir"),
563                       &ui_textbox("dir", undef, 50)." ".
564                       &file_chooser_button("dir", 1), 3, $tds);
565
566         # Restore permissions?
567         print &ui_table_row(&hlink($text{'restore_perms'},"perms"),
568                       &ui_yesno_radio("perms", 1), 1, $tds);
569
570         # Uncompress?
571         print &ui_table_row(&hlink($text{'restore_gzip'},"rgzip"),
572                       &ui_select("gzip", $_[1]->{'gzip'},
573                                 [ [ 0, $text{'no'} ],
574                                   [ 1, $text{'dump_gzip1'} ],
575                                   [ 2, $text{'dump_gzip2'} ] ]), 1, $tds);
576
577         print &ui_table_row(&hlink($text{'restore_keep'},"keep"),
578                       &ui_yesno_radio("keep", 0), 1, $tds);
579
580         # Multiple files
581         print &ui_table_row(&hlink($text{'restore_multi'},"rmulti"),
582                       &ui_yesno_radio("multi", 0), 1, $tds);
583         }
584 elsif ($_[0] eq 'xfs') {
585         # xfs restore options
586
587         # Target dir
588         print &ui_table_row(&hlink($text{'restore_dir'},"rdir"),
589                       &ui_textbox("dir", undef, 50)." ".
590                       &file_chooser_button("dir", 1), 3, $tds);
591
592         # Overwrite
593         print &ui_table_row(&hlink($text{'restore_over'},"rover"),
594                 &ui_radio("over", 0, [ [ 0, $text{'restore_over0'} ],
595                                        [ 1, $text{'restore_over1'} ],
596                                        [ 2, $text{'restore_over2'} ] ]),
597                 3, $tds);
598
599         # Attributes?
600         print &ui_table_row(&hlink($text{'restore_noattribs'},"rnoattribs"),
601                 &ui_radio("noattribs", 0, [ [ 0, $text{'yes'} ],
602                                             [ 1, $text{'no'} ] ]), 1, $tds);
603
604         # Label to restore from
605         print &ui_table_row(&hlink($text{'restore_label'},"rlabel"),
606                 &ui_textbox("label", undef, 20), 1, $tds);
607         }
608 else {
609         # ext2/3 restore options
610         print &ui_table_row(&hlink($text{'restore_rsh'},"rrsh"),
611                       &rsh_command_input("rsh_def", "rsh", $_[1]->{'rsh'}),
612                       3, $tds);
613
614         # Password option for SSH
615         print &ui_table_row(&hlink($text{'dump_pass2'},"passs"),
616                       &ui_password("pass", $_[1]->{'pass'}, 20),
617                       3, $tds);
618
619         # Files to restore
620         print &ui_table_row(&hlink($text{'restore_files'},"rfiles"),
621                       &ui_opt_textbox("files", undef, 40, $text{'restore_all'},
622                                       $text{'restore_sel'}), 3, $tds);
623
624         # Target dir
625         print &ui_table_row(&hlink($text{'restore_dir'},"rdir"),
626                       &ui_textbox("dir", undef, 50)." ".
627                       &file_chooser_button("dir", 1), 3, $tds);
628
629         # Multiple files
630         print &ui_table_row(&hlink($text{'restore_multi'},"rmulti"),
631                       &ui_yesno_radio("multi", 0), 1, $tds);
632         }
633
634 # Show only
635 print &ui_table_row(&hlink($text{'restore_test'},"rtest"),
636               &ui_yesno_radio("test", 1), 1, $tds);
637 }
638
639 # parse_restore(filesystem)
640 # Parses inputs from restore_form() and returns a command to be passed to
641 # restore_backup()
642 sub parse_restore
643 {
644 local $cmd;
645 if ($_[0] eq 'tar') {
646         $cmd = "tar";
647         if ($in{'test'}) {
648                 $cmd .= " -t -v";
649                 }
650         else {
651                 $cmd .= " -x";
652                 }
653         }
654 elsif ($_[0] eq 'xfs') {
655         $cmd = "xfsrestore";
656         $cmd .= " -t" if ($in{'test'});
657         }
658 else {
659         $cmd = "restore";
660         $cmd .= ($in{'test'} ? " -t" : " -x");
661         }
662 if ($in{'mode'} == 0) {
663         $in{'file'} || &error($text{'restore_efile'});
664         $cmd .= " -f '$in{'file'}'";
665         }
666 else {
667         &to_ipaddress($in{'host'}) ||
668             &to_ip6address($in{'host'}) ||
669                 &error($text{'restore_ehost'});
670         $in{'huser'} =~ /^\S*$/ || &error($text{'restore_ehuser'});
671         $in{'hfile'} || &error($text{'restore_ehfile'});
672         if ($in{'huser'}) {
673                 $cmd .= " -f '$in{'huser'}\@$in{'host'}:$in{'hfile'}'";
674                 }
675         else {
676                 $cmd .= " -f '$in{'host'}:$in{'hfile'}'";
677                 }
678         }
679 if ($_[0] eq 'tar') {
680         # parse tar options
681         $cmd .= " -p" if ($in{'perms'});
682         $cmd .= " -z" if ($in{'gzip'} == 1);
683         $cmd .= " --bzip" if ($in{'gzip'} == 2);
684         $cmd .= " -k" if ($in{'keep'});
685         if ($in{'multi'}) {
686                 !-c $in{'file'} && !-b $in{'file'} ||
687                         &error($text{'restore_emulti'});
688                 $in{'mode'} == 0 || &error($text{'restore_emulti2'});
689                 $cmd .= " -M -F \"$rmulti_cmd $in{'file'}\"";
690                 }
691         local $rsh = &rsh_command_parse("rsh_def", "rsh");
692         if ($rsh) {
693                 $cmd .= " --rsh-command=".quotemeta($rsh);
694                 }
695         $cmd .= " $in{'extra'}" if ($in{'extra'});
696         if (!$in{'files_def'}) {
697                 $in{'files'} || &error($text{'restore_efiles'});
698                 $cmd .= " $in{'files'}";
699                 }
700         -d $in{'dir'} || &error($text{'restore_edir'});
701         $cmd = "cd '$in{'dir'}' && $cmd";
702         if ($in{'multi'}) {
703                 $cmd = "$rmulti_cmd $in{'file'} 1 && $cmd";
704                 }
705         }
706 elsif ($_[0] eq 'xfs') {
707         # parse xfs options
708         $cmd .= " -E" if ($in{'over'} == 1);
709         $cmd .= " -e" if ($in{'over'} == 2);
710         $cmd .= " -A" if ($in{'noattribs'});
711         $cmd .= " -L '$in{'label'}'" if ($in{'label'});
712         $cmd .= " -F";
713         $cmd .= " $in{'extra'}" if ($in{'extra'});
714         if (!$in{'test'}) {
715                 -d $in{'dir'} || &error($text{'restore_edir'});
716                 $cmd .= " '$in{'dir'}'";
717                 }
718         }
719 else {
720         # parse ext2/3 options
721         local $rsh = &rsh_command_parse("rsh_def", "rsh");
722         if ($rsh) {
723                 $cmd = "RSH=\"$rsh\" $cmd";
724                 }
725
726         if ($in{'multi'}) {
727                 $cmd .= " -M";
728                 if ($dump_version >= 0.428 && $in{'extra'} !~ /-a/) {
729                         $cmd .= " -a";
730                         }
731                 }
732         $cmd .= " -u";          # force overwrite
733         $cmd .= " $in{'extra'}" if ($in{'extra'});
734         if (!$in{'files_def'}) {
735                 $in{'files'} || &error($text{'restore_efiles'});
736                 $cmd .= " $in{'files'}";
737                 }
738         -d $in{'dir'} || &error($text{'restore_edir'});
739         }
740 return $cmd;
741 }
742
743 # restore_backup(filesystem, command)
744 # Restores a backup based on inputs from restore_form(), and displays the results
745 sub restore_backup
746 {
747 &additional_log('exec', undef, $_[1]);
748 $ENV{'DUMP_PASSWORD'} = $in{'pass'};
749 if ($_[0] eq 'xfs') {
750         # Just run the xfsrestore command
751         &open_execute_command(CMD, "$_[1] 2>&1 </dev/null", 1);
752         while(<CMD>) {
753                 print &html_escape($_);
754                 }
755         close(CMD);
756         return $? || undef;
757         }
758 else {
759         # Need to supply prompts
760         &foreign_require("proc", "proc-lib.pl");
761         local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "cd '$in{'dir'}' ; $_[1]");
762         local $donevolume;
763         while(1) {
764                 local $rv = &wait_for($fh, "(.*next volume #)", "(.*set owner.mode for.*\\[yn\\])", "((.*)\\[yn\\])", "(.*enter volume name)", "password:", "yes\\/no", "(.*\\n)");
765                 last if ($rv < 0);
766                 print &html_escape($matches[1]);
767                 if ($rv == 0) {
768                         if ($donevolume++) {
769                                 return $text{'restore_evolume'};
770                                 }
771                         else {
772                                 syswrite($fh, "1\n", 2);
773                                 }
774                         }
775                 elsif ($rv == 1) {
776                         syswrite($fh, "n\n", 2);
777                         }
778                 elsif ($rv == 3) {
779                         syswrite($fh, "\n", 1);
780                         }
781                 elsif ($rv == 2) {
782                         return &text('restore_equestion',
783                                      "<tt>$matches[2]</tt>");
784                         }
785                 elsif ($rv == 4) {
786                         syswrite($fh, "$in{'pass'}\n");
787                         }
788                 elsif ($rv == 5) {
789                         syswrite($fh, "yes\n");
790                         }
791                 }
792         close($fh);
793         waitpid($fpid, 0);
794         return $? || undef;
795         }
796 }
797
798 1;
799