#!/usr/bin/perl use strict; use warnings; use File::Basename; # various exit codes -- is there some standard # library of exit code constants? my $success = 0; my $badArgs = 1; my $badScriptFile = 2; my $badFailedDeletion = 3; my $badFailedScp = 4; my $badRemoteScriptExecute = 5; # Take a local script, copy it to a remote machine, # set minimal permissions there to make it executable, # run it on the remote machine (via ssh), then delete it. if( @ARGV < 2 ) { &usage(); exit $badArgs; } my ($remote_host_name, $script_filename) = (shift,shift); unless( -x $script_filename ) { print STDERR "$script_filename does not exist or is not executable; quitting\n"; exit $badScriptFile; } if( my $remote_tmpfile_name = &scpFileToRemoteMachine( $script_filename, $remote_host_name ) ) { if( my $remote_cmd_output = &executeRemoteCommand( $remote_host_name, $remote_tmpfile_name ) ) { if( !&delete_remote_file( $remote_host_name, $remote_tmpfile_name ) ) { exit $badFailedDeletion; } print STDOUT $remote_cmd_output; } else { exit $badRemoteScriptExecute; } } else { exit $badFailedScp; } exit $success; sub usage{ my $progName = $0; $progName = (&fileparse( $progName ) )[0]; print STDERR "Usage: $progName [remote machine name] [local script file]\n", "copies [local script file] to [remote machine name] and runs it there\n"; } # safely copy a file to a remote machine, making sure that we # don't overwrite any remote files in the process. Uses # mktemp(1), which is probably the most portable command we're # going to get our hands on. # UPDATE (27 Oct 2005): use scp -p to preserve permissions. # If perms are greater than rwx for the owner, print and error # and bail. sub scpFileToRemoteMachine { my ($script_filename, $remote_hostname) = (shift, shift); my $remote_tmpfile = &createLocalOrRemoteTmpfile($remote_hostname); my $retval = system( "scp -p $script_filename $remote_hostname:$remote_tmpfile >/dev/null 2>&1 " ); if( $retval != 0) { print STDERR "Something bad happened when scp'ing $script_filename to $remote_hostname\n"; return 0; } else { return $remote_tmpfile; } } sub executeRemoteCommand { my ($hostname, $remote_filename) = (shift, shift); my $cmdout = `ssh $hostname $remote_filename"`; if( $? != 0 ) { print STDERR "Something bad happened executing $remote_filename on $hostname\n"; return 0; } return $cmdout; } sub createLocalOrRemoteTmpfile { my $hostname = ''; my $cmdline = 'mktemp "/tmp/tmp.XXXXXX"'; my $retval = 0; my $tmp_file_name = ''; if( @_ ) { $hostname = $_[0]; # if they want a remote tmpfile, prepend the ssh command # that connects to that machine and creates the tmpfile $cmdline = "ssh $hostname " . $cmdline; } print STDERR "Connecting to $hostname ...\n"; $tmp_file_name = `$cmdline`; chomp $tmp_file_name; if( $? != 0) { print STDERR "Something nasty happened when trying to run $cmdline\n"; return 0; # this won't be a problem as long as the tmpfile isn't named '0'. } return $tmp_file_name; } sub delete_remote_file { my ($hostname, $remote_filename) = (shift, shift); system( "ssh $hostname rm $remote_filename" ); if( $? != 0) { print STDERR "Something bad happened trying to delete $remote_filename on $hostname\n"; return 0; } else { return 1; } }