mirror of
https://github.com/duncs/clusterssh.git
synced 2025-07-03 01:43:25 +00:00

The code wasn't taking into account being called under another name correctly, so wasn't calling the correct comms method.
465 lines
12 KiB
Perl
465 lines
12 KiB
Perl
package App::ClusterSSH::Config;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use version;
|
|
our $VERSION = version->new('0.02');
|
|
|
|
use Carp;
|
|
use Try::Tiny;
|
|
|
|
use FindBin qw($Script);
|
|
|
|
use base qw/ App::ClusterSSH::Base /;
|
|
use App::ClusterSSH::Cluster;
|
|
|
|
my $clusters;
|
|
my %old_clusters;
|
|
my @app_specific = (qw/ command title comms method ssh rsh telnet ccon /);
|
|
my %default_config = (
|
|
terminal => "xterm",
|
|
terminal_args => "",
|
|
terminal_title_opt => "-T",
|
|
terminal_colorize => 1,
|
|
terminal_bg_style => 'dark',
|
|
terminal_allow_send_events => "-xrm '*.VT100.allowSendEvents:true'",
|
|
terminal_font => "6x13",
|
|
terminal_size => "80x24",
|
|
|
|
use_hotkeys => "yes",
|
|
key_quit => "Control-q",
|
|
key_addhost => "Control-Shift-plus",
|
|
key_clientname => "Alt-n",
|
|
key_history => "Alt-h",
|
|
key_retilehosts => "Alt-r",
|
|
key_paste => "Control-v",
|
|
mouse_paste => "Button-2",
|
|
auto_quit => "yes",
|
|
auto_close => 5,
|
|
window_tiling => "yes",
|
|
window_tiling_direction => "right",
|
|
console_position => "",
|
|
|
|
screen_reserve_top => 0,
|
|
screen_reserve_bottom => 60,
|
|
screen_reserve_left => 0,
|
|
screen_reserve_right => 0,
|
|
|
|
terminal_reserve_top => 5,
|
|
terminal_reserve_bottom => 0,
|
|
terminal_reserve_left => 5,
|
|
terminal_reserve_right => 0,
|
|
|
|
terminal_decoration_height => 10,
|
|
terminal_decoration_width => 8,
|
|
|
|
rsh_args => "",
|
|
telnet_args => "",
|
|
ssh_args => "",
|
|
|
|
extra_cluster_file => "",
|
|
|
|
unmap_on_redraw => "no", # Debian #329440
|
|
|
|
show_history => 0,
|
|
history_width => 40,
|
|
history_height => 10,
|
|
|
|
command => q{},
|
|
max_host_menu_items => 30,
|
|
|
|
max_addhost_menu_cluster_items => 6,
|
|
menu_send_autotearoff => 0,
|
|
menu_host_autotearoff => 0,
|
|
|
|
use_all_a_records => 0,
|
|
|
|
send_menu_xml_file => $ENV{HOME} . '/.csshrc_send_menu',
|
|
);
|
|
|
|
sub new {
|
|
my ( $class, %args ) = @_;
|
|
|
|
my $self = $class->SUPER::new(%default_config);
|
|
|
|
( my $comms = $Script ) =~ s/^c//;
|
|
|
|
$comms = 'telnet' if ( $comms eq 'tel' );
|
|
$comms = 'console' if ( $comms eq 'con' );
|
|
|
|
# list of allowed comms methods
|
|
if ( 'ssh rsh telnet console' !~ m/\b$comms\b/ ) {
|
|
$self->{comms} = 'ssh';
|
|
}
|
|
else {
|
|
$self->{comms} = $comms;
|
|
}
|
|
|
|
if ( $self->{comms}
|
|
&& ( !$self->{ $self->{comms} } || !-e $self->{ $self->{comms} } ) )
|
|
{
|
|
$self->{ $self->{comms} } = $self->find_binary( $self->{comms} );
|
|
}
|
|
|
|
$self->{terminal} = $self->find_binary( $self->{terminal} );
|
|
|
|
$self->{title} = uc($Script);
|
|
|
|
$clusters = App::ClusterSSH::Cluster->new();
|
|
|
|
return $self->validate_args(%args);
|
|
}
|
|
|
|
sub validate_args {
|
|
my ( $self, %args ) = @_;
|
|
|
|
my @unknown_config = ();
|
|
|
|
foreach my $config ( sort( keys(%args) ) ) {
|
|
if ( grep /$config/, @app_specific ) {
|
|
|
|
# $self->{$config} ||= 'unknown';
|
|
next;
|
|
}
|
|
|
|
if ( exists $self->{$config} ) {
|
|
$self->{$config} = $args{$config};
|
|
}
|
|
else {
|
|
push( @unknown_config, $config );
|
|
}
|
|
}
|
|
|
|
if (@unknown_config) {
|
|
croak(
|
|
App::ClusterSSH::Exception::Config->throw(
|
|
unknown_config => \@unknown_config,
|
|
error => $self->loc(
|
|
'Unknown configuration parameters: [_1]',
|
|
join( ',', @unknown_config )
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
return $self;
|
|
}
|
|
|
|
sub parse_config_file {
|
|
my ( $self, $config_file ) = @_;
|
|
|
|
$self->debug( 2, 'Loading in config file: ', $config_file );
|
|
|
|
if ( !-e $config_file || !-r $config_file ) {
|
|
croak(
|
|
App::ClusterSSH::Exception::Config->throw(
|
|
error => $self->loc(
|
|
'File [_1] does not exist or cannot be read', $config_file
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
open( CFG, $config_file ) or die("Couldnt open $config_file: $!");
|
|
my $l;
|
|
my %read_config;
|
|
while ( defined( $l = <CFG> ) ) {
|
|
next
|
|
if ( $l =~ /^\s*$/ || $l =~ /^#/ )
|
|
; # ignore blank lines & commented lines
|
|
$l =~ s/#.*//; # remove comments from remaining lines
|
|
$l =~ s/\s*$//; # remove trailing whitespace
|
|
|
|
# look for continuation lines
|
|
chomp $l;
|
|
if ( $l =~ s/\\\s*$// ) {
|
|
$l .= <CFG>;
|
|
redo unless eof(CFG);
|
|
}
|
|
|
|
next unless $l =~ m/\s*(\S+)\s*=\s*(.*)\s*/;
|
|
my ( $key, $value ) = ( $1, $2 );
|
|
if ( defined $key && defined $value ) {
|
|
$read_config{$key} = $value;
|
|
$self->debug( 3, "$key=$value" );
|
|
}
|
|
}
|
|
close(CFG);
|
|
|
|
# grab any c'lusters from the config before validating it
|
|
if ( $read_config{clusters} ) {
|
|
$self->debug( 3, "Picked up clusters defined in $config_file" );
|
|
foreach my $cluster ( sort split / /, $read_config{clusters} ) {
|
|
if ( $read_config{$cluster} ) {
|
|
$clusters->register_tag( $cluster, $read_config{$cluster} );
|
|
$old_clusters{$cluster} = $read_config{$cluster};
|
|
delete( $read_config{$cluster} );
|
|
}
|
|
}
|
|
delete( $read_config{clusters} );
|
|
}
|
|
|
|
# tidy up entries, just in case
|
|
$read_config{terminal_font} =~ s/['"]//g
|
|
if ( $read_config{terminal_font} );
|
|
|
|
$self->validate_args(%read_config);
|
|
}
|
|
|
|
sub load_configs {
|
|
my ( $self, @configs ) = @_;
|
|
|
|
if ( -e $ENV{HOME} . '/.csshrc' ) {
|
|
warn(
|
|
$self->loc(
|
|
'NOTICE: [_1] is no longer used - please see documentation and remove',
|
|
$ENV{HOME} . '/.csshrc'
|
|
),
|
|
$/
|
|
);
|
|
}
|
|
|
|
for my $config (
|
|
'/etc/csshrc',
|
|
$ENV{HOME} . '/.csshrc',
|
|
$ENV{HOME} . '/.clusterssh/config',
|
|
)
|
|
{
|
|
$self->parse_config_file($config) if ( -e $config );
|
|
}
|
|
|
|
# write out default config file if necesasry
|
|
try {
|
|
$self->write_user_config_file();
|
|
}
|
|
catch {
|
|
warn $_, $/;
|
|
};
|
|
|
|
# Attempt to load in provided config files. Also look for anything
|
|
# relative to config directory
|
|
for my $config (@configs) {
|
|
next unless ($config); # can be null when passed from Getopt::Long
|
|
$self->parse_config_file($config) if ( -e $config );
|
|
|
|
my $file = $ENV{HOME} . '/.clusterssh/config_' . $config;
|
|
$self->parse_config_file($file) if ( -e $file );
|
|
}
|
|
|
|
return $self;
|
|
}
|
|
|
|
sub write_user_config_file {
|
|
my ($self) = @_;
|
|
|
|
return if ( -f "$ENV{HOME}/.clusterssh/config" );
|
|
|
|
if ( !-d "$ENV{HOME}/.clusterssh" ) {
|
|
if ( !mkdir("$ENV{HOME}/.clusterssh") ) {
|
|
croak(
|
|
App::ClusterSSH::Exception::Config->throw(
|
|
error => $self->loc(
|
|
'Unable to create directory [_1]: [_2]',
|
|
'$HOME/.clusterssh', $!
|
|
),
|
|
),
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
if ( open( CONFIG, ">", "$ENV{HOME}/.clusterssh/config" ) ) {
|
|
foreach ( sort( keys(%$self) ) ) {
|
|
print CONFIG "$_=$self->{$_}\n";
|
|
}
|
|
close(CONFIG);
|
|
}
|
|
else {
|
|
croak(
|
|
App::ClusterSSH::Exception::Config->throw(
|
|
error => $self->loc(
|
|
'Unable to write default [_1]: [_2]',
|
|
'$HOME/.clusterssh/config',
|
|
$!
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return $self if ( !%old_clusters );
|
|
|
|
if ( open( my $fh, ">", "$ENV{HOME}/.clusterssh/clusters" ) ) {
|
|
print $fh '# '
|
|
. $self->loc('Tag definitions moved from old .csshrc file'), $/;
|
|
foreach ( sort( keys(%old_clusters) ) ) {
|
|
print $fh $_, ' ', join( ' ', $old_clusters{$_} ), $/;
|
|
}
|
|
close($fh);
|
|
}
|
|
return $self;
|
|
}
|
|
|
|
# could use File::Which for some of this but we also search a few other places
|
|
# just in case $PATH isnt set up right
|
|
sub find_binary {
|
|
my ( $self, $binary ) = @_;
|
|
|
|
if ( !$binary ) {
|
|
croak(
|
|
App::ClusterSSH::Exception::Config->throw(
|
|
error => $self->loc('argument not provided'),
|
|
),
|
|
);
|
|
}
|
|
|
|
$self->debug( 2, "Looking for $binary" );
|
|
|
|
# if not found, strip the path and look again
|
|
if ( $binary =~ m!^/! ) {
|
|
if ( -f $binary ) {
|
|
$self->debug( 2, "$binary already fully qualified" );
|
|
return $binary;
|
|
}
|
|
else {
|
|
$self->debug( 2, "$binary not found - re-searching" );
|
|
$binary =~ s!^.*/!!;
|
|
}
|
|
}
|
|
|
|
my $path;
|
|
if ( !-x $binary || substr( $binary, 0, 1 ) ne '/' ) {
|
|
|
|
foreach (
|
|
split( /:/, $ENV{PATH} ), qw!
|
|
/bin
|
|
/sbin
|
|
/usr/sbin
|
|
/usr/bin
|
|
/usr/local/bin
|
|
/usr/local/sbin
|
|
/opt/local/bin
|
|
/opt/local/sbin
|
|
!
|
|
)
|
|
{
|
|
$self->debug( 3, "Looking in $_" );
|
|
|
|
if ( -f $_ . '/' . $binary && -x $_ . '/' . $binary ) {
|
|
$path = $_ . '/' . $binary;
|
|
$self->debug( 2, "Found at $path" );
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$self->debug( 2, "Already configured OK" );
|
|
$path = $binary;
|
|
}
|
|
if ( !$path || !-f $path || !-x $path ) {
|
|
croak(
|
|
App::ClusterSSH::Exception::Config->throw(
|
|
error => $self->loc(
|
|
'"[_1]" binary not found - please amend $PATH or the cssh config file',
|
|
$binary
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
chomp($path);
|
|
return $path;
|
|
}
|
|
|
|
sub dump {
|
|
my ( $self, $no_exit, ) = @_;
|
|
|
|
$self->debug( 3, 'Dumping config to STDOUT' );
|
|
print( '# Configuration dump produced by "cssh -u"', $/ );
|
|
|
|
foreach my $key ( sort keys %$self ) {
|
|
if ( grep /$key/, @app_specific ) {
|
|
next;
|
|
}
|
|
print $key, '=', $self->{$key}, $/;
|
|
}
|
|
|
|
$self->exit if ( !$no_exit );
|
|
}
|
|
|
|
#use overload (
|
|
# q{""} => sub {
|
|
# my ($self) = @_;
|
|
# return $self->{hostname};
|
|
# },
|
|
# fallback => 1,
|
|
#);
|
|
|
|
1;
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
ClusterSSH::Config
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Object representing application configuration
|
|
|
|
=head1 METHODS
|
|
|
|
=over 4
|
|
|
|
=item $host=ClusterSSH::Config->new ({ })
|
|
|
|
Create a new configuration object.
|
|
|
|
=item $config->parse_config_file('<filename>');
|
|
|
|
Read in configuration from given filename
|
|
|
|
=item $config->validate_args();
|
|
|
|
Validate and apply all configuration loaded at this point
|
|
|
|
=item $path = $config->find_binary('<name>');
|
|
|
|
Locate the binary <name> and return the full path. Doesn't just search
|
|
$PATH in case the environment isn't set up correctly
|
|
|
|
=item $config->load_configs(@extra);
|
|
|
|
Load up configuration from known locations (warn if .csshrc file found) and
|
|
load in option files as necessary.
|
|
|
|
=item $config->write_user_config_file();
|
|
|
|
Write out default $HOME/.clusterssh/config file (before option config files
|
|
are loaded).
|
|
|
|
=item $config->dump()
|
|
|
|
Write currently defined configuration to STDOUT
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Duncan Ferguson, C<< <duncan_j_ferguson at yahoo.co.uk> >>
|
|
|
|
=head1 LICENSE AND COPYRIGHT
|
|
|
|
Copyright 1999-2010 Duncan Ferguson.
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of either: the GNU General Public License as published
|
|
by the Free Software Foundation; or the Artistic License.
|
|
|
|
See http://dev.perl.org/licenses/ for more information.
|
|
|
|
=cut
|
|
|
|
1;
|