summaryrefslogtreecommitdiffstats
path: root/bin/net_applet
diff options
context:
space:
mode:
Diffstat (limited to 'bin/net_applet')
-rwxr-xr-xbin/net_applet585
1 files changed, 585 insertions, 0 deletions
diff --git a/bin/net_applet b/bin/net_applet
new file mode 100755
index 0000000..fd2b79b
--- /dev/null
+++ b/bin/net_applet
@@ -0,0 +1,585 @@
+#!/usr/bin/perl
+
+use strict;
+
+use POSIX ":sys_wait_h";
+use lib qw(/usr/lib/libDrakX);
+# i18n: IMPORTANT: to get correct namespace (drakx-net instead of libDrakX)
+BEGIN { unshift @::textdomains, 'drakx-net' }
+use c;
+use common;
+use standalone;
+use network::network;
+use network::tools;
+use network::connection;
+use network::connection::ethernet;
+use network::vpn;
+use run_program;
+use mygtk2 qw(gtknew gtkset);
+use dbus_object;
+use network::monitor;
+use network::signal_strength;
+use detect_devices;
+use Gtk2::Notify -init, 'notify';
+
+use ugtk2 qw(:create :helpers :wrappers :dialogs);
+
+my $onstartupfile = "$ENV{HOME}/.net_applet";
+my $system_file = '/etc/sysconfig/drakx-net';
+shouldStart() or die "$onstartupfile should be set to TRUE or use net_applet --force\n";
+
+#- Allow multiple instances, but only one per user:
+is_running('net_applet') and die "net_applet already running\n";
+
+package network::net_applet;
+
+use mygtk2 qw(gtknew gtkset);
+use common;
+
+our ($current_state, $current_interface);
+our ($icon, $notification_queue);
+our $dbus;
+our ($interactive_cb, $ifw, $ifw_alert);
+
+our %wireless_networks;
+
+our %pixbufs =
+ (
+ firewall => gtknew('Pixbuf', file => 'drakfirewall'),
+ firewall_icon => gtknew('Pixbuf', file => 'drakfirewall')->scale_simple(24, 24, 'hyper'),
+ state => { map { $_ => gtknew('Pixbuf', file => $_) } qw(connected disconnected unconfigured connecting) },
+ link_level => { map {
+ $_ => gtknew('Pixbuf', file => 'wifi-' . sprintf('%03d', $_))->scale_simple(24, 24, 'hyper');
+ } qw(20 40 60 80 100) },
+ encryption => { map {
+ $_ => gtknew('Pixbuf', file => "encryption-$_-24");
+ } qw(open weak strong) },
+ );
+
+sub get_current_network() {
+ detect_devices::is_wireless_interface($current_interface) && find { $_->{current} } values %wireless_networks;
+}
+
+sub get_state_pixbuf() {
+ my $wnet = $current_state eq 'connected' && get_current_network();
+ $wnet ?
+ network::signal_strength::get_strength_icon($wnet) :
+ $pixbufs{state}{$current_state};
+}
+
+sub update_tray_icon() {
+ if (!$ifw_alert || $icon->get_storage_type ne 'pixbuf') {
+ $icon->set_from_pixbuf(get_state_pixbuf());
+ } else {
+ $icon->set_from_stock('gtk-dialog-warning');
+ }
+}
+
+1;
+
+package main;
+
+my ($current_description, $simple_menu, $menu, $wireless_device, $timeout, $update_timeout);
+add_icon_path("/usr/share/libDrakX/pixmaps/");
+
+my $net = {};
+my $watched_interface;
+
+my %global_settings = getVarsFromSh($system_file);
+
+sub get_state_message {
+ my ($o_interface) = @_;
+ my $interface = $o_interface || $current_interface;
+ my $network = network::net_applet::get_current_network();
+ formatAlaTeX(
+ $current_state eq 'connected' ?
+ N("Network is up on interface %s.", get_interface_name($interface)) .
+ "\n\n" . N("IP address: %s", network::tools::get_interface_ip_address($net, $interface)) .
+ "\n\n" . N("Gateway: %s", [ network::tools::get_interface_status($interface) ]->[1]) .
+ "\n\n" . N("DNS: %s", $net->{resolv}{dnsServer}) .
+ ($network && "\n\n" . N("Connected to %s (link level: %d %%)", $network->{name}, $network->{signal_strength}))
+ : $current_state eq 'disconnected' ?
+ N("Network is down on interface %s.", get_interface_name($interface))
+ : $current_state eq 'unconfigured' ?
+ N("You do not have any configured Internet connection.
+Run the \"%s\" assistant from the Mageia Linux Control Center", N("Set up a new network interface (LAN, ISDN, ADSL, ...)"))
+ :
+ N("Connecting...")
+ );
+}
+
+sub get_interface_type {
+ my ($interface) = @_;
+ my $ifcfg = $net->{ifcfg}{$interface};
+ require network::connection;
+ $ifcfg && network::connection->find_ifcfg_type($ifcfg);
+}
+
+sub get_interface_icon {
+ my ($interface) = @_;
+ my $type = get_interface_type($interface);
+ $type && $type->get_type_icon;
+}
+
+sub get_interface_name {
+ my ($interface) = @_;
+ my $type = get_interface_type($interface);
+ my $type_name = $type && $type->get_type_description;
+ $type_name ? "$type_name ($interface)" : $interface;
+}
+
+my %actions = (
+ 'upNetwork' => {
+ name => sub { N("Connect %s", get_interface_name($_[0])) },
+ launch => sub { network::tools::start_interface($_[0], 1) }
+ },
+ 'downNetwork' => {
+ name => sub { N("Disconnect %s", get_interface_name($_[0])) },
+ launch => sub { network::tools::stop_interface($_[0], 1) }
+ },
+ 'monitorNetwork' => {
+ name => N("Monitor Network"),
+ launch => \&run_net_monitor
+ },
+ 'monitorIFW' => {
+ name => N("Interactive Firewall"),
+ launch => \&run_drakids
+ },
+ 'wireless' => {
+ name => N("Manage wireless networks"),
+ launch => sub { run_drakroam() }
+ },
+ 'drakvpn' => {
+ name => N("Manage VPN connections"),
+ launch => sub { run_program::raw({ detach => 1 }, '/usr/sbin/drakvpn'); },
+ },
+ 'confNetwork' => {
+ name => N("Configure Network"),
+ launch => sub { system("/usr/sbin/drakconnect &") }
+ },
+ 'chooseInterface' => {
+ name => N("Watched interface"),
+ choices => sub { N("Auto-detect"), sort keys %{$net->{ifcfg}} },
+ choice_selected => sub {
+ $watched_interface ? $_[0] eq $watched_interface :
+ $_[0] eq N("Auto-detect")
+ },
+ launch => sub {
+ $watched_interface = $_[0] eq N("Auto-detect") ? undef : $_[0];
+ checkNetworkForce();
+ }
+ },
+ 'setInterface' => {
+ name => N("Active interfaces"),
+ use_checkbox => 1,
+ choices => sub { sort keys %{$net->{ifcfg}} },
+ choice_selected => sub {
+ my ($is_up, $_gw) = network::tools::get_interface_status($_[0]);
+ $is_up;
+ },
+ format_choice => \&get_interface_name,
+ get_icon => \&get_interface_icon,
+ launch => sub {
+ my ($is_up, $_gw) = network::tools::get_interface_status($_[0]);
+ if ($is_up) {
+ network::tools::stop_interface($_[0], 1);
+ } else {
+ network::tools::start_interface($_[0], 1);
+ }
+ checkNetworkForce();
+ }
+ },
+ 'chooseProfile' => {
+ name => N("Profiles"),
+ choices => sub { network::network::netprofile_list() },
+ choice_selected => sub { $_[0] eq $net->{PROFILE} },
+ launch => sub {
+ require run_program;
+ $net->{PROFILE} = $_[0];
+ run_program::raw({ detach => 1 }, common::wrap_command_for_root('/sbin/set-netprofile', $net->{PROFILE}));
+ }
+ },
+ 'chooseVPN' => {
+ name => N("VPN connection"),
+ header => "drakvpn",
+ choices => sub {
+ map { $_->get_configured_connections } network::vpn::list_types
+ },
+ allow_single_choice => 1,
+ format_choice => \&network::vpn::get_label,
+ choice_selected => sub { $_[0]->is_started },
+ launch => sub {
+ require interactive; $_[0]->is_started ?
+ $_[0]->stop : $_[0]->start(interactive->vnew)
+ },
+ },
+ 'help' => {
+ name => N("Help"),
+ launch => sub { system("drakhelp --id internet-connection &") }
+ },
+ 'quit' => {
+ name => N("Quit"),
+ launch => \&mainQuit
+ },
+);
+
+
+$icon = Gtk2::StatusIcon->new;
+
+eval { $dbus = dbus_object::system_bus() } if !defined($global_settings{NET_APPLET_DBUS}) || text2bool($global_settings{NET_APPLET_DBUS});
+eval { $net->{monitor} = network::monitor->new($dbus) } if $dbus;
+if ($dbus) {
+ require network::net_applet::ifw;
+ network::net_applet::ifw::init();
+}
+if ($dbus) {
+ $dbus->{connection}->add_filter(sub {
+ my ($_con, $msg) = @_;
+ if ($msg->get_interface eq 'org.mageia.network' && $msg->get_member eq 'status') {
+ my ($status, $interface) = $msg->get_args_list;
+ print "got connection status event: $status $interface\n";
+ if ($status eq "add") {
+ checkNetworkForce();
+ }
+ }
+ });
+ $dbus->{connection}->add_match("type='signal',interface='org.mageia.network'");
+ dbus_object::set_gtk2_watch_helper($dbus);
+}
+
+$notification_queue = Gtk2::Notify::Queue->new($icon);
+
+$icon->signal_connect(activate => sub {
+ my ($_icon, $button, $time) = @_;
+ if ($ifw_alert) {
+ run_drakids();
+ } elsif ($simple_menu) {
+ $simple_menu->popup(undef, undef, undef, undef, $button, $time);
+ } else {
+ run_netcenter();
+ }
+ });
+$icon->signal_connect(popup_menu => sub {
+ my ($_icon, $button, $time) = @_;
+ $menu->popup(undef, undef, undef, undef, $button, $time) if $menu;
+ });
+
+checkNetworkForce();
+cronNetwork();
+gtkflush(); #- for notifications to appear on the status icon position
+network::net_applet::ifw::get_unprocessed_ifw_messages() if $ifw;
+
+$SIG{HUP} = sub {
+ print "received SIGHUP, reloading network configuration\n";
+ checkNetworkForce();
+};
+$SIG{USR1} = sub {
+ # clear all ifw notifications
+ my @packets = eval { $network::net_applet::ifw->get_reports };
+};
+
+# do not create zombies (#20552)
+$SIG{CHLD} = \&harvester;
+sub harvester {
+ my $pid;
+ do {
+ # we don't care about our child processes
+ $pid = waitpid(-1, &WNOHANG);
+ } while $pid > 0;
+}
+
+Gtk2->main;
+
+ugtk2::exit(0);
+
+sub is_running {
+ my ($name, $o_user) = @_;
+ my $user = $o_user || $ENV{USER};
+ any {
+ my ($ppid, $pid, $n) = /^\s*(\d+)\s+(\d+)\s+(.*)/;
+ $ppid != 1 && $pid != $$ && $n eq $name;
+ } `ps -o '%P %p %c' -u $user`;
+}
+
+sub is_running_match {
+ # (eugeni) this matches part of a running command.
+ # Right now it is only used to detect if ifup script is running.
+ my ($name, $o_user) = @_;
+ my $user = $o_user || $ENV{USER};
+ any {
+ my ($ppid, $pid, $n) = /^\s*(\d+)\s+(\d+)\s+(.*)/;
+ $ppid != 1 && $pid != $$ && $n =~ $name;
+ } `ps -o '%P %p %c' -u $user`;
+}
+
+sub shouldStart() {
+ my ($opt) = @ARGV;
+ if ($opt eq '--force' || $opt eq '-f') {
+ return 1;
+ }
+ return getAutoStart();
+}
+sub run_net_monitor() {
+ run_program::raw({ detach => 1 }, '/usr/bin/net_monitor', '--defaultintf', $current_interface) unless is_running('net_monitor');
+}
+sub run_netcenter() {
+ run_program::raw({ detach => 1 }, '/usr/bin/draknetcenter') unless is_running('draknetcenter', 'root');
+}
+sub run_drakroam {
+ my ($o_ap) = @_;
+ run_program::raw({ detach => 1 }, '/usr/sbin/drakroam', if_($o_ap, "--ap=$o_ap")) unless is_running('drakroam', 'root');
+}
+sub run_drakids() {
+ $ifw_alert = 0;
+ if (is_running('drakids')) {
+ eval { $ifw->send_manage_request };
+ } else {
+ run_program::raw({ detach => 1 }, '/usr/sbin/drakids');
+ }
+}
+sub generate_wireless_menuitem {
+ my ($wnet) = @_;
+ my $menuitem = {};
+ $menuitem->{widget} = Gtk2::CheckMenuItem->new;
+ $menuitem->{widget}->set_draw_as_radio(1);
+ $menuitem->{widget}->add(gtkpack_(gtkshow(gtknew('HBox')),
+ 1, gtkset_alignment($menuitem->{label} = gtknew('Label'), 0, 0.5),
+ 0, $menuitem->{strength} = Gtk2::Image->new,
+ 0, $menuitem->{security} = Gtk2::Image->new,
+ ));
+ $menuitem->{activate} = $menuitem->{widget}->signal_connect('activate' => sub {
+ if ($net->{monitor} && exists $wnet->{id}) {
+ eval { $net->{monitor}->select_network($wnet->{id}) };
+ $@ and err_dialog(N("Interactive Firewall"), N("Unable to contact daemon"));
+ } else {
+ run_drakroam($wnet->{ap});
+ }
+ checkNetworkForce();
+ });
+ update_wireless_item($menuitem, $wnet);
+ push @{$wnet->{menuitems}}, $menuitem;
+ return $menuitem->{widget};
+}
+sub update_wireless_item {
+ my ($menuitem, $wnet) = @_;
+ $menuitem->{label}->set_text($wnet->{name});
+ $menuitem->{security}->set_from_pixbuf($pixbufs{encryption}{$wnet->{flags} =~ /WPA/i ? 'strong' : $wnet->{flags} =~ /WEP/i ? 'weak' : 'open'});
+ $menuitem->{strength}->set_from_pixbuf(network::signal_strength::get_strength_icon($wnet));
+
+ $menuitem->{widget}->signal_handler_block($menuitem->{activate});
+ $menuitem->{widget}->set_active($wnet->{current});
+ $menuitem->{widget}->signal_handler_unblock($menuitem->{activate});
+}
+sub checkWireless() {
+ $wireless_device or return;
+ my ($networks) = network::monitor::list_wireless($net->{monitor});
+ my $force_applet_update;
+ foreach (keys %$networks) {
+ exists $wireless_networks{$_} or $force_applet_update = 1;
+ put_in_hash($wireless_networks{$_} ||= {}, $networks->{$_});
+ }
+ if ($force_applet_update) {
+ undef $current_state;
+ } else {
+ foreach my $wnet (values %wireless_networks) {
+ my $is_valuable = exists $networks->{$wnet->{ap}};
+ foreach (@{$wnet->{menuitems}}) {
+ update_wireless_item($_, $wnet) if $is_valuable;
+ $_->{widget}->visible($is_valuable);
+ }
+ }
+ }
+}
+sub checkNetwork() {
+ my ($gw_intf, $_is_up, $gw_address) = $watched_interface ?
+ ($watched_interface, network::tools::get_interface_status($watched_interface)) :
+ network::tools::get_default_connection($net);
+ my $connecting = is_running_match('ifup', 'root');
+ go2State($gw_address ? 'connected' : $connecting ? 'connecting' : $gw_intf ? 'disconnected' : 'unconfigured', $gw_intf);
+}
+sub checkNetworkForce() {
+ $net = {};
+ network::network::read_net_conf($net);
+ undef $current_state;
+ $wireless_device = detect_devices::get_wireless_interface();
+ checkWireless();
+ checkNetwork();
+}
+sub cronNetwork() {
+ my $i;
+ $timeout = Glib::Timeout->add(2000, sub {
+ checkWireless() if !($i++%30);
+ checkNetwork();
+ 1;
+ });
+}
+sub go2State {
+ my ($state_type, $interface) = @_;
+ my $need_update;
+ my ($old_interface, $old_description);
+ if ($current_interface ne $interface) {
+ my $card = find { $_->[0] eq $interface } network::connection::ethernet::get_eth_cards();
+ if ($state_type eq 'disconnected') {
+ $old_interface = $current_interface;
+ $old_description = $current_description;
+ }
+ $current_description = $card && $card->[2];
+ $current_interface = $interface;
+ $need_update = 1;
+ }
+ if ($current_state ne $state_type) {
+ my $show = defined $current_state && $state_type ne 'connecting'; # don't show notification at applet startup and when establishing a connection
+ $current_state = $state_type;
+ $notification_queue->add({
+ title => $old_description || $current_description || N("Network connection"),
+ pixbuf => network::net_applet::get_state_pixbuf(),
+ message => get_state_message($old_interface || $current_interface),
+ }) if $show;
+ $need_update = 1;
+ }
+
+ update_applet() if $need_update;
+}
+
+sub update_applet() {
+ $wireless_device = detect_devices::get_wireless_interface();
+
+ # Re-checking wireless networks (#40912)
+ checkWireless();
+
+ generate_menu();
+
+ network::net_applet::update_tray_icon();
+ $icon->set_tooltip(get_state_message());
+}
+
+sub create_menu_choices {
+ my ($action, $o_allow_single_choice) = @_;
+ my @choices = $actions{$action}{choices}->();
+ #- don't add submenu if only zero or one choice exists
+ my $allow_single_choice = $actions{$action}{allow_single_choice} || $o_allow_single_choice;
+ @choices > ($allow_single_choice ? 0 : 1) or return ();
+ my $selected = $actions{$action}{choice_selected};
+ my $format = $actions{$action}{format_choice};
+ my $get_icon = $actions{$action}{get_icon};
+ map {
+ my $choice = $_;
+ my $label = $format ? $format->($choice) : $choice;
+ my $w = gtkshow(gtkset_active(gtkadd(
+ Gtk2::CheckMenuItem->new,
+ gtknew('HBox', children => [
+ 1, gtkset_alignment(gtknew('Label', text => $label), 0, 0.5),
+ $get_icon ?
+ (0, gtknew('Image', file => $get_icon->($_))) :
+ (),
+ ])), $selected->($choice)));
+ gtksignal_connect($w, activate => sub { $actions{$action}{launch}->($choice) });
+ $w->set_draw_as_radio(!$actions{$action}{use_checkbox});
+ $w;
+ } $actions{$action}{choices}->();
+}
+
+sub create_action_item {
+ my ($action) = @_;
+ my $name = ref($actions{$action}{name}) eq 'CODE' ? $actions{$action}{name}->($current_interface) : $actions{$action}{name};
+ if (exists $actions{$action}{choices}) {
+ my @menu = create_menu_choices($action);
+ @menu || $actions{$action}{header} or return ();
+ gtkshow(create_menu($name,
+ $actions{$action}{header} ? (
+ create_action_item($actions{$action}{header}),
+ gtkshow(Gtk2::SeparatorMenuItem->new),
+ ) : (),
+ @menu,
+ ));
+ } else {
+ gtksignal_connect(gtkshow(Gtk2::MenuItem->new_with_label($name)), activate => sub { $actions{$action}{launch}->($current_interface) });
+ }
+}
+
+sub empty_menu {
+ my ($menu) = @_;
+ delete $_->{menuitems} foreach values %wireless_networks;
+ $menu->destroy if $menu;
+ Gtk2::Menu->new;
+}
+
+sub get_wireless_networks_sorted() {
+ sort {
+ $b->{current} <=> $a->{current} || $b->{signal_strength} <=> $a->{signal_strength} || $a->{name} cmp $b->{name};
+ } values %wireless_networks;
+}
+
+sub generate_simple_menu() {
+ $simple_menu = empty_menu($simple_menu);
+
+ if ($wireless_device) {
+ my @networks = get_wireless_networks_sorted();
+ my @valuable_networks = splice @networks, 0, 7;
+ gtkappend($simple_menu,
+ (map { generate_wireless_menuitem($_) } @valuable_networks),
+ (@networks ? create_menu(N("More networks"), map { generate_wireless_menuitem($_) } @networks) : ()),
+ Gtk2::SeparatorMenuItem->new,
+ );
+ }
+ gtkappend($simple_menu, create_menu_choices('setInterface', 'allow_single_choice'));
+}
+
+sub generate_menu() {
+ $menu = empty_menu($menu);
+
+ my (@settings);
+ my $interactive;
+ eval { $interactive = $ifw->get_interactive };
+
+ if ($current_state eq 'connected') {
+ $menu->append(create_action_item($_)) foreach qw(downNetwork monitorNetwork);
+ } elsif ($current_state eq 'disconnected') {
+ $menu->append(create_action_item('upNetwork'));
+ }
+ $menu->append(create_action_item('monitorIFW')) if $current_state ne 'unconfigured' && defined $interactive;
+
+ $menu->append(create_action_item('confNetwork'));
+
+ push @settings, create_action_item('chooseInterface') if $current_state ne 'unconfigured';
+
+ push @settings, create_action_item('chooseProfile');
+ if (defined $interactive) {
+ $interactive_cb = gtkshow(gtksignal_connect(gtkset_active(Gtk2::CheckMenuItem->new_with_label(N("Interactive Firewall automatic mode")),
+ !$interactive),
+ toggled => sub { eval { $ifw->set_interactive(to_bool(!$_[0]->get_active)) } }));
+ push @settings, $interactive_cb;
+ }
+ push @settings, gtkshow(gtksignal_connect(gtkset_active(Gtk2::CheckMenuItem->new_with_label(N("Always launch on startup")), getAutoStart()),
+ toggled => sub { setAutoStart(uc(bool2text($_[0]->get_active))) }));
+
+ $menu->append(gtkshow(Gtk2::SeparatorMenuItem->new));
+ if ($current_state ne 'unconfigured' && $wireless_device) {
+ $menu->append(gtkshow(create_menu(N("Wireless networks"),
+ create_action_item('wireless'),
+ gtkshow(Gtk2::SeparatorMenuItem->new),
+ map { generate_wireless_menuitem($_) } get_wireless_networks_sorted())));
+ }
+ if (my $vpn = create_action_item('chooseVPN')) { $menu->append($vpn) }
+ if (my $set = $current_state ne 'unconfigured' && create_action_item('setInterface')) { $menu->append($set) }
+ $menu->append(gtkshow(create_menu(N("Settings"), @settings)));
+ $menu->append(gtkshow(Gtk2::SeparatorMenuItem->new));
+ $menu->append(create_action_item('help'));
+ $menu->append(create_action_item('quit'));
+ $menu;
+}
+sub mainQuit() {
+ Glib::Source->remove($timeout) if $timeout;
+ Glib::Source->remove($update_timeout) if $update_timeout;
+ Gtk2->main_quit;
+}
+sub getAutoStart() {
+ my %p = getVarsFromSh($onstartupfile);
+ return to_bool($p{AUTOSTART} ne 'FALSE');
+}
+sub setAutoStart {
+ my $state = shift;
+ output_p $onstartupfile,
+ qq(AUTOSTART=$state
+);
+}