Handle hostnames with upper-case letters
[webmin.git] / mount / freebsd-lib.pl
1 # freebsd-lib.pl
2 # Mount table functions for freebsd
3
4 $uname_release = `uname -r`;
5 if (&has_command("mount_smbfs")) {
6         $smbfs_support = 1;
7         $nsmb_conf = "/etc/nsmb.conf";
8         }
9
10 # Return information about a filesystem, in the form:
11 #  directory, device, type, options, fsck_order, mount_at_boot
12 # If a field is unused or ignored, a - appears instead of the value.
13 # Swap-filesystems (devices or files mounted for VM) have a type of 'swap',
14 # and 'swap' in the directory field
15 sub list_mounts
16 {
17 local(@rv, @p, @o, $_, $i, $j); $i = 0;
18
19 # Get /etc/fstab mounts
20 open(FSTAB, $config{'fstab_file'});
21 while(<FSTAB>) {
22         local(@o, $at_boot);
23         chop; s/#.*$//g;
24         if (!/\S/) { next; }
25         @p = split(/\s+/, $_);
26         if ($p[2] eq "proc" || $p[2] eq "procfs") { $p[2] = $p[0] = "proc"; }
27         elsif ($p[2] eq "swap") { $p[1] = "swap"; }
28         elsif ($p[2] eq "smbfs") {
29                 # Need to get nsmb.conf options, covert share and extract user
30                 $p[0] = lc($p[0]);
31                 local $noptions = &read_nsmb($p[0]);
32                 local %options;
33                 &parse_options($p[3]);
34                 if ($p[0] =~ /^\/\/(\S+)\@(\S+)\/(.*)$/) {
35                         $p[0] = "\\\\$2\\$3";
36                         $options{'user'} = $1;
37                         }
38                 elsif ($p[0] =~ /\/\/(\S+)\/(.*)$/) {
39                         $p[0] = "\\\\$1\\$2";
40                         }
41                 %options = ( %options, %$noptions );
42                 $p[3] = &join_options();
43                 }
44         $rv[$i] = [ $p[1], $p[0], $p[2] ];
45         $rv[$i]->[5] = "yes";
46         @o = split(/,/ , $p[3] eq "defaults" ? "" : $p[3]);
47         if (($j = &indexof("noauto", @o)) >= 0) {
48                 # filesytem is not mounted at boot
49                 splice(@o, $j, 1);
50                 $rv[$i]->[5] = "no";
51                 }
52         $rv[$i]->[3] = (@o ? join(',' , @o) : "-");
53         $rv[$i]->[4] = (@p >= 5 ? $p[5] : 0);
54         $i++;
55         }
56 close(FSTAB);
57 return @rv;
58 }
59
60
61 # create_mount(directory, device, type, options, fsck_order, mount_at_boot)
62 # Add a new entry to the fstab file, old or new automounter file
63 sub create_mount
64 {
65 local(@mlist, @amd, $_, $opts);
66
67 &open_tempfile(FSTAB, ">> $config{'fstab_file'}");
68 &print_mount_line(@_);
69 &close_tempfile(FSTAB);
70 }
71
72 # print_mount_line(directory, device, type, options, fsck_order, mount_at_boot)
73 sub print_mount_line
74 {
75 if ($_[2] eq "smbfs") {
76         # Adding an SMB mount, which needs special handling
77         local %options;
78         &parse_options("smbfs", $_[3]);
79         local $share;
80         if ($options{'user'} && $_[1] =~ /^\\\\(.*)\\(.*)$/) {
81                 $share = "//$options{'user'}\@$1/$2";
82                 }
83         else {
84                 ($share = $_[1]) =~ s/\\/\//g;
85                 }
86         &print_tempfile(FSTAB, "$share  $_[0]  $_[2]");
87         local $roptions = &update_nsmb($share, \%options);
88         delete($roptions->{'user'});
89         $opts = &join_options($_[2], $roptions);
90         }
91 else {
92         # Adding a normal mount to the fstab file
93         &print_tempfile(FSTAB, "$_[1]  $_[0]  $_[2]");
94         $opts = $_[3] eq "-" ? "" : $_[3];
95         }
96 if ($_[5] eq "no") {
97         $opts = join(',' , (split(/,/ , $opts) , "noauto"));
98         }
99 if ($opts eq "") {
100         &print_tempfile(FSTAB, "  defaults");
101         }
102 else {
103         &print_tempfile(FSTAB, "  $opts");
104         }
105 &print_tempfile(FSTAB, "  0  ");
106 &print_tempfile(FSTAB, $_[4] eq "-" ? "0\n" : "$_[4]\n");
107 }
108
109
110 # change_mount(num, directory, device, type, options, fsck_order, mount_at_boot)
111 # Change an existing permanent mount
112 sub change_mount
113 {
114 local($i, @fstab, $line, $opts, $j, @amd);
115 $i = 0;
116
117 # Update fstab file
118 open(FSTAB, $config{fstab_file});
119 @fstab = <FSTAB>;
120 close(FSTAB);
121 &open_tempfile(FSTAB, "> $config{fstab_file}");
122 foreach (@fstab) {
123         chop; ($line = $_) =~ s/#.*$//g;
124         if ($line =~ /\S/ && $i++ == $_[0]) {
125                 # Found the line to replace
126                 &print_mount_line(@_[1..$#_]);
127                 }
128         else {
129                 &print_tempfile(FSTAB, $_,"\n");
130                 }
131         }
132 &close_tempfile(FSTAB);
133 }
134
135
136 # delete_mount(index)
137 # Delete an existing permanent mount
138 sub delete_mount
139 {
140 local($i, @fstab, $line, $opts, $j, @amd);
141 $i = 0;
142
143 # Update fstab file
144 open(FSTAB, $config{fstab_file});
145 @fstab = <FSTAB>;
146 close(FSTAB);
147 &open_tempfile(FSTAB, "> $config{fstab_file}");
148 foreach (@fstab) {
149         chop; ($line = $_) =~ s/#.*$//g;
150         if ($line !~ /\S/ || $i++ != $_[0]) {
151                 # Don't delete this line
152                 &print_tempfile(FSTAB, $_,"\n");
153                 }
154         }
155 &close_tempfile(FSTAB);
156 }
157
158
159 # list_mounted()
160 # Return a list of all the currently mounted filesystems and swap files.
161 # The list is in the form:
162 #  directory device type options
163 # Under FreeBSD, there seems to be no way to get additional mount options
164 # used by filesystems like NFS etc. Even getting the full details of mounted
165 # filesystems requires C code! So we have to call a specially-written external
166 # program to get the mount list
167 sub list_mounted
168 {
169 # get the list of mounted filesystems
170 local(@rv, $_);
171 local $cmd = $uname_release =~ /^[789]\.[0-9]/ ? "freebsd-mounts-7" :
172              $uname_release =~ /^[56]\.[0-9]/ ? "freebsd-mounts-5" :
173              $uname_release =~ /^4\.[0-9]/ ? "freebsd-mounts-4" :
174              $uname_release =~ /^3\.[1-9]/ ? "freebsd-mounts-3" :
175                                              "freebsd-mounts-2";
176 &compile_program($cmd, '.*86');
177 open(CMD, "$module_config_directory/$cmd |");
178 while(<CMD>) {
179         local @p = split(/\t/, $_);
180         if ($p[2] eq "procfs" || $p[1] eq "procfs") { $p[1] = $p[2] = "proc"; }
181         elsif ($p[2] eq "mfs") { $p[1] =~ s/:.*$//; }
182         elsif ($p[2] eq "smbfs") {
183                 # Need to get nsmb.conf options, covert share and extract user
184                 $p[1] = lc($p[1]);
185                 local $noptions = &read_nsmb($p[1]);
186                 local %options;
187                 &parse_options($p[3]);
188                 if ($p[1] =~ /^\/\/(\S+)\@(\S+)\/(.*)$/) {
189                         $p[1] = "\\\\$2\\$3";
190                         $options{'user'} = $1;
191                         }
192                 elsif ($p[1] =~ /\/\/(\S+)\/(.*)$/) {
193                         $p[1] = "\\\\$1\\$2";
194                         }
195                 %options = ( %options, %$noptions );
196                 $p[3] = &join_options();
197                 }
198         push(@rv, \@p);
199         }
200 close(CMD);
201
202 # add output from swapinfo
203 local $out;
204 &execute_command("swapinfo", undef, \$out, undef, 0, 1);
205 foreach (split(/\n/, $out)) {
206         if (/^(\/\S+)\s+\d+\s+\d+/) {
207                 push(@rv, [ "swap", $1, "swap", "-" ]);
208                 }
209         }
210 return @rv;
211 }
212
213
214 # mount_dir(directory, device, type, options)
215 # Mount a new directory from some device, with some options. Returns 0 if ok,
216 # or an error string if failed
217 sub mount_dir
218 {
219 local($out, $opts, $shar, %options, %smbopts);
220 if ($_[2] eq "swap") {
221         # Use swapon to add the swap space..
222         $out = &backquote_logged("swapon $_[1] 2>&1");
223         if ($?) { return "<pre>$out</pre>"; }
224         }
225 elsif ($_[2] eq "smbfs") {
226         # Need special handling for SMB mounts
227         local %options;
228         &parse_options($_[2], $_[3]);
229         local $share;
230         if ($options{'user'} && $_[1] =~ /^\\\\(.*)\\(.*)$/) {
231                 $share = "//$options{'user'}\@$1/$2";
232                 }
233         else {
234                 ($share = $_[1]) =~ s/\\/\//g;
235                 }
236         local $roptions = &update_nsmb($share, \%options);
237         delete($roptions->{'user'});
238         local $opts = &join_options($_[2], $roptions);
239         $opts = $opts ne "-" ? " -o $opts" : "";
240         &foreign_require("proc");
241         local ($fh, $fpid) = &proc::pty_process_exec_logged(
242                 "mount -t $_[2] $opts $share $_[0]");
243         local $got;
244         local $rv = &wait_for($fh, "Password:");
245         $got .= $wait_for_input;
246         if ($rv == 0) {
247                 print $fh $options{'nsmb_password'},"\n";
248                 }
249         $rv = &wait_for($fh);
250         $got .= $wait_for_input;
251         close($fh);
252         if ($? || $got =~ /failed|error|syserr|usage:/i) { return "<pre>$out</pre>"; }
253         }
254 else {
255         # Can use mount command for this filesystem
256         $opts = $_[3] eq "-" ? "" :
257                         "-o ".join(',', grep { !/quota/ } split(/,/, $_[3]));
258         $out = &backquote_logged("mount -t $_[2] $opts $_[1] $_[0] 2>&1");
259         if ($?) { return "<pre>$out</pre>"; }
260         }
261 return 0;
262 }
263
264
265 # unmount_dir(directory, device, type)
266 # Unmount a directory that is currently mounted. Returns 0 if ok,
267 # or an error string if failed
268 sub unmount_dir
269 {
270 local($out, %smbopts, $dir);
271 if ($_[2] eq "swap") {
272         # Not possible!
273         &error("Swap space cannot be removed");
274         }
275 else {
276         $out = &backquote_logged("umount $_[0] 2>&1");
277         if ($?) { return "<pre>$out</pre>"; }
278         }
279 return 0;
280 }
281
282
283 # mount_modes(type)
284 # Given a filesystem type, returns 4 numbers that determine how the file
285 # system can be mounted, and whether it can be fsck'd
286 # The first is:
287 #  0 - cannot be permanently recorded
288 #       (smbfs under linux)
289 #  1 - can be permanently recorded, and is always mounted at boot
290 #       (swap under linux)
291 #  2 - can be permanently recorded, and may or may not be mounted at boot
292 #       (most normal filesystems)
293 # The second is:
294 #  0 - mount is always permanent => mounted when saved
295 #       (swap under linux)
296 #  1 - doesn't have to be permanent
297 #       (normal fs types)
298 # The third is:
299 #  0 - cannot be fsck'd at boot time
300 #  1 - can be be fsck'd at boot
301 # The fourth is:
302 #  0 - can be unmounted
303 #  1 - cannot be unmounted
304 sub mount_modes
305 {
306 if ($_[0] eq "swap")
307         { return (2, 1, 0, 1); }
308 elsif ($_[0] eq "ufs")
309         { return (2, 1, 1, 0); }
310 else
311         { return (2, 1, 0, 0); }
312 }
313
314
315 # disk_space(type, directory)
316 # Returns the amount of total and free space for some filesystem, or an
317 # empty array if not appropriate.
318 sub disk_space
319 {
320 if (&get_mounted($_[1], "*") < 0) { return (); }
321 if ($_[0] eq "proc" || $_[0] eq "swap") { return (); }
322 my $out;
323 &execute_command("df -k ".quotemeta($_[1]), undef, \$out, undef, 0, 1);
324 if ($out =~ /Mounted on\n\S+\s+(\S+)\s+\S+\s+(\S+)/) {
325         return ($1, $2);
326         }
327 else {
328         return ( );
329         }
330 }
331
332
333 # list_fstypes()
334 # Returns an array of all the supported filesystem types. If a filesystem is
335 # found that is not one of the supported types, generate_location() and
336 # generate_options() will not be called for it.
337 sub list_fstypes
338 {
339 local @rv = ("ufs", "nfs", "cd9660", "msdos", "swap");
340 push(@rv, "ext2fs") if (&has_command("mount_ext2fs"));
341 push(@rv, "ntfs") if (&has_command("mount_ntfs"));
342 push(@rv, "smbfs") if ($smbfs_support);
343 return @rv;
344 }
345
346
347 # fstype_name(type)
348 # Given a short filesystem type, return a human-readable name for it
349 sub fstype_name
350 {
351 local(%fsmap);
352 %fsmap = ("ufs", "FreeBSD Unix Filesystem",
353           "nfs", "Network Filesystem",
354           "cd9660", "ISO9660 CD-ROM",
355           "msdos", "MS-DOS Filesystem",
356           "ext2fs", "Linux Filesystem",
357           "ntfs", "Windows NT Filesystem",
358           "swap", "Virtual Memory",
359           "proc", "Process Image Filesystem",
360           "smbfs", "Windows Networking Filesystem");
361 return $config{long_fstypes} && $fsmap{$_[0]} ? $fsmap{$_[0]} : uc($_[0]);
362 }
363
364
365 # multiple_mount(type)
366 # Returns 1 if filesystems of this type can be mounted multiple times, 0 if not
367 sub multiple_mount
368 {
369 return $_[0] eq "nfs" || $_[0] eq "smbfs";
370 }
371
372
373 # generate_location(type, location)
374 # Output HTML for editing the mount location of some filesystem.
375 sub generate_location
376 {
377 if ($_[0] eq "nfs") {
378         # NFS mount from some host and directory
379         $_[1] =~ /^([^:]+):(.*)$/;
380         print "<tr> <td><b>NFS Hostname</b></td>\n";
381         print "<td><input name=nfs_host size=20 value=\"$1\">\n";
382         &nfs_server_chooser_button("nfs_host");
383         print "</td>\n";
384         print "<td><b>NFS Directory</b></td>\n";
385         print "<td><input name=nfs_dir size=20 value=\"$2\">\n";
386         &nfs_export_chooser_button("nfs_host", "nfs_dir");
387         print "</td> </tr>\n";
388         }
389 elsif ($_[0] eq "smbfs") {
390         # SMB mount from some server and share
391         $_[1] =~ /^\\\\(.*)\\(.*)$/;
392         print "<tr> <td><b>$text{'linux_smbserver'}</b></td>\n";
393         print "<td><input name=smbfs_server value=\"$1\" size=20>\n";
394         &smb_server_chooser_button("smbfs_server");
395         print "</td>\n";
396         print "<td><b>$text{'linux_smbshare'}</b></td>\n";
397         print "<td><input name=smbfs_share value=\"$2\" size=20>\n";
398         &smb_share_chooser_button("smbfs_server", "smbfs_share");
399         print "</td> </tr>\n";
400         }
401 else {
402         if ($_[0] eq "swap") {
403                 # Swap file or device
404                 printf "<tr> <td valign=top><b>Swap File</b></td>\n";
405                 }
406         else {
407                 # Disk-based filesystem
408                 printf "<tr> <td valign=top><b>%s Disk</b></td>\n",
409                         &fstype_name($_[0]);
410                 }
411         print "<td colspan=3>\n";
412         if ($_[1] =~ /^\/dev\/ad(\d)s(\d)([a-z]*)$/) {
413                 $disk_dev = 0; $ide_t = $1; $ide_s = $2; $ide_p = $3;
414                 }
415         elsif ($_[1] =~ /^\/dev\/da(\d)s(\d)([a-z]*)$/) {
416                 $disk_dev = 1; $scsi_t = $1; $scsi_s = $2; $scsi_p = $3;
417                 }
418         else { $disk_dev = 2; }
419
420         printf "<input type=radio name=disk_dev value=0 %s> IDE Hard Disk:\n",
421                 $disk_dev == 0 ? "checked" : "";
422         print "Device <input name=ide_t size=3 value=\"$ide_t\">\n";
423         print "Slice <input name=ide_s size=3 value=\"$ide_s\">\n";
424         print "Partition <input name=ide_p size=3 value=\"$ide_p\"><br>\n";
425
426         printf "<input type=radio name=disk_dev value=1 %s> SCSI Disk:\n",
427                 $disk_dev == 1 ? "checked" : "";
428         print "Device <input name=scsi_t size=3 value=\"$scsi_t\">\n";
429         print "Slice <input name=scsi_s size=3 value=\"$scsi_s\">\n";
430         print "Partition <input name=scsi_p size=3 value=\"$scsi_p\"><br>\n";
431
432         printf "<input type=radio name=disk_dev value=2 %s> Other Device:\n",
433                 $disk_dev == 2 ? "checked" : "";
434         printf "<input size=20 name=dev_path value=\"%s\"><br>\n",
435                 $disk_dev == 2 ? $_[1] : "";
436         print "</td> </tr>\n";
437         }
438 }
439
440
441 # generate_options(type, newmount)
442 # Output HTML for editing mount options for a particular filesystem 
443 # under this OS
444 sub generate_options
445 {
446 if ($_[0] ne "swap") {
447         # These options are common to all filesystems
448         print "<tr> <td><b>Read-only?</b></td>\n";
449         printf "<td nowrap><input type=radio name=bsd_ro value=1 %s> Yes\n",
450                 defined($options{"rdonly"}) || defined($options{"ro"})
451                         ? "checked" : "";
452         printf "<input type=radio name=bsd_ro value=0 %s> No</td>\n",
453                 defined($options{"rdonly"}) || defined($options{"ro"})
454                         ? "" : "checked";
455
456         print "<td><b>Buffer writes to filesystem?</b></td>\n";
457         printf"<td nowrap><input type=radio name=bsd_sync value=0 %s> Yes\n",
458                 defined($options{"sync"}) ? "" : "checked";
459         printf "<input type=radio name=bsd_sync value=1 %s> No</td> </tr>\n",
460                 defined($options{"sync"}) ? "checked" : "";
461
462         print "<tr> <td><b>Allow device files?</b></td>\n";
463         printf "<td nowrap><input type=radio name=bsd_nodev value=0 %s> Yes\n",
464                 defined($options{"nodev"}) ? "" : "checked";
465         printf "<input type=radio name=bsd_nodev value=1 %s> No</td>\n",
466                 defined($options{"nodev"}) ? "checked" : "";
467
468         print "<td><b>Allow execution of binaries?</b></td>\n";
469         printf"<td nowrap><input type=radio name=bsd_noexec value=0 %s> Yes\n",
470                 defined($options{"noexec"}) ? "" : "checked";
471         printf "<input type=radio name=bsd_noexec value=1 %s> No</td> </tr>\n",
472                 defined($options{"noexec"}) ? "checked" : "";
473
474         print "<tr> <td><b>Disallow setuid programs?</b></td>\n";
475         printf "<td nowrap><input type=radio name=bsd_nosuid value=1 %s> Yes\n",
476                 defined($options{"nosuid"}) ? "checked" : "";
477         printf "<input type=radio name=bsd_nosuid value=0 %s> No</td>\n",
478                 defined($options{"nosuid"}) ? "" : "checked";
479
480         print "<td><b>Update access times?</b></td>\n";
481         printf"<td nowrap><input type=radio name=bsd_noatime value=0 %s> Yes\n",
482                 defined($options{"noatime"}) ? "" : "checked";
483         printf "<input type=radio name=bsd_noatime value=1 %s> No</td> </tr>\n",
484                 defined($options{"noatime"}) ? "checked" : "";
485
486         if ($uname_release =~ /^[34]\./) {
487                 # FreeBSD 3.x has some more options
488                 print "<tr> <td><b>Follow symbolic links?</b></td>\n";
489                 printf "<td nowrap><input type=radio name=bsd_nosymfollow value=0 %s> Yes\n",
490                         defined($options{"nosymfollow"}) ? "" : "checked";
491                 printf "<input type=radio name=bsd_nosymfollow value=1 %s> No</td>\n",
492                         defined($options{"nosymfollow"}) ? "checked" : "";
493
494                 print "<td><b>Files inherit group from SUID directories?</b></td>\n";
495                 printf"<td nowrap><input type=radio name=bsd_suiddir value=1 %s> Yes\n",
496                         defined($options{"suiddir"}) ? "checked" : "";
497                 printf "<input type=radio name=bsd_suiddir value=0 %s> No</td> </tr>\n",
498                         defined($options{"suiddir"}) ? "" : "checked";
499                 }
500         }
501
502 if ($_[0] eq "ufs") {
503         # UFS filesystems support quotas
504         print "<tr> <td><b>User quotas at boot</b></td> <td colspan=3>\n";
505         printf "<input type=radio name=ufs_userquota value=0 %s> Disabled\n",
506                 defined($options{'userquota'}) ? "" : "checked";
507         printf "<input type=radio name=ufs_userquota value=1 %s> Enabled\n",
508                 defined($options{'userquota'}) && $options{'userquota'} eq ""
509                         ? "checked" : "";
510         printf "<input type=radio name=ufs_userquota value=2 %s>\n",
511                 $options{'userquota'} ? "checked" : "";
512         print "Enabled, use file\n";
513         printf "<input name=ufs_userquota_file size=30 value=\"%s\">\n",
514                 $options{'userquota'};
515         print "</td> </tr>\n";
516                 
517         print "<tr> <td><b>Group quotas at boot</b></td> <td colspan=3>\n";
518         printf "<input type=radio name=ufs_groupquota value=0 %s> Disabled\n",
519                 defined($options{'groupquota'}) ? "" : "checked";
520         printf "<input type=radio name=ufs_groupquota value=1 %s> Enabled\n",
521                 defined($options{'groupquota'}) && $options{'groupquota'} eq ""
522                         ? "checked" : "";
523         printf "<input type=radio name=ufs_groupquota value=2 %s>\n",
524                 $options{'groupquota'} ? "checked" : "";
525         print "Enabled, use file\n";
526         printf "<input name=ufs_groupquota_file size=30 value=\"%s\">\n",
527                 $options{'groupquota'};
528         print "</td> </tr>\n";
529         }
530 elsif ($_[0] eq "nfs") {
531         # NFS filesystems have lots more options
532         print "<tr> <td><b>Retry mounts in background?</b></td>\n";
533         printf "<td nowrap><input type=radio name=nfs_b value=1 %s> Yes\n",
534                 defined($options{"-b"}) ? "checked" : "";
535         printf "<input type=radio name=nfs_b value=0 %s> No</td>\n",
536                 defined($options{"-b"}) ? "" : "checked";
537
538         print "<td><b>Return error on timeouts?</b></td>\n";
539         printf "<td nowrap><input type=radio name=nfs_s value=1 %s> Yes\n",
540                 defined($options{"-s"}) ? "checked" : "";
541         printf "<input type=radio name=nfs_s value=0 %s> No</td> </tr>\n",
542                 defined($options{"-s"}) ? "" : "checked";
543
544         print "<tr> <td><b>Timeout</b></td>\n";
545         printf "<td nowrap><input type=radio name=nfs_t_def value=1 %s> Default\n",
546                 defined($options{"-t"}) ? "" : "checked";
547         printf "<input type=radio name=nfs_t_def value=0 %s>\n",
548                 defined($options{"-t"}) ? "checked" : "";
549         printf "<input size=5 name=nfs_t value=\"$options{'-t'}\"></td>\n";
550
551         print "<td><b>Number of Retransmissions</b></td>\n";
552         printf "<td nowrap><input type=radio name=nfs_x_def value=1 %s> Default\n",
553                 defined($options{"-x"}) ? "" : "checked";
554         printf "<input type=radio name=nfs_x_def value=0 %s>\n",
555                 defined($options{"-x"}) ? "checked" : "";
556         print "<input size=5 name=nfs_x value=\"$options{'-x'}\"></td> </tr>\n";
557
558         print "<tr> <td><b>NFS version</b></td> <td nowrap>\n";
559         local $v = defined($options{"-2"}) ? 2 :
560                    defined($options{"-3"}) ? 3 : 0;
561         printf "<input type=radio name=nfs_ver value=0 %s> Auto\n",
562                 $v ? "" : "checked";
563         printf "<input type=radio name=nfs_ver value=2 %s> V2\n",
564                 $v == 2 ? "checked" : "";
565         printf "<input type=radio name=nfs_ver value=3 %s> V3</td>\n",
566                 $v == 3 ? "checked" : "";
567
568         print "<td><b>Mount retries</b></td>\n";
569         printf "<td nowrap><input type=radio name=nfs_r_def value=1 %s> Default\n",
570                 defined($options{"-R"}) ? "" : "checked";
571         printf "<input type=radio name=nfs_r_def value=0 %s>\n",
572                 defined($options{"-R"}) ? "checked" : "";
573         print "<input size=5 name=nfs_r value=\"$options{'-R'}\"></td> </tr>\n";
574
575         print "<tr> <td><b>Read-ahead blocks</b></td>\n";
576         printf "<td nowrap><input type=radio name=nfs_a_def value=1 %s> Default\n",
577                 defined($options{"-a"}) ? "" : "checked";
578         printf "<input type=radio name=nfs_a_def value=0 %s>\n",
579                 defined($options{"-a"}) ? "checked" : "";
580         print "<input size=5 name=nfs_a value=\"$options{'-a'}\"></td>\n";
581
582         print "<td><b>RPC Protocol</b></td>\n";
583         printf "<td nowrap><input type=radio name=nfs_t2 value=1 %s> TCP\n",
584                 defined($options{"-T"}) ? "checked" : "";
585         printf "<input type=radio name=nfs_t2 value=0 %s> UDP</td> </tr>\n",
586                 defined($options{"-T"}) ? "" : "checked";
587         }
588 elsif ($_[0] eq "msdos"){
589         # MS-DOS filesystems options deal with filling in
590         # missing unix functionality
591         print "<tr> <td><b>User files are owned by</b></td>\n";
592         printf "<td><input name=msdos_u size=8 value=\"%s\">\n",
593                 defined($options{"-u"}) ? getpwuid($options{"-u"}) : "";
594         print &user_chooser_button("msdos_u", 0),"</td>\n";
595
596         print "<td><b>Group files are owned by</b></td>\n";
597         printf "<td><input name=msdos_g size=8 value=\"%s\">\n",
598                 defined($options{"-g"}) ? getgrgid($options{"-g"}) : "";
599         print &group_chooser_button("msdos_g", 0),"</td>\n";
600
601         print "<tr> <td><b>File permissions mask</b></td>\n";
602         printf "<td><input type=radio name=msdos_m_def value=1 %s> Default\n",
603                 defined($options{"-m"}) ? "" : "checked";
604         printf "<input type=radio name=msdos_m_def value=0 %s>\n",
605                 defined($options{"-m"}) ? "checked" : "";
606         print "<input size=5 name=msdos_m value=\"$options{'-m'}\"></td>\n";
607         }
608 elsif ($_[0] eq "cd9660") {
609         # CDROM filesystem
610         print "<tr> <td><b>Ignore Unix Attributes?</b></td>\n";
611         printf "<td><input type=radio name=cd9660_r value=1 %s> Yes\n",
612                 defined($options{"-r"}) ? "checked" : "";
613         printf "<input type=radio name=cd9660_r value=0 %s> No</td>\n",
614                 defined($options{"-r"}) ? "" : "checked";
615
616         print "<td><b>Show version numbers?</b></td>\n";
617         printf "<td><input type=radio name=cd9660_g value=1 %s> Yes\n",
618                 defined($options{"-g"}) ? "checked" : "";
619         printf "<input type=radio name=cd9660_g value=0 %s> No</td> </tr>\n",
620                 defined($options{"-g"}) ? "" : "checked";
621
622         print "<tr> <td><b>Use extended attributes?</b></td>\n";
623         printf "<td><input type=radio name=cd9660_e value=1 %s> Yes\n",
624                 defined($options{"-e"}) ? "checked" : "";
625         printf "<input type=radio name=cd9660_e value=0 %s> No</td> </tr>\n",
626                 defined($options{"-e"}) ? "" : "checked";
627         }
628 elsif ($_[0] eq "ntfs") {
629         # Windows NT filesystem
630         print "<tr> <td><b>Display MSDOS 8.3 filenames?</b></td>\n";
631         printf "<td><input type=radio name=ntfs_a value=1 %s> Yes\n",
632                 defined($options{"-a"}) ? "checked" : "";
633         printf "<input type=radio name=ntfs_a value=0 %s> No</td>\n",
634                 defined($options{"-a"}) ? "" : "checked";
635
636         print "<td><b>Case sensitive filenames?</b></td>\n";
637         printf "<td><input type=radio name=ntfs_i value=0 %s> Yes\n",
638                 defined($options{"-i"}) ? "" : "checked";
639         printf "<input type=radio name=ntfs_i value=1 %s> No</td> </tr>\n",
640                 defined($options{"-i"}) ? "checked" : "";
641
642         print "<tr> <td><b>User files are owned by</b></td>\n";
643         printf "<td><input type=radio name=ntfs_u_def value=1 %s> Default\n",
644                 defined($options{"-u"}) ? "" : "checked";
645         printf "<input type=radio name=ntfs_u_def value=0 %s>\n",
646                 defined($options{"-u"}) ? "checked" : "";
647         printf "<input name=ntfs_u size=8 value='%s'> %s</td>\n",
648                 defined($options{"-u"}) ? scalar(getpwuid($options{"-u"})) : "",
649                 &user_chooser_button("ntfs_u");
650
651         print "<td><b>Group files are owned by</b></td>\n";
652         printf "<td><input type=radio name=ntfs_g_def value=1 %s> Default\n",
653                 defined($options{"-u"}) ? "" : "checked";
654         printf "<input type=radio name=ntfs_g_def value=0 %s>\n",
655                 defined($options{"-u"}) ? "checked" : "";
656         printf "<input name=ntfs_g size=8 value='%s'> %s</td> </tr>\n",
657                 defined($options{"-g"}) ? scalar(getgrgid($options{"-g"})) : "",
658                 &group_chooser_button("ntfs_g");
659         }
660 elsif ($_[0] eq "swap") {
661         # Swap has no options..
662         print "<tr> <td><i>No Options Available</i></td> </tr>\n";
663         }
664 elsif ($_[0] eq "smbfs") {
665         # SMBFS has some special options
666         print "<tr> <td><b>$text{'linux_username'}</b></td>\n";
667         printf "<td><input name=smbfs_user size=15 value=\"%s\"></td>\n",
668                 $options{"user"};
669
670         print "<td><b>$text{'linux_password'}</b></td>\n";
671         printf "<td><input type=password name=smbfs_password size=15 value=\"%s\"></td> </tr>\n",
672                 $options{"nsmb_password"};
673
674         print "<tr> <td><b>$text{'linux_wg'}</b></td>\n";
675         printf "<td colspan=3><input type=radio name=smbfs_workgroup_def value=1 %s> $text{'linux_auto'}\n",
676                 defined($options{"nsmb_workgroup"}) ? "" : "checked";
677         printf "<input type=radio name=smbfs_workgroup_def value=0 %s>\n",
678                 defined($options{"nsmb_workgroup"}) ? "checked" : "";
679         print "<input size=10 name=smbfs_workgroup value=\"$options{'nsmb_workgroup'}\"></td> </tr>\n";
680
681         print "<tr> <td><b>$text{'linux_mname'}</b></td>\n";
682         printf "<td colspan=3><input type=radio name=smbfs_addr_def value=1 %s> %s\n",
683                 defined($options{"nsmb_addr"}) ? "" : "checked", $text{'linux_auto'};
684         printf "<input type=radio name=smbfs_addr_def value=0 %s>\n",
685                 defined($options{"nsmb_addr"}) ? "checked" : "";
686         print "<input size=30 name=smbfs_addr value=\"$options{'nsmb_addr'}\"></td> </tr>\n";
687         }
688 }
689
690
691 # check_location(type)
692 # Parse and check inputs from %in, calling &error() if something is wrong.
693 # Returns the location string for storing in the fstab file
694 sub check_location
695 {
696 if ($_[0] eq "nfs") {
697         local($out, $temp, $mout, $dirlist);
698
699         # Use ping and showmount to see if the host exists and is up
700         if ($in{nfs_host} !~ /^\S+$/) {
701                 &error("'$in{nfs_host}' is not a valid hostname");
702                 }
703         &execute_command("ping -c 1 '$in{nfs_host}'", undef, \$out, \$out);
704         if ($out =~ /unknown host/i) {
705                 &error("The host '$in{nfs_host}' does not exist");
706                 }
707         elsif ($out =~ /100\% packet loss/) {
708                 &error("The host '$in{nfs_host}' is down");
709                 }
710         &execute_command("showmount -e '$in{nfs_host}'", undef, \$out, \$out);
711         if ($out =~ /Unable to receive/) {
712                 &error("The host '$in{nfs_host}' does not support NFS");
713                 }
714         elsif ($?) {
715                 &error("Failed to get mount list : $out");
716                 }
717
718         # Validate directory name
719         foreach (split(/\n/, $out)) {
720                 if (/^(\/\S+)/) { $dirlist .= "$1\n"; }
721                 }
722         if ($in{nfs_dir} !~ /^\/\S+$/) {
723                 &error("'$in{nfs_dir}' is not a valid directory name. The ".
724                        "available directories on $in{nfs_host} are:".
725                        "<pre>$dirlist</pre>");
726                 }
727
728         # Try a test mount to see if filesystem is available
729         $temp = &transname();
730         &make_dir($temp, 0755);
731         &execute_command("mount $in{nfs_host}:$in{nfs_dir} $temp",
732                          undef, \$mout, \$mout);
733         if ($mout =~ /No such file or directory/) {
734                 &error("The directory '$in{nfs_dir}' does not exist on the ".
735                        "host $in{nfs_host}. The available directories are:".
736                        "<pre>$dirlist</pre>");
737                 }
738         elsif ($mout =~ /Permission denied/) {
739                 &error("This host is not allowed to mount the directory ".
740                        "$in{nfs_dir} from $in{nfs_host}");
741                 }
742         elsif ($?) {
743                 &error("NFS Error - $mout");
744                 }
745         # It worked! unmount
746         &execute_command("umount $temp");
747         &unlink_file($temp);
748         return "$in{nfs_host}:$in{nfs_dir}";
749         }
750 elsif ($_[0] eq "smbfs") {
751         # A windows server filesystem .. check the server and share
752         $in{'smbfs_server'} =~ /\S/ || &error($text{'linux_eserver'});
753         $in{'smbfs_share'} =~ /\S/ || &error($text{'linux_eshare'});
754         return "\\\\".lc($in{'smbfs_server'})."\\".lc($in{'smbfs_share'});
755         }
756 else {
757         # This is some kind of disk-based filesystem.. get the device name
758         if ($in{'disk_dev'} == 0) {
759                 $in{'ide_t'} =~ /^\d+$/ ||
760                         &error("'$in{ide_t}' is not a valid device number");
761                 $in{'ide_s'} =~ /^\d+$/ ||
762                         &error("'$in{ide_s}' is not a valid slice number");
763                 $in{'ide_p'} =~ /^[a-z]*$/ ||
764                         &error("'$in{ide_p}' is not a valid partition letter");
765                 $dv = "/dev/ad$in{ide_t}s$in{ide_s}$in{ide_p}";
766                 }
767         elsif ($in{'disk_dev'} == 1) {
768                 $in{'scsi_t'} =~ /^\d+$/ ||
769                         &error("'$in{scsi_t}' is not a valid device number");
770                 $in{'scsi_s'} =~ /^\d+$/ ||
771                         &error("'$in{scsi_s}' is not a valid slice number");
772                 $in{'scsi_p'} =~ /^[a-z]*$/ ||
773                         &error("'$in{scsi_p}' is not a valid partition letter");
774                 $dv = "/dev/da$in{scsi_t}s$in{scsi_s}$in{scsi_p}";
775                 }
776         else {
777                 $dv = $in{'dev_path'};
778                 }
779
780         # If the device entered is a symlink, follow it
781         if ($dvlink = readlink($dv)) {
782                 if ($dvlink =~ /^\//) { $dv = $dvlink; }
783                 else {  $dv =~ /^(.*\/)[^\/]+$/;
784                         $dv = $1.$dvlink;
785                         }
786                 }
787
788         # Check if the device actually exists and uses the right filesystem
789         (-r $dv) || &error("The device file '$dv' does not exist");
790         return $dv;
791         }
792 }
793
794 # check_options(type, device, directory)
795 # Read options for some filesystem from %in, and use them to update the
796 # %options array. Options handled by the user interface will be set or
797 # removed, while unknown options will be left untouched.
798 sub check_options
799 {
800 local($k, @rv);
801
802 # Parse the common options first..
803 if ($_[0] ne "swap") {
804         delete($options{"ro"}); delete($options{"rw"});
805         delete($options{"rdonly"});
806         if ($in{'bsd_ro'}) { $options{'ro'} = ''; }
807         else { $options{'rw'} = ""; }
808
809         delete($options{"sync"}); delete($options{"async"});
810         if ($in{'bsd_sync'}) { $options{'sync'} = ''; }
811
812         delete($options{'nodev'});
813         if ($in{'bsd_nodev'}) { $options{'nodev'} = ''; }
814
815         delete($options{'noexec'});
816         if ($in{'bsd_noexec'}) { $options{'noexec'} = ''; }
817
818         delete($options{'nosuid'});
819         if ($in{'bsd_nosuid'}) { $options{'nosuid'} = ''; }
820
821         delete($options{'noatime'});
822         if ($in{'bsd_noatime'}) { $options{'noatime'} = ''; }
823
824         if ($uname_release =~ /^[34]\./) {
825                 delete($options{'nosymfollow'});
826                 $options{'nosymfollow'} = '' if ($in{'bsd_nosymfollow'});
827
828                 delete($options{'suiddir'});
829                 $options{'suiddir'} = '' if ($in{'bsd_suiddir'});
830                 }
831         }
832 else {
833         # Swap always has the sw option
834         $options{'sw'} = "";
835         }
836
837 if ($_[0] eq "ufs") {
838         # Parse UFS quota options
839         delete($options{'userquota'}) if ($in{'ufs_userquota'} == 0);
840         $options{'userquota'} = "" if ($in{'ufs_userquota'} == 1);
841         $options{'userquota'} = $in{'ufs_groupquota_file'}
842                 if ($in{'ufs_userquota'} == 2);
843
844         delete($options{'groupquota'}) if ($in{'ufs_groupquota'} == 0);
845         $options{'groupquota'} = "" if ($in{'ufs_groupquota'} == 1);
846         $options{'groupquota'} = $in{'ufs_groupquota_file'}
847                 if ($in{'ufs_groupquota'} == 2);
848         }
849 elsif ($_[0] eq "nfs") {
850         # NFS has a few specific options..
851         delete($options{'-b'});
852         $options{'-b'} = "" if ($in{'nfs_b'});
853
854         delete($options{'-s'});
855         $options{'-s'} = "" if ($in{'nfs_s'});
856
857         delete($options{'-t'});
858         $options{'-t'} = $in{'nfs_t'} if (!$in{'nfs_t_def'});
859
860         delete($options{'-x'});
861         $options{'-x'} = $in{'nfs_x'} if (!$in{'nfs_x_def'});
862
863         delete($options{'-2'}); delete($options{'-3'});
864         $options{'-2'} = "" if ($in{'nfs_ver'} == 2);
865         $options{'-3'} = "" if ($in{'nfs_ver'} == 3);
866
867         delete($options{'-R'});
868         $options{'-R'} = $in{'nfs_r'} if (!$in{'nfs_r_def'});
869
870         delete($options{'-a'});
871         $options{'-a'} = $in{'nfs_a'} if (!$in{'nfs_a_def'});
872
873         delete($options{'-T'});
874         $options{'-T'} = "" if ($in{'nfs_t2'});
875         }
876 elsif ($_[0] eq "msdos") {
877         # MSDOS options for file ownership/perms
878         delete($options{"-u"}); delete($options{"-g"});
879         if ($in{'msdos_u'} ne "") { $options{'-u'} = getpwnam($in{'msdos_u'}); }
880         if ($in{'msdos_g'} ne "") { $options{'-g'} = getgrnam($in{'msdos_g'}); }
881
882         delete($options{"-m"});
883         if (!$in{'msdos_m_def'}) {
884                 $in{'msdos_m'} =~ /^[0-7]{3}$/ ||
885                         &error("'$in{'msdos_m'}' is not a valid octal mask");
886                 $options{'-m'} = $in{'msdos_m'};
887                 }
888         }
889 elsif ($_[0] eq "cd9660") {
890         # Options for iso9660 cd-roms
891         delete($options{'-r'});
892         $options{'-r'} = "" if ($in{'cd9660_r'});
893
894         delete($options{'-g'});
895         $options{'-g'} = "" if ($in{'cd9660_g'});
896
897         delete($options{'-e'});
898         $options{'-e'} = "" if ($in{'cd9660_e'});
899         }
900 elsif ($_[0] eq "ntfs") {
901         delete($options{"-u"}); delete($options{"-g"});
902         if ($in{'ntfs_u'} ne "") { $options{'-u'} = getpwnam($in{'ntfs_u'}); }
903         if ($in{'ntfs_g'} ne "") { $options{'-g'} = getgrnam($in{'ntfs_g'}); }
904
905         delete($options{"-a"});
906         $options{"-a"} = '' if ($in{'ntfs_a'});
907
908         delete($options{"-i"});
909         $options{"-i"} = '' if ($in{'ntfs_i'});
910         }
911 elsif ($_[0] eq "smbfs") {
912         # Parse SMBFS options
913         delete($options{'user'});
914         $options{'user'} = $in{'smbfs_user'} if ($in{'smbfs_user'});
915
916         delete($options{'nsmb_password'});
917         $options{'nsmb_password'} = $in{'smbfs_password'}
918                 if ($in{'smbfs_password'});
919
920         delete($options{'nsmb_addr'});
921         if (!$in{"smbfs_addr_def"}) {
922                 &check_ipaddress($in{"smbfs_addr"}) ||
923                         &error($text{'freebsd_eaddr'});
924                 $options{'nsmb_addr'} = $in{"smbfs_addr"};
925                 }
926
927         delete($options{'nsmb_workgroup'});
928         if (!$in{"smbfs_workgroup_def"}) {
929                 $in{"smbfs_workgroup"} =~ /^\S+$/ ||
930                         &error($text{'freebsd_eworkgroup'});
931                 $options{'nsmb_workgroup'} = $in{"smbfs_workgroup"};
932                 }
933         }
934
935 # Return options string
936 return &join_options();
937 }
938
939 # create_swap(file, size, units)
940 # Calls dd and mkswap to setup a swap file
941 sub create_swap
942 {
943 local($out, $bl);
944 $bl = $_[1] * ($_[2] eq "g" ? 1024*1024 :
945                $_[2] eq "m" ? 1024 : 1);
946 $out = &backquote_logged("dd if=/dev/zero of=$_[0] bs=1024 count=$bl 2>&1");
947 if ($?) { return "dd failed : $out"; }
948 $out = &backquote_logged("mkswap $_[0] $bl 2>&1");
949 if ($?) { return "mkswap failed : $out"; }
950 &system_logged("sync >/dev/null 2>&1");
951 return 0;
952 }
953
954 # exports_list(host, dirarray, clientarray)
955 # Fills the directory and client array references with exports from some
956 # host. Returns an error string if something went wrong
957 sub exports_list
958 {
959 local($dref, $cref, $out, $_);
960 $dref = $_[1]; $cref = $_[2];
961 &execute_command("showmount -e ".quotemeta($_[0]), undef, \$out, \$out, 0, 1);
962 if ($?) { return $out; }
963 foreach (split(/\n/, $out)) {
964         if (/^(\/\S*)\s+(.*)$/) {
965                 push(@$dref, $1); push(@$cref, $2);
966                 }
967         }
968 return undef;
969 }
970
971 # broadcast_addr()
972 # Returns a useable broadcast address for finding NFS servers
973 sub broadcast_addr
974 {
975 local($out);
976 &execute_command("ifconfig -a", undef, \$out, \$out, 0, 1);
977 if ($out =~ /broadcast\s+(\S+)\s+/) { return $1; }
978 return "255.255.255.255";
979 }
980
981 sub device_name
982 {
983 return $_[0];
984 }
985
986 sub files_to_lock
987 {
988 return ( $config{'fstab_file'}, $nsmb_conf );
989 }
990
991 # get_nsmb_conf(server, share, user)
992 # Finds a single nsmb.conf section
993 sub get_nsmb_conf
994 {
995 local $conf;
996 local $insection = 0;
997 local $lnum = 0;
998 open(CONF, $nsmb_conf);
999 while(<CONF>) {
1000         s/\r|\n//g;
1001         s/^\s*#.*$//;
1002         if (/^\s*\[([^:]+):([^:]+):([^:]+)\]/ &&
1003             lc($1) eq lc($_[0]) && lc($2) eq lc($_[2]) && lc($3) eq lc($_[1])) {
1004                 # Start of section
1005                 $insection = 1;
1006                 $conf = { 'line' => $lnum,
1007                           'eline' => $lnum,
1008                           'server' => lc($_[0]),
1009                           'user' => lc($_[2]),
1010                           'share' => lc($_[1]) };
1011                 }
1012         elsif (/^\s*\[.*\]/) {
1013                 # Start of another section
1014                 $insection = 0;
1015                 }
1016         elsif (/^\s*(\S+)\s*=\s*(\S+)/ && $insection) {
1017                 $conf->{'values'}->{lc($1)} = $2;
1018                 $conf->{'eline'} = $lnum;
1019                 }
1020         $lnum++;
1021         }
1022 close(CONF);
1023 return $conf;
1024 }
1025
1026 # save_nsmb_conf(&conf)
1027 # Updates or creates a single nsmb.conf section
1028 sub save_nsmb_conf
1029 {
1030 local $lref = &read_file_lines($nsmb_conf);
1031 local @lines = ( "[$_[0]->{'server'}:$_[0]->{'user'}:$_[0]->{'share'}]" );
1032 foreach $k (keys %{$_[0]->{'values'}}) {
1033         push(@lines, $k."=".$_[0]->{'values'}->{$k});
1034         }
1035 if (defined($_[0]->{'line'})) {
1036         # Modifying
1037         splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1,
1038                @lines);
1039         }
1040 else {
1041         # Adding
1042         push(@$lref, @lines);
1043         }
1044 &flush_file_lines();
1045 }
1046
1047 # update_nsmb(share, &options)
1048 sub update_nsmb
1049 {
1050 local ($server, $share, $user);
1051 if ($_[0] =~ /^[\\\/]{2}(\S+)\@(\S+)[\\\/](\S+)$/) {
1052         ($user, $server, $share) = ($1, $2, $3);
1053         }
1054 elsif ($_[0] =~ /^[\\\/]{2}(\S+)[\\\/](\S+)$/) {
1055         ($user, $server, $share) = ("root", $1, $2);
1056         }
1057 else {
1058         &error("Invalid share $_[0]");
1059         }
1060 local $conf = &get_nsmb_conf($server, $share, $user);
1061 $conf ||= { "server" => $server,
1062             "share" => $share,
1063             "user" => $user };
1064 $conf->{'values'} = { };
1065 local %others;
1066 foreach $k (keys %{$_[1]}) {
1067         if ($k =~ /^nsmb_(.*)$/) {
1068                 $conf->{'values'}->{$1} = $_[1]->{$k};
1069                 }
1070         else {
1071                 $others{$k} = $_[1]->{$k};
1072                 }
1073         }
1074 &save_nsmb_conf($conf);
1075 return \%others;
1076 }
1077
1078 # read_nsmb(share)
1079 # Returns a hash reference containing options for some share
1080 sub read_nsmb
1081 {
1082 local ($server, $share, $user);
1083 if ($_[0] =~ /^[\\\/]{2}(\S+)\@(\S+)[\\\/](\S+)$/) {
1084         ($user, $server, $share) = ($1, $2, $3);
1085         }
1086 elsif ($_[0] =~ /^[\\\/]{2}(\S+)[\\\/](\S+)$/) {
1087         ($user, $server, $share) = ("root", $1, $2);
1088         }
1089 else {
1090         &error("Invalid share $_[0]");
1091         }
1092 local $conf = &get_nsmb_conf($server, $share, $user);
1093 if ($conf) {
1094         local (%rv, $k);
1095         foreach $k (keys %{$conf->{'values'}}) {
1096                 $rv{"nsmb_".$k} = $conf->{'values'}->{$k};
1097                 }
1098         return \%rv;
1099         }
1100 return undef;
1101 }
1102
1103 1;
1104