Description: |
Update: I have since re-implemented this functionality (with configuration dialogs) using Gtk and Ruby. It's available here.
Warning: Don't use this script as an example of how to write Gtk GUI code in Perl - it uses the old 'Gtk' API (which was current in 2002 when I wrote the script) not the more modern Gtk2 API.
This script scratches a particular itch of mine - I'm administering multiple hosts and regularly want to open a new SSH session to a particular host in a new window. This script provides the list of hosts as a drop-down menu.
It's my first attempt at a GNOME panel applet so I'm interested in feedback from Perl-Gtk gurus who spot unduly complex bits. I'm posting this because I didn't find much in the way of sample code on the web.
I'm running RedHat 7.3 and Ximian GNOME so if it doesn't work for you, you may need to upgrade to the latest versions. The only module I needed over and above RH+Ximian was XML::Simple, but I had that already :-)
Ultimately, I want to implement a configuration dialog but
it's functional now so here goes ... |
#!/usr/bin/perl -w
######################################################################
+########
# $Id: sshmenu_applet,v 1.10 2002/06/07 14:21:37 grantm Exp $
#
# Title: sshmenu
#
# Author: Grant McLean <grantm@cpan.org>
#
# Description:
#
# A GNOME panel applet which provides a pull-down list of hosts. When
+ a host
# is selected, an SSH session to that host is spawned in a new termina
+l window.
#
use strict;
######################################################################
+########
# SSHMenu - OO Package which implements this application
#
package SSHMenu;
use strict;
use Gtk::lazy;
use Gnome::Applet;
use XML::Simple;
set_locale Gtk;
use constant TRUE => 1;
use constant FALSE => 0;
use constant APPLET_NAME => 'sshmenu_applet';
######################################################################
+########
# Constructor: new()
#
sub new {
my $class = shift;
my $self = { @_ };
unless($self->{ConfigFile}) {
$self->{ConfigFile} = $ENV{HOME} . '/.sshmenu';
}
bless($self, $class);
$self->read_config();
return($self);
}
######################################################################
+########
# Method: event_loop()
#
# Performs initialisation and enters event loop, never returns.
#
sub event_loop {
my $self = shift;
#$self->add_key();
init Gnome::AppletWidget APPLET_NAME;
$self->{applet} = new Gnome::AppletWidget APPLET_NAME;
$self->{applet}->realize;
my $button = new Gtk::Button("SSH");
$button->signal_connect('event', sub { $self->button_press(@_); });
$self->{applet}->add($button);
$self->{applet}->show_all();
Gtk::Tooltips->new()->set_tip(
$button, $self->{config}->{globals}->{tooltip}, FALSE
);
$self->build_menu();
gtk_main Gnome::AppletWidget;
}
######################################################################
+########
# Method: build_menu()
#
# Creates and populates a Gtk menu widget from contents of config file
+.
#
sub build_menu {
my $self = shift;
$self->{menu} = undef; # delete old menu if there was one
my $menu = new Gtk::Menu();
foreach my $host (@{$self->{config}->{host}}) {
my $menu_item = new Gtk::MenuItem($host->{title});
$menu->append($menu_item);
$menu_item->signal_connect('activate', sub { open_win($self, $host
+) });
$menu_item->show();
}
# Add a menu separator followed by some standard menu items
my $menu_item = new Gtk::MenuItem();
$menu->append($menu_item);
$menu_item->show();
$menu_item = new Gtk::MenuItem('Edit host list');
$menu->append($menu_item);
$menu_item->signal_connect('activate',
sub { system("gvim $self->{ConfigFile}"); }
);
$menu_item->show();
$menu_item = new Gtk::MenuItem('Add SSH key to Agent');
$menu->append($menu_item);
$menu_item->signal_connect('activate', sub { $self->add_key(1) } );
$menu_item->show();
$menu_item = new Gtk::MenuItem('Remove SSH keys from Agent');
$menu->append($menu_item);
$menu_item->signal_connect('activate', sub { $self->remove_keys() }
+);
$menu_item->show();
$self->{menu} = $menu;
}
######################################################################
+########
# Method: read_config()
#
# Looks for XML format config file called $HOME/.sshmenu and slurps th
+e config
# data into $self->{config}. This routine is called once at startup a
+nd then
# again before the menu is displayed, to check whether the config file
+ has
# changed. If the file has not changed, returns false.
# If the config file is read successfully, return true. If there is a
+n error
# in the config file, XML::Simple will call die and we don't trap that
+ (yet).
# A default config will be created if it does not exist.
#
sub read_config {
my $self = shift;
unless(-e $self->{ConfigFile}) {
$self->default_config();
$self->save_config();
return;
}
my $curr_timestamp = (stat($self->{ConfigFile}))[9];
if($self->{config_timestamp}) {
return if($curr_timestamp == $self->{config_timestamp});
}
$self->{config} = XMLin(
$self->{ConfigFile},
forcearray => [ 'host' ],
keyattr => [],
suppressempty => '',
);
$self->{config}->{globals}->{tooltip} ||=
'Open an SSH session in a new window';
$self->{config_timestamp} = $curr_timestamp;
}
######################################################################
+########
# Method: default_config()
#
# Sets up default configuration settings in $self->{config}.
#
sub default_config {
my $self = shift;
$self->{config} = {
host => [
{
title => 'localhost',
sshparams => '127.0.0.1',
winparams => ''
}
]
}
}
######################################################################
+########
# Method: save_config()
#
# Writes out the contents of $self->{config} to $HOME/.sshmenu
#
sub save_config {
my $self = shift;
XMLout(
$self->{config},
rootname => 'config',
outputfile => $self->{ConfigFile},
noattr => 1,
xmldecl => 1,
);
}
######################################################################
+########
# Method: add_key()
#
# Adds the default SSH key to the agent. Remembers whether a key has
+been
# added.
#
sub add_key {
my $self = shift;
my $force = shift;
unless($force) {
return if($self->{have_key});
my $keylist = `ssh-add -l`;
if($keylist =~ m{.ssh/id}) {
$self->{have_key} = 1;
return;
}
}
$ENV{SSH_ASKPASS} = '/usr/libexec/openssh/x11-ssh-askpass';
system("ssh-add </dev/null >/dev/null 2>&1");
}
######################################################################
+########
# Method: remove_keys()
#
# Removes all keys from SSH agent.
#
sub remove_keys {
my $self = shift;
system("ssh-add -D </dev/null >/dev/null 2>&1");
}
######################################################################
+########
# Method: button_press()
#
# Display menu in response to a button-press event.
#
sub button_press {
my ($self, $button, $event) = @_;
return(FALSE) if(!$event->{type} or $event->{type} ne 'button_pres
+s');
return(FALSE) if($event->{button} != 1);
if($self->read_config()) {
$self->build_menu();
}
$self->{menu}->popup(undef, undef, $event->{button}, 0, undef);
return (TRUE);
}
######################################################################
+########
# Method: open_win()
#
# Spawn an SSH session in a new shell window.
#
sub open_win {
my $self = shift;
my $host = shift;
$self->add_key();
my $command =
"gnome-terminal --use-factory --start-factory-server " .
"--title='$host->{title}' $host->{winparams} " .
"--command='ssh $host->{sshparams}'";
system("$command &");
}
######################################################################
+########
# Main script - instantiates the application object.
#
package main;
my $app = new SSHMenu;
$app->event_loop();
exit;
|