Gnome shell extension login callback

277 Views Asked by At

I've created a custom GJS extension to connect into VPN. Basically it's a wrapper around a shell script, which is controlled from taskbar. There is a one issue, that after PC goes into suspend mode and back, extension is still displaying, that is disconnected, while it's still connected.

const GObject = imports.gi.GObject;
const St = imports.gi.St;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;

const Gettext = imports.gettext;
const _ = Gettext.gettext;

const Me = imports.misc.extensionUtils.getCurrentExtension();
const MainLoop = imports.mainloop;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;

let close_connection = `
    pkexec kill -SIGINT $(pidof openconnect) 2>&1
`;

let create_connection = `
    trap clean SIGINT
    clean() {
        pkexec kill -SIGINT $(pidof openconnect) 2>&1
    }

    vpn-ura-pke &
    wait
`;

let _icon;
let _connectionSwitch;
let _last_connection = false;
let _already_running = false;
let _proc = null;

const iconsState = [
    'network-vpn-acquiring-symbolic', // disconnected
    'network-vpn-symbolic' // connected
];

function setConnectionState(connected) {
    // prevent same notification changing
    if (_last_connection == connected) return;

    _icon.icon_name = iconsState[connected ? 1 : 0];
    Main.notify('VPN URA', (connected ? 'connected' : 'disconnected'));
    _last_connection = connected;
}

// read line callback
function onProcLine(stream, result) {
    try {
        let line = stream.read_line_finish_utf8(result)[0];

        if (line !== null) {
            // process read line
            log("onProcLine:" + line);
            // check connection status
            if (line.includes('Connected as ')) setConnectionState(true);
            else if(line.includes('Logout successful')) setConnectionState(false);

            stream.read_line_async(0, null, onProcLine.bind(this));
        }
    } catch (ex) {
        logError(ex);
    }
}

// exec async process
async function execCheck(argv) {
    _proc = new Gio.Subprocess({
        argv: argv,
        flags: (Gio.SubprocessFlags.STDIN_PIPE |
            Gio.SubprocessFlags.STDOUT_PIPE |
            Gio.SubprocessFlags.STDERR_PIPE)
    });
    _proc.init(null);

    try {
        let stdoutStream = new Gio.DataInputStream({
            base_stream: _proc.get_stdout_pipe()
        });

        stdoutStream.read_line_async(
            GLib.PRIORITY_DEFAULT,
            null,
            onProcLine.bind(this)
        );

        _proc.wait_check_async(null, (_proc, res) => {
            try {
                if (!_proc.wait_check_finish(res)) {
                    let status = _proc.get_exit_status();
                    setConnectionState(false);

                    if (status != 0) {
                        throw new Gio.IOErrorEnum({
                            code: Gio.io_error_from_errno(status),
                            message: GLib.strerror(status)
                        });
                    }
                }
            } catch (ex) {
                setConnectionState(false);
                logError(ex);
            } finally {
                _connectionSwitch.setToggleState(false);
            }
        });
    } catch (ex) {
        logError(ex);
    }
}

const Indicator = GObject.registerClass(
class Indicator extends PanelMenu.Button {
    toggleConnection(enabled) {
        if (enabled) {
            log("enable connection");
            // start process
            execCheck([
                'bash',
                '-c',
                create_connection]
            );
        } else {
            log("disable conenction");
            // close running process
            if (_proc) _proc.send_signal(2);
            else if (_already_running) {
                // kill process
                Gio.Subprocess.new([
                    'bash',
                    '-c',
                    close_connection],
                    Gio.SubprocessFlags.STDOUT_PIPE
                );
                _already_running = false;
                setConnectionState(false);
            }
        }
    }

    _init() {
        super._init(0.0, _('VPN URA'));

        // set icon
        _icon = new St.Icon({
            icon_name: iconsState[0],
            style_class: 'system-status-icon'
        });
        this.add_child(_icon);

        // toggle connection
        _connectionSwitch = new PopupMenu.PopupSwitchMenuItem('Connection', false);
        _connectionSwitch.connect('toggled', (_item, state) => {
            this.toggleConnection(state);
        });
        this.menu.addMenuItem(_connectionSwitch);

        // check if process is not already running
        let [, , , status] = GLib.spawn_command_line_sync('pidof openconnect');
        if (status == 0) {
            _already_running = true;
            _connectionSwitch.setToggleState(true);
            setConnectionState(true);
        }
   }
});

class Extension {
    constructor(uuid) {
        this._uuid = uuid;
    }

    enable() {
        _already_running = false;

        this._indicator = new Indicator();
        Main.panel.addToStatusArea(this._uuid, this._indicator, 1);
    }

    disable() {
        _proc = null;

        _connectionSwitch = null;
        _last_connection = false;

        _icon.destroy();
        _icon = null;

        this._indicator.destroy();
        this._indicator = null;
    }
}

function init(meta) {
    return new Extension(meta.uuid);
}

Even if I added a case, where I was trying to use pidof to detect if process is running already. It caught only the case, when process was started outside from extension, but not the case, which I wanted.

How to bind into callback, which is fired up when session is restored? Or is there any another way to handle this?

Thanks, Andy

1

There are 1 best solutions below

1
mahdiaqallal On

I do also attempt to write a basic gnome extension for the same purpose. Being very new to gjs coding, I found those answers regarding connection updating, in my research. Hope some of it might help you :

Util.spawnCommandLine does not work on GNOME Shell extension

How to get OS name while writing gnome-extensions

GLib run command with root privileges

Running an asynchronous function in a GNOME extension