#-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # # Copyright (C) 2000-2001 Ximian, Inc. # # Authors: Hans Petter Jansson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published # by the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. package Shares::NFS; use Utils::Parse; sub get_distro_nfs_file { return "/etc/dfs/dfstab" if ($Utils::Backend::tool{"system"} eq "SunOS"); return "/etc/exports"; } sub get_share_client_info { my ($client) = @_; my ($pattern, $options_str, @options, $option); my ($rw); $client =~ /^([a-zA-Z0-9.-_*?@\/]+)/; $pattern = $1; $pattern = "0.0.0.0/0" if $pattern eq ""; $rw = 0; if ($client =~ /\((.+)\)/) { $option_str = $1; @options = ($option_str =~ /([a-zA-Z0-9_=-]+),?/mg); for $option (@options) { $rw = 1 if ($option eq "rw"); # Add supported NFS export options here. Some might have to be split on '='. } } return [ $pattern, !$rw ]; } sub get_share_info { my ($clients) = @_; my (@share_info, $client); foreach $client (@$clients) { push @share_info, &get_share_client_info ($client); } return \@share_info; } sub get_share_info_sunos { my ($str) = @_; my (@options, $opt, @values, @share_info); @options = split (/\s*,\s*/, $str); foreach $opt (@options) { my ($option, $value); @values = split /\s*=\s*/, $opt; $option = $values[0]; $value = $values[1]; # only support "rw" and "ro" at the moment if ($option eq "rw" || $option eq "ro") { my ($rw, $client); $rw = ($option eq "rw") ? 1 : 0; if (!$value) { push @share_info, [ $rw, "0.0.0.0/0" ]; } else { my @clients; # get the clients list @clients = split (/:/, $value); foreach $client (@clients) { push @share_info, [ $client, $rw ]; } } } } return \@share_info; } sub get_client_opts_sunos { my ($clients) = @_; my (@rw_clients, @ro_clients, $client, $str, $i); foreach $i (@$clients) { #FIXME: broken logic? if (!$$i[1]) { push @rw_clients, $$i[0]; } else { push @ro_clients, $$i[0]; } } # get rw clients if (scalar (@rw_clients)) { $str .= "rw=" . join (":", @rw_clients); } # get ro clients if (scalar (@ro_clients)) { $str .= ",ro=" . join (":", @ro_clients); } return $str; } sub get_export_line { my ($share) = @_; my ($str, $i); if ($Utils::Backend::tool{"system"} eq "SunOS") { $str = "share -F nfs"; $str .= " -o " . &get_client_opts_sunos ($$share[1]); $str .= " " . $$share[0]; } else { $str = sprintf ("%-15s ", $$share[0]); foreach $i (@{$$share[1]}) { $str .= $$i[0]; #FIXME: broken logic? $str .= "(rw)" if (!$$i[1]); $str .= " "; } $str .= "\n"; } return $str; } sub share_line_matches { my ($share, $line) = @_; return 0 if (&Utils::Util::ignore_line ($line)); chomp $line; if ($Utils::Backend::tool{"system"} eq "SunOS") { return 0 if ($line !~ /\-F\s+nfs/); return 1 if ($line =~ /$$share[0]$/); } else { my @arr; @arr = split /[ \t]+/, $line; return 1 if ($arr[0] eq $$share[0]); } } sub add_entry { my ($share, $file) = @_; my ($buff); $buff = &Utils::File::load_buffer ($file); push @$buff, &get_export_line ($share); &Utils::File::save_buffer ($buff, $file); } sub delete_entry { my ($share, $file) = @_; my ($buff, $i, $line, @arr); $buff = &Utils::File::load_buffer ($file); &Utils::File::join_buffer_lines ($buff); $i = 0; while ($$buff[$i]) { if (&share_line_matches ($share, $$buff[$i])) { delete $$buff[$i]; } $i++; } &Utils::File::clean_buffer ($buff); &Utils::File::save_buffer ($buff, $file); } sub change_entry { my ($old_share, $share, $file) = @_; my ($buff, $i, $line, @arr); $buff = &Utils::File::load_buffer ($file); &Utils::File::join_buffer_lines ($buff); $i = 0; while ($$buff[$i]) { if (&share_line_matches ($old_share, $$buff[$i])) { $$buff[$i] = &get_export_line ($share); } $i++; } &Utils::File::clean_buffer ($buff); &Utils::File::save_buffer ($buff, $file); } sub get_dfstab_shares { my ($file, $type) = @_; my ($buff, $line, @arr); # dfstab example: # # share [-F fstype] [-o fs_options ] [-d description] [pathname [resourcename]] # .e.g, # share -F nfs -o rw=engineering -d "home dirs" /export/home2 $buff = &Utils::File::load_buffer ($file); &Utils::File::join_buffer_lines ($buff); return [] if (!$buff); foreach $line (@$buff) { chomp $line; if ($line =~ /^\s*\S*share\s+(.*)/) { my $share; my $line = $1; if ($line =~ /\s-F\s+(\S+)/) { $share->{'type'} = $1; } else { $share->{'type'} = "nfs"; } # skip undesired shares next if ($share->{'type'} ne $type); if ($line =~ /\s+(\/\S+)/ || $line =~ /\s+(\/)/ || $line eq "/") { $share->{'dir'} = $1; } if ($line =~ /-o\s+"([^\"]+)"/ || $line =~ /-o\s+(\S+)/) { $share->{'opts'} = $1; } #if ($line =~ /-d\s+\"([^\"]+)\"/ || $line =~ /-d\s+(\S+)/) { $share->{'desc'} = $1; } push @arr, $share; } } return \@arr; } sub get { my ($nfs_file); my (@sections, @table, $entries); my ($point, $share_info); $nfs_file = &get_distro_nfs_file (); if ($Utils::Backend::tool{"system"} eq "SunOS") { my $shares = &get_dfstab_shares ($nfs_file, "nfs"); foreach $share (@$shares) { $point = $share->{'dir'}; $share_info = &get_share_info_sunos ($share->{'opts'}); push @table, [ $point, $share_info ]; } } else { $entries = &Utils::Parse::split_hash_with_continuation ($nfs_file, "[ \t]+", "[ \t]+"); foreach $point (keys %$entries) { my $clients = $$entries{$point}; $share_info = &get_share_info ($clients); push @table, [ $point, $share_info ]; } } return \@table; } sub set { my ($config) = @_; my ($nfs_exports_file); my ($old_config, %shares); my (%config_hash, %old_config_hash); my ($state, $i); $nfs_exports_name = &get_distro_nfs_file (); $old_config = &get (); foreach $i (@$config) { $shares{$$i[0]} |= 1; $config_hash{$$i[0]} = $i; } foreach $i (@$old_config) { $shares{$$i[0]} |= 2; $old_config_hash{$$i[0]} = $i; } foreach $i (sort keys (%shares)) { $state = $shares{$i}; if ($state == 1) { # These entries have been added &add_entry ($config_hash{$i}, $nfs_exports_name); } elsif ($state == 2) { # These entries have been deleted &delete_entry ($old_config_hash{$i}, $nfs_exports_name); } elsif (($state == 3) && (!Utils::Util::struct_eq ($config_hash{$i}, $old_config_hash{$i}))) { # These entries have been modified &change_entry ($old_config_hash{$i}, $config_hash{$i}, $nfs_exports_name); } } if ($Utils::Backend::tool{"system"} eq "SunOS") { &Utils::File::run ("unshareall", "-F", "nfs"); &Utils::File::run ("shareall", "-F", "nfs"); } } sub get_files { my ($files); push @$files, &get_distro_nfs_file (); return $files; } 1;