Improved the feed-back.
[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, the files to send, the files to get
44   back when the execution is over, 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}" ]] && (exit "Remote executable already defined!" >&2 && exit 1)
101                 REMOTE_EXEC="${value}"
102                 ;;
103
104             PRE)
105                 check_remote_is_defined
106                 ssh < /dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${value}"
107                 ;;
108
109             SEND)
110                 check_remote_is_defined
111                 echo "xremote: -- sending files --------------------------------------------"
112                 tar c ${value} | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mxv"
113                 ;;
114
115             HOST)
116                 [[ "${REMOTE_DIR}" ]] && (exit "Remote host already defined!" >&2 && exit 1)
117                 REMOTE_HOST="${value}"
118                 [[ "${REMOTE_HOST}" ]] || REMOTE_HOST="${XREMOTE_HOST}"
119                 [[ "${REMOTE_HOST}" ]] || (echo "xremote: No remote host specified." >&2 && exit 1)
120                 REMOTE_DIR="$(ssh </dev/null "${REMOTE_HOST}" mktemp -d /tmp/xremote.from_"$(hostname)_$(date +%Y%m%d-%H%M%S)".XXXXXX)"
121                 echo "xremote: target is ${REMOTE_HOST}"
122                 ;;
123         esac
124     fi
125
126 done < "${main}"
127
128 ######################################################################
129
130 check_remote_is_defined
131
132 tar c "${main}" | ssh "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar mx"
133
134 echo "xremote: -- running the executable -----------------------------------"
135
136 if [[ "${REMOTE_EXEC}" ]]
137 then
138     REMOTE_COMMAND="${REMOTE_EXEC} ${main}"
139 else
140     REMOTE_COMMAND="./${main}"
141 fi
142
143 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && ${REMOTE_COMMAND}"
144
145 ######################################################################
146
147 # Disable globbing to keep wildcards for the remote side
148
149 echo "xremote: -- retrieving files -----------------------------------------"
150
151 set -f
152
153 while read line
154 do
155     if [[ "${line}" =~ '@XREMOTE' ]]
156     then
157         label=$(echo "${line}" | sed -e 's/^.*@XREMOTE_\([^:]*\):.*$/\1/')
158         value=$(echo "${line}" | sed -e 's/^.*@XREMOTE_[^:]*: *\(.*\)$/\1/')
159         case "${label}" in
160             GET)
161                 check_remote_is_defined
162                 ssh </dev/null "${REMOTE_HOST}" "cd \"${REMOTE_DIR}\" && tar 2>/dev/null c ${value}" | tar mxv
163                 ;;
164         esac
165     fi
166 done < "${main}"
167
168 set +f
169
170 echo "xremote: -- finished -------------------------------------------------"
171
172 ######################################################################