mirror of
https://github.com/webmin/webmin.git
synced 2025-07-20 16:48:46 +00:00
330 lines
8.3 KiB
Perl
Executable File
330 lines
8.3 KiB
Perl
Executable File
# export-lib.pl
|
|
# Common functions for the linux exports file
|
|
|
|
use strict;
|
|
use warnings;
|
|
no warnings 'redefine';
|
|
no warnings 'uninitialized';
|
|
BEGIN { push(@INC, ".."); };
|
|
use WebminCore;
|
|
&init_config();
|
|
our %access = &get_module_acl();
|
|
our ($module_root_directory, %text, %config, %gconfig);
|
|
our @list_exports_cache;
|
|
&foreign_require("mount", "mount-lib.pl");
|
|
|
|
# list_exports()
|
|
# Returns a list of all exports
|
|
sub list_exports
|
|
{
|
|
my (@rv, $pos, $h, $o, $line);
|
|
return @list_exports_cache if (@list_exports_cache);
|
|
open(EXP, "<".$config{'exports_file'});
|
|
my $lnum = 0;
|
|
while(my $line = <EXP>) {
|
|
my $slnum = $lnum;
|
|
$line =~ s/\s+$//g;
|
|
while($line =~ /\\$/) {
|
|
# continuation character!
|
|
$line =~ s/\\$//;
|
|
$line .= <EXP>;
|
|
$line =~ s/\s+$//g;
|
|
$lnum++;
|
|
}
|
|
if ($line =~ /^(#*)\s*(\/\S*)\s+(.*)$/) {
|
|
my $active = !$1;
|
|
my $dir = $2;
|
|
my $rest = $3;
|
|
if ($dir =~ /^$config{'exports_file'}/) {
|
|
$lnum++;
|
|
next;
|
|
}
|
|
$pos = 0;
|
|
while($rest =~ /^([^\s+\(\)]*)\(([^\)]*)\)\s*(.*)$/ ||
|
|
$rest =~ /^([^\s+\(\)]+)\s*()(.*)$/) {
|
|
my %exp;
|
|
$exp{'active'} = $active;
|
|
$exp{'dir'} = $dir;
|
|
$exp{'host'} = $1;
|
|
$exp{'options'} = { };
|
|
my $ostr = $2;
|
|
$rest = $3;
|
|
while($ostr =~ /^([a-z_]+)=([0-9,\-]+)\s*,\s*(.*)$/ ||
|
|
$ostr =~ /^([a-z_]+)=([0-9,\-]+)(.*)$/ ||
|
|
$ostr =~ /^([a-z_]+)=([^,\s]+),(.*)$/ ||
|
|
$ostr =~ /^([a-z_]+)=([^,\s]+)(.*)$/ ||
|
|
$ostr =~ /^([a-z_]+)()\s*,\s*(.*)$/ ||
|
|
$ostr =~ /^([a-z_]+)()(.*)$/) {
|
|
if ($2 ne "") { $exp{'options'}->{$1} = $2; }
|
|
else { $exp{'options'}->{$1} = ""; }
|
|
$ostr = $3;
|
|
}
|
|
$exp{'line'} = $slnum;
|
|
$exp{'eline'} = $lnum;
|
|
$exp{'pos'} = $pos++;
|
|
$exp{'index'} = scalar(@rv);
|
|
push(@rv, \%exp);
|
|
}
|
|
}
|
|
$lnum++;
|
|
}
|
|
close(EXP);
|
|
@list_exports_cache = @rv;
|
|
return @list_exports_cache;
|
|
}
|
|
|
|
# delete_export(&export)
|
|
# Delete an existing export
|
|
sub delete_export
|
|
{
|
|
my ($export) = @_;
|
|
my @exps = &list_exports();
|
|
my @same = grep { $_ ne $export && $_->{'line'} eq $_[0]->{'line'} } @exps;
|
|
my $lref = &read_file_lines($config{'exports_file'});
|
|
if (@same) {
|
|
# other exports on the same line.. cannot totally delete
|
|
splice(@$lref, $export->{'line'}, $export->{'eline'}-$export->{'line'}+1,
|
|
&make_exports_line(@same));
|
|
map { $_->{'line'} = $_->{'eline'} = $export->{'line'} } @same;
|
|
}
|
|
else {
|
|
# remove export line
|
|
splice(@$lref, $export->{'line'}, $export->{'eline'}-$export->{'line'}+1);
|
|
# unmount the directory if it is mounted with --bind
|
|
my $dir = $_[0]->{'dir'};
|
|
my @mounted = &mount::list_mounted();
|
|
for(my $i=0; $i<@mounted; $i++) {
|
|
my $p = $mounted[$i];
|
|
if (($p->[0] eq $dir) and ($p->[2] eq "bind")) {
|
|
&mount::unmount_dir($p->[1], $p->[0], $p->[2]);
|
|
}
|
|
}
|
|
# remove it from the fstab file
|
|
my @mounts = &mount::list_mounts();
|
|
for(my $i=0; $i<@mounts; $i++) {
|
|
my $p = $mounts[$i];
|
|
if (($p->[0] eq $dir) and ($p->[2] eq "bind")) {
|
|
&mount::delete_mount($i);
|
|
}
|
|
}
|
|
}
|
|
@list_exports_cache = grep { $_ ne $export } @list_exports_cache;
|
|
&flush_file_lines();
|
|
}
|
|
|
|
# create_export_via_pfs(&export)
|
|
sub create_export_via_pfs
|
|
{
|
|
my ($export) = @_;
|
|
|
|
use File::Basename;
|
|
use File::Path;
|
|
my $export_pfs = 1;
|
|
my $pfs = $export->{'pfs'};
|
|
$pfs =~ s/\/$//;
|
|
|
|
# Check if the pfs is already exported
|
|
my $lref = &read_file_lines($config{'exports_file'}, 1);
|
|
foreach my $line (@$lref) {
|
|
if ($line =~ /^$pfs[\s|\t]/) {
|
|
if ($line !~ /fsid=0/) {
|
|
&error(&text('save_pfs', $pfs));
|
|
}
|
|
$export_pfs = 0;
|
|
}
|
|
}
|
|
|
|
# Mount the directory in the pfs
|
|
my $add_line = 1;
|
|
my $to_be_mounted = 1;
|
|
my $dir = $export->{'dir'};
|
|
my $expt_dir = $dir;
|
|
$expt_dir =~ s/\/$//;
|
|
$expt_dir = $pfs."/".basename($expt_dir);
|
|
|
|
# Add it in the fstab file if it is not already in
|
|
my @mounts = &mount::list_mounts();
|
|
for(my $i=0; $i<@mounts; $i++) {
|
|
my $p = $mounts[$i];
|
|
if ($p->[0] eq $expt_dir && $p->[1] eq $dir && $p->[2] eq "bind") {
|
|
$add_line = 0;
|
|
}
|
|
}
|
|
if ($add_line) {
|
|
&mount::create_mount($expt_dir, $dir, "bind", "");
|
|
}
|
|
|
|
# Mount it if it is not already mounted
|
|
my @mounted = &mount::list_mounted();
|
|
for(my $i=0; $i<@mounted; $i++) {
|
|
my $p = $mounted[$i];
|
|
if ($p->[0] eq $expt_dir && $p->[1] eq $dir && $p->[2] eq "bind") {
|
|
$to_be_mounted = 0;
|
|
}
|
|
}
|
|
if ($to_be_mounted) {
|
|
eval { mkpath($expt_dir) };
|
|
if ($@) {
|
|
&error(&text('save_create_dir', $expt_dir));
|
|
}
|
|
my $err = &mount::mount_dir($expt_dir, $dir, "bind", "");
|
|
&error($err) if ($err);
|
|
}
|
|
|
|
# Export the directory $expt_dir
|
|
$export->{'dir'} = $expt_dir;
|
|
&create_export($export);
|
|
|
|
if ($export_pfs) {
|
|
# Export the pfs with the "fsid=0" option
|
|
$export->{'dir'} = $pfs;
|
|
$export->{'options'}->{'fsid'} = "0";
|
|
&create_export($export);
|
|
}
|
|
}
|
|
|
|
# create_export(&export)
|
|
# Add one new export to the config file
|
|
sub create_export
|
|
{
|
|
my ($export) = @_;
|
|
my $fh = "EXP";
|
|
&open_tempfile($fh, ">>$config{'exports_file'}");
|
|
&print_tempfile($fh, &make_exports_line($export),"\n");
|
|
&close_tempfile($fh);
|
|
}
|
|
|
|
# modify_export(&export, &old)
|
|
# Update one export in the config file
|
|
sub modify_export
|
|
{
|
|
my ($export, $old) = @_;
|
|
my @exps = &list_exports();
|
|
my @same = grep { $_->{'line'} eq $old->{'line'} } @exps;
|
|
my $lref = &read_file_lines($config{'exports_file'});
|
|
if ($export->{'dir'} eq $old->{'dir'} &&
|
|
$export->{'active'} == $old->{'active'} || @same == 1) {
|
|
# directory or active not changed, or on a line of it's own
|
|
splice(@same, &indexof($old, @same), 1, $export);
|
|
splice(@$lref, $old->{'line'}, $old->{'eline'} - $old->{'line'} + 1,
|
|
&make_exports_line(@same));
|
|
}
|
|
else {
|
|
# move to a line of it's own
|
|
splice(@same, &indexof($old, @same), 1);
|
|
splice(@$lref, $old->{'line'}, $old->{'eline'} - $old->{'line'} + 1,
|
|
&make_exports_line(@same));
|
|
push(@$lref, &make_exports_line($export));
|
|
}
|
|
&flush_file_lines();
|
|
}
|
|
|
|
# make_exports_line([&export]+)
|
|
# Returns the text lines for one or more exports
|
|
sub make_exports_line
|
|
{
|
|
my @htxt;
|
|
foreach my $e (@_) {
|
|
my %opts = $e->{'options'} ? %{$e->{'options'}} : ( );
|
|
if (%opts || !$e->{'host'}) {
|
|
push(@htxt, $e->{'host'}."(".
|
|
join(",", map { $opts{$_} eq "" ? $_
|
|
: "$_=$opts{$_}" }
|
|
(keys %opts)).")");
|
|
}
|
|
else {
|
|
push(@htxt, $e->{'host'});
|
|
}
|
|
}
|
|
return ($_[0]->{'active'} ? "" : "#").$_[0]->{'dir'}."\t".join(" ", @htxt);
|
|
}
|
|
|
|
# nfs_max_version(host)
|
|
# Return the max NFS version allowed on a server
|
|
sub nfs_max_version
|
|
{
|
|
my ($host) = @_;
|
|
my $max = 0;
|
|
my $out = &backquote_command("rpcinfo -p ".quotemeta($host)." 2>&1");
|
|
if ($?) {
|
|
# NFS server is down, take a guess based on kernel
|
|
my $out = &backquote_command("uname -r");
|
|
if ($out =~ /^(\d+)\./ && $1 >= 3 ||
|
|
$out =~ /^(\d+)\.(\d+)\./ && $1 == 2 && $2 > 6) {
|
|
return 4;
|
|
}
|
|
return 3;
|
|
}
|
|
foreach my $line (split(/\n/, $out)) {
|
|
if ($line =~ / +(\d) +.*nfs/ && $1 > $max) {
|
|
$max = $1;
|
|
}
|
|
}
|
|
return $max;
|
|
}
|
|
|
|
# describe_host(host)
|
|
# Given a host, regexp or netgroup return a human-readable version
|
|
sub describe_host
|
|
{
|
|
my ($h) = @_;
|
|
if ($h eq "=public") {
|
|
return $text{'exports_webnfs'};
|
|
}
|
|
elsif ($h =~ /^gss\//) {
|
|
return &text('exports_gss', "<i>".&html_escape($h)."</i>");
|
|
}
|
|
elsif ($h =~ /^\@(.*)/) {
|
|
return &text('exports_ngroup', "<i>".&html_escape("$1")."</i>");
|
|
}
|
|
elsif ($h =~ /^(\S+)\/(\S+)$/) {
|
|
return &text('exports_net', "<i>".&html_escape("$1/$2")."</i>");
|
|
}
|
|
elsif ($h eq "" || $h eq "*") {
|
|
return $text{'exports_all'};
|
|
}
|
|
elsif ($h =~ /\*/) {
|
|
return &text('exports_hosts', "<i>".&html_escape($h)."</i>");
|
|
}
|
|
else {
|
|
return &text('exports_host', "<i>".&html_escape($h)."</i>");
|
|
}
|
|
}
|
|
|
|
# has_nfs_commands()
|
|
# Returns 1 if all NFS server commands are installed
|
|
sub has_nfs_commands
|
|
{
|
|
return !&has_command("rpc.nfsd") && !&has_command("nfsd") &&
|
|
!&has_command("rpc.knfsd") ? 0 : 1;
|
|
}
|
|
|
|
# restart_mountd()
|
|
# Apply the /etc/exports configuration
|
|
sub restart_mountd
|
|
{
|
|
# Try exportfs -r first
|
|
if ($config{'apply_cmd'} && &find_byname("nfsd") && &find_byname("mountd")) {
|
|
my $out = &backquote_logged("$config{'apply_cmd'} 2>&1 </dev/null");
|
|
if (!$? && $out !~ /invalid|error|failed/i) {
|
|
# Looks like it worked!
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
&system_logged("$config{'portmap_command'} >/dev/null 2>&1 </dev/null")
|
|
if ($config{'portmap_command'});
|
|
my $temp = &transname();
|
|
my $rv = &system_logged("($config{'restart_command'}) </dev/null >$temp 2>&1");
|
|
my $out = &read_file_contents($temp);
|
|
unlink($temp);
|
|
if ($rv) {
|
|
# something went wrong.. return an error
|
|
return "<pre>".&html_escape($out)."</pre>";
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
1;
|
|
|