Как продолжение темы http://archlinux.org.ru/forum/topic/13769/?page=1

Выпилил гномовский нетворгманагер - изначально не нравиось то кол-во тапов, которое требовалось для смены точки доступа. Как выпилил - не спрашивайте, не отвечу, проблемно. Вернулся к голому netctl. Для него есть неработающее расширение https://extensions.gnome.org/extension/718/netctl-systray-menu/. Стал его читать, ибо суть есть скрипт. Скрипт повсеместно требует рутовых прав, реализуя их через gksudo. Также скрипт пытается даже сервисы перезапускать systemctl stop и т.д. Даже не всякий раз скрипт отображается в строке Гнома.
В предельном случае в это Гном-расширение можно было бы засунуть скрипт балансировки между сетками http://archlinux.org.ru/forum/topic/13769/?page=1, на работу которого просто не нарадуюсь.

Что и как надо сделать -

1. поскольку аплет запускается с правами пользователя, а нам надо обращаться к netctl и iwconfig, который, кстати, внезапно может требовать рутовых прав для получения сведений о качестве сигнала, следует разрешить использование netctl, iwconfig, wifi-menu, wpa_cli без рутового пароля (это не ставит безопасность под угрозу, поскольку повсеместно применяемый Гномнетворкманагер дает тот же функционал, но без рутовых прав) -


echo 'имя_юзера_в_системе ALL=(ALL) NOPASSWD: /usr/bin/netctl, /usr/bin/wifi-menu',  /usr/bin/iwconfig, /usr/bin/wpa_cli'>>  /etc/sudoers

1.1. поскольку управление сетью командами netctl конфликтует с авто-сервисом самого netctl, следует прибить и остановить его авто-сервис-


systemctl disable [email protected]
systemctl stop [email protected]

Изначально авторская версия расширения по п. 2 пытается командовать systemctl, но как-то неудачно. Я счел это излишним и удалил.

2. Загрузить само расширение (сам не хочу заводить свое, во всяком случае пока - ибо я же просто модицифицировал чужое расширение) https://extensions.gnome.org/extension/718/netctl-systray-menu/

3. найти в своем хомовнике
/home/юзер/.local/share/gnome-shell/extensions/netctlsystraymenugnome@prmurthy/extension.js

4. открыть его редактором и заменить содержимое на нижеследующее -


/*
 * Netctl Menu is a Gnome 3 extension that allows you to switch between netctl
 * profiles using a menu in the notification area.

 * This version has been tested with Gnome version 3.12
 *
 * Author: Pradeep Murthy (prmurthy ****at**** yahoo ****dot **** com)
 * This was originally based on an applet by Tjaart van der Walt
 *
 * 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 3 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const version = 3.0
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const St = imports.gi.St;

const GObject = imports.gi.GObject;
const Signals = imports.signals;
const Mainloop = imports.mainloop;
const MessageTray = imports.ui.messageTray;
const Util = imports.misc.util;

// Icons
const NETWORK_OFFLINE = "network-error-symbolic";
const NETWORK_ETHERNET = "network-wired-symbolic";
const NETWORK_WL_CONNECTED_0 = "network-wireless-signal-none-symbolic";
const NETWORK_WL_CONNECTED_1 = "network-wireless-signal-weak-symbolic";
const NETWORK_WL_CONNECTED_2 = "network-wireless-signal-ok-symbolic";
const NETWORK_WL_CONNECTED_3 = "network-wireless-signal-good-symbolic";
const NETWORK_WL_CONNECTED_4 = "network-wireless-signal-excellent-symbolic";

// Variables
let indicator;
let event = null;
let iface;
let wl_contype;
let enet_static_iface;
let wl_static_iface;

function Netctl() {
    this._init.apply(this, arguments);
}

function execute_async(command) {
    try {
        let[result, argv] = GLib.shell_parse_argv(command);
        let[returnval, pid] = GLib.spawn_async(null, argv, null, GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, null);
        GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {
            close_async(pid);
        });
        return pid;
    } catch (e) {
        global.logError(e);
    }
}

function close_async(pid) {
    try {
        GLib.spawn_close_pid(pid);
    } catch (e) {
        global.logError(e);
    }
}

Netctl.prototype = {
    __proto__: PanelMenu.Button.prototype,
    // above changed from PanelMenu.SystemStatusButton for 3.12 compatibility
    // This means that this will not work with older Gnome versions
    // For older versions, change this to PanelMenu.SystemStatusButton.prototype

    _init: function(){
        PanelMenu.Button.prototype._init.call(this, 0.0, 'netctl', false);
        // Alignment (0.0) and menu parameter (false) added for compatibility with newer gnome versions
        // This means that this will not work with older Gnome versions
        // For older version, change this to ....call(this, 'netctl')

        // We include the set_icon() and update_menu() becaust otherwise the
        // icon will only appear after the timeout on refresh_details() have passed
        this._update_menu();
        this._set_icon();

        // Will keep updating the status area icon and updating the menu every x seconds
        this._refresh_details();
    },

    _get_interface: function () {
    // gets the currently active interface. returns the active interface name if any

        let check_interface_exists = GLib.spawn_command_line_sync("ip route list")[1].toString();

        // if this returns a null, then there are no active interfaces
        if (check_interface_exists == "") {
            iface = "";
            this._get_static_interface();

            wl_contype = "N";
            return "";
        }
        let iface0 = check_interface_exists.split(" ", 5)[4].toString();
        return iface0;
    },

    _get_static_interface: function () {
    // gets the names of the network devices in the system. sets the global variables wl_static_interface and enet_static_interface
    // we are assuming that there is only 1 wireless and ethernet interface each, and the interface names start with w and e respectively
    // If there is more than one interface each, the last interface will be the static interface selected

        let ifacelist = GLib.spawn_command_line_sync("ls /sys/class/net").toString();
        all_iface = ifacelist.split("\n", 3);
        for (i = 0; i < all_iface.length; i++) {
            if (all_iface[i].charAt(0) == "w") {
                wl_static_iface = all_iface[i]
            } else
            if (all_iface[i].charAt(0) == "e") {
                enet_static_iface = all_iface[i]
            }
        }
    },

    _get_connected_networks: function () {
    // finds out the name of the network we are connected to, and how
    // returns the connection type (Ethernet, Auto, Manual), Name of the connected wireless network if any, and signal quality as a 2digit decimal

        if (iface == "") {
            return [wl_contype, "None", 0];
        }

        // interface name begins with e, so it must be Ethernet
        if (iface.charAt(0) == "e") {
            wl_contype = "E";
            return [wl_contype, "Ethernet", 0];
        } else

        // interface name begins with w so it must be wireless
        if (iface.charAt(0) == "w") {
            let t1 = GLib.spawn_command_line_sync("sudo iwconfig").toString().match(/Quality=.*/).toString();
            let qmax = t1.substr(11, 2).valueOf();
            let q = t1.substr(8, 2).valueOf();
            let strength = (q / qmax).toFixed(2);

            let profiles = GLib.spawn_command_line_sync("sudo netctl list")[1].toString();
            let connected = profiles.match(/\*.*/g);
            if (connected == null) {

                //if above returns null, netctl could be in auto mode. So we check as below
                let shellcmd = "sudo wpa_cli -i " + iface + " status";
                let connected = GLib.spawn_command_line_sync(shellcmd)[1].toString().match(/(?:.*?(id_str.*)){1}/)[1].toString().slice(7);
                wl_contype = "A";
                return [wl_contype, connected, strength]; //we are connected automatically
            } else {
                wl_contype = "M";
                return [wl_contype, connected.toString().slice(2), strength];
            } // we are connected manually
        } else {
            return ["N", "None", 0];
        } // Something's wrong if we are here
    },

    _set_icon: function () {
    // Sets the systray icon and the tooltip text, after getting data from _get_connected_networks

        let icon_name = "";
        let networkname = this._get_connected_networks();
        if (networkname[1] == "Ethernet") {
            icon_name = NETWORK_ETHERNET;
            tooltiptext = "Connected via Ethernet";
        } else if (networkname[1] == null || networkname[1] == "None") {
            icon_name = NETWORK_OFFLINE;
            tooltiptext = "Not Connected";
        } else {
            let contype;
            let quality = (networkname[2] * 100).valueOf();
            if (networkname[0] == "A") {
                contype = "Auto: ";
            } else
            if (networkname[0] == "M") {
                contype = "Manual: ";
            }
            tooltiptext = contype + "Connected to " + networkname[1].toString() + " : " + quality.toString() + "%";
            if (quality < 25) {
                icon_name = NETWORK_WL_CONNECTED_1;
            } else
            if (quality < 50) {
                icon_name = NETWORK_WL_CONNECTED_2;
            } else
            if (quality < 75) {
                icon_name = NETWORK_WL_CONNECTED_3;
            } else
            if (quality <= 100) {
                icon_name = NETWORK_WL_CONNECTED_4;
            }
        }

        let statusIcon = new St.Icon({
            icon_name: icon_name,
            icon_size: 16
        });

        this.actor.get_children().forEach(function (c) {
            c.destroy()
        });
        this.actor.add_actor(statusIcon);
    },

    _get_network_profiles: function () {
    // gets the list of netctl profiles
    // returns an array of profile names

        var profileString = GLib.spawn_command_line_sync("sudo netctl list")[1].toString();
        var profileArray = profileString.split("\n")
        return profileArray.splice(0, profileArray.length - 1)
    },

    _switch_to_profile: function (newprofileName) {
    // switches to selected manual profile. Not used for switching to auto profile because netctl switch-to doesnt work for switching from manual profile to auto

        let _wl_contype = wl_contype;
        let oldprofile = this._get_connected_networks();
        let msg = "Switching to" + newprofileName + ". Please enter your password";
        if (_wl_contype == "A") {
            shellcmd = "sudo netctl switch-to " + newprofileName + "\'" + "\"";
            let pid = execute_async(shellcmd);
            // wait for child to exit and then check if it worked
            GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {
                Netctl.prototype._switch_back_on_failure(_wl_contype, oldprofile[1].toString(), newprofileName);
            });

        } else if (_wl_contype == "M" || _wl_contype == "N") {
            shellcmd = "sudo netctl switch-to " + newprofileName;
            let pid = execute_async(shellcmd);

            // wait for child to exit and then check if it worked
            GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {
                Netctl.prototype._switch_back_on_failure(_wl_contype, oldprofile[1].toString(), newprofileName);
            });

        }
    },

    _switch_back_on_failure: function (contype, oldprofileName, newprofileName) {
    // checks if the switch to a new profile worked. If it didnt, switches back to old profile. calls _get_connected_networks to update variables. updates the menu
    // if iface is set to null by _get_interface, it means we arent connected any more. The switch failed, so need to switch back

        iface = this._get_interface();
        this._get_connected_networks();

        if (iface == "") {
            if (contype == "A") {
                msg = "The wireless network" + newprofileName + " is not available. Re-enabling the auto profile. Please enter your password again";
                shellcmd = "netctl stop-all";
                execute_async(shellcmd);
            } else
            if (contype == "M") {
                msg = "The wireless network" + newprofileName + " is not available. Switching back to" + oldprofileName + ". Please enter your password again";
                shellcmd = "sudo netctl start " + oldprofileName + "\'" + "\"";
                execute_async(shellcmd);
            } else {
                Main.notify("Netctl: Sorry, the wireless network" + newprofileName + " is not available");
            }
        } else {
            Main.notify("Netctl: Connected to" + newprofileName);
        }
        this._update_menu();

    },
//запуск wifi-menu
    _add_wifi_menu_menu_item: function () {
    // adds menu item for wifi-menu

        let menuItem = new PopupMenu.PopupMenuItem("  Доступные сети, подключиться");
        this.menu.addMenuItem(menuItem);
        menuItem.connect('activate', Lang.bind(this, function () {
            shellcmd = "/usr/bin/gnome-terminal -e 'sudo /usr/bin/wifi-menu'"
	    var pid = execute_async(shellcmd);
        }));
    },
    _add_profile_menu_item: function (profile) {
    // adds profiles to the menu. provides click action for inactive profiles

        if (!profile.match(/\*.*/g)) {
            let menuItem = new PopupMenu.PopupMenuItem(profile);
            this.menu.addMenuItem(menuItem);
            menuItem.connect('activate', Lang.bind(this, function () {
                this._switch_to_profile(profile);
            }));
        } else {
            this.menu.addMenuItem(new PopupMenu.PopupMenuItem(profile, {
                reactive: false
            }));
        }
    },
//отрисовка меню
    _update_menu: function () {
    // creates the menu

        this.menu.removeAll();
        iface = this._get_interface();

        var profiles = this._get_network_profiles();
        for (let i = 0; i < profiles.length; i++) {
            this._add_profile_menu_item(profiles[i]);
        }

        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
        this._add_wifi_menu_menu_item();
    },

    _refresh_details: function () {
    // refreshes the menu and icons/tooltip text

        event = GLib.timeout_add_seconds(0, 5, Lang.bind(this, function () {
            this._update_menu();
            this._set_icon();
            return true;
        }));

    }
}

function init() {
}

function enable() {
    indicator = new Netctl();
    Main.panel.addToStatusArea('netctl', indicator);
    indicator._get_connected_networks();
}

function disable() {
    indicator.destroy();
    Mainloop.source_remove(event);
    indicator = null;
}

5. Перезагрузить (перечитать) Гном - alt+F2 r, включить расширение.

Радоваться простенькому аплету Wi-Fi.

Чем он лучше штатного гномовского? -

1. не тратит батарейку на лишние сканирования доступных сеток. Пользователь обычно сам знает, когда и что искать
2. подключается к любой известной сетке в 2 клика
3. сканирует и подключается к любой новой сетке (запускает wifi-menu) в 2 клика (третий идет на выбор нужной сетки)

Что хочу еще прикрутить -
- дополнить раскрывающимся подменю на удаление разовых сеток
- включение vpn