Typo.
[xremote.git] / xremote.sh
1 #!/bin/bash
2
3 #########################################################################
4 # This program is free software: you can redistribute it and/or modify  #
5 # it under the terms of the version 3 of the GNU General Public License #
6 # as published by the Free Software Foundation.                         #
7 #                                                                       #
8 # This program is distributed in the hope that it will be useful, but   #
9 # WITHOUT ANY WARRANTY; without even the implied warranty of            #
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      #
11 # General Public License for more details.                              #
12 #                                                                       #
13 # You should have received a copy of the GNU General Public License     #
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.  #
15 #                                                                       #
16 # Written by and Copyright (C) Francois Fleuret                         #
17 # Contact <francois@fleuret.org> for comments & bug reports             #
18 #########################################################################
19
20 set -e
21
22 # set -o pipefail
23
24 ######################################################################
25
26 function check_remote_is_defined () {
27     if [[ "${REMOTE_HOST}" ]] && [[ "${REMOTE_DIR}" ]]
28     then
29         return 0
30     else
31         echo "@XREMOTE_HOST should come first." >&2
32         exit 1
33     fi
34 }
35
36 function help () {
37     cat <<EOF
38 xremote.sh <script> [arguments]
39
40   This script takes a script as argument and executes it remotely in a
41   temporary directory on a ssh-accessible server.
42
43   It parses the script first to find embedded arguments which defines
44   the hostname on which to run, the files to send, the files to get
45   back when the execution is over, and commands to execute before
46   running the executable remotely.
47
48   These arguments can appear multiple times, except the one that
49   specifies the remote host.
50
51   Example:
52
53     @XREMOTE_HOST: elk.fleuret.org
54     @XREMOTE_EXEC: python3
55     @XREMOTE_SEND: main.cf
56     @XREMOTE_GET: *.dat
57     @XREMOTE_PRE: ln -s /home/fleuret/data/pytorch ./data
58
59   If no argument is provided to @XREMOTE_HOST, the environment
60   variable \$XREMOTE_HOST is used instead
61
62   Contact <francois@fleuret.org> for comments.
63
64 EOF
65     return 0
66 }
67
68 function cleanup_remote_tmp () {
69     if [[ "${REMOTE_HOST}" ]] && [[ "${REMOTE_DIR}" ]]
70     then
71         echo "xremote: Clean up remote workdir."
72         ssh "${REMOTE_HOST}" "rm -rf \"${REMOTE_DIR}\""
73     fi
74 }
75
76 ######################################################################
77
78 [[ -x "$1" ]] || (help && exit 1)
79
80 main="$(basename "$1")"
81
82 cd "$(dirname "$1")"
83
84 shift
85
86 trap cleanup_remote_tmp EXIT
87
88 ######################################################################
89
90 while read line
91 do
92
93     if [[ "${line}" =~ '@XREMOTE' ]]
94     then
95
96         label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
97         value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
98
99         case "${label}" in
100
101             EXEC)
102                 check_remote_is_defined
103                 [[ "${REMOTE_EXEC}" ]] && (exit "Remote executable already defined!" >&2 && exit 1)
104                 REMOTE_EXEC="${value}"
105                 ;;
106
107             PRE)
108                 check_remote_is_defined
109                 echo "xremote: ${value}"
110                 ssh < /dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${value}"
111                 ;;
112
113             SEND)
114                 check_remote_is_defined
115                 echo "xremote: -- sending files --------------------------------------------"
116                 tar ch ${value} | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mxv"
117                 ;;
118
119             HOST)
120                 [[ "${REMOTE_DIR}" ]] && (exit "Remote host already defined!" >&2 && exit 1)
121                 REMOTE_HOST="${value}"
122                 [[ "${REMOTE_HOST}" ]] || REMOTE_HOST="${XREMOTE_HOST}"
123                 [[ "${REMOTE_HOST}" ]] || (echo "xremote: No remote host specified." >&2 && exit 1)
124                 REMOTE_DIR="$(ssh </dev/null "${REMOTE_HOST}" mktemp -d /tmp/xremote.from_"$(hostname)_$(date +%Y%m%d-%H%M%S)".XXXXXX)"
125                 echo "xremote: target is ${REMOTE_HOST}"
126                 ;;
127         esac
128     fi
129
130 done < "${main}"
131
132 ######################################################################
133
134 check_remote_is_defined
135
136 tar c "${main}" | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
137
138 echo "xremote: -- running the executable -----------------------------------"
139
140 if [[ "${REMOTE_EXEC}" ]]
141 then
142     REMOTE_COMMAND="${REMOTE_EXEC} ${main}"
143 else
144     REMOTE_COMMAND="./${main}"
145 fi
146
147 ######################################################################
148
149 # I find this slightly ugly ...
150 for s in "$@"
151 do
152   quoted_args="${quoted_args} \"${s}\""
153 done
154
155 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${REMOTE_COMMAND} ${quoted_args}"
156
157 ######################################################################
158
159 # Disable globbing to keep wildcards for the remote side
160
161 echo "xremote: -- retrieving files -----------------------------------------"
162
163 set -f
164
165 while read line
166 do
167     if [[ "${line}" =~ '@XREMOTE' ]]
168     then
169         label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
170         value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
171         case "${label}" in
172             GET)
173                 check_remote_is_defined
174                 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar 2>/dev/null c ${value}" | tar mxv
175                 ;;
176         esac
177     fi
178 done < "${main}"
179
180 set +f
181
182 echo "xremote: -- finished -------------------------------------------------"
183
184 ######################################################################