b6c8a4041421355cf620e960be7721ca6aa95ba1
[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 ERR
184
185 # Helper.
186 gf="guestfish --remote --"
187
188 # Launch back-end, inspect for operating systems, and get the guest
189 # root disk.
190 root=$($gf inspect-get-roots)
191
192 if [ "$root" = "" ]; then
193     echo "$program: no operating system was found on this disk"
194     exit 1
195 fi
196
197 if [ "$verbose" = "yes" ]; then
198     echo root: "$root"
199 fi
200
201 # Get the guest type.
202 type="$($gf -inspect-get-type $root)"
203
204 if [ "$type" = "linux" ]; then
205     distro="$($gf -inspect-get-distro $root)"
206 fi
207
208 if [ "$type" = "windows" ]; then
209     systemroot="$($gf -inspect-get-windows-systemroot $root)"
210 fi
211
212 #----------------------------------------------------------------------
213 # Useful functions.
214
215 # erase_line filename regex
216 #
217 # Erase line(s) in a file that match the given regex.
218 erase_line ()
219 {
220     $gf download "$1" "$tmpdir/file"
221     sed "/$2/d" < "$tmpdir/file" > "$tmpdir/file.1"
222     $gf upload "$tmpdir/file.1" "$1"
223 }
224
225 # rm_files wildcard
226 #
227 # Remove files.  Doesn't fail if no files exist.  Note the wildcard
228 # parameter cannot contain spaces or characters that need special
229 # quoting.
230 rm_files ()
231 {
232     files=$($gf glob-expand "$1")
233     for f in $files; do
234         $gf rm "$f"
235     done
236 }
237
238 # rm_file filename
239 #
240 # Remove a single file.  No error if the file doesn't exist or is not
241 # a file.
242 rm_file ()
243 {
244     t=$($gf is-file "$1")
245     if [ "$t" = "true" ]; then
246         $gf rm "$1"
247     fi
248 }
249
250 #----------------------------------------------------------------------
251 # The sysprep operations.
252
253 if [ "$hostname" = "yes" ]; then
254     case "$type/$distro" in
255         linux/fedora)
256             $gf download /etc/sysconfig/network "$tmpdir/network"
257             echo "HOSTNAME=$hostname_param" > "$tmpdir/network.1"
258             sed '/^HOSTNAME=/d' < "$tmpdir/network" >> "$tmpdir/network.1"
259             $gf upload "$tmpdir/network.1" /etc/sysconfig/network ;;
260         linux/debian|linux/ubuntu)
261             $gf write /etc/hostname "$hostname_param"
262     esac
263 fi
264
265 if [ "$net_hwaddr" = "yes" ]; then
266     case "$type/$distro" in
267         linux/fedora)
268             # XXX these filenames can have spaces and untrusted chars in them!
269             files=$($gf glob-expand '/etc/sysconfig/network-scripts/ifcfg-*')
270             for f in $files; do
271                 erase_line "$f" "^HWADDR="
272             done
273     esac
274 fi
275
276 if [ "$ssh_hostkeys" = "yes" -a "$type" != "windows" ]; then
277     rm_files "/etc/ssh/*_host_*"
278 fi
279
280 if [ "$udev_persistent_net" = "yes" -a "$type" = "linux" ]; then
281     rm_file /etc/udev/rules.d/70-persistent-net.rules
282 fi
283
284 # Clean up and close down.
285
286 $gf umount-all
287 $gf sync
288 $gf exit
289
290 exit 0