Cosmetics.
[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_EXEC: python3
54     @XREMOTE_SEND: main.cf
55     @XREMOTE_GET: *.dat
56     @XREMOTE_PRE: ln -s /home/fleuret/data/pytorch ./data
57
58   If no argument is provided to @XREMOTE_HOST, the environment
59   variable $XREMOTE_HOST is used instead
60
61   Contact <francois@fleuret.org> for comments.
62
63 EOF
64     return 0
65 }
66
67 function cleanup_remote_tmp () {
68     if [[ "${REMOTE_HOST}" ]] && [[ "${REMOTE_DIR}" ]]
69     then
70         echo "xremote: Clean up remote workdir."
71         ssh "${REMOTE_HOST}" "rm -rf \"${REMOTE_DIR}\""
72     fi
73 }
74
75 ######################################################################
76
77 [[ -x "$1" ]] || (help && exit 1)
78
79 main="$(basename "$1")"
80
81 cd "$(dirname "$1")"
82
83 trap cleanup_remote_tmp EXIT
84
85 ######################################################################
86
87 while read line
88 do
89
90     if [[ "${line}" =~ '@XREMOTE' ]]
91     then
92
93         label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
94         value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
95
96         case "${label}" in
97
98             EXEC)
99                 check_remote_is_defined
100                 REMOTE_EXEC="${value}"
101                 ;;
102
103             PRE)
104                 check_remote_is_defined
105                 ssh < /dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${value}"
106                 ;;
107
108             SEND)
109                 check_remote_is_defined
110                 tar c ${value} | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
111                 ;;
112
113             HOST)
114                 [[ "${REMOTE_DIR}" ]] && (exit "Remote host already defined!" >&2 && exit 1)
115                 REMOTE_HOST="${value}"
116                 [[ "${REMOTE_HOST}" ]] || REMOTE_HOST="${XREMOTE_HOST}"
117                 [[ "${REMOTE_HOST}" ]] || (echo "xremote: No remote host specified." >&2 && exit 1)
118                 REMOTE_DIR="$(ssh </dev/null "${REMOTE_HOST}" mktemp -d /tmp/xremote.from_"$(hostname)_$(date +%Y%m%d-%H%M%S)".XXXXXX)"
119                 ;;
120         esac
121     fi
122
123 done < "${main}"
124
125 ######################################################################
126
127 check_remote_is_defined
128
129 tar c "${main}" | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
130
131 echo "xremote: on ${REMOTE_HOST}"
132
133 if [[ "${REMOTE_EXEC}" ]]
134 then
135     REMOTE_COMMAND="${REMOTE_EXEC} ${main}"
136 else
137     REMOTE_COMMAND="./${main}"
138 fi
139
140 echo "xremote: -- stdout ---------------------------------------------------"
141 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${REMOTE_COMMAND}"
142
143 ######################################################################
144
145 # Disable globbing to keep wildcards for the remote side
146
147 echo "xremote: -- retrieve results -----------------------------------------"
148
149 set -f
150
151 while read line
152 do
153     if [[ "${line}" =~ '@XREMOTE' ]]
154     then
155         label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
156         value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
157         case "${label}" in
158             GET)
159                 check_remote_is_defined
160                 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar 2>/dev/null c ${value}" | tar mxv
161                 ;;
162         esac
163     fi
164 done < "${main}"
165
166 set +f
167
168 echo "xremote: -- finished -------------------------------------------------"
169
170 ######################################################################