New tool: virt-sysprep: system preparation for guests.
[libguestfs.git] / clone / virt-sysprep.in
1 #!/bin/bash -
2 # @configure_input@
3 # libguestfs virt-sysprep tool
4 # Copyright (C) 2011 Red Hat Inc.
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 of the License, or
9 # (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 unset CDPATH
21 program="virt-sysprep"
22 version="@PACKAGE_VERSION@"
23
24 TEMP=`getopt \
25         -o a:c:d:vVx \
26         --long help,add:,connect:,domain:,enable:,format::,hostname:,list-operations,verbose,version \
27         -n $program -- "$@"`
28 if [ $? != 0 ]; then
29     echo "$program: problem parsing the command line arguments"
30     exit 1
31 fi
32 eval set -- "$TEMP"
33
34 # This array accumulates the arguments we pass through to guestfish.
35 declare -a guestfish
36 guestfish[0]="guestfish"
37 guestfish[1]="--rw"
38 guestfish[2]="--listen"
39 guestfish[3]="-i"
40 i=4
41
42 verbose=
43 add_params=0
44 enable=
45 hostname_param=localhost.localdomain
46
47 usage ()
48 {
49     echo "Usage:"
50     echo "  $program [--options] -d domname"
51     echo "  $program [--options] -a disk.img [-a disk.img ...]"
52     echo
53     echo "Read $program(1) man page for more information."
54     echo
55     echo "NOTE: $program modifies the guest or disk image *in place*."
56     exit $1
57 }
58
59 while true; do
60     case "$1" in
61         -a|--add)
62             guestfish[i++]="-a"
63             guestfish[i++]="$2"
64             ((add_params++))
65             shift 2;;
66         -c|--connect)
67             guestfish[i++]="-c"
68             guestfish[i++]="$2"
69             shift 2;;
70         -d|--domain)
71             guestfish[i++]="-d"
72             guestfish[i++]="$2"
73             ((add_params++))
74             shift 2;;
75         --enable)
76             if [ -n "$enable" ]; then
77                 echo "error: --enable option can only be given once"
78                 exit 1
79             fi
80             enable="$2"
81             shift 2;;
82         --format)
83             if [ -n "$2" ]; then
84                 guestfish[i++]="--format=$2"
85             else
86                 guestfish[i++]="--format"
87             fi
88             shift 2;;
89         --help)
90             usage 0;;
91         --hostname)
92             hostname_param="$2"
93             shift 2;;
94         --list-operations)
95             enable=list
96             shift;;
97         -v|--verbose)
98             guestfish[i++]="-v"
99             verbose=yes
100             shift;;
101         -V|--version)
102             echo "$program $version"
103             exit 0;;
104         -x)
105             guestfish[i++]="-x"
106             shift;;
107         --)
108             shift
109             break;;
110         *)
111             echo "Internal error!"
112             exit 1;;
113     esac
114 done
115
116 # Different sysprep operations that can be enabled.  Default is to
117 # enable all of these, although some of them are only done on certain
118 # guest types (see details below).
119 if [ -z "$enable" ]; then
120     hostname=yes
121     net_hwaddr=yes
122     ssh_hostkeys=yes
123     udev_persistent_net=yes
124 elif [ "$enable" = "list" ]; then
125     echo "hostname"
126     echo "net-hwaddr"
127     echo "ssh-hostkeys"
128     echo "udev-persistent-net"
129     exit 0
130 else
131     for opt in $(echo "$enable" | sed 's/,/ /g'); do
132         case "$opt" in
133             hostname)              hostname=yes ;;
134             net-hwaddr)            net_hwaddr=yes ;;
135             ssh-hostkeys)          ssh_hostkeys=yes ;;
136             udev-persistent-net)   udev_persistent_net=yes ;;
137             *)
138                 echo "error: unknown --enable feature: $opt"
139                 exit 1
140         esac
141     done
142 fi
143
144 # Make sure there were no extra parameters on the command line.
145 if [ $# -gt 0 ]; then
146     echo "error: $program: extra parameters on the command line"
147     echo
148     usage 1
149 fi
150
151 # Did the user specify at least one -a or -d option?
152 if [ $add_params -eq 0 ]; then
153     echo "error: $program: you need at least one -a or -d option"
154     echo
155     usage 1
156 fi
157
158 # end of command line parsing
159 #----------------------------------------------------------------------
160
161 set -e
162
163 if [ "$verbose" = "yes" ]; then
164     echo command: "${guestfish[@]}"
165 fi
166
167 # Create a temporary directory for general purpose use during operations.
168 tmpdir="$(mktemp -d)"
169
170 # Call guestfish.
171 GUESTFISH_PID=
172 eval $("${guestfish[@]}")
173 if [ -z "$GUESTFISH_PID" ]; then
174     echo "$program: guestfish didn't start up, see error messages above"
175     exit 1
176 fi
177
178 cleanup ()
179 {
180     kill $GUESTFISH_PID >/dev/null 2>&1 ||:
181     rm -rf "$tmpdir" ||:
182 }
183 trap cleanup EXIT
184
185 # Launch back-end, inspect for operating systems, and get the guest
186 # root disk.
187 root=$(guestfish --remote inspect-get-roots)
188
189 if [ "$root" = "" ]; then
190     echo "$program: no operating system was found on this disk"
191     exit 1
192 fi
193
194 if [ "$verbose" = "yes" ]; then
195     echo root: "$root"
196 fi
197
198 # Get the guest type.
199 type="$(guestfish --remote -- -inspect-get-type $root)"
200
201 if [ "$type" = "linux" ]; then
202     distro="$(guestfish --remote -- -inspect-get-distro $root)"
203 fi
204
205 if [ "$type" = "windows" ]; then
206     systemroot="$(guestfish --remote -- -inspect-get-windows-systemroot $root)"
207 fi
208
209 #----------------------------------------------------------------------
210 # Useful functions.
211
212 # erase_line filename regex
213 #
214 # Erase line(s) in a file that match the given regex.
215 erase_line ()
216 {
217     guestfish --remote -- download "$1" "$tmpdir/file"
218     sed "/$2/d" < "$tmpdir/file" > "$tmpdir/file.1"
219     guestfish --remote -- upload "$tmpdir/file.1" "$1"
220 }
221
222 # rm_files wildcard
223 #
224 # Remove files.  Doesn't fail if no files exist.  Note the wildcard
225 # parameter cannot contain spaces or characters that need special
226 # quoting.
227 rm_files ()
228 {
229     files=$(guestfish --remote -- glob-expand "$1")
230     for f in $files; do
231         guestfish --remote -- rm "$f"
232     done
233 }
234
235 # rm_file filename
236 #
237 # Remove a single file.  No error if the file doesn't exist or is not
238 # a file.
239 rm_file ()
240 {
241     t=$(guestfish --remote -- is-file "$1")
242     if [ "$t" = "true" ]; then
243         guestfish --remote -- rm "$1"
244     fi
245 }
246
247 #----------------------------------------------------------------------
248 # The sysprep operations.
249
250 if [ "$hostname" = "yes" ]; then
251     case "$type/$distro" in
252         linux/fedora)
253             guestfish --remote -- \
254                 download /etc/sysconfig/network "$tmpdir/network"
255             echo "HOSTNAME=$hostname_param" > "$tmpdir/network.1"
256             sed '/^HOSTNAME=/d' < "$tmpdir/network" >> "$tmpdir/network.1"
257             guestfish --remote -- \
258                 upload "$tmpdir/network.1" /etc/sysconfig/network ;;
259         linux/debian|linux/ubuntu)
260             guestfish --remote -- write /etc/hostname "$hostname_param"
261     esac
262 fi
263
264 if [ "$net_hwaddr" = "yes" ]; then
265     case "$type/$distro" in
266         linux/fedora)
267             # XXX these filenames can have spaces and untrusted chars in them!
268             files=$(guestfish --remote -- glob-expand '/etc/sysconfig/network-scripts/ifcfg-*')
269             for f in $files; do
270                 erase_line "$f" "^HWADDR="
271             done
272     esac
273 fi
274
275 if [ "$ssh_hostkeys" = "yes" -a "$type" != "windows" ]; then
276     rm_files "/etc/ssh/*_host_*"
277 fi
278
279 if [ "$udev_persistent_net" = "yes" -a "$type" = "linux" ]; then
280     rm_file /etc/udev/rules.d/70-persistent-net.rules
281 fi
282
283 # Clean up and close down.
284
285 guestfish --remote umount-all
286 guestfish --remote sync
287 guestfish --remote exit
288
289 exit 0