-
Notifications
You must be signed in to change notification settings - Fork 0
/
farm
executable file
·201 lines (173 loc) · 7.19 KB
/
farm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/bin/bash
#FARMUX: Launch a single command on a remote machine, inside a fresh tmux session. Optionally copy the command binary to the host (if a full path is specified).
if [ -r /etc/farmux/farmux.conf ]; then
source /etc/farmux/farmux.conf
fi
if [ -r ~/.config/farmux/farmux.conf ]; then
echo "sourcing config file"
source ~/.config/farmux/farmux.conf
elif [ ! -e ~/.config/farmux/farmux.conf ] ; then
echo "creating default config file"
mkdir -p ~/.config/farmux;
printf "\n#This is the farmux default configuration file.\n#Uncomment lines below to alter their default values.\n\n#FARMUX_DEFAULT_ARGUMENT=0\n#FARMUX_DEFAULT_HOST=localhost\n#FARMUX_DEFAULT_USER=\"\"#Leave blank to use current user\n#FARMUX_PREPEND_COMMAND=\"\" #This string will be prepended to each command run on the remote machine.\n#FARMUX_SUFFIX_COMMAND=\"\" #This string be appended to each command run on the remote machine.\n#FARMUX_USE_SCP=1 #Set to 0 to copy files to the remote using tar+ssh, instead of scp.\n" > ~/.config/farmux/farmux.conf
fi
ssh_user=${FARMUX_DEFAULT_USER:-"$USER"}
ssh_host=${FARMUX_DEFAULT_HOST:-"localhost"}
ssh_args=${FARMUX_DEFAULT_SSH_ARGS:-""}
scp_args=${FARMUX_DEFAULT_SCP_ARGS:-""}
local_tmp=${FARMUX_LOCAL_TMP:-"/tmp"}
host_tmp=${FARMUX_HOST_TMP:-"/tmp"}
verbose=0
quiet=0
dry_run=0
arg_pos=${FARMUX_DEFAULT_ARGUMENT:-"0"} #by default, do not copy any argument to the host.
prepend_command=${FARMUX_PREPEND_COMMAND:-""}
append_command=${FARMUX_APPEND_COMMAND:-""}
use_scp=${FARMUX_USE_SCP:-"1"}
options=':qvtp:c:h'
safe_copy=${FARMUX_SAFE_COPY:-"0"}
if [ "$1" -eq "$1" ] 2> /dev/null
then
arg_pos=$1
shift
fi
# Usage statement
print_usage()
{
cat <<EOF
Usage: $0 [options] command [arguments for 'command'...]
Where 'command' is a binary to be executed on the remote host, with arguments.
If 'command' is specified with a full path, then it will be copied to the host before executing it remotely (by default).
Options:
-v Enable verbose logging.
-d Do a dry run (nothing will be copied to or executed on the remote host).
-h Print this help message and exit.
-p Argument number to copy (defaults to p = 1, copying the command; use -t 0 to prevent copying.)
EOF
}
while getopts $options option
do
case $option in
h|help )
print_usage
exit 0
;;
q ) verbose=0; quiet=1;;
v ) verbose=1;;
c ) exec_command=$OPTARG; copy_command=$OPTARG;;
d ) dry_run=1;; # do dry run
p ) arg_pos=$OPTARG;;
esac
done
shift $(($OPTIND - 1))
if [ "$1" -eq "$1" ] 2> /dev/null
then
arg_pos=$1
shift
fi
if ( [ "$ssh_host" == "local_host" ] || [ "$ssh_host" == "127.0.0.1" ] ) && [ "$quiet" -eq "0" ]; then
echo "Warning: running on local host ($ssh_host)"
fi
exec_command="ssh $ssh_args -t -t $ssh_user@$ssh_host" #'-t -t' forces an interactive terminal to be created
copy_command="ssh $ssh_args $ssh_user@$ssh_host"
full_scp_args="$scp_args $ssh_user@$ssh_host"
full_scp_args=$(echo "${full_scp_args}" | xargs) #trim whitespace off the scp args -- this is improtant!
#allow -1 to wrap around to last argument
arg_pos=$((arg_pos%$#))
if [ "$arg_pos" -lt "0" ]; then
arg_pos=$(($#+$arg_pos))
fi
if [ "$verbose" -gt "0" ]; then
echo "FARMUX_EXEC_COMMAND: $exec_command"
echo "FARMUX_COPY_COMMAND: $copy_command"
echo "FARMUX_SCP_COMMAND: scp $full_scp_args"
echo "FARMUX_PREPEND_COMMAND: $prepend_command"
echo "FARMUX_LOCAL_TMP: $local_tmp"
echo "FARMUX_HOST_TMP: $host_tmp"
echo "Argument position to copy to remote server if executable: ${arg_pos} (0 disables copying)"
fi
cleanup=""
if [ "$arg_pos" -gt "0" ]; then
command=${@:arg_pos:1}
if [[ -x $command ]]; then
#copy the file to a temporary file on the host
cmd_name=$(basename $command)
if [ "$safe_copy" -gt "0" ]; then
command2="$(${exec_command} mktemp ${host_tmp}/${cmd_name}.XXXXXXXX)" #create a temporary file on the remote server
command2=$(basename ${command2})
else
#create a temporary file on local server, which we will _hope_ is also a valid temporary file on the remote server.
#this is not safe, but it spares us a potentially slow ssh call
command2="$(mktemp ${local_tmp}/${cmd_name}.XXXXXXXX)" #command2="${cmd_name}.${$}"
rm $command2 #remove this local tmp file, since we don't actually need it
command2=$(basename ${command2})
fi
if [ "$verbose" -gt "0" ]; then
echo "Copying ${command} to ${host}:${host_tmp}/${command2}"
fi
if [ "$use_scp" -eq "0" ]; then
#should probably use a remote call to mktemp...
#one challenge is partly that for performance reasons
#we want to avoid making multiple ssh calls (required to call mktemp on the host).
#A second challenge is that with the way that tar is being used below, we need the temp file to
#have the same name on the host and destination (this is likely solveable)
#could also use scp here, but by using tar+ssh, we get to let the user set just one environment variable (FARM_EXEC_COMMAND).
#ideally, I'd like to fold this argument into the main ssh below, but it is not obvious how to do that while preserving the interactive session.
if [ "$verbose" -gt "0" ]; then
echo "tar cpf - $command2 | $copy_command \"tar xpf - -C ${host_tmp}\""
fi
if [ "$dry_run" -eq "0" ]; then
cp $command ${local_tmp}/${command2}
dr=${PWD}
cd ${local_tmp}
tar cpf - $command2 | $copy_command "tar xpf - -C ${host_tmp}"
cd $dr
rm ${local_tmp}/$command2
else
echo "(Not actually copying because '-t' was set)"
fi
else
if [ "$verbose" -gt "0" ]; then
echo "scp $command $full_scp_args:${host_tmp}/${command2}"
fi
#use scp to copy the file to the remote.
if [ "$dry_run" -eq "0" ]; then
scp $command $full_scp_args:${host_tmp}/${command2}
else
echo "(Not actually copying because '-t' was set)"
fi
fi
command2="${host_tmp}/${command2}"
cleanup="rm -f ${command2}"
command=$command2
else
if [ "$verbose" -gt "0" ]; then
echo "(Not copying system command or non-binary file '$command' to remote)"
fi
fi
full_command="${prepend_command} ${@:1:arg_pos-1} $command ${@:$arg_pos+1}"
else
command=""
full_command="${prepend_command} $@"
fi
if [ "$verbose" -gt "0" ]; then
echo "Executing command: '${full_command}'"
fi
if [ "$dry_run" -eq "0" ]; then
farmux_command=$(mktemp ${local_tmp}/farmux.XXXXXXXX.sh) #"/tmp/farm_cmd$$.sh"
function finish {
rm -f ${farmux_command}
}
trap finish EXIT
SESSION="F$$" #what should the session be named? Should be short but unique....
#first cd to the user's home dir. Then attempt to cd to this system's current dir, if it exists on the server.
#next, create a new tmux session, and launch the command in that session.
#finally, delete the executable, if we copied it to the host.
printf "cd ~\ncd $PWD\ntmux new -s $SESSION -d\ntmux send-keys -t $SESSION '${full_command}' ENTER\ntmux a -t $SESSION\n$cleanup\n" > $farmux_command
if [ "$verbose" -gt "0" ]; then
echo "$exec_command \"$(cat $farmux_command)\""
fi
$exec_command "$(cat $farmux_command)"
else
echo "(Not executing command because '-t' was set)"
fi