185 lines
5.8 KiB
Plaintext
185 lines
5.8 KiB
Plaintext
|
#!/bin/bash
|
||
|
#
|
||
|
# sshsudo: 1.0
|
||
|
# Author: jiwu.liu@gmail.com
|
||
|
#
|
||
|
# The script executes sudo command on one or more remote computers.
|
||
|
# Its arguments are the names to remote sudoers and remote computers.
|
||
|
# The script read the password from the standard input, which should
|
||
|
# be the same for all sudoer accounts. No password is explicitly shown
|
||
|
# in the terminal or logged to files.
|
||
|
# If the command is a local shell script, use '-r' to copy it to
|
||
|
# the remote computers to execute. A temporary script file will be
|
||
|
# automatically created for this purpose.
|
||
|
#
|
||
|
#
|
||
|
# TO DO: It can not execute a command that use the shell special charaters
|
||
|
# on the remote computer, such as & ; ! *, because all arguments
|
||
|
# are wrapped with ''. That means, all special characters have only
|
||
|
# literal meanings. In the case when they are needed, use a script
|
||
|
# instead of a command.
|
||
|
#
|
||
|
# Example:sshsudo foo@bar.net apt-get check
|
||
|
# sshsudo -r foo@bar.net myscript.sh
|
||
|
|
||
|
|
||
|
usage()
|
||
|
{
|
||
|
local ProgName=$1;
|
||
|
echo "Usage: $ProgName -r -v [-u user] AccountList Command [Arguments]"
|
||
|
echo " -u Set the default user unless it is given within remote account list"
|
||
|
echo " -r Copy the command(usually the path to local script) to remote computers before execution"
|
||
|
echo " -v Verbose output"
|
||
|
echo " AccountList: [user1@]computer1,[user2@]computer2,[user3@]computer3,..."
|
||
|
echo " or the name to file which contains accounts(user@computer) in separate lines"
|
||
|
echo " Command: The Command/Script to be executed"
|
||
|
echo " Arguments: All arguments to be passed to command."
|
||
|
}
|
||
|
|
||
|
pipewrap()
|
||
|
{
|
||
|
# This function reads in the input from a terminal and output the same.
|
||
|
# The only purpose is to insert PASSWORD for "sudo -S" command.
|
||
|
# After initially insert the password, it simply copy input from terminal
|
||
|
# and send it to ssh command directly
|
||
|
echo $1 # which is password
|
||
|
local lockFile=$2;
|
||
|
while true; do
|
||
|
# The function will exit when output pipe is closed,
|
||
|
if [ ! -e $lockFile ]; then
|
||
|
return 0
|
||
|
fi
|
||
|
# i.e., the ssh
|
||
|
read -t 1 line
|
||
|
if [ $? -eq 0 ]; then
|
||
|
# successfully read
|
||
|
echo $line
|
||
|
fi
|
||
|
done
|
||
|
}
|
||
|
|
||
|
runsudo ()
|
||
|
{
|
||
|
# receive its arguments
|
||
|
local ACCOUNT=$1;
|
||
|
local PASSWORD=$2;
|
||
|
local COMMAND=$3;
|
||
|
local ARGUMENTS=$4;
|
||
|
local DEFAULTUSER=$5;
|
||
|
local COPY=$6;
|
||
|
local VERBOSE=$7;
|
||
|
|
||
|
# Parse account which has the form user@computer.domain
|
||
|
local REMOTEUSER=${ACCOUNT%%@*}
|
||
|
local REMOTECOMPUTER=${ACCOUNT#*@}
|
||
|
local REMOTESCRIPT=
|
||
|
local REMOTECOMMAND=$COMMAND
|
||
|
if [ $REMOTEUSER = $REMOTECOMPUTER ]; then
|
||
|
# There is no @, i.e., only computer name is given
|
||
|
REMOTEUSER=$DEFAULTUSER
|
||
|
fi
|
||
|
echo =========================${REMOTEUSER}@$REMOTECOMPUTER: sudo $COMMAND $ARGUMENTS
|
||
|
if [ $COPY -ne 0 ]; then
|
||
|
# Make a script + seconds since 1970-01-01
|
||
|
REMOTESCRIPT=/tmp/$COMMAND`date +%s`
|
||
|
if [ $VERBOSE -ne 0 ]; then
|
||
|
echo $REMOTECOMPUTER: Secure copy \"$COMMAND\" to \"$REMOTESCRIPT\"
|
||
|
fi
|
||
|
# Copy to remote computer. Quote target name on the remote computer for script name
|
||
|
# that contains " "
|
||
|
sshpass -p "$PASSWORD" scp "$COMMAND" "$REMOTEUSER@$REMOTECOMPUTER:'$REMOTESCRIPT'"
|
||
|
REMOTECOMMAND=$REMOTESCRIPT
|
||
|
fi
|
||
|
|
||
|
# Assume we are lucky, result is fine.
|
||
|
local RES=DONE
|
||
|
# Invalidate the sudo timestamp. Simplify the situation. Henceforth sudo always ask for a password
|
||
|
sshpass -p "$PASSWORD" ssh "$REMOTEUSER@$REMOTECOMPUTER" "sudo -K"
|
||
|
|
||
|
# Use lock file to inform pipewrap function to exit.
|
||
|
local lockFile=`mktemp`
|
||
|
eval pipewrap '$PASSWORD' '$lockFile' | (sshpass -p "$PASSWORD" ssh "$REMOTEUSER@$REMOTECOMPUTER" "sudo -S '$REMOTECOMMAND' $ARGUMENTS"; rm "$lockFile")
|
||
|
|
||
|
if [ $? -ne 0 ]; then
|
||
|
RES=FAILED
|
||
|
fi
|
||
|
if [ $COPY -ne 0 ]; then
|
||
|
sshpass -p $PASSWORD ssh "$REMOTEUSER@$REMOTECOMPUTER" rm \'$REMOTESCRIPT\'
|
||
|
if [ $VERBOSE -ne 0 ]; then
|
||
|
echo Remove temporary script \"$REMOTESCRIPT\" at [$REMOTECOMPUTER]
|
||
|
fi
|
||
|
fi
|
||
|
echo ----------------------------------${RES}!!----------------------------------------
|
||
|
}
|
||
|
|
||
|
|
||
|
#=====================================Main Script========================================
|
||
|
#
|
||
|
# Set default values for variables
|
||
|
COMMAND=
|
||
|
ARGUMENTS=
|
||
|
COPY=0
|
||
|
VERBOSE=0
|
||
|
ACCOUNTLIST=
|
||
|
DEFAULTUSER=$USER
|
||
|
|
||
|
# Parse the command line arguments with '-'
|
||
|
while getopts u:hrv o ; do
|
||
|
case "$o" in
|
||
|
[?]) usage $0
|
||
|
exit 1;;
|
||
|
h) usage $0
|
||
|
exit 0;;
|
||
|
u) DEFAULTUSER=$OPTARG;;
|
||
|
v) VERBOSE=1;;
|
||
|
r) COPY=1;;
|
||
|
esac
|
||
|
done
|
||
|
|
||
|
# Read the rest command line arguments
|
||
|
if [ $(($#-$OPTIND+1)) -lt 2 ]; then
|
||
|
echo Error: Account list and command have to be in the arguments
|
||
|
usage $0
|
||
|
exit 4
|
||
|
fi
|
||
|
|
||
|
shift $(($OPTIND-1))
|
||
|
ACCOUNTLIST=$1
|
||
|
shift
|
||
|
COMMAND=$1
|
||
|
shift
|
||
|
for PARAM; do
|
||
|
ARGUMENTS=$ARGUMENTS\'$PARAM\'" "
|
||
|
done
|
||
|
|
||
|
# Check the validity of the script command if remote copy is needed
|
||
|
if [ $COPY -ne 0 ]; then
|
||
|
if [ ! -e "$COMMAND" ]; then
|
||
|
echo \"$COMMAND\" does not exist.
|
||
|
exit 3
|
||
|
fi
|
||
|
if [ ! -x "$COMMAND" ]; then
|
||
|
echo \"$COMMAND\" can not be executed.
|
||
|
exit 3
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
# Read in the password from the STDIN
|
||
|
read -s -p "Please enter your password:" PASSWORD
|
||
|
echo
|
||
|
|
||
|
# Start running
|
||
|
if [ -e $ACCOUNTLIST ]; then # read accounts from a file
|
||
|
for ACCOUNT in `cat $ACCOUNTLIST`; do
|
||
|
runsudo "$ACCOUNT" "$PASSWORD" "$COMMAND" "$ARGUMENTS" "$DEFAULTUSER" "$COPY" "$VERBOSE";
|
||
|
done
|
||
|
|
||
|
else # ACCOUNTLIST is a comma separated list of user@computer strings.
|
||
|
# Change the internal separation field.
|
||
|
IFS=,
|
||
|
for ACCOUNT in $ACCOUNTLIST; do
|
||
|
runsudo "$ACCOUNT" "$PASSWORD" "$COMMAND" "$ARGUMENTS" "$DEFAULTUSER" "$COPY" "$VERBOSE";
|
||
|
done
|
||
|
fi
|
||
|
#EOF
|