2 # Common functions for the linux exports file
4 BEGIN { push(@INC, ".."); };
7 %access = &get_module_acl();
8 &foreign_require("mount", "mount-lib.pl");
11 # Returns a list of all exports
14 local (@rv, $pos, $lnum, $h, $o, $line);
15 return @list_exports_cache if (@list_exports_cache);
16 open(EXP, $config{'exports_file'});
18 while($line = <EXP>) {
21 while($line =~ /\\$/) {
22 # continuation character!
28 if ($line =~ /^(#*)\s*(\/\S*)\s+(.*)$/) {
32 if ($dir =~ /^$config{'exports_file'}/) {
37 while($rest =~ /^([^\s+\(\)]*)\(([^\)]*)\)\s*(.*)$/ ||
38 $rest =~ /^([^\s+\(\)]+)\s*()(.*)$/) {
40 $exp{'active'} = $active;
45 while($ostr =~ /^([a-z_]+)=([0-9,\-]+)\s*,\s*(.*)$/ ||
46 $ostr =~ /^([a-z_]+)=([0-9,\-]+)(.*)$/ ||
47 $ostr =~ /^([a-z_]+)=([^,\s]+),(.*)$/ ||
48 $ostr =~ /^([a-z_]+)=([^,\s]+)(.*)$/ ||
49 $ostr =~ /^([a-z_]+)()\s*,\s*(.*)$/ ||
50 $ostr =~ /^([a-z_]+)()(.*)$/) {
51 if ($2 ne "") { $exp{'options'}->{$1} = $2; }
52 else { $exp{'options'}->{$1} = ""; }
55 $exp{'line'} = $slnum;
56 $exp{'eline'} = $lnum;
58 $exp{'index'} = scalar(@rv);
65 @list_exports_cache = @rv;
66 return @list_exports_cache;
69 # delete_export(&export)
70 # Delete an existing export
73 local @exps = &list_exports();
74 local @same = grep { $_ ne $_[0] && $_->{'line'} eq $_[0]->{'line'} } @exps;
75 local $lref = &read_file_lines($config{'exports_file'});
77 # other exports on the same line.. cannot totally delete
78 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'}-$_[0]->{'line'}+1,
79 &make_exports_line(@same));
80 map { $_->{'line'} = $_->{'eline'} = $_[0]->{'line'} } @same;
84 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'}-$_[0]->{'line'}+1);
85 # unmount the directory if it is mounted with --bind
86 local $dir = $_[0]->{'dir'};
87 local @mounted = &mount::list_mounted();
88 for(my $i=0; $i<@mounted; $i++) {
90 if (($p->[0] eq $dir) and ($p->[2] eq "bind")) {
91 &mount::unmount_dir($p->[1], $p->[0], $p->[2]);
94 # remove it from the fstab file
95 local @mounts = &mount::list_mounts();
96 for(my $i=0; $i<@mounts; $i++) {
98 if (($p->[0] eq $dir) and ($p->[2] eq "bind")) {
99 &mount::delete_mount($i);
103 @list_exports_cache = grep { $_ ne $_[0] } @list_exports_cache;
107 # create_export_via_pfs(&export)
108 sub create_export_via_pfs
112 local $export_pfs = 1;
113 local $pfs = $_[0]->{'pfs'};
115 # Check if the pfs is already exported
116 open(EXP, "< $config{'exports_file'}");
117 while (my $line = <EXP>) {
118 if ($line =~ /^$pfs[\s|\t]/) {
119 if ($line !~ /fsid=0/) {
120 &error($text{'save_pfs'}, $pfs);
127 # Mount the directory in the pfs
129 local $to_be_mounted = 1;
130 local $dir = $_[0]->{'dir'};
131 local $expt_dir = $dir;
132 $expt_dir =~ s/\/$//;
133 $expt_dir = $pfs."/".basename($expt_dir);
135 # Add it in the fstab file if it is not already in
136 local @mounts = &mount::list_mounts();
137 for(my $i=0; $i<@mounts; $i++) {
139 if (($p->[0] eq $expt_dir) and ($p->[1] eq $dir) and ($p->[2] eq "bind")) {
144 &mount::create_mount($expt_dir, $dir, "bind", "");
147 # Mount it if it is not already mounted
148 local @mounted = &mount::list_mounted();
149 for(my $i=0; $i<@mounted; $i++) {
150 my $p = $mounted[$i];
151 if (($p->[0] eq $expt_dir) and ($p->[1] eq $dir) and ($p->[2] eq "bind")) {
155 if ($to_be_mounted) {
156 eval { mkpath($expt_dir) };
158 &error($text{'save_create_dir'}, $expt_dir );
160 my $err = &mount::mount_dir($expt_dir, $dir, "bind", "");
161 &error($err) if ($err);
162 &webmin_log("mount", undef, undef, { 'dev' => $expt_dir,
167 # Export the directory $expt_dir
168 $_[0]->{'dir'} = $expt_dir;
169 create_export($_[0]);
172 # Export the pfs with the "fsid=0" option
173 $_[0]->{'dir'} = $pfs;
174 $_[0]->{'options'}->{'fsid'} = "0";
175 create_export($_[0]);
179 # create_export(&export)
182 &open_tempfile(EXP, ">>$config{'exports_file'}");
183 &print_tempfile(EXP, &make_exports_line($_[0]),"\n");
184 &close_tempfile(EXP);
187 # modify_export(&export, &old)
190 local @exps = &list_exports();
191 local @same = grep { $_->{'line'} eq $_[1]->{'line'} } @exps;
192 local $lref = &read_file_lines($config{'exports_file'});
193 if ($_[0]->{'dir'} eq $_[1]->{'dir'} &&
194 $_[0]->{'active'} == $_[1]->{'active'} || @same == 1) {
195 # directory or active not changed, or on a line of it's own
196 splice(@same, &indexof($_[1],@same), 1, $_[0]);
197 splice(@$lref, $_[1]->{'line'}, $_[1]->{'eline'}-$_[1]->{'line'}+1,
198 &make_exports_line(@same));
201 # move to a line of it's own
202 splice(@same, &indexof($_[1],@same), 1);
203 splice(@$lref, $_[1]->{'line'}, $_[1]->{'eline'}-$_[1]->{'line'}+1,
204 &make_exports_line(@same));
205 push(@$lref, &make_exports_line($_[0]));
210 # make_exports_line([&export]+)
211 sub make_exports_line
215 local %opts = %{$e->{'options'}};
216 if (%opts || !$e->{'host'}) {
217 push(@htxt, $e->{'host'}."(".
218 join(",", map { $opts{$_} eq "" ? $_
222 else { push(@htxt, $e->{'host'}); }
224 return ($_[0]->{'active'} ? "" : "#").$_[0]->{'dir'}."\t".join(" ", @htxt);
227 # file_chooser_button2(input, type, name, disabled)
228 # A file_chooser_button which can be disabled
229 sub file_chooser_button2
231 local $disabled = ($_[3] == 1) ? "disabled" : "";
232 return "<input type=button name=$_[2] onClick='ifield = document.forms[0].$_[0]; chooser = window.open(\"$gconfig{'webprefix'}/chooser.cgi?type=$_[1]&chroot=/&file=\"+escape(ifield.value), \"chooser\", \"toolbar=no,menubar=no,scrollbar=no,width=400,height=300\"); chooser.ifield = ifield; window.ifield = ifield' value=\"...\" $disabled>\n";
235 # nfs_max_version(host)
236 # Return the max NFS version allowed on a server
239 local($_, $max, $out);
241 $out = `rpcinfo -p $_[0] 2>&1`;
242 if ($?) { return 3; }
243 foreach (split(/\n/, $out)) {
244 if ((/ +(\d) +.*nfs/) && ($1 > $max)) {
250 # describe_host(host)
251 # Given a host, regexp or netgroup return a human-readable version
254 local $h = &html_escape($_[0]);
255 if ($h eq "=public") { return $text{'exports_webnfs'}; }
256 elsif ($h =~ /^gss\//) { return &text('exports_gss', "<i>$h</i>"); }
257 elsif ($h =~ /^\@(.*)/) { return &text('exports_ngroup', "<i>$1</i>"); }
258 elsif ($h =~ /^(\S+)\/(\S+)$/) {
259 return &text('exports_net', "<i>$1/$2</i>"); }
260 elsif ($h eq "" || $h eq "*") { return $text{'exports_all'}; }
261 elsif ($h =~ /\*/) { return &text('exports_hosts', "<i>$h</i>"); }
262 else { return &text('exports_host', "<i>$h</i>"); }
267 return !&has_command("rpc.nfsd") && !&has_command("nfsd") &&
268 !&has_command("rpc.knfsd") ? 0 : 1;
272 # Apply the /etc/exports configuration
275 # Try exportfs -r first
276 if ($config{'apply_cmd'} && &find_byname("nfsd") && &find_byname("mountd")) {
277 local $out = &backquote_logged("$config{'apply_cmd'} 2>&1 </dev/null");
278 if (!$? && $out !~ /invalid|error|failed/i) {
279 # Looks like it worked!
284 &system_logged("$config{'portmap_command'} >/dev/null 2>&1 </dev/null")
285 if ($config{'portmap_command'});
286 local $temp = &transname();
287 local $rv = &system_logged("($config{'restart_command'}) </dev/null >$temp 2>&1");
288 local $out = `cat $temp`;
291 # something went wrong.. return an error
292 return "<pre>$out</pre>";