#!/usr/bin/perl # # Copyright (C) 2005 by Mandriva aginies _ateuh_ mandriva.com # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, 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 General Public License for more details. # # You should have received a copy of the GNU 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. my $version = "0.1"; # i18n: IMPORTANT: to get correct namespace (draknfs instead of libDrakX) BEGIN { unshift @::textdomains, 'draknfs' } use strict; use lib qw(/usr/lib/libDrakX); use standalone; use common; use any; use interactive; # must come *after* definition of textdomains for proper initialisation use ugtk2 qw(:ask :wrappers :create :dialogs); my $in = 'interactive'->vnew('su'); use constant FALSE => 0; use constant TRUE => 1; sub test_root { unless ($> == 0) { err_dialog(N("Error!"), N("You are not root. Exiting...")); die unless $::testing; } } my $CONF = "/etc/exports"; my @listshare; my @listuserid = qw(root_squash no_root_squash all_squash no_all_squash); push @listuserid, ""; my @listsecure = qw(secure insecure ); push @listsecure, ""; my @listsync = qw(async sync); push @listsync, ""; my @listr = qw(ro rw); push @listr, ""; use constant COLUMN_DIR => 0; use constant COLUMN_ACCESS => 1; use constant COLUMN_RIGHT => 2; use constant COLUMN_OPTIONS => 3; use constant NUM_COLUMNS => 4; my %size_groups = map { $_ => Gtk2::SizeGroup->new('horizontal') } qw(label widget button); my $label_and_widgets = sub { my ($label, $widget, $button) = @_; gtkpack_(Gtk2::HBox->new(0,5), 0, gtkadd_widget($size_groups{label}, $label), 1, gtkadd_widget($size_groups{widget}, $widget), 2, gtkadd_widget($size_groups{button}, $button), ); }; my $fdwidget = sub { my ($data, $label) = @_; my $fd = new Gtk2::FileSelection(N("Directory Selection")); $fd->set_modal(TRUE); $fd->signal_connect("destroy", sub { $fd->hide }); $fd->ok_button->signal_connect(clicked => sub { my $file = $fd->get_filename; -d $file or err_dialog(N("Error!"), N("Should be a directory.")) and return; $data->set_text($file); $fd->hide; }, $fd); $fd->cancel_button->signal_connect(clicked => sub { $fd->hide }); return $fd; }; sub get_nfs_data { # /home/nis *(async,rw,no_root_squash) # /home/nis/guibo/Build *(async,rw,no_root_squash) foreach (cat_($CONF)) { my ($dir, $access, $right, $options) = m/^(\/\S+)\s+(\S*)\((\S*)\)\s+(\S*)/; $dir and push @listshare, { dir => $dir, access => $access, right => $right, options => $options, }; } } sub write_conf { output($CONF, "# generated by drakhosts.pl\n"); foreach my $a (@listshare) { append_to_file($CONF, "$a->{dir} $a->{access}($a->{right}) $a->{options}\n"); } } my $help_access = N("NFS clients may be specified in a number of ways: single host: You may specify a host either by an abbreviated name recognized be the resolver, the fully qualified domain name, or an IP address. netgroups: NIS netgroups may be given as \@group. wildcards: machine names may contain the wildcard characters * and ?. For instance: *.cs.foo.edu matches all hosts in the domain cs.foo.edu. IP networks: you can also export directories to all hosts on an IP (sub-)network simultaneously. for example, either `/255.255.252.0' or `/22' appended to the network base address result. "); my $help_userid = N(""); my $help_global = N(""); sub help_b { my ($tittle, $help_data) = @_; gtksignal_connect(new Gtk2::Button(N("Help")), clicked => sub { my $dialog = new Gtk2::Dialog(); $dialog->set_title("Help"); $dialog->set_modal(1); gtkpack_($dialog->vbox, 0, gtkadd(Gtk2::Frame->new(N($tittle)), gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5), 0, $help_data, ), ), ); $dialog->show_all; } ); } sub add_modify_entry { my ($widget, $treeview, $wanted) = @_; my $model = $treeview->get_model; my $selection = $treeview->get_selection; my $iter; my ($i, $dir, $access, $right, $all_right, $anonuid, $anongid, $options); my ($lr, $luserid, $lsecure, $lsync); undef $i; undef $iter; map { $_ = Gtk2::Entry->new } $dir, $access, $anongid, $anonuid, $options; map { $_ = Gtk2::OptionMenu->new() } $lr, $luserid, $lsecure, $lsync; my $luserid = new Gtk2::OptionMenu(); $luserid->set_popdown_strings(@listuserid); $lr->set_popdown_strings(@listr); $lsync->set_popdown_strings(@listsync); $lsecure->set_popdown_strings(@listsecure); my $file_dialog = $fdwidget->($dir, ""); my $button = Gtk2::Button->new(N("dir path")); $button->signal_connect(clicked => sub { $file_dialog->show }); # test if modify or add a nfs share my $dialog = new Gtk2::Dialog(); $dialog->set_title("Draknfs $wanted entry"); $dialog->set_modal(1); $dialog->set_resizable(1); if ($wanted =~ /modify/) { $iter = $selection->get_selected; my $path = $model->get_path($iter); $i = ($path->get_indices)[0]; $dir->set_text($listshare[$i]{dir}); $access->set_text($listshare[$i]{access}); # list of all rigth in bracket # $anongid, $anonuid, $lr, $luserid, $lsecure, $lsync; $right = $listshare[$i]{right}; my @opts = split(/,/, $right); map { $_->set_text("") } $lr, $lsync, $anonuid, $anongid, $luserid, $lsecure; foreach my $opt (@opts) { if ($opt =~ m/(\bro\b|\brw\b)/) { $lr->set_text($opt); } elsif ($opt =~ m/\bsync\b|\basync\b/) { $lsync->set_text($opt); } elsif ($opt =~ m/anongid=(\d+)/) { $anongid->set_text($1); } elsif ($opt =~ m/anonuid=(\d+)/) { $anonuid->set_text($1); } elsif ($opt =~ m/(no_root_squash|root_squash|all_squash|no_all_squash)/) { $luserid->set_text($opt); } elsif ($opt =~ m/(\bsecure\b|\binsecure\b)/) { $lsecure->set_text($opt); } else { next } } } gtkpack_($dialog->vbox, 0, gtkadd(Gtk2::Frame->new(N("NFS directory")), gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5), 0, $label_and_widgets->(N("Directory:"), $dir, $button), ), ), 0, gtkadd(Gtk2::Frame->new(N("Host access")), gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5), 0, $label_and_widgets->(N("Access:"), $access, help_b("Help User ID", $help_access)), ), ), 0, gtkadd(Gtk2::Frame->new(N("User ID Mapping")), gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5), 0, $label_and_widgets->(N("User ID:"), $luserid, help_b("Help User ID", $help_userid)), 0, $label_and_widgets->(N("anonuid:"), $anonuid, ""), 0, $label_and_widgets->(N("anongid:"), $anongid, ""), ), ), 0, gtkadd(Gtk2::Frame->new(N("General Options")), gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5), 0, $label_and_widgets->(N("Force sync:"), $lsync, help_b("Help General options", $help_global)), 0, $label_and_widgets->(N("port below 1024:"), $lsecure, ""), 0, $label_and_widgets->(N("Read/Write request:"), $lr, ""), ), ), 0, gtkadd(Gtk2::Frame->new(N("More Options")), gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5), 0, $label_and_widgets->(N("options:"), $options, ""), ), ), 0, create_okcancel({ cancel_clicked => sub { $dialog->destroy }, ok_clicked => sub { my ($anonu, $anong); $anonuid->get_text() and $anonu = "anonuid=" . $anonuid->get_text(); $anongid->get_text() and $anong = "anongid=" . $anongid->get_text(); map { $_ and $all_right = $_ . "," . $all_right; } $luserid->get_text(), $anonu, $anong, $lsync->get_text(), $lsecure->get_text(), $lr->get_text(); if ($wanted =~ /add/) { -d $dir->get_text() or err_dialog(N("Error!"), N("Please enter a directory to share.")) and return; $iter = $model->append; $i = "-1"; push @listshare, { dir => $dir->get_text(), access => $access->get_text(), right => $all_right, options => $options->get_text(), }; } $listshare[$i]{right} = $all_right; $listshare[$i]{access} = $access->get_text(); $listshare[$i]{dir} = $dir->get_text(); $listshare[$i]{options} = $options->get_text(); $model->set($iter, COLUMN_DIR, $listshare[$i]{dir}, COLUMN_ACCESS, $listshare[$i]{access}, COLUMN_RIGHT, $all_right, COLUMN_OPTIONS, $listshare[$i]{options}, ); $dialog->destroy; write_conf(); }, }, ), ); $dialog->show_all; } sub remove_entry { my ($widget, $treeview) = @_; my $model = $treeview->get_model; my $selection = $treeview->get_selection; my $iter = $selection->get_selected; if ($iter) { my $path = $model->get_path($iter); my $i = ($path->get_indices)[0]; ask_okcancel("Remove entry ?", "Remove $listshare[$i]{dir}") or return; $model->remove($iter); splice @listshare, $i, 1; } write_conf(); } sub create_model() { get_nfs_data(); my $model = Gtk2::ListStore->new("Glib::String", "Glib::String", "Glib::String", "Glib::String"); foreach my $a (@listshare) { my $iter = $model->append; $model->set($iter, COLUMN_DIR, $a->{dir}, COLUMN_ACCESS, $a->{access}, COLUMN_RIGHT, $a->{right}, COLUMN_OPTIONS, $a->{options}, ), } return $model; } # add colum to model sub add_columns { my $treeview = shift; my $model = $treeview->get_model; each_index { my $renderer = Gtk2::CellRendererText->new; $renderer->set(editable => 0); $renderer->signal_connect(edited => \&cell_edited, $model); $renderer->set_data(column => $::i); $treeview->insert_column_with_attributes(-1, $_, $renderer, 'text' => $::i); } N("Share Directory"), N("Hosts Wildcard"), N("General Options"), N("More options"); } ############### # Main Program ############### # create model #test_root(); my $model = create_model(); my $window = ugtk2->new("Draknfs $version"); $window->{rwindow}->set_size_request(530, 300) unless $::isEmbedded; my $W = $window->{window}; $W->signal_connect(delete_event => sub { ugtk2->exit }); my $treeview = Gtk2::TreeView->new_with_model($model); $treeview->set_rules_hint(TRUE); $treeview->get_selection->set_mode('single'); add_columns($treeview); my $okcancel = create_okcancel({ cancel_clicked => sub { ugtk2->exit }, ok_clicked => \&write_conf, }, ); # main interface $W->add(gtkpack_(Gtk2::VBox->new(0,0), 1, create_scrolled_window($treeview), 0, gtkpack_(gtkset_border_width(Gtk2::HBox->new, 3), 0, gtksignal_connect(Gtk2::Button->new(N("Add an NFS share")), clicked => sub { eval { add_modify_entry($model, $treeview, "add") }; my $err = $@; if ($err) { err_dialog(N("Error"), N("Failed to add NFS share.") . "\n\n" . $err); } }), 0, gtksignal_connect(Gtk2::Button->new(N("Modify an NFS share")), clicked => sub { eval { add_modify_entry($model, $treeview, "modify") }; my $err = $@; if ($err) { err_dialog(N("Error"), N("Failed to Modify NFS share.") . "\n\n" . $err); } }), 0, gtksignal_connect(Gtk2::Button->new(N("Remove an NFS share")), clicked => sub { eval { remove_entry($model, $treeview) }; my $err = $@; if ($err) { err_dialog(N("Error"), N("Failed to remove an NFS share.") . "\n\n" . $err); } }), 0, Gtk2::VSeparator->new, 0, gtksignal_connect(Gtk2::Button->new(N("Reload NFS server")), clicked => sub { system("/sbin/service nfs reload"); }), ), if_($::isEmbedded, 0, $okcancel), ), ); $W->show_all; Gtk2->main;