summaryrefslogtreecommitdiffstats
path: root/perl-install
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install')
-rw-r--r--perl-install/standalone/drakhosts252
-rw-r--r--perl-install/standalone/draknfs370
2 files changed, 622 insertions, 0 deletions
diff --git a/perl-install/standalone/drakhosts b/perl-install/standalone/drakhosts
new file mode 100644
index 000000000..c76762158
--- /dev/null
+++ b/perl-install/standalone/drakhosts
@@ -0,0 +1,252 @@
+#!/usr/bin/perl -w
+#
+# 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 (drakpxelinux instead of libDrakX)
+BEGIN { unshift @::textdomains, 'drakhosts' }
+
+use lib qw(/usr/lib/libDrakX);
+use standalone;
+use strict;
+use common;
+use network::network;
+
+use interactive;
+# must come *after* definition of textdomains for proper initialisation
+use ugtk2 qw(:ask :wrappers :create :dialogs);
+
+use constant FALSE => 0;
+use constant TRUE => 1;
+
+my $HOSTS = "/etc/hosts";
+my @listhosts;
+
+use constant COLUMN_IP => 0;
+use constant COLUMN_HOSTNAME => 1;
+use constant COLUMN_ALIAS => 2;
+use constant NUM_COLUMNS => 3;
+
+my $in = 'interactive'->vnew('su');
+
+my %size_groups = map { $_ => Gtk2::SizeGroup->new('horizontal') } qw(label widget);
+my $label_and_widgets = sub {
+ my ($label, $widget) = @_;
+ gtkpack_(Gtk2::HBox->new(0,5),
+ 0, gtkadd_widget($size_groups{label}, $label),
+ 1, gtkadd_widget($size_groups{widget}, $widget),
+ );
+};
+
+
+sub get_host_data {
+# 127.0.0.1 localhost.localdomain localhost
+# 10.0.1.253 guibpiv.guibland.com
+ foreach (cat_($HOSTS)) {
+ my ($ip, $name, $alias) = $_ =~ /^(\d\S*)\s+(\S*)\s+(.*)$/;
+ $ip and push @listhosts, {
+ ip => $ip,
+ hostname => $name,
+ alias => $alias,
+ };
+ }
+}
+
+sub write_conf_hosts {
+ output($HOSTS, "# generated by drakhosts.pl\n");
+ foreach my $a (@listhosts) {
+ append_to_file($HOSTS, "$a->{ip} $a->{hostname} $a->{alias}\n");
+ }
+}
+
+sub add_modify_entry {
+ my ($widget, $treeview, $wanted) = @_;
+ my $model = $treeview->get_model;
+ my $selection = $treeview->get_selection;
+ my $iter;
+ my ($i, $ip, $hostname, $alias, $oldip);
+ undef $i;
+ undef $iter;
+
+ map { $_ = Gtk2::Entry->new } $ip, $hostname, $alias;
+
+# test if modify or add a host
+
+ my $dialog = new Gtk2::Dialog();
+ $dialog->set_title("Drakhosts $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];
+ $ip->set_text($listhosts[$i]{ip});
+ $hostname->set_text($listhosts[$i]{hostname});
+ $alias->set_text($listhosts[$i]{alias});
+ $oldip = $listhosts[$i]{ip};
+ }
+
+ gtkpack_($dialog->vbox,
+ 0, gtkadd(Gtk2::Frame->new(N("Please $wanted information")),
+ gtkpack_(gtkset_border_width(Gtk2::VBox->new, 5),
+ 0, $label_and_widgets->(N("IP address:"), $ip),
+ 0, $label_and_widgets->(N("Host name:"), $hostname),
+ 0, $label_and_widgets->(N("Host Aliases:"), $alias),
+ ),
+ ),
+ 0, create_okcancel({
+ cancel_clicked => sub { $dialog->destroy },
+ ok_clicked => sub {
+ is_ip($ip->get_text()) or err_dialog(N("Error!"), N("Please enter a valid IP address.")) and return;
+ my $testip = chomp_($ip->get_text());
+ my $testname = chomp_($hostname->get_text());
+ my $toldip = chomp_($oldip);
+ if ($testip !~ /$toldip/ or $wanted =~ /add/) {
+ foreach my $a (@listhosts) {
+ if ($a->{ip} =~ /$testip/) {
+ err_dialog(N("Error!"), N("Same IP is already in %s file.", $HOSTS)) and return;
+ }
+ }
+ }
+ if ($wanted =~ /add/) {
+ $iter = $model->append;
+ $i = "-1";
+ push @listhosts, {
+ ip => $ip->get_text(),
+ hostname => $hostname->get_text(),
+ alias => $alias->get_text(),
+ };
+ }
+ $listhosts[$i]{hostname} = $hostname->get_text();
+ $listhosts[$i]{alias} = $alias->get_text();
+ $listhosts[$i]{ip} = $ip->get_text();
+ $model->set($iter,
+ COLUMN_IP, $listhosts[$i]{ip},
+ COLUMN_HOSTNAME, $listhosts[$i]{hostname},
+ COLUMN_ALIAS, $listhosts[$i]{alias},
+ );
+ $dialog->destroy;
+ write_conf_hosts();
+ },
+ },
+ ),
+ );
+ $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 $listhosts[$i]{hostname}") or return;
+ $model->remove($iter);
+ splice @listhosts, $i, 1;
+ }
+ write_conf_hosts();
+}
+
+sub create_model() {
+ get_host_data();
+ my $model = Gtk2::ListStore->new("Glib::String", "Glib::String", "Glib::String");
+ foreach my $a (@listhosts) {
+ my $iter = $model->append;
+ $model->set($iter,
+ COLUMN_IP, $a->{ip},
+ COLUMN_HOSTNAME, $a->{hostname},
+ COLUMN_ALIAS, $a->{alias},
+ ),
+ }
+ 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("IP address"), N("Host name"), N("Host Aliases");
+}
+
+
+###############
+# Main Program
+###############
+# create model
+my $model = create_model();
+
+my $window = ugtk2->new("Drakhosts $version");
+$window->{rwindow}->set_size_request(400, 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_hosts,
+ },
+ );
+
+# 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 a host")), clicked => sub {
+ eval { add_modify_entry($model, $treeview, "add") };
+ my $err = $@;
+ if ($err) {
+ err_dialog(N("Error"), N("Failed to add host.") . "\n\n" . $err);
+ }
+ }),
+ 0, gtksignal_connect(Gtk2::Button->new(N("Modify a host")), clicked => sub {
+ eval { add_modify_entry($model, $treeview, "modify") };
+ my $err = $@;
+ if ($err) {
+ err_dialog(N("Error"), N("Failed to Modify host.") . "\n\n" . $err);
+ }
+ }),
+
+ 0, gtksignal_connect(Gtk2::Button->new(N("Remove a host")), clicked => sub {
+ eval { remove_entry($model, $treeview) };
+ my $err = $@;
+ if ($err) {
+ err_dialog(N("Error"), N("Failed to remove host.") . "\n\n" . $err);
+ }
+ }),
+ 0, Gtk2::VSeparator->new,
+ ),
+ if_($::isEmbedded, 0, $okcancel),
+ ),
+ );
+
+$W->show_all;
+Gtk2->main;
diff --git a/perl-install/standalone/draknfs b/perl-install/standalone/draknfs
new file mode 100644
index 000000000..c1c812572
--- /dev/null
+++ b/perl-install/standalone/draknfs
@@ -0,0 +1,370 @@
+#!/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;