Handle hostnames with upper-case letters
[webmin.git] / fsdump / freebsd-lib.pl
1 # freebsd-lib.pl
2
3 # supported_filesystems()
4 # Returns a list of filesystem types on which dumping is supported
5 sub supported_filesystems
6 {
7 local @rv;
8 push(@rv, "ufs") if (&has_command("dump"));
9 return @rv;
10 }
11
12 # multiple_directory_support(fs)
13 # Returns 1 if some filesystem dump supports multiple directories
14 sub multiple_directory_support
15 {
16 return 0;
17 }
18
19 $supports_tar = 1;
20 $tar_command = &has_command("gtar") || &has_command("tar");
21
22 # dump_form(&dump)
23 sub dump_form
24 {
25 # Display common options
26 print &ui_table_row(&hlink($text{'dump_dest'}, "dest"),
27    &ui_radio("mode", $_[0]->{'host'} ? 1 : 0,
28         [ [ 0, $text{'dump_file'}." ".
29                &ui_textbox("file", $_[0]->{'file'}, 50).
30                " ".&file_chooser_button("file")."<br>" ],
31           [ 1, &text('dump_host',
32                      &ui_textbox("host", $_[0]->{'host'}, 15),
33                      &ui_textbox("huser", $_[0]->{'huser'}, 8),
34                      &ui_textbox("hfile", $_[0]->{'hfile'}, 20)) ] ]), 3);
35
36 if ($_[0]->{'fs'} eq 'tar') {
37         # Display gnutar options
38         print &ui_table_row(&hlink($text{'dump_rsh'},"rsh"),
39                 &rsh_command_input("rsh_def", "rsh", $_[0]->{'rsh'}), 3);
40
41         # Password option for SSH
42         print &ui_table_row(&hlink($text{'dump_pass'},"pass"),
43                 &ui_password("pass", $_[0]->{'pass'}, 20), 3);
44         }
45 }
46
47 sub dump_options_form
48 {
49 local ($dump, $tds) = @_;
50 if ($_[0]->{'fs'} eq 'tar') {
51         # Display gnutar options
52         print &ui_table_row(&hlink($text{'dump_label'},"label"),
53                             &ui_textbox("label", $_[0]->{'label'}, 15),
54                             1, $tds);
55
56         print &ui_table_row(&hlink($text{'dump_blocks'},"blocks"),
57                             &ui_opt_textbox("blocks", $_[0]->{'blocks'}, 8,
58                                             $text{'dump_auto'})." kB",
59                             3, $tds);
60
61         print &ui_table_row(&hlink($text{'dump_gzip'},"gzip"),
62                             &ui_select("gzip", int($_[0]->{'gzip'}),
63                                 [ [ 0, $text{'no'} ],
64                                   [ 1, $text{'dump_gzip1'} ] ]), 1, $tds);
65
66         print &ui_table_row(&hlink($text{'dump_multi'},"multi"),
67                             &ui_yesno_radio("multi", int($_[0]->{'multi'})),
68                             1, $tds);
69
70         print &ui_table_row(&hlink($text{'dump_links'},"links"),
71                             &ui_yesno_radio("links", int($_[0]->{'links'})),
72                             1, $tds);
73
74         print &ui_table_row(&hlink($text{'dump_xdev'},"xdev"),
75                             &ui_yesno_radio("xdev", int($_[0]->{'xdev'})),
76                             1, $tds);
77         }
78 else {
79         # Display ufs backup options
80         print &ui_table_row(&hlink($text{'dump_update'},"update"),
81                             &ui_yesno_radio("update", int($_[0]->{'update'})),
82                             1, $tds);
83
84         print &ui_table_row(&hlink($text{'dump_level'},"level"),
85                             &ui_select("level", int($_[0]->{'level'}),
86                                 [ map { [ $_, $text{'dump_level_'.$_} ] }
87                                       (0 .. 9) ]), 1, $tds);
88
89         print &ui_table_row(&hlink($text{'dump_blocks'},"blocks"),
90                             &ui_opt_textbox("blocks", $_[0]->{'blocks'}, 8,
91                                             $text{'dump_auto'})." kB",
92                             3, $tds);
93
94         print &ui_table_row(&hlink($text{'dump_honour'},"honour"),
95                             &ui_yesno_radio("honour", int($_[0]->{'honour'})),
96                             1, $tds);
97         }
98 }
99
100 # parse_dump(&dump)
101 sub parse_dump
102 {
103 # Parse common options
104 if ($in{'mode'} == 0) {
105         $in{'file'} =~ /\S/ || &error($text{'dump_efile'});
106         $_[0]->{'file'} = $in{'file'};
107         delete($_[0]->{'host'});
108         delete($_[0]->{'huser'});
109         delete($_[0]->{'hfile'});
110         }
111 else {
112         &to_ipaddress($in{'host'}) ||
113             &to_ip6address($in{'host'}) ||
114                 &error($text{'dump_ehost'});
115         $_[0]->{'host'} = $in{'host'};
116         $in{'huser'} =~ /^\S+$/ || &error($text{'dump_ehuser'});
117         $in{'huser'} =~ /\@/ && &error($text{'dump_ehuser2'});
118         $_[0]->{'huser'} = $in{'huser'};
119         $in{'hfile'} || &error($text{'dump_ehfile'});
120         $_[0]->{'hfile'} = $in{'hfile'};
121         delete($_[0]->{'file'});
122         }
123
124 if ($_[0]->{'fs'} eq 'tar') {
125         # Parse tar options
126         $_[0]->{'rsh'} = &rsh_command_parse("rsh_def", "rsh");
127         $_[0]->{'pass'} = $in{'pass'};
128         $in{'label'} =~ /^\S*$/ && length($in{'label'}) < 16 ||
129                 &error($text{'dump_elabel'});
130         $_[0]->{'label'} = $in{'label'};
131         if ($in{'blocks_def'}) {
132                 delete($_[0]->{'blocks'});
133                 }
134         else {
135                 $in{'blocks'} =~ /^\d+$/ || &error($text{'dump_eblocks'});
136                 $_[0]->{'blocks'} = $in{'blocks'};
137                 $in{'gzip'} && &error($text{'dump_egzip'});
138                 }
139         $_[0]->{'gzip'} = $in{'gzip'};
140         $_[0]->{'multi'} = $in{'multi'};
141         $_[0]->{'links'} = $in{'links'};
142         $_[0]->{'xdev'} = $in{'xdev'};
143         if ($in{'multi'}) {
144                 !-c $in{'file'} && !-b $in{'file'} ||
145                         &error($text{'dump_emulti'});
146                 $in{'gzip'} && &error($text{'dump_egzip2'});
147                 }
148         }
149 else {
150         # Parse ufs options
151         local $mp;
152         foreach $m (&foreign_call("mount", "list_mounted")) {
153                 $mp++ if ($m->[0] eq $in{'dir'});
154                 }
155         $mp || &error($text{'dump_emp'});
156
157         $_[0]->{'update'} = $in{'update'};
158         $_[0]->{'level'} = $in{'level'};
159         $_[0]->{'honour'} = $in{'honour'};
160         if ($in{'blocks_def'}) {
161                 delete($_[0]->{'blocks'});
162                 }
163         else {
164                 $in{'blocks'} =~ /^\d+$/ || &error($text{'dump_eblocks'});
165                 $_[0]->{'blocks'} = $in{'blocks'};
166                 }
167         }
168 }
169
170 # execute_dump(&dump, filehandle, escape, background-mode, [time])
171 # Executes a dump and displays the output
172 sub execute_dump
173 {
174 local $fh = $_[1];
175 local ($cmd, $flags);
176
177 if ($_[0]->{'huser'}) {
178         $flags = "-f '$_[0]->{'huser'}\@$_[0]->{'host'}:".
179                 &date_subs($_[0]->{'hfile'}, $_[4])."'";
180         }
181 elsif ($_[0]->{'host'}) {
182         $flags = "-f '$_[0]->{'host'}:".&date_subs($_[0]->{'hfile'}, $_[4])."'";
183         }
184 else {
185         $flags = "-f '".&date_subs($_[0]->{'file'}, $_[4])."'";
186         }
187 local $tapecmd = $_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar' ? $multi_cmd :
188                  $_[0]->{'multi'} ? undef :
189                  $_[3] && !$config{'nonewtape'} ? $newtape_cmd : $notape_cmd;
190 if ($_[0]->{'fs'} eq 'tar') {
191         # Construct tar command
192         $cmd = "$tar_command -c $flags";
193         $cmd .= " -V '$_[0]->{'label'}'" if ($_[0]->{'label'});
194         $cmd .= " -L $_[0]->{'blocks'}" if ($_[0]->{'blocks'});
195         $cmd .= " -z" if ($_[0]->{'gzip'});
196         $cmd .= " -M" if ($_[0]->{'multi'});
197         $cmd .= " -h" if ($_[0]->{'links'});
198         $cmd .= " -l" if ($_[0]->{'xdev'});
199         $cmd .= " -F \"$tapecmd $_[0]->{'id'}\""
200                 if (!$_[0]->{'gzip'});
201         $cmd .= " --rsh-command=$_[0]->{'rsh'}"
202                 if ($_[0]->{'rsh'} && $_[0]->{'host'});
203         $cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
204         $cmd .= " '$_[0]->{'dir'}'";
205         }
206 else {
207         # Construct ufs dump command
208         $cmd = "dump -$_[0]->{'level'} $flags";
209         $cmd .= " -u" if ($_[0]->{'update'});
210         if ($_[0]->{'blocks'}) {
211                 $cmd .= " -B $_[0]->{'blocks'}";
212                 }
213         else {
214                 $cmd .= " -a";
215                 }
216         $cmd .= " -h 0" if ($_[0]->{'honour'});
217         $cmd .= " $_[0]->{'extra'}" if ($_[0]->{'extra'});
218         $cmd .= " '$_[0]->{'dir'}'";
219         }
220
221 &system_logged("sync");
222 sleep(1);
223 $ENV{'DUMP_PASSWORD'} = $_[0]->{'pass'};
224 local $got = &run_ssh_command($cmd, $fh, $_[2], $_[0]->{'pass'});
225 if ($_[0]->{'multi'} && $_[0]->{'fs'} eq 'tar') {
226         # Run multi-file switch command one last time
227         &execute_command("$multi_cmd $_[0]->{'id'} >/dev/null 2>&1");
228         }
229 return $got ? 0 : 1;
230 }
231
232 # dump_dest(&dump)
233 sub dump_dest
234 {
235 if ($_[0]->{'file'}) {
236         return "<tt>".&html_escape($_[0]->{'file'})."</tt>";
237         }
238 elsif ($_[0]->{'huser'}) {
239         return "<tt>".&html_escape("$_[0]->{'huser'}\@$_[0]->{'host'}:$_[0]->{'hfile'}")."</tt>";
240         }
241 else {
242         return "<tt>".&html_escape("$_[0]->{'host'}:$_[0]->{'hfile'}")."</tt>";
243         }
244 }
245
246 # missing_restore_command(filesystem)
247 sub missing_restore_command
248 {
249 return &has_command("restore") ? undef : $cmd;
250 }
251
252 # restore_form(filesystem, [&dump])
253 sub restore_form
254 {
255 local ($fs, $dump, $tds) = @_;
256
257 # common options
258 print &ui_table_row(&hlink($text{'restore_src'}, "rsrc"),
259    &ui_radio("mode", $_[1]->{'host'} ? 1 : 0,
260         [ [ 0, $text{'dump_file'}." ".
261                &ui_textbox("file", $_[1]->{'file'}, 50).
262                " ".&file_chooser_button("file")."<br>" ],
263           [ 1, &text('dump_host',
264                      &ui_textbox("host", $_[1]->{'host'}, 15),
265                      &ui_textbox("huser", $_[1]->{'huser'}, 8),
266                      &ui_textbox("hfile", $_[1]->{'hfile'}, 20)) ] ]), 3, $tds);
267
268 if ($_[0] eq 'tar') {
269         # tar restore options
270         print &ui_table_row(&hlink($text{'restore_rsh'},"rrsh"),
271                       &rsh_command_input("rsh_def", "rsh", $_[1]->{'rsh'}),
272                       3, $tds);
273
274         # Password option for SSH
275         print &ui_table_row(&hlink($text{'dump_pass2'},"passs"),
276                       &ui_password("pass", $_[1]->{'pass'}, 20),
277                       3, $tds);
278
279         # Files to restore
280         print &ui_table_row(&hlink($text{'restore_files'},"rfiles"),
281                       &ui_opt_textbox("files", undef, 40, $text{'restore_all'},
282                                       $text{'restore_sel'}), 3, $tds);
283
284         # Target dir
285         print &ui_table_row(&hlink($text{'restore_dir'},"rdir"),
286                       &ui_textbox("dir", undef, 50)." ".
287                       &file_chooser_button("dir", 1), 3, $tds);
288
289         # Restore permissions?
290         print &ui_table_row(&hlink($text{'restore_perms'},"perms"),
291                       &ui_yesno_radio("perms", 1), 1, $tds);
292
293         # Uncompress?
294         print &ui_table_row(&hlink($text{'restore_gzip'},"rgzip"),
295                       &ui_select("gzip", $_[1]->{'gzip'},
296                                 [ [ 0, $text{'no'} ],
297                                   [ 1, $text{'dump_gzip1'} ] ]), 1, $tds);
298
299         print &ui_table_row(&hlink($text{'restore_keep'},"keep"),
300                       &ui_yesno_radio("keep", 0), 1, $tds);
301
302         # Multiple files
303         print &ui_table_row(&hlink($text{'restore_multi'},"rmulti"),
304                       &ui_yesno_radio("multi", 0), 1, $tds);
305
306         # Show only
307         print &ui_table_row(&hlink($text{'restore_test'},"rtest"),
308                       &ui_yesno_radio("test", 1), 1, $tds);
309         }
310 else {
311         # ufs restore options, files to restore
312         print &ui_table_row(&hlink($text{'restore_files'},"rfiles"),
313                       &ui_opt_textbox("files", undef, 40, $text{'restore_all'},
314                                       $text{'restore_sel'}), 3, $tds);
315
316         # Target dir
317         print &ui_table_row(&hlink($text{'restore_dir'},"rdir"),
318                       &ui_textbox("dir", undef, 50)." ".
319                       &file_chooser_button("dir", 1), 3, $tds);
320
321         # Show only
322         print &ui_table_row(&hlink($text{'restore_nothing'},"rnothing"),
323                       &ui_yesno_radio("nothing", 1), 1, $tds);
324         }
325 }
326
327 # parse_restore(filesystem)
328 # Parses inputs from restore_form() and returns a command to be passed to
329 # restore_backup()
330 sub parse_restore
331 {
332 local $cmd;
333 if ($_[0] eq "tar") {
334         $cmd = $tar_command;
335         if ($in{'test'}) {
336                 $cmd .= " -t -v";
337                 }
338         else {
339                 $cmd .= " -x";
340                 }
341         }
342 else {
343         $cmd .= "restore".($in{'test'} ? " -t" : " -x");
344         }
345 if ($in{'mode'} == 0) {
346         $in{'file'} || &error($text{'restore_efile'});
347         $cmd .= " -f '$in{'file'}'";
348         }
349 else {
350         &to_ipaddress($in{'host'}) ||
351             &to_ip6address($in{'host'}) ||
352                 &error($text{'restore_ehost'});
353         $in{'huser'} =~ /^\S*$/ || &error($text{'restore_ehuser'});
354         $in{'hfile'} || &error($text{'restore_ehfile'});
355         if ($in{'huser'}) {
356                 $cmd .= " -f '$in{'huser'}\@$in{'host'}:$in{'hfile'}'";
357                 }
358         else {
359                 $cmd .= " -f '$in{'host'}:$in{'hfile'}'";
360                 }
361         }
362
363 if ($_[0] eq 'tar') {
364         # parse tar options
365         $cmd .= " -p" if ($in{'perms'});
366         $cmd .= " -z" if ($in{'gzip'});
367         $cmd .= " -k" if ($in{'keep'});
368         if ($in{'multi'}) {
369                 !-c $in{'file'} && !-b $in{'file'} ||
370                         &error($text{'restore_emulti'});
371                 $in{'mode'} == 0 || &error($text{'restore_emulti2'});
372                 $cmd .= " -M -F \"$rmulti_cmd $in{'file'}\"";
373                 }
374         local $rsh = &rsh_command_parse("rsh_def", "rsh");
375         if ($rsh) {
376                 $cmd .= " --rsh-command=".quotemeta($rsh);
377                 }
378         $cmd .= " $in{'extra'}" if ($in{'extra'});
379         if (!$in{'files_def'}) {
380                 $in{'files'} || &error($text{'restore_efiles'});
381                 $cmd .= " $in{'files'}";
382                 }
383         -d $in{'dir'} || &error($text{'restore_edir'});
384         $cmd = "cd '$in{'dir'}' && $cmd";
385         if ($in{'multi'}) {
386                 $cmd = "$rmulti_cmd $in{'file'} 1 && $cmd";
387                 }
388         }
389 else {
390         # parse ufs options
391         $cmd .= " -N" if ($in{'nothing'});
392         $cmd .= " $in{'extra'}" if ($in{'extra'});
393         if (!$in{'files_def'}) {
394                 $in{'files'} || &error($text{'restore_efiles'});
395                 $cmd .= " $in{'files'}";
396                 }
397         -d $in{'dir'} || &error($text{'restore_edir'});
398         }
399
400 return $cmd;
401 }
402
403 # restore_backup(filesystem, command)
404 # Restores a backup based on inputs from restore_form(), and displays the results
405 sub restore_backup
406 {
407 &additional_log('exec', undef, $_[1]);
408 $ENV{'DUMP_PASSWORD'} = $in{'pass'};
409
410 # Need to supply prompts
411 &foreign_require("proc", "proc-lib.pl");
412 local ($fh, $fpid) = &foreign_call("proc", "pty_process_exec", "cd '$in{'dir'}' ; $_[1]");
413 local $donevolume;
414 while(1) {
415         local $rv = &wait_for($fh, "(next volume #)", "(set owner.mode for.*\\[yn\\])", "((.*)\\[yn\\])", "password:", "yes\\/no", "(.*\\n)");
416         last if ($rv < 0);
417         print &html_escape($matches[1]);
418         if ($rv == 0) {
419                 if ($donevolume++) {
420                         return $text{'restore_evolume'};
421                         }
422                 else {
423                         syswrite($fh, "1\n", 2);
424                         }
425                 }
426         elsif ($rv == 1) {
427                 syswrite($fh, "n\n", 2);
428                 }
429         elsif ($rv == 2) {
430                 return &text('restore_equestion',
431                              "<tt>$matches[2]</tt>");
432                 }
433         elsif ($rv == 3) {
434                 syswrite($fh, "$in{'pass'}\n");
435                 }
436         elsif ($rv == 4) {
437                 syswrite($fh, "yes\n");
438                 }
439         }
440 close($fh);
441 waitpid($fpid, 0);
442 return $? || undef;
443 }
444
445 1;
446