autodetect the mktemp/tempfile command (reported by DUCLOS Andre)
[util-vserver.git] / util-vserver / scripts / vserver-copy
1 #!/bin/sh
2
3 # Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 # based on vserver-copy by Mark Lawrence <nomad@null.net>
5 #  
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2, or (at your option)
9 # any later version.
10 #  
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #  
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 # Copy/Sync a virtual host from one machine to another
21 #
22 # History:
23 #
24 # 2003-04-04: Version 0.4 - Mark lawrence
25 # - Set "ONBOOT=no" in the destination .conf file when --startstop
26 #   is not used, in case the destination roothost reboots. We don't
27 #   want two copies of a vserver running at the same time.
28 #
29 # 2003-03-04: Version 0.3 - Mark lawrence
30 # - Changed all checks for [ "" != "$var" ] into [ -n|-z "$var" ]. "" doesn't
31 #   seem to work for bash on the Sparc architecture.
32 # - Changed $ssh variable into $shcmd.
33 #
34 # 2003-02-23: Version 0.2 - Mark Lawrence
35 # - Set ONBOOT to "no" in the original .conf file when the "-s" flag is 
36 #   used so that if/when you reboot the source roothost you don't have the
37 #   same vserver and IP address running on two machines.
38
39 : ${UTIL_VSERVER_VARS:=$(dirname $0)/util-vserver-vars}
40 test -e "$UTIL_VSERVER_VARS" || {
41     echo "Can not find util-vserver installation; aborting..."
42     exit 1
43 }
44 . "$UTIL_VSERVER_VARS"
45
46 VERSION="0.4"
47 umask 022
48 me=${0##*/}
49
50 mktemp=$(which mktemp 2>/dev/null) || mktemp=$(which tempfile 2>/dev/null) || {
51     echo $"Can not find mktemp or tempfile" >&2
52     exit 1
53 }
54
55 ### Helper functions ###
56
57 # Save stdin and stdout for later use
58 exec 3>&1
59 exec 4>&2
60
61 noninteractive () {
62         exec &> /dev/null
63 }
64
65 interactive () {
66         exec 1>&3
67         exec 2>&4
68 }
69
70 info () {
71         ! $quiet && echo "I: $me: $1" >&3
72 }
73
74 warn () {
75         ! $quiet && echo "W: $me: $1" >&4
76 }
77
78 error () {
79         ! $quiet && echo "E: $me: $2" >&4
80         exit $1
81 }
82
83
84 ### Usage/Info functions ###
85
86 usage () {
87     cat <<EOF 1>&2
88 Usage:  $me [-hVvqidrRs] vserver newname
89         $me [-hVvqidrRs] vserver host:[newname]
90 EOF
91 }
92
93 full_usage () {
94         usage
95         cat <<EOF
96
97 $me uses rsync to make a copy of a vserver. If the destination
98 name contains a host specification the vserver will be synchronised to
99 the remote destination over ssh/rsh.
100
101 This can be used on a running vserver to make a warm backup. With the -s
102 flag a vserver can even be operationally moved to different hardware within
103 seconds.
104
105 The -i and -d flags can be used to minimally reconfigure the destination
106 vserver (rewrites /etc/vservers/newname.conf and $VROOTDIR/newname/etc/hosts)
107
108 Options:
109         -h, --help              this help
110         -V, --version           copyright and version information
111         -v, --verbose           show all output
112         -q, --quiet             direct all output to /dev/null (no password
113                                 prompt for logins on remote hosts!)
114         -d, --domain [string]   new dns domain (must be used with -i)
115         -i, --ip [addr]         new IP address (must be used with -d)
116         -r, --vsroot            location of "/vserver/" directory
117         -R, --rsh               use rsh (instead of default ssh) for
118                                 network transport
119         -s, --stopstart         stop the local vserver before copying and start
120                                 it on the destination host afterwards
121
122 EOF
123 }
124
125 full_version () {
126     cat <<EOF
127 $me version $VERSION
128 Copyright (c) 2002 Mark Lawrence   <nomad@null.net>
129
130 This program is free software; you can redistribute it and/or modify
131 it under the terms of the GNU General Public License as published by
132 the Free Software Foundation; either version 2 of the License, or (at
133 your option) any later version.
134
135 EOF
136 }
137
138
139 ### Default values and Command line options ###
140
141 stopstart=(false)
142 verbose=(false)
143 quiet=(false)
144 shcmd="ssh"
145 rsflag="-e"
146 rsh=(false)
147 colon=":"
148 domain=""
149 ip=""
150 vsroot=$VROOTDIR
151
152 if [ $# -eq 0 ]; then  # Script invoked with no command-line args?
153         usage
154         exit 1
155 fi  
156
157 temp=$(getopt -o hVvqd:i:rRs --long help,version,verbose,quiet,domain:,ip:,vsroot,rsh,stopstart, -n $me -- "$@")
158
159 if [ $? -ne 0 ]; then
160         echo "  (See -h for help)"
161         exit 1
162 fi
163
164 # Note the quotes around `$temp': they are essential!
165 eval set -- "$temp"
166
167 while true; do
168         case "$1" in
169                 -h|--help)      full_usage
170                                 exit 1
171                                 ;;
172                 -V|--version)   full_version
173                                 exit 1
174                                 ;;
175                 -v|--verbose)   verbose=(true)
176                                 shift
177                                 ;;
178                 -q|--quiet)     quiet=(true)
179                                 shift
180                                 ;;
181                 -d|--domain)    domain="$2"
182                                 shift 2
183                                 ;;
184                 -i|--ip)        ip="$2"
185                                 shift 2
186                                 ;;
187                 -r|--vsroot)    vsroot="$2"
188                                 shift 2
189                                 ;;
190                 -R|--rsh)       rsh=(true)
191                                 shift
192                                 ;;
193                 -s|--stopstart) stopstart=(true)
194                                 shift
195                                 ;;
196                 --)             shift
197                                 break
198                                 ;;
199                 *)              echo "Internal error!"
200                                 exit 1
201                                 ;;
202         esac
203 done
204
205 if [ $# -ne 2 ]; then
206         usage
207         exit 1
208 fi
209
210
211 ### ###
212
213 # By default we are reasonably quiet (ouput only via info, warn & error)
214 if $verbose; then
215         interactive
216 else
217         noninteractive
218 fi
219
220 now=$(date)
221 info "called on $(hostname) at $now"
222
223
224 vserver=$1
225 vconf=/etc/vservers/$vserver.conf
226 vroot=$vsroot/$vserver
227
228 if $rsh; then
229         shcmd="rsh"
230 fi
231
232 if (echo $2 | grep '^[a-z][a-z0-9]\+$'); then
233         dhost=""
234         newname=$2
235         shcmd=""
236         rsflag=""
237         colon=""
238         if $rsh; then
239                 warn "rsh is set but not used for a local copy"
240         fi
241 elif (echo $2 | grep '^[a-z].*[a-z0-9]:$'); then
242         dhost=${2/:/}
243         newname=$vserver
244 elif (echo $2 | grep '^[a-z].*[a-z0-9]:[a-z].*[a-z0-9]$'); then
245         dhost=${2/:*/}
246         newname=${2/*:/}
247 else
248         error 1 "Second argument must be of the form \"[host:]name\" or \"host:\""
249 fi
250
251 target=$vsroot/$newname
252 targetconf=/etc/vservers/$newname.conf
253
254
255 ### Perform some sanity checks ###
256
257 if [ ! -d $vroot ]; then
258         error 1 "Directory \"$vroot\" does not exist"
259 fi
260
261 if [ ! -e $vconf ]; then
262         error 1 "Vserver file \"$vconf\" does not exist"
263 fi
264
265 if [ -z "$dhost" ] && [ "$vserver" == "$newname" ]; then
266         error 1 "Source and destination names cannot be the same on the localhost"
267 fi
268
269 if [ -n "$dhost" ] && ! (host $dhost | grep 'has address'); then
270         warn "$dhost does not resolve into an IP address"
271 fi
272
273 if [ \( -n "$ip" -a -z "$domain" \) -o \
274      \( -z "$ip" -a -n "$domain" \) ]
275 then
276         error 1 "Both IP address and domain must be specified together"
277 fi
278
279 if [ -n "$ip" ] && \
280 ! (echo $ip | grep '^[0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}$' ); then
281         error 1 "\"$ip\" is not a valid IP address"
282 fi
283
284 # This works both locally and remote
285 if ($shcmd $dhost $SBINDIR/vserver $newname running | grep 'is running'); then
286         warn "destination vserver \"$newname\" is running" 
287         error 1 "Cannot copy over a running vserver"
288 fi
289
290
291 ### Do the copy ###
292
293 info "Attempting to copy $vserver to $dhost$colon$newname"
294
295 if $stopstart; then
296         info "Stopping virtual server \"$vserver\" on localhost"
297         $SBINDIR/vserver $vserver stop
298 fi
299
300 info "Syncing directories"
301 # trailing slashes very important in the rsync!
302 if ! rsync -avxz $rsflag $shcmd $vroot/ $dhost$colon$target/; then
303         error 1 "rsync failed"
304 fi
305
306 if [ -n "$ip" -a -n "$domain" ]; then
307         # Insert the new IPROOT/S_HOSTNAME values into the config file
308         info "Modifying $targetconf"
309         tmpf=$($mktemp)
310         if (sed -e "s/^S_HOSTNAME=.*/S_HOSTNAME=\"$newname\"/" \
311                 -e "s/^IPROOT=.*/IPROOT=\"$ip\"/" $vconf > $tmpf)
312         then
313                 if ! rsync -v $rsflag $shcmd $tmpf $dhost$colon$targetconf; then
314                         error $? "vserver config file copy/change failed"
315                 fi
316
317         else
318                 warn "Unable to reconfigure virtual server config file"
319         fi
320
321         # create a new /etc/hostname
322         info "Creating hostname file"
323         echo $newname > $tmpf
324         if ! rsync -v $rsflag $shcmd $tmpf $dhost$colon$target/etc/hostname; then
325                 error 1 "vserver /etc/hostname copy failed"
326         fi
327
328         info "Creating /etc/hosts"
329         cat << EOF > $tmpf
330 # /etc/hosts (automatically generated by $me)
331
332 127.0.0.1       localhost
333 $ip     $newname.$domain        $newname
334
335 # The following lines are desirable for IPv6 capable hosts
336
337 ::1     ip6-localhost ip6-loopback
338 fe00::0 ip6-localnet
339 ff00::0 ip6-mcastprefix
340 ff02::1 ip6-allnodes
341 ff02::2 ip6-allrouters
342 ff02::3 ip6-allhosts
343 EOF
344
345         # copy /etc/hosts
346         if ! rsync -v $rsflag $shcmd $tmpf $dhost$colon$target/etc/hosts; then
347                 error 1 "vserver /etc/hosts copy failed"
348         fi
349         rm -f $tmpf
350
351 else
352         if ! $stopstart; then
353                 # Make sure that this vserver doesn't start on the 
354                 # destination host if it reboots
355                 tmpf=$($mktemp)
356                 sed -e 's/^ONBOOT=.*/ONBOOT=no/' $vconf > $tmpf
357                 vconf=$tmpf
358         fi
359
360         # copy newname.conf unchanged
361         info "Copying $targetconf"
362         if ! rsync -v $rsflag $shcmd $vconf $dhost$colon$targetconf; then
363                 error 1 "vserver config file copy/change failed"
364         fi
365
366         rm -f $tmpf
367 fi
368
369
370 if $stopstart; then
371         info "Starting virtual server \"$vserver\" on $dhost"
372         $shcmd $dhost $SBINDIR/vserver $vserver start
373         if ($shcmd $dhost $SBINDIR/vserver $vserver running | \
374         grep 'not running'); then
375                 error 1 "Virtual server \"$vserver\" failed to start on $dhost"
376         fi
377
378         # Make sure that we don't start the original on next boot
379         tmpf=$($mktemp)
380         sed -e 's/^ONBOOT=.*/ONBOOT=no/' $vconf > $tmpf
381         mv $tmpf $vconf
382 fi
383
384 exit 0