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