diff --git a/Build.PL b/Build.PL
index be3afb3..467c201 100644
--- a/Build.PL
+++ b/Build.PL
@@ -6,7 +6,7 @@ use Module::Build;
my $build = Module::Build->new(
meta_merge => {
resources => {
- repository => [
+ Repository => [
'http://clusterssh.git.sourceforge.net/',
'http://github.com/duncs/clusterssh',
],
@@ -34,6 +34,10 @@ my $build = Module::Build->new(
'File::Which' => 0,
'File::Temp' => 0,
'Test::DistManifest' => 0,
+ 'Test::Differences' => 0,
+ },
+ configure_requires => {
+ 'Module::Build' => 0,
},
add_to_cleanup => ['App-ClusterSSH-*'],
create_makefile_pl => 'traditional',
diff --git a/Changes b/Changes
index c1fe7e9..485932d 100644
--- a/Changes
+++ b/Changes
@@ -1,3 +1,34 @@
+2013-03-05 Duncan Ferguson <duncan_ferguson@user.sf.net> - v4.01_05
+- New option (-m, --unique-servers) to remove repeated servers when openeing terminals (Thanks to Oliver Meissner)
+- Drop MYMETA.yml and .json files from the distribution
+- Do not set default user name to prevent overriding ssh configuration
+
+2013-02-26 Duncan Ferguson <duncan_ferguson@user.sf.net> - v4.01_04
+- Fixed 'ccon' not calling the correct command (Sf bug 3605002)
+- Fixed clusters not being defined correctly within the .clusterssh/config file (Sf bug 3605675)
+
+2013-02-15 Duncan Ferguson <duncan_ferguson@user.sf.net> - v4.01_03
+* Correct documentation for references to $HOME/.clusterssh/config
+* Re-add user back into the configurartion file
+* Add in missing newline for some error messages
+* Allow the path to rsh/ssh/telnet to be defined in the configuration file
+* Move .csshrc to .csshrc.DISABLED since it should no longer be used
+* Error emitted when adding a host via the "Hosts" drop-down (Debian bug ID #578208)
+* Pastes uses a strange keyboard layout (Debian bug ID #364565)
+* Cope with being invoked by 'clusterssh' (Debian bug ID #644368)
+* Fix migration of .csshrc when not working as expected (Debian bug ID #673507)
+* Remove doc references to 'always_tile' as renamed 'window_tiling' (Debian bug ID #697371)
+* Updated manpage whatis entries (patch by Tony Mancill)
+* Fix watch line expression to catch 4.x series tarballs (Debian patch LP ID #1076897)
+* Allow tests to pass successfully when run as root
+* Fix cssh starting if xterm is not installed (Sf bug 3494988)
+* Set WM_CLASS on windows to 'cssh' (Sf bug 3187736)
+
+2012-12-09 Duncan Ferguson <duncan_ferguson@user.sf.net> - v4.01_02
+* Fix logic when using 'autoclose' on the command line or config file
+* Fix $HOME/.clusterssh/clusters being read in
+* Fix 'ctel', 'crsh' and 'ccon'so they work as expected
+
2011-12-09 Duncan Ferguson <duncan_ferguson@user.sf.net> - v4.01_01
* Include missing files from release tarballs
diff --git a/MANIFEST b/MANIFEST
index cebe4b1..a4b4983 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -14,13 +14,11 @@ lib/App/ClusterSSH/Helper.pm
lib/App/ClusterSSH/Host.pm
lib/App/ClusterSSH/L10N.pm
lib/App/ClusterSSH/L10N/en.pm
-Makefile
-Makefile.old
Makefile.PL
MANIFEST
MANIFEST.SKIP
+META.json
META.yml
-MYMETA.json
README
t/00-load.t
t/01l10n.t
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
index 3753e38..4900842 100644
--- a/MANIFEST.SKIP
+++ b/MANIFEST.SKIP
@@ -4,7 +4,11 @@
^Build$
^.git/
^.gitignore
+^Makefile$
+^Makefile.old$
^MANIFEST\.bak$
-^MYMETA.yml$
+MYMETA.json
+MYMETA.yml
+pm_to_blib
.*\.swp$
^WIP_TASKS$
diff --git a/META.json b/META.json
new file mode 100644
index 0000000..26b6d74
--- /dev/null
+++ b/META.json
@@ -0,0 +1,94 @@
+{
+ "abstract" : "A container for functions of the ClusterSSH programs",
+ "author" : [
+ "Duncan Ferguson <duncan_j_ferguson@yahoo.co.uk>"
+ ],
+ "dynamic_config" : 1,
+ "generated_by" : "Module::Build version 0.4003, CPAN::Meta::Converter version 2.112621",
+ "license" : [
+ "perl_5"
+ ],
+ "meta-spec" : {
+ "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+ "version" : "2"
+ },
+ "name" : "App-ClusterSSH",
+ "prereqs" : {
+ "build" : {
+ "requires" : {
+ "File::Temp" : 0,
+ "File::Which" : 0,
+ "Readonly" : 0,
+ "Test::Differences" : 0,
+ "Test::DistManifest" : 0,
+ "Test::Pod" : 0,
+ "Test::Pod::Coverage" : 0,
+ "Test::Trap" : 0
+ }
+ },
+ "configure" : {
+ "requires" : {
+ "Module::Build" : 0
+ }
+ },
+ "runtime" : {
+ "requires" : {
+ "Exception::Class" : "1.31",
+ "Locale::Maketext" : 0,
+ "Tk" : "800.022",
+ "Try::Tiny" : 0,
+ "X11::Protocol" : "0.56",
+ "version" : 0
+ }
+ }
+ },
+ "provides" : {
+ "App::ClusterSSH" : {
+ "file" : "lib/App/ClusterSSH.pm",
+ "version" : "4.01_05"
+ },
+ "App::ClusterSSH::Base" : {
+ "file" : "lib/App/ClusterSSH/Base.pm",
+ "version" : "0.02"
+ },
+ "App::ClusterSSH::Cluster" : {
+ "file" : "lib/App/ClusterSSH/Cluster.pm",
+ "version" : "0.01"
+ },
+ "App::ClusterSSH::Config" : {
+ "file" : "lib/App/ClusterSSH/Config.pm",
+ "version" : "0.02"
+ },
+ "App::ClusterSSH::Helper" : {
+ "file" : "lib/App/ClusterSSH/Helper.pm",
+ "version" : "0.02"
+ },
+ "App::ClusterSSH::Host" : {
+ "file" : "lib/App/ClusterSSH/Host.pm",
+ "version" : "0.03"
+ },
+ "App::ClusterSSH::L10N" : {
+ "file" : "lib/App/ClusterSSH/L10N.pm",
+ "version" : 0
+ },
+ "App::ClusterSSH::L10N::en" : {
+ "file" : "lib/App/ClusterSSH/L10N/en.pm",
+ "version" : 0
+ }
+ },
+ "release_status" : "testing",
+ "resources" : {
+ "bugtracker" : {
+ "web" : "http://sourceforge.net/tracker/?group_id=89139"
+ },
+ "homepage" : "http://clusterssh.sourceforge.net/",
+ "license" : [
+ "http://dev.perl.org/licenses/"
+ ],
+ "x_Repository" : [
+ "http://clusterssh.git.sourceforge.net/",
+ "http://github.com/duncs/clusterssh"
+ ]
+ },
+ "version" : "4.01_05"
+}
diff --git a/META.yml b/META.yml
index bdf1020..7e93bb8 100644
--- a/META.yml
+++ b/META.yml
@@ -6,13 +6,15 @@ build_requires:
File::Temp: 0
File::Which: 0
Readonly: 0
+ Test::Differences: 0
Test::DistManifest: 0
Test::Pod: 0
Test::Pod::Coverage: 0
Test::Trap: 0
configure_requires:
- Module::Build: 0.36
-generated_by: 'Module::Build version 0.3603'
+ Module::Build: 0
+dynamic_config: 1
+generated_by: 'Module::Build version 0.4003, CPAN::Meta::Converter version 2.112621'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -21,7 +23,7 @@ name: App-ClusterSSH
provides:
App::ClusterSSH:
file: lib/App/ClusterSSH.pm
- version: 4.01_01
+ version: 4.01_05
App::ClusterSSH::Base:
file: lib/App/ClusterSSH/Base.pm
version: 0.02
@@ -30,17 +32,19 @@ provides:
version: 0.01
App::ClusterSSH::Config:
file: lib/App/ClusterSSH/Config.pm
- version: 0.01
+ version: 0.02
App::ClusterSSH::Helper:
file: lib/App/ClusterSSH/Helper.pm
- version: 0.01
+ version: 0.02
App::ClusterSSH::Host:
file: lib/App/ClusterSSH/Host.pm
version: 0.03
App::ClusterSSH::L10N:
file: lib/App/ClusterSSH/L10N.pm
+ version: 0
App::ClusterSSH::L10N::en:
file: lib/App/ClusterSSH/L10N/en.pm
+ version: 0
requires:
Exception::Class: 1.31
Locale::Maketext: 0
@@ -52,7 +56,7 @@ resources:
bugtracker: http://sourceforge.net/tracker/?group_id=89139
homepage: http://clusterssh.sourceforge.net/
license: http://dev.perl.org/licenses/
- repository:
+ x_Repository:
- http://clusterssh.git.sourceforge.net/
- http://github.com/duncs/clusterssh
-version: 4.01_01
+version: 4.01_05
diff --git a/Makefile.PL b/Makefile.PL
index 4e126ef..059ea01 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,32 +1,33 @@
-# Note: this file was auto-generated by Module::Build::Compat version 0.3603
+# Note: this file was auto-generated by Module::Build::Compat version 0.4003
use ExtUtils::MakeMaker;
WriteMakefile
(
- 'NAME' => 'App::ClusterSSH',
- 'VERSION_FROM' => 'lib/App/ClusterSSH.pm',
- 'PREREQ_PM' => {
- 'Exception::Class' => '1.31',
- 'File::Temp' => 0,
- 'File::Which' => 0,
- 'Locale::Maketext' => 0,
- 'Readonly' => 0,
- 'Test::DistManifest' => 0,
- 'Test::Pod' => 0,
- 'Test::Pod::Coverage' => 0,
- 'Test::Trap' => 0,
- 'Tk' => '800.022',
- 'Try::Tiny' => 0,
- 'X11::Protocol' => '0.56',
- 'version' => '0'
- },
- 'INSTALLDIRS' => 'site',
- 'EXE_FILES' => [
- 'bin/ccon',
- 'bin/crsh',
- 'bin/cscp',
- 'bin/cssh',
- 'bin/ctel'
- ],
- 'PL_FILES' => {}
- )
+ 'NAME' => 'App::ClusterSSH',
+ 'VERSION_FROM' => 'lib/App/ClusterSSH.pm',
+ 'PREREQ_PM' => {
+ 'Exception::Class' => '1.31',
+ 'File::Temp' => 0,
+ 'File::Which' => 0,
+ 'Locale::Maketext' => 0,
+ 'Readonly' => 0,
+ 'Test::Differences' => 0,
+ 'Test::DistManifest' => 0,
+ 'Test::Pod' => 0,
+ 'Test::Pod::Coverage' => 0,
+ 'Test::Trap' => 0,
+ 'Tk' => '800.022',
+ 'Try::Tiny' => 0,
+ 'X11::Protocol' => '0.56',
+ 'version' => '0'
+ },
+ 'INSTALLDIRS' => 'site',
+ 'EXE_FILES' => [
+ 'bin/ccon',
+ 'bin/crsh',
+ 'bin/cscp',
+ 'bin/cssh',
+ 'bin/ctel'
+ ],
+ 'PL_FILES' => {}
+)
;
diff --git a/THANKS b/THANKS
index 3af7cd0..bc2b3f7 100644
--- a/THANKS
+++ b/THANKS
@@ -40,3 +40,4 @@ Simon Fraser
Stefan Steiner
Ryan Brown
Brandon Perkins
+Oliver Meissner
diff --git a/bin/cssh b/bin/cssh
index 3a11dc1..6b95202 100755
--- a/bin/cssh
+++ b/bin/cssh
@@ -66,12 +66,12 @@ that host. Re-selecting it will plug it back in.
=item *
If your window manager menu bars are obscured by terminal windows see
-the C<screen_reserve_XXXXX> options in the F<csshrc> file (see L</"FILES">).
+the C<screen_reserve_XXXXX> options in the F<$HOME/.clusterssh/config> file (see L</"FILES">).
=item *
If the terminals overlap too much see the C<terminal_reserve_XXXXX>
-options in the F<csshrc> file (see L</"FILES">).
+options in the F<$HOME/.clusterssh/config> file (see L</"FILES">).
=item *
@@ -115,7 +115,7 @@ be due to either the C<-xrm> terminal option which enables C<AllowSendEvents>
(some terminal do not require this option, other terminals have another
method for enabling it - see your terminal documention) or the
C<ConnectTimeout> ssh option (see the configuration option C<-o> or file
-C<csshrc> below to resolve this).
+C<$HOME/.clusterssh/config> below to resolve this).
=back
@@ -201,7 +201,7 @@ F<$HOME/.ssh/config>).
=item --output-config,-u
Output the current configuration in the same format used by the
-F<$HOME/.csshrc> file.
+F<$HOME/.clusterssh/config> file.
=item --port,-p <port>
@@ -224,6 +224,10 @@ Enable|Disable window tiling (overriding the config file)
Specify the initial part of the title used in the console and client windows
+=item --unique-servers,-m
+
+Connect to each host only once
+
=item --use_all_a_records,-A
If a hostname resolves to multiple IP addresses, toggle whether or not to
@@ -255,7 +259,7 @@ on standard port (e.g not listening on port 22) and ssh_config cannot be used.
=item <tag> ...
Open a series of xterms defined by <tag> within either /etc/clusters or
-F<$HOME/.csshrc> (see L</"FILES">).
+F<$HOME/.clusterssh/config> (see L</"FILES">).
Note: specifying a username on a cluster tag will override any usernames
defined in the cluster
@@ -354,13 +358,13 @@ nested, but be aware of recursive tags which are not checked for.
Clusters may also be specified either directly (see C<clusters> configuration
options) or indirectly (see C<extra_cluster_file> configuration option)
-in the users F<$HOME/.csshrc> file.
+in the users F<$HOME/.clusterssh/config> file.
NOTE: there is a special cluster tag called C<default> - any tags or hosts
included within this tag will be automatically opened if no other tags
are specified on the command line.
-=item F</etc/csshrc> & F<$HOME/.csshrc>
+=item F</etc/csshrc> & F<$HOME/.clusterssh/config>
This file contains configuration overrides - the defaults are as marked.
Default options are overwritten first by the global file, and then by the
@@ -377,10 +381,6 @@ should be written as
=over
-=item always_tile = yes
-
-Setting to anything other than C<yes> does not perform window tiling (see also -G).
-
=item auto_close = 5
Close terminal window after this many seconds. If set to 0 will instead wait
@@ -472,10 +472,21 @@ program start
Default key sequence to paste text into the console window using the mouse.
See below notes on shortcuts.
+=item rsh = rsh
+
+=item ssh = ssh
+
+=item telnet = telnet
+
+Set the path to the specific binary to use for the communication method, else
+uses the first match found in $PATH
+
=item rsh_args = <blank>
=item ssh_args = "-x -o ConnectTimeout=10"
+=item telnet_args = <blank>
+
Sets any arguments to be used with the communication method (defaults to ssh
arguments).
@@ -725,7 +736,7 @@ If you have issues running cssh, first try:
C<< cssh -e [user@]<hostname>[:port] >>
This performs two tests to confirm cssh is able to work properly with the
-settings provided within the F<.csshrc> file (or internal defaults).
+settings provided within the F<$HOME/.clusterssh/config> file (or internal defaults).
1. test the terminal window works with the options provided
@@ -734,7 +745,7 @@ settings provided within the F<.csshrc> file (or internal defaults).
Configuration options to watch for in ssh are
- Doesnt understand "-o ConnectTimeout=10" - remove the option
- in the F<.csshrc> file
+ in the F<$HOME/.clusterssh/config> file
- OpenSSH-3.8 using untrusted ssh tunnels - use "-Y" instead of "-X"
or use "ForwardX11Trusted yes' in ssh_config (if you change the
@@ -751,7 +762,7 @@ C<< perl -MTk -e 'print $Tk::VERSION,$/' >>
C<< perl -MX11::Protocol -e 'print $X11::Protocol::VERSION,$/' >>
-C<< cat /etc/csshrc $HOME/.csshrc >>
+C<< cat /etc/csshrc $HOME/.clusterssh/config >>
=item *
diff --git a/lib/App/ClusterSSH.pm b/lib/App/ClusterSSH.pm
index 793de94..ec568fc 100644
--- a/lib/App/ClusterSSH.pm
+++ b/lib/App/ClusterSSH.pm
@@ -3,7 +3,7 @@ package App::ClusterSSH;
use 5.008.004;
use warnings;
use strict;
-use version; our $VERSION = version->new('4.01_01');
+use version; our $VERSION = version->new('4.01_05');
use Carp;
@@ -116,12 +116,13 @@ my @options_spec = (
'font|f=s',
'list|L',
'use_all_a_records|A',
+ 'unique-servers|m',
);
my %options;
-my %windows; # hash for all window definitions
-my %menus; # hash for all menu definitions
-my @servers; # array of servers provided on cmdline
-my %servers; # hash of server cx info
+my %windows; # hash for all window definitions
+my %menus; # hash for all menu definitions
+my @servers; # array of servers provided on cmdline
+my %servers; # hash of server cx info
my $xdisplay;
my %keyboardmap;
my $sysconfigdir = "/etc";
@@ -277,53 +278,68 @@ sub load_keyboard_map() {
logmsg( 1, "Loading keymaps and keycodes" );
- foreach ( 0 .. $#keyboard ) {
- if ( defined $keyboard[$_][3] ) {
- if ( defined( $keycodetosym{ $keyboard[$_][3] } ) ) {
- $keyboardmap{ $keycodetosym{ $keyboard[$_][3] } }
- = 'sa' . ( $_ + $min );
- }
- else {
- logmsg( 2, "Unknown keycode ", $keyboard[$_][3] )
- if ( $keyboard[$_][3] != 0 );
- }
- }
- if ( defined $keyboard[$_][2] ) {
- if ( defined( $keycodetosym{ $keyboard[$_][2] } ) ) {
- $keyboardmap{ $keycodetosym{ $keyboard[$_][2] } }
- = 'a' . ( $_ + $min );
- }
- else {
- logmsg( 2, "Unknown keycode ", $keyboard[$_][2] )
- if ( $keyboard[$_][2] != 0 );
- }
- }
- if ( defined $keyboard[$_][1] ) {
- if ( defined( $keycodetosym{ $keyboard[$_][1] } ) ) {
- $keyboardmap{ $keycodetosym{ $keyboard[$_][1] } }
- = 's' . ( $_ + $min );
- }
- else {
- logmsg( 2, "Unknown keycode ", $keyboard[$_][1] )
- if ( $keyboard[$_][1] != 0 );
- }
- }
- if ( defined $keyboard[$_][0] ) {
- if ( defined( $keycodetosym{ $keyboard[$_][0] } ) ) {
- $keyboardmap{ $keycodetosym{ $keyboard[$_][0] } }
- = 'n' . ( $_ + $min );
+ my %keyboard_modifier_priority = (
+ 'sa' => 3, # lowest
+ 'a' => 2,
+ 's' => 1,
+ 'n' => 0, # highest
+ );
+
+ my %keyboard_stringlike_modifiers = reverse %keyboard_modifier_priority;
+
+ # try to associate $keyboard=X11->GetKeyboardMapping table with X11::Keysyms
+ foreach my $i ( 0 .. $#keyboard ) {
+ for my $modifier ( 0 .. 3 ) {
+ if ( defined( $keycodetosym{ $keyboard[$i][$modifier] } ) ) {
+
+ # keyboard layout contains the keycode at $modifier level
+ if (defined(
+ $keyboardmap{ $keycodetosym{ $keyboard[$i][$modifier]
+ } }
+ )
+ )
+ {
+
+# we already have a mapping, let's see whether current one is better (lower shift state)
+ my ( $mod_code, $key_code )
+ = $keyboardmap{ $keycodetosym{ $keyboard[$i]
+ [$modifier] } } =~ /^(\D+)(\d+)$/;
+
+ # it is not easy to get around our own alien logic storing modifiers ;-)
+ if ( $modifier < $keyboard_modifier_priority{$mod_code} )
+ {
+
+ # YES! current keycode have priority over old one (phew!)
+ $keyboardmap{ $keycodetosym{ $keyboard[$i][$modifier]
+ } }
+ = $keyboard_stringlike_modifiers{$modifier}
+ . ( $i + $min );
+ }
+ }
+ else {
+
+ # we don't yet have a mapping... piece of cake!
+ $keyboardmap{ $keycodetosym{ $keyboard[$i][$modifier] } }
+ = $keyboard_stringlike_modifiers{$modifier}
+ . ( $i + $min );
+ }
}
else {
- logmsg( 2, "Unknown keycode ", $keyboard[$_][0] )
- if ( $keyboard[$_][0] != 0 );
+
+ # we didn't get the code from X11::Keysyms
+ if ( $keyboard[$i][$modifier] != 0 ) {
+
+ # ignore code=0
+ logmsg( 2, "Unknown keycode ", $keyboard[$i][$modifier] );
+ }
}
}
-
- # dont know these two key combs yet...
- #$keyboardmap{ $keycodetosym { $keyboard[$_][4] } } = $_ + $min;
- #$keyboardmap{ $keycodetosym { $keyboard[$_][5] } } = $_ + $min;
}
+ # dont know these two key combs yet...
+ #$keyboardmap{ $keycodetosym { $keyboard[$_][4] } } = $_ + $min;
+ #$keyboardmap{ $keycodetosym { $keyboard[$_][5] } } = $_ + $min;
+
#print "$_ => $keyboardmap{$_}\n" foreach(sort(keys(%keyboardmap)));
#print "keysymtocode: $keysymtocode{o}\n";
#die;
@@ -383,8 +399,9 @@ sub resolve_names(@) {
if ( defined($hostobj) ) {
my @alladdrs = map { inet_ntoa($_) } @{ $hostobj->addr_list };
if ( $#alladdrs > 0 ) {
- $self->cluster->register_tag($dirty, @alladdrs);
- logmsg( 3, 'Expanded to ', $self->cluster->get_tag($dirty) );
+ $self->cluster->register_tag( $dirty, @alladdrs );
+ logmsg( 3, 'Expanded to ',
+ $self->cluster->get_tag($dirty) );
}
else {
logmsg( 3, 'Only one A record' );
@@ -407,11 +424,22 @@ sub resolve_names(@) {
# now clean the array up
@servers = grep { $_ !~ m/^$/ } @servers;
+ if ($self->config->{unique_servers}) {
+ logmsg( 3, 'removing duplicate server names' );
+ @servers=remove_repeated_servers(@servers);
+ }
+
logmsg( 3, 'leaving with ', $_ ) foreach (@servers);
logmsg( 2, 'Resolving cluster names: completed' );
return (@servers);
}
+sub remove_repeated_servers {
+ my %all=();
+ @all{@_}=1;
+ return (keys %all);
+}
+
sub change_main_window_title() {
my ($self) = @_;
my $number = keys(%servers);
@@ -588,7 +616,7 @@ sub open_client_windows(@) {
my $server_object = App::ClusterSSH::Host->parse_host_string($_);
my $username = $server_object->get_username();
- $username = $self->config->{user} if ( $self->config->{user} );
+ $username = $self->config->{user} if ( !$username && $self->config->{user} );
my $port = $server_object->get_port();
$port = $self->config->{port} if ( $self->config->{port} );
my $server = $server_object->get_hostname();
@@ -751,7 +779,7 @@ sub get_font_size() {
eval { (%font_info) = $xdisplay->QueryFont($font); }
|| die( "Fatal: Unrecognised font used ($terminal_font).\n"
- . "Please amend \$HOME/.csshrc with a valid font (see man page).\n"
+ . "Please amend \$HOME/.clusterssh/config with a valid font (see man page).\n"
);
$self->config->{internal_font_width}
@@ -763,7 +791,7 @@ sub get_font_size() {
|| !$self->config->{internal_font_height} )
{
die( "Fatal: Unrecognised font used ($terminal_font).\n"
- . "Please amend \$HOME/.csshrc with a valid font (see man page).\n"
+ . "Please amend \$HOME/.clusterssh/config with a valid font (see man page).\n"
);
}
@@ -1134,7 +1162,7 @@ sub add_host_by_name() {
$self->open_client_windows(@names);
}
- if ( $menus{listbox}->curselection() ) {
+ if ( defined $menus{listbox} && $menus{listbox}->curselection() ) {
my @hosts = $menus{listbox}->get( $menus{listbox}->curselection() );
logmsg( 2, "host=", join( ' ', @hosts ) );
$self->open_client_windows( $self->resolve_names(@hosts) );
@@ -1265,7 +1293,8 @@ sub setup_repeat() {
sub create_windows() {
my ($self) = @_;
logmsg( 2, "create_windows: started" );
- $windows{main_window} = MainWindow->new( -title => "ClusterSSH" );
+ $windows{main_window}
+ = MainWindow->new( -title => "ClusterSSH", -class => 'cssh', );
$windows{main_window}->withdraw; # leave withdrawn until needed
if ( defined( $self->config->{console_position} )
@@ -1279,6 +1308,7 @@ sub create_windows() {
-textvariable => \$menus{entrytext},
-insertborderwidth => 4,
-width => 25,
+ -class => 'cssh',
)->pack(
-fill => "x",
-expand => 1,
@@ -1291,6 +1321,7 @@ sub create_windows() {
-height => $self->config->{history_height},
-state => 'normal',
-takefocus => 0,
+ -class => 'cssh',
);
$windows{history}->bindtags(undef);
@@ -1353,6 +1384,7 @@ sub create_windows() {
-popover => $windows{main_window},
-overanchor => "c",
-popanchor => "c",
+ -class => 'cssh',
-font => [
-family => "interface system",
-size => 10,
@@ -1367,6 +1399,7 @@ sub create_windows() {
-overanchor => "c",
-title => "Cssh Documentation",
-buttons => ['Close'],
+ -class => 'cssh',
);
my $manpage = `pod2text -l -q=\"\" $0 2>/dev/null`;
@@ -1385,17 +1418,19 @@ sub create_windows() {
-title => "Add Host(s) or Cluster(s)",
-buttons => [ 'Add', 'Cancel' ],
-default_button => 'Add',
+ -class => 'cssh',
);
if ( $self->config->{max_addhost_menu_cluster_items}
- && scalar $self->cluster->list_tags() )
+ && scalar $self->cluster->list_tags() )
{
- if (scalar
- scalar $self->cluster->list_tags() < $self->config->{max_addhost_menu_cluster_items} )
+ if (scalar scalar $self->cluster->list_tags()
+ < $self->config->{max_addhost_menu_cluster_items} )
{
$menus{listbox} = $windows{addhost}->Listbox(
-selectmode => 'extended',
-height => scalar $self->cluster->list_tags(),
+ -class => 'cssh',
)->pack();
}
else {
@@ -1404,6 +1439,7 @@ sub create_windows() {
-scrollbars => 'e',
-selectmode => 'extended',
-height => $self->config->{max_addhost_menu_cluster_items},
+ -class => 'cssh',
)->pack();
}
$menus{listbox}->insert( 'end', sort $self->cluster->list_tags() );
@@ -1415,6 +1451,7 @@ sub create_windows() {
-width => 20,
-label => 'Host',
-labelPack => [ -side => 'left', ],
+ -class => 'cssh',
)->pack( -side => 'left' );
logmsg( 2, "create_windows: completed" );
@@ -1543,7 +1580,7 @@ sub key_event {
logmsg( 3, "key=:$key:" );
if ( $combo =~ /^$key$/ ) {
- logmsg(3, "matched combo");
+ logmsg( 3, "matched combo" );
if ( $event eq "KeyRelease" ) {
logmsg( 2, "Received hotkey: $hotkey" );
send_text_to_all_servers('%s')
@@ -1600,8 +1637,8 @@ sub key_event {
sub create_menubar() {
my ($self) = @_;
logmsg( 2, "create_menubar: started" );
- $menus{bar} = $windows{main_window}->Menu;
- $windows{main_window}->configure( -menu => $menus{bar} );
+ $menus{bar} = $windows{main_window}->Menu();
+ $windows{main_window}->configure( -menu => $menus{bar}, );
$menus{file} = $menus{bar}->cascade(
-label => 'File',
@@ -1626,22 +1663,22 @@ sub create_menubar() {
-menuitems => [
[ "command",
"Retile Windows",
- -command => sub{ $self->retile_hosts },
+ -command => sub { $self->retile_hosts },
-accelerator => $self->config->{key_retilehosts},
],
# [ "command", "Capture Terminal", -command => \&capture_terminal, ],
[ "command",
"Toggle active state",
- -command => sub{ $self->toggle_active_state() },
+ -command => sub { $self->toggle_active_state() },
],
[ "command",
"Close inactive sessions",
- -command => sub{ $self->close_inactive_sessions() },
+ -command => sub { $self->close_inactive_sessions() },
],
[ "command",
"Add Host(s) or Cluster(s)",
- -command => sub{ $self->add_host_by_name, },
+ -command => sub { $self->add_host_by_name, },
-accelerator => $self->config->{key_addhost},
],
'',
@@ -1667,7 +1704,8 @@ sub create_menubar() {
);
$windows{main_window}->bind( '<KeyPress>' => [ $self => 'key_event' ], );
- $windows{main_window}->bind( '<KeyRelease>' => [ $self => 'key_event' ], );
+ $windows{main_window}
+ ->bind( '<KeyRelease>' => [ $self => 'key_event' ], );
logmsg( 2, "create_menubar: completed" );
}
@@ -1797,12 +1835,15 @@ sub run {
}
if ( $options{action} ) {
- $self->config->{command} = $options{action} ;
+ $self->config->{command} = $options{action};
}
+ $self->config->{unique_servers} = 1 if $options{'unique-servers'};
+
$self->config->{auto_quit} = "yes" if $options{autoquit};
$self->config->{auto_quit} = "no" if $options{'no-autoquit'};
- $self->config->{auto_close} = $options{autoclose} if $options{'autoclose'};
+ $self->config->{auto_close} = $options{autoclose}
+ if defined $options{'autoclose'};
$self->config->{window_tiling} = "yes" if $options{tile};
$self->config->{window_tiling} = "no" if $options{'no-tile'};
@@ -1815,7 +1856,7 @@ sub run {
$self->config->{terminal_font} = $options{font} if ( $options{font} );
$self->config->{terminal_args} = $options{'term-args'}
- if ( $options{'term-args'} );
+ if ( $options{'term-args'} );
if ( $self->config->{terminal_args} =~ /-class (\w+)/ ) {
$self->config->{terminal_allow_send_events}
= "-xrm '$1.VT100.allowSendEvents:true'";
@@ -1998,6 +2039,8 @@ the code until this time.
=item populate_send_menu_entries_from_xml
+=item remove_repeated_servers
+
=item resolve_names
=item retile_hosts
diff --git a/lib/App/ClusterSSH/Base.pm b/lib/App/ClusterSSH/Base.pm
index 95a92ed..e99cca4 100644
--- a/lib/App/ClusterSSH/Base.pm
+++ b/lib/App/ClusterSSH/Base.pm
@@ -157,7 +157,7 @@ sub set_config {
=head1 NAME
-App::ClusterSSH::Base
+App::ClusterSSH::Base - Base object provding utility functions
=head1 SYNOPSIS
diff --git a/lib/App/ClusterSSH/Cluster.pm b/lib/App/ClusterSSH/Cluster.pm
index aabbbe8..3724b31 100644
--- a/lib/App/ClusterSSH/Cluster.pm
+++ b/lib/App/ClusterSSH/Cluster.pm
@@ -26,7 +26,7 @@ sub new {
sub get_clusters {
my ( $self, @files ) = @_;
- for my $file ( '/etc/clusters', @files ) {
+ for my $file ( '/etc/clusters', $ENV{HOME}.'/.clusterssh/clusters',@files ) {
$self->debug(3, 'Loading in config from: ', $file);
$self->read_cluster_file($file);
}
@@ -118,7 +118,7 @@ sub list_tags {
=head1 NAME
-App::ClusterSSH::Cluster
+App::ClusterSSH::Cluster - Object representing cluster configuration
=head1 SYNOPSIS
diff --git a/lib/App/ClusterSSH/Config.pm b/lib/App/ClusterSSH/Config.pm
index dbe7c42..0c2fef0 100644
--- a/lib/App/ClusterSSH/Config.pm
+++ b/lib/App/ClusterSSH/Config.pm
@@ -4,20 +4,25 @@ use strict;
use warnings;
use version;
-our $VERSION = version->new('0.01');
+our $VERSION = version->new('0.02');
use Carp;
use Try::Tiny;
use FindBin qw($Script);
+use File::Copy;
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 = (
+my @app_specific = (qw/ command title comms method /);
+
+# list of config items to not write out when writing the default config
+my @ignore_default_config = (qw/ user /);
+
+my %default_config = (
terminal => "xterm",
terminal_args => "",
terminal_title_opt => "-T",
@@ -54,9 +59,14 @@ my %default_config = (
terminal_decoration_height => 10,
terminal_decoration_width => 8,
- rsh_args => "",
- telnet_args => "",
- ssh_args => "",
+ console => 'console',
+ console_args => '',
+ rsh => 'rsh',
+ rsh_args => "",
+ telnet => 'telnet',
+ telnet_args => "",
+ ssh => 'ssh',
+ ssh_args => "",
extra_cluster_file => "",
@@ -76,6 +86,9 @@ my %default_config = (
use_all_a_records => 0,
send_menu_xml_file => $ENV{HOME} . '/.csshrc_send_menu',
+
+ # don't set username here as takes precendence over ssh config
+ user => '',
);
sub new {
@@ -84,21 +97,19 @@ sub new {
my $self = $class->SUPER::new(%default_config);
( my $comms = $Script ) =~ s/^c//;
- $self->{comms} = $comms;
+
+ $comms = 'telnet' if ( $comms eq 'tel' );
+ $comms = 'console' if ( $comms eq 'con' );
+ $comms = 'ssh' if ( $comms eq 'lusterssh' );
# list of allowed comms methods
- if ( 'ssh rsh telnet console' !~ m/\B$comms\B/ ) {
+ if ( 'ssh rsh telnet console' !~ m/\b$comms\b/ ) {
$self->{comms} = 'ssh';
}
-
- if ( $self->{comms}
- && ( !$self->{ $self->{comms} } || !-e $self->{ $self->{comms} } ) )
- {
- $self->{ $self->{comms} } = $self->find_binary( $self->{comms} );
+ else {
+ $self->{comms} = $comms;
}
- $self->{terminal} = $self->find_binary( $self->{terminal} );
-
$self->{title} = uc($Script);
$clusters = App::ClusterSSH::Cluster->new();
@@ -131,13 +142,39 @@ sub validate_args {
App::ClusterSSH::Exception::Config->throw(
unknown_config => \@unknown_config,
error => $self->loc(
- 'Unknown configuration parameters: [_1]',
+ 'Unknown configuration parameters: [_1]' . $/,
join( ',', @unknown_config )
)
)
);
}
+ if ( !$self->{comms} ) {
+ croak(
+ App::ClusterSSH::Exception::Config->throw(
+ error => $self->loc( 'Invalid variable: comms' . $/ ),
+ ),
+ );
+ }
+
+ if ( !$self->{ $self->{comms} } ) {
+ croak(
+ App::ClusterSSH::Exception::Config->throw(
+ error => $self->loc(
+ 'Invalid variable: [_1]' . $/,
+ $self->{comms}
+ ),
+ ),
+ );
+ }
+
+ # # Don't search for the path to the binary - assume it is on the path
+ # # or defined correctly in the config.
+ # if( !-e $self->{ $self->{comms} } )
+ # {
+ # $self->{ $self->{comms} } = $self->find_binary( $self->{comms} );
+ # }
+
return $self;
}
@@ -150,7 +187,8 @@ sub parse_config_file {
croak(
App::ClusterSSH::Exception::Config->throw(
error => $self->loc(
- 'File [_1] does not exist or cannot be read', $config_file
+ 'File [_1] does not exist or cannot be read' . $/,
+ $config_file
),
),
);
@@ -182,12 +220,13 @@ sub parse_config_file {
}
close(CFG);
- # grab any c'lusters from the config before validating it
+ # grab any clusters 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} );
+ $clusters->register_tag( $cluster,
+ split( / /, $read_config{$cluster} ) );
$old_clusters{$cluster} = $read_config{$cluster};
delete( $read_config{$cluster} );
}
@@ -205,16 +244,6 @@ sub parse_config_file {
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',
@@ -248,6 +277,30 @@ sub load_configs {
sub write_user_config_file {
my ($self) = @_;
+ # attempt to move the old config file to one side
+ if ( -f "$ENV{HOME}/.csshrc" ) {
+ eval { move( "$ENV{HOME}/.csshrc", "$ENV{HOME}/.csshrc.DISABLED" ) };
+
+ if ($@) {
+ croak(
+ App::ClusterSSH::Exception::Config->throw(
+ error => $self->loc(
+ 'Unable to move [_1] to [_2]: [_3]' . $/,
+ '$HOME/.csshrc', '$HOME/.csshrc.DISABLED', $@
+ ),
+ )
+ );
+ }
+ else {
+ warn(
+ $self->loc(
+ 'Moved [_1] to [_2]' . $/, '$HOME/.csshrc',
+ '$HOME/.csshrc.DISABLED'
+ ),
+ );
+ }
+ }
+
return if ( -f "$ENV{HOME}/.clusterssh/config" );
if ( !-d "$ENV{HOME}/.clusterssh" ) {
@@ -255,7 +308,7 @@ sub write_user_config_file {
croak(
App::ClusterSSH::Exception::Config->throw(
error => $self->loc(
- 'Unable to create directory [_1]: [_2]',
+ 'Unable to create directory [_1]: [_2]' . $/,
'$HOME/.clusterssh', $!
),
),
@@ -264,34 +317,58 @@ sub write_user_config_file {
}
}
+ # Debian #673507 - migrate clusters prior to writing ~/.clusterssh/config
+ # in order to update the extra_cluster_file property
+ 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);
+ }
+ else {
+ croak(
+ App::ClusterSSH::Exception::Config->throw(
+ error => $self->loc(
+ 'Unable to write [_1]: [_2]' . $/,
+ '$HOME/.clusterssh/clusters',
+ $!
+ ),
+ ),
+ );
+ }
+ }
+
if ( open( CONFIG, ">", "$ENV{HOME}/.clusterssh/config" ) ) {
foreach ( sort( keys(%$self) ) ) {
- print CONFIG "$_=$self->{$_}\n";
+ my $comment='';
+ if ( grep /$_/, @ignore_default_config ) {
+ $comment='#';
+ }
+ print CONFIG ${comment},$_,'=',$self->{$_},$/;
}
close(CONFIG);
+ warn(
+ $self->loc(
+ 'Created new configuration file within [_1]' . $/,
+ '$HOME/.clusterssh/'
+ )
+ );
}
else {
croak(
App::ClusterSSH::Exception::Config->throw(
error => $self->loc(
- 'Unable to write default [_1]: [_2]',
- '$HOME/.clusterssh/config',
- $!
+ '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;
}
@@ -303,7 +380,7 @@ sub find_binary {
if ( !$binary ) {
croak(
App::ClusterSSH::Exception::Config->throw(
- error => $self->loc('argument not provided'),
+ error => $self->loc('argument not provided') . $/,
),
);
}
@@ -355,7 +432,8 @@ sub find_binary {
croak(
App::ClusterSSH::Exception::Config->throw(
error => $self->loc(
- '"[_1]" binary not found - please amend $PATH or the cssh config file',
+ '"[_1]" binary not found - please amend $PATH or the cssh config file'
+ . $/,
$binary
),
),
@@ -373,10 +451,14 @@ sub dump {
print( '# Configuration dump produced by "cssh -u"', $/ );
foreach my $key ( sort keys %$self ) {
+ my $comment='';
if ( grep /$key/, @app_specific ) {
next;
}
- print $key, '=', $self->{$key}, $/;
+ if ( grep /$key/, @ignore_default_config ) {
+ $comment='#';
+ }
+ print $comment, $key, '=', $self->{$key}, $/;
}
$self->exit if ( !$no_exit );
@@ -396,7 +478,7 @@ sub dump {
=head1 NAME
-ClusterSSH::Config
+ClusterSSH::Config - Object representing application configuration
=head1 SYNOPSIS
diff --git a/lib/App/ClusterSSH/Helper.pm b/lib/App/ClusterSSH/Helper.pm
index 07c5898..4289e77 100644
--- a/lib/App/ClusterSSH/Helper.pm
+++ b/lib/App/ClusterSSH/Helper.pm
@@ -4,7 +4,7 @@ use strict;
use warnings;
use version;
-our $VERSION = version->new('0.01');
+our $VERSION = version->new('0.02');
use Carp;
use Try::Tiny;
@@ -22,12 +22,12 @@ sub new {
sub script {
my ($self, $config ) = @_;
- my $comms = $config->{comms};
- my $comms_args = $config->{$comms.'_args'};
- my $command = $config->{command};
+ my $comms = $config->{ $config->{comms} };
+ my $comms_args = $config->{ $config->{comms} . '_args'};
+ my $config_command = $config->{command};
my $autoclose = $config->{auto_close};
- my $postcommand = $autoclose ? "echo Press RETURN to continue; read IGNORE" : "sleep $autoclose";
+ my $postcommand = $autoclose ? "echo Sleeping for $autoclose seconds; sleep $autoclose" : "echo Press RETURN to continue; read IGNORE"; # : "sleep $autoclose";
# # P = pipe file
# # s = server
@@ -119,7 +119,10 @@ sub script {
\$command .= "\$svr";
}
}
- \$command .= " \\\"$command\\\" ; $postcommand";
+ if("$config_command") {
+ \$command .= " \\\"$config_command\\\"";
+ }
+ \$command .= " ; $postcommand";
warn("Running:\$command\\n"); # for debug purposes
exec(\$command);
HERE
@@ -145,7 +148,7 @@ sub script {
=head1 NAME
-ClusterSSH::Helper
+ClusterSSH::Helper - Object representing helper script
=head1 SYNOPSIS
diff --git a/lib/App/ClusterSSH/Host.pm b/lib/App/ClusterSSH/Host.pm
index c9c00be..7d352e5 100644
--- a/lib/App/ClusterSSH/Host.pm
+++ b/lib/App/ClusterSSH/Host.pm
@@ -301,7 +301,7 @@ use overload (
=head1 NAME
-ClusterSSH::Host
+ClusterSSH::Host - Object representing a host.
=head1 SYNOPSIS
diff --git a/t/15config.t b/t/15config.t
index 81d8f70..cce91a6 100644
--- a/t/15config.t
+++ b/t/15config.t
@@ -8,6 +8,7 @@ use Test::More;
use Test::Trap;
use File::Which qw(which);
use File::Temp qw(tempdir);
+use Test::Differences;
use Readonly;
@@ -21,7 +22,7 @@ $config = App::ClusterSSH::Config->new();
isa_ok( $config, 'App::ClusterSSH::Config' );
Readonly::Hash my %default_config => {
- terminal => "/usr/bin/xterm",
+ terminal => "xterm",
terminal_args => "",
terminal_title_opt => "-T",
terminal_colorize => 1,
@@ -59,9 +60,14 @@ Readonly::Hash my %default_config => {
ssh => '/usr/bin/ssh',
- rsh_args => "",
- telnet_args => "",
- ssh_args => "",
+ console => 'console',
+ console_args => '',
+ rsh => 'rsh',
+ rsh_args => "",
+ telnet => 'telnet',
+ telnet_args => "",
+ ssh => 'ssh',
+ ssh_args => "",
extra_cluster_file => "",
@@ -72,8 +78,8 @@ Readonly::Hash my %default_config => {
history_height => 10,
command => q{},
- title => q{15CONFIG.T},
- comms => q{ssh},
+ title => q{15CONFIG.T},
+ comms => q{ssh},
max_host_menu_items => 30,
max_addhost_menu_cluster_items => 6,
@@ -88,6 +94,7 @@ Readonly::Hash my %default_config => {
debug => 0,
lang => 'en',
+ user => '',
};
my %expected = %default_config;
is_deeply( $config, \%expected, 'default config is correct' );
@@ -101,7 +108,7 @@ trap {
};
isa_ok( $trap->die, 'App::ClusterSSH::Exception::Config' );
is( $trap->die,
- 'Unknown configuration parameters: doesnt_exist,whoops',
+ 'Unknown configuration parameters: doesnt_exist,whoops' . $/,
'got correct error message'
);
is_deeply(
@@ -134,7 +141,7 @@ trap {
};
isa_ok( $trap->die, 'App::ClusterSSH::Exception::Config' );
is( $trap->die,
- "File $file does not exist or cannot be read",
+ "File $file does not exist or cannot be read" . $/,
'got correct error message'
);
@@ -166,7 +173,7 @@ trap {
is( $trap->leaveby, 'die', 'died ok' );
isa_ok( $trap->die, 'App::ClusterSSH::Exception::Config' );
is( $trap->die,
- 'Unknown configuration parameters: missing,rubbish',
+ 'Unknown configuration parameters: missing,rubbish' . $/,
'die message correct'
);
isa_ok( $config, "App::ClusterSSH::Config" );
@@ -197,7 +204,7 @@ trap {
is( $trap->leaveby, 'die', 'died ok' );
isa_ok( $trap->die, 'App::ClusterSSH::Exception::Config' );
isa_ok( $config, "App::ClusterSSH::Config" );
-is( $trap->die, 'argument not provided', 'die message correct' );
+is( $trap->die, 'argument not provided' . $/, 'die message correct' );
isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->stdout, q{}, 'Expecting no STDOUT' );
is( $trap->stderr, q{}, 'Expecting no STDERR' );
@@ -210,7 +217,8 @@ is( $trap->leaveby, 'die', 'died ok' );
isa_ok( $trap->die, 'App::ClusterSSH::Exception::Config' );
isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->die,
- '"missing" binary not found - please amend $PATH or the cssh config file',
+ '"missing" binary not found - please amend $PATH or the cssh config file'
+ . $/,
'die message correct'
);
isa_ok( $config, "App::ClusterSSH::Config" );
@@ -241,11 +249,11 @@ is( $trap->stdout, q{}, 'Expecting no STDOUT' );
is( $trap->stderr, q{}, 'Expecting no STDERR' );
is_deeply( $config, \%expected, 'amended config is correct' );
is( $path, which('ls'), 'Found correct path to "ls"' );
-is( $path, $newpath, 'No change made from find_binary');
+is( $path, $newpath, 'No change made from find_binary' );
# give false path to force another search
trap {
- $newpath = $config->find_binary('/does/not/exist/'.$path);
+ $newpath = $config->find_binary( '/does/not/exist/' . $path );
};
is( $trap->leaveby, 'return', 'returned ok' );
isa_ok( $config, "App::ClusterSSH::Config" );
@@ -254,7 +262,7 @@ is( $trap->stdout, q{}, 'Expecting no STDOUT' );
is( $trap->stderr, q{}, 'Expecting no STDERR' );
is_deeply( $config, \%expected, 'amended config is correct' );
is( $path, which('ls'), 'Found correct path to "ls"' );
-is( $path, $newpath, 'No change made from find_binary');
+is( $path, $newpath, 'No change made from find_binary' );
note('Checks on loading configs');
note('empty dir');
@@ -268,7 +276,10 @@ isa_ok( $config, "App::ClusterSSH::Config" );
isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->die, undef, 'die message correct' );
is( $trap->stdout, q{}, 'Expecting no STDOUT' );
-is( $trap->stderr, q{}, 'Expecting no STDERR' );
+is( $trap->stderr,
+ 'Created new configuration file within $HOME/.clusterssh/' . $/,
+ 'Got correct STDERR output for .csshrc'
+);
#note(qx/ls -laR $ENV{HOME}/);
ok( -d $ENV{HOME} . '/.clusterssh', '.clusterssh dir exists' );
@@ -292,9 +303,9 @@ isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->die, undef, 'die message correct' );
is( $trap->stdout, q{}, 'Expecting no STDOUT' );
is( $trap->stderr,
- 'NOTICE: '
- . $ENV{HOME}
- . '/.csshrc is no longer used - please see documentation and remove'
+ 'Moved $HOME/.csshrc to $HOME/.csshrc.DISABLED'
+ . $/
+ . 'Created new configuration file within $HOME/.clusterssh/'
. $/,
'Got correct STDERR output for .csshrc'
);
@@ -303,6 +314,12 @@ ok( -f $ENV{HOME} . '/.clusterssh/config', '.clusterssh config file exists' );
is_deeply( $config, \%expected, 'amended config is correct' );
note('.csshrc warning and .clusterssh dir plus config');
+
+# need to recreate .csshrc as it was just moved
+open( $csshrc, '>', $ENV{HOME} . '/.csshrc' );
+print $csshrc 'auto_quit = no', $/;
+close($csshrc);
+$expected{auto_quit} = 'no';
open( $csshrc, '>', $ENV{HOME} . '/.clusterssh/config' );
print $csshrc 'window_tiling = no', $/;
close($csshrc);
@@ -317,10 +334,7 @@ isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->die, undef, 'die message correct' );
is( $trap->stdout, q{}, 'Expecting no STDOUT' );
is( $trap->stderr,
- 'NOTICE: '
- . $ENV{HOME}
- . '/.csshrc is no longer used - please see documentation and remove'
- . $/,
+ 'Moved $HOME/.csshrc to $HOME/.csshrc.DISABLED' . $/,
'Got correct STDERR output for .csshrc'
);
ok( -d $ENV{HOME} . '/.clusterssh', '.clusterssh dir exists' );
@@ -395,7 +409,7 @@ is( $trap->leaveby, 'die', 'died ok' );
isa_ok( $trap->die, 'App::ClusterSSH::Exception::Config' );
isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->die,
- 'Unable to create directory $HOME/.clusterssh: File exists',
+ 'Unable to create directory $HOME/.clusterssh: File exists' . $/,
'die message correct'
);
isa_ok( $config, "App::ClusterSSH::Config" );
@@ -414,7 +428,7 @@ is( $trap->leaveby, 'die', 'died ok' );
isa_ok( $trap->die, 'App::ClusterSSH::Exception::Config' );
isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->die,
- 'Unable to write default $HOME/.clusterssh/config: Is a directory',
+ 'Unable to write default $HOME/.clusterssh/config: Is a directory' . $/,
'die message correct'
);
isa_ok( $config, "App::ClusterSSH::Config" );
@@ -434,7 +448,7 @@ is( $trap->leaveby, 'return', 'died ok' );
isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->stdout, q{}, 'Expecting no STDOUT' );
is( $trap->stderr,
- q{Unable to create directory $HOME/.clusterssh: File exists} . $/,
+ q{Unable to create directory $HOME/.clusterssh: File exists} . $/ . $/,
'Expecting no STDERR'
);
@@ -451,19 +465,23 @@ isa_ok( $config, "App::ClusterSSH::Config" );
isa_ok( $config, "App::ClusterSSH::Config" );
is( $trap->stdout, q{}, 'Expecting no STDOUT' );
is( $trap->stderr,
- q{Unable to write default $HOME/.clusterssh/config: Is a directory} . $/,
+ q{Unable to write default $HOME/.clusterssh/config: Is a directory}
+ . $/
+ . $/,
'Expecting no STDERR'
);
note('Checking dump');
-$config = App::ClusterSSH::Config->new();
+$config = App::ClusterSSH::Config->new(
+ send_menu_xml_file => $ENV{HOME} . '/.csshrc_send_menu' );
trap {
$config->dump();
};
-my $expected = <<'EOF';
-# Configuration dump produced by "cssh -u"
+my $expected = qq{# Configuration dump produced by "cssh -u"
auto_close=5
auto_quit=yes
+console=console
+console_args=
console_position=
debug=0
extra_cluster_file=
@@ -481,16 +499,19 @@ max_host_menu_items=30
menu_host_autotearoff=0
menu_send_autotearoff=0
mouse_paste=Button-2
+rsh=rsh
rsh_args=
screen_reserve_bottom=60
screen_reserve_left=0
screen_reserve_right=0
screen_reserve_top=0
-send_menu_xml_file=/home/dferguson/.csshrc_send_menu
+send_menu_xml_file=} . $ENV{HOME} . qq{/.csshrc_send_menu
show_history=0
+ssh=ssh
ssh_args=
+telnet=telnet
telnet_args=
-terminal=/usr/bin/xterm
+terminal=xterm
terminal_allow_send_events=-xrm '*.VT100.allowSendEvents:true'
terminal_args=
terminal_bg_style=dark
@@ -507,12 +528,14 @@ terminal_title_opt=-T
unmap_on_redraw=no
use_all_a_records=0
use_hotkeys=yes
+#user=
window_tiling=yes
window_tiling_direction=right
-EOF
+};
+
isa_ok( $config, "App::ClusterSSH::Config" );
-is( $trap->die, undef, 'die message correct' );
-is( $trap->stdout, $expected, 'Expecting no STDOUT' );
-is( $trap->stderr, q{}, 'Expecting no STDERR' );
+is( $trap->die, undef, 'die message correct' );
+eq_or_diff( $trap->stdout, $expected, 'Expecting no STDOUT' );
+is( $trap->stderr, q{}, 'Expecting no STDERR' );
done_testing();
diff --git a/t/30cluster.t b/t/30cluster.t
index 487bbfc..1beadec 100644
--- a/t/30cluster.t
+++ b/t/30cluster.t
@@ -8,6 +8,7 @@ use Test::More;
use Test::Trap;
use File::Which qw(which);
use File::Temp qw(tempdir);
+use English '-no_match_vars';
use Readonly;
@@ -27,24 +28,33 @@ $cluster1->register_tag( 'people', @expected );
my @got = $cluster2->get_tag('people');
-is_deeply( \@got, \@expected,
- 'Shared cluster object' );
+is_deeply( \@got, \@expected, 'Shared cluster object' );
# should pass without issue
trap {
$cluster1->read_cluster_file( $Bin . '/30cluster.doesnt exist' );
};
-is( ! $trap, '', 'coped with missing file ok' );
+is( !$trap, '', 'coped with missing file ok' );
isa_ok( $cluster1, 'App::ClusterSSH::Cluster' );
-my $no_read=$Bin . '/30cluster.cannot_read';
-chmod 0000, $no_read;
-trap {
- $cluster1->read_cluster_file( $no_read );
-};
-chmod 0644, $no_read;
-isa_ok( $trap->die, 'App::ClusterSSH::Exception::Cluster' );
-is( $trap->die, "Unable to read file $no_read: Permission denied", 'Error on reading an existing file ok');
+# no point running this test as root since root cannot be blocked
+# from accessing the file
+if ( $EUID != 0 ) {
+ my $no_read = $Bin . '/30cluster.cannot_read';
+ chmod 0000, $no_read;
+ trap {
+ $cluster1->read_cluster_file($no_read);
+ };
+ chmod 0644, $no_read;
+ isa_ok( $trap->die, 'App::ClusterSSH::Exception::Cluster' );
+ is( $trap->die,
+ "Unable to read file $no_read: Permission denied",
+ 'Error on reading an existing file ok'
+ );
+}
+else {
+ pass('Cannot test for lack of read access when run as root');
+}
@expected = ('host1');
$cluster1->read_cluster_file( $Bin . '/30cluster.file1' );
@@ -53,18 +63,15 @@ is_deeply( \@got, \@expected, 'read simple file OK' );
@expected = ('host1');
$cluster1->read_cluster_file( $Bin . '/30cluster.file2' );
-@got=$cluster1->get_tag('tag1');
-is_deeply( \@got,
- \@expected, 'read more complex file OK' );
+@got = $cluster1->get_tag('tag1');
+is_deeply( \@got, \@expected, 'read more complex file OK' );
@expected = ('host2');
-@got=$cluster1->get_tag('tag2');
-is_deeply( \@got,
- \@expected, 'read more complex file OK' );
+@got = $cluster1->get_tag('tag2');
+is_deeply( \@got, \@expected, 'read more complex file OK' );
@expected = ( 'host3', 'host4' );
-@got=$cluster1->get_tag('tag3');
-is_deeply( \@got,
- \@expected, 'read more complex file OK' );
+@got = $cluster1->get_tag('tag3');
+is_deeply( \@got, \@expected, 'read more complex file OK' );
done_testing();
|