Added @XREMOTE_EXEC.
[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 set -o pipefail
22
23 ######################################################################
24
25 function check_remote_is_defined () {
26     if [[ "${REMOTE_HOST}" ]] && [[ "${REMOTE_DIR}" ]]
27     then
28         return 0
29     else
30         echo "@XREMOTE_HOST should come first." >&2
31         exit 1
32     fi
33 }
34
35 function help () {
36     cat <<EOF
37 xremote.sh <script>
38
39   This script takes a script as argument and executes it remotely in a
40   temporary directory on a ssh-accessible server.
41
42   It parses the script first to find embedded arguments which defines
43   the hostname on which to run it, the files to send, the files to
44   get back when the execution is done, and commands to execute before
45   running the executable remotely.
46
47   These arguments can appear multiple times, except the one that
48   specifies the remote host.
49
50   Example:
51
52     @XREMOTE_HOST: elk.fleuret.org
53     @XREMOTE_SEND: mnist.py
54     @XREMOTE_GET: *.dat
55     @XREMOTE_PRE: ln -s /home/fleuret/data/pytorch ./data
56
57   If no argument is provided to @XREMOTE_HOST, the environment
58   variable $XREMOTE_HOST is used instead
59
60   Contact <francois@fleuret.org> for comments.
61
62 EOF
63     return 0
64 }
65
66 function cleanup_remote_tmp () {
67     if [[ "${REMOTE_HOST}" ]] && [[ "${REMOTE_DIR}" ]]
68     then
69         echo "Clean up remote workdir."
70         ssh "${REMOTE_HOST}" "rm -rf \"${REMOTE_DIR}\""
71     fi
72 }
73
74 ######################################################################
75
76 [[ -x "$1" ]] || (help && exit 1)
77
78 main="$(basename "$1")"
79
80 cd "$(dirname "$1")"
81
82 trap cleanup_remote_tmp EXIT
83
84 ######################################################################
85
86 while read line
87 do
88
89     if [[ "${line}" =~ '@XREMOTE' ]]
90     then
91
92         label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
93         value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
94
95         case "${label}" in
96
97             EXEC)
98                 check_remote_is_defined
99                 REMOTE_EXEC="${value}"
100                 ;;
101
102             PRE)
103                 check_remote_is_defined
104                 ssh < /dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${value}"
105                 ;;
106
107             SEND)
108                 check_remote_is_defined
109                 tar c "${value}" | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
110                 ;;
111
112             HOST)
113                 [[ "${REMOTE_DIR}" ]] && (exit "Remote host already defined!" >&2 && exit 1)
114                 REMOTE_HOST="${value}"
115                 [[ "${REMOTE_HOST}" ]] || REMOTE_HOST="${XREMOTE_HOST}"
116                 [[ "${REMOTE_HOST}" ]] || (echo "No remote host specified." >&2 && exit 1)
117                 REMOTE_DIR="$(ssh </dev/null "${REMOTE_HOST}" mktemp -d /tmp/xremote.from_"$(hostname)_$(date +%Y%m%d)".XXXXXX)"
118                 ;;
119         esac
120     fi
121
122 done < "${main}"
123
124 ######################################################################
125
126 check_remote_is_defined
127
128 tar c "${main}" | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
129
130 echo "----------------------------------------------------------------------"
131 echo "-- On ${REMOTE_HOST}"
132 echo "----------------------------------------------------------------------"
133
134 if [[ "${REMOTE_EXEC}" ]]
135 then
136     REMOTE_COMMAND="${REMOTE_EXEC} ${main}"
137 else
138     REMOTE_COMMAND="./${main}"
139 fi
140
141 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${REMOTE_COMMAND}"
142 echo "----------------------------------------------------------------------"
143
144 ######################################################################
145
146 # Disable globbing to keep wildcards for the remote side
147
148 set -f
149
150 while read line
151 do
152     if [[ "${line}" =~ '@XREMOTE' ]]
153     then
154         label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
155         value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
156         case "${label}" in
157             GET)
158                 check_remote_is_defined
159                 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar c ${value}" | tar mxv
160                 ;;
161         esac
162     fi
163 done < "${main}"
164
165 set +f
166
167 ######################################################################