virt-sysprep: Use /dev/urandom instead of /dev/random.
[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 # Uncomment this to see every shell command that is executed.
25 #set -x
26
27 TEMP=`getopt \
28         -o a:c:d:vVx \
29         --long help,add:,connect:,domain:,enable:,format::,hostname:,list-operations,selinux-relabel,no-selinux-relabel,verbose,version \
30         -n $program -- "$@"`
31 if [ $? != 0 ]; then
32     echo "$program: problem parsing the command line arguments"
33     exit 1
34 fi
35 eval set -- "$TEMP"
36
37 # This array accumulates the arguments we pass through to guestmount.
38 declare -a params
39 i=0
40
41 verbose=
42 add_params=0
43 enable=
44 hostname_param=localhost.localdomain
45 selinux_relabel=auto
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             params[i++]="-a"
63             params[i++]="$2"
64             ((add_params++))
65             shift 2;;
66         -c|--connect)
67             params[i++]="-c"
68             params[i++]="$2"
69             shift 2;;
70         -d|--domain)
71             params[i++]="-d"
72             params[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                 params[i++]="--format=$2"
85             else
86                 params[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         --selinux-relabel)
98             selinux_relabel=yes
99             shift;;
100         --no-selinux-relabel)
101             selinux_relabel=no
102             shift;;
103         -v|--verbose)
104             params[i++]="-v"
105             verbose=yes
106             shift;;
107         -V|--version)
108             echo "$program $version"
109             exit 0;;
110         -x)
111             # Can't pass the -x option directly to guestmount because
112             # that stops guestmount from forking, which means we can't
113             # coordinate with guestmount when it has finished
114             # initializing.  So instead set just the underlying option
115             # in libguestfs by exporting LIBGUESTFS_TRACE.
116             # Unfortunately (a) this omits FUSE calls, but don't worry
117             # about that for now, and more importantly (b) trace
118             # messages disappear into never-never land after the fork.
119             export LIBGUESTFS_TRACE=1
120             shift;;
121         --)
122             shift
123             break;;
124         *)
125             echo "Internal error!"
126             exit 1;;
127     esac
128 done
129
130 # Different sysprep operations that can be enabled.  Default is to
131 # enable all of these, although some of them are only done on certain
132 # guest types (see details below).
133 if [ -z "$enable" ]; then
134     dhcp_client_state=yes
135     dhcp_server_state=yes
136     hostname=yes
137     logfiles=yes
138     net_hwaddr=yes
139     random_seed=yes
140     rhn_systemid=yes
141     smolt_uuid=yes
142     ssh_hostkeys=yes
143     udev_persistent_net=yes
144     yum_uuid=yes
145 elif [ "$enable" = "list" ]; then
146     echo "dhcp-client-state"
147     echo "dhcp-server-state"
148     echo "hostname"
149     echo "logfiles"
150     echo "net-hwaddr"
151     echo "random-seed"
152     echo "rhn-systemid"
153     echo "smolt-uuid"
154     echo "ssh-hostkeys"
155     echo "udev-persistent-net"
156     echo "yum-uuid"
157     exit 0
158 else
159     for opt in $(echo "$enable" | sed 's/,/ /g'); do
160         case "$opt" in
161             dhcp-client-state)     dhcp_client_state=yes ;;
162             dhcp-server-state)     dhcp_server_state=yes ;;
163             hostname)              hostname=yes ;;
164             logfiles)              logfiles=yes ;;
165             net-hwaddr)            net_hwaddr=yes ;;
166             random-seed)           random_seed=yes ;;
167             rhn-systemid)          rhn_systemid=yes ;;
168             smolt-uuid)            smolt_uuid=yes ;;
169             ssh-hostkeys)          ssh_hostkeys=yes ;;
170             udev-persistent-net)   udev_persistent_net=yes ;;
171             yum-uuid)              yum_uuid=yes ;;
172             *)
173                 echo "error: unknown --enable feature: $opt"
174                 exit 1
175         esac
176     done
177 fi
178
179 # Make sure there were no extra parameters on the command line.
180 if [ $# -gt 0 ]; then
181     echo "error: $program: extra parameters on the command line"
182     echo
183     usage 1
184 fi
185
186 # Did the user specify at least one -a or -d option?
187 if [ $add_params -eq 0 ]; then
188     echo "error: $program: you need at least one -a or -d option"
189     echo
190     usage 1
191 fi
192
193 # end of command line parsing
194 #----------------------------------------------------------------------
195
196 set -e
197
198 if [ "$verbose" = "yes" ]; then
199     echo params: "${params[@]}"
200 fi
201
202 # Create a temporary directory for general purpose use during operations.
203 tmpdir="$(mktemp -d)"
204
205 cleanup ()
206 {
207     if [ -d $tmpdir/mnt ]; then
208         fusermount -u $tmpdir/mnt >/dev/null 2>&1 ||:
209     fi
210     rm -rf $tmpdir ||:
211 }
212 trap cleanup EXIT ERR
213
214 # Run virt-inspector and grab inspection information about this guest.
215 virt-inspector "${params[@]}" > $tmpdir/xml
216 xmlstarlet sel -t -c \
217     "string(/operatingsystems/operatingsystem[position()=1]/name)" \
218     $tmpdir/xml > $tmpdir/type
219 xmlstarlet sel -t -c \
220     "string(/operatingsystems/operatingsystem[position()=1]/distro)" \
221     $tmpdir/xml > $tmpdir/distro ||:
222 xmlstarlet sel -t -c \
223     "string(/operatingsystems/operatingsystem[position()=1]/package_format)" \
224     $tmpdir/xml > $tmpdir/package_format ||:
225 xmlstarlet sel -t -c \
226     "string(/operatingsystems/operatingsystem[position()=1]/package_management)" \
227     $tmpdir/xml > $tmpdir/package_management ||:
228
229 type="$(cat $tmpdir/type)"
230 distro="$(cat $tmpdir/distro)"
231 package_format="$(cat $tmpdir/package_format)"
232 package_management="$(cat $tmpdir/package_management)"
233
234 # Mount the disk.
235 mkdir $tmpdir/mnt
236 guestmount --rw -i "${params[@]}" $tmpdir/mnt
237
238 mnt="$tmpdir/mnt"
239
240 #----------------------------------------------------------------------
241 # The sysprep operations.
242
243 if [ "$dhcp_client_state" = "yes" ]; then
244     case "$type" in
245         linux)
246             rm -rf $mnt/var/lib/dhclient/*
247             # RHEL 3:
248             rm -rf $mnt/var/lib/dhcp/*
249             ;;
250     esac
251 fi
252
253 if [ "$dhcp_server_state" = "yes" ]; then
254     case "$type" in
255         linux)
256             rm -rf $mnt/var/lib/dhcpd/*
257             ;;
258     esac
259 fi
260
261 if [ "$hostname" = "yes" ]; then
262     case "$type/$distro" in
263         linux/fedora)
264             echo "HOSTNAME=$hostname_param" > $mnt/etc/sysconfig/network.new
265             sed '/^HOSTNAME=/d' < $mnt/etc/sysconfig/network >> $mnt/etc/sysconfig/network.new
266             mv -f $mnt/etc/sysconfig/network.new $mnt/etc/sysconfig/network
267             created_files=yes
268             ;;
269         linux/debian|linux/ubuntu)
270             echo "$hostname_param" > $mnt/etc/hostname
271             created_files=yes
272             ;;
273     esac
274 fi
275
276 if [ "$logfiles" = "yes" ]; then
277     case "$type" in
278         linux)
279             rm -rf $mnt/var/log/*.log*
280             rm -rf $mnt/var/log/audit/*
281             rm -rf $mnt/var/log/btmp*
282             rm -rf $mnt/var/log/cron*
283             rm -rf $mnt/var/log/dmesg*
284             rm -rf $mnt/var/log/lastlog*
285             rm -rf $mnt/var/log/maillog*
286             rm -rf $mnt/var/log/mail/*
287             rm -rf $mnt/var/log/messages*
288             rm -rf $mnt/var/log/secure*
289             rm -rf $mnt/var/log/spooler*
290             rm -rf $mnt/var/log/tallylog*
291             rm -rf $mnt/var/log/wtmp*
292             ;;
293     esac
294 fi
295
296 if [ "$net_hwaddr" = "yes" ]; then
297     case "$type/$distro" in
298         linux/fedora)
299             if [ -d $mnt/etc/sysconfig/network-scripts ]; then
300                 rm_hwaddr ()
301                 {
302                     sed '/^HWADDR=/d' < "$1" > "$1.new"
303                     mv -f "$1.new" "$1"
304                 }
305                 export -f rm_hwaddr
306                 find $mnt/etc/sysconfig/network-scripts \
307                     -name 'ifcfg-*' -type f \
308                     -exec bash -c 'rm_hwaddr "$0"' {} \;
309                 created_files=yes
310             fi
311             ;;
312     esac
313 fi
314
315 if [ "$random_seed" = "yes" -a "$type" = "linux" ]; then
316     f=
317     if [ -f $mnt/var/lib/random-seed ]; then
318         # Fedora
319         f=$mnt/var/lib/random-seed
320     elif [ -f $mnt/var/lib/urandom/random-seed ]; then
321         # Debian
322         f=$mnt/var/lib/urandom/random-seed
323     fi
324     if [ -n "$f" ]; then
325         dd if=/dev/urandom of="$f" bs=8 count=1 conv=nocreat,notrunc 2>/dev/null
326     fi
327 fi
328
329 if [ "$rhn_systemid" = "yes" -a "$type/$distro" = "linux/rhel" ]; then
330     rm -f $mnt/etc/sysconfig/rhn/systemid
331 fi
332
333 if [ "$smolt_uuid" = "yes" -a "$type" = "linux" ]; then
334     rm -f $mnt/etc/sysconfig/hw-uuid
335     rm -f $mnt/etc/smolt/uuid
336     rm -f $mnt/etc/smolt/hw-uuid
337 fi
338
339 if [ "$ssh_hostkeys" = "yes" -a "$type" != "windows" ]; then
340     rm -rf $mnt/etc/ssh/*_host_*
341 fi
342
343 if [ "$udev_persistent_net" = "yes" -a "$type" = "linux" ]; then
344     rm -f $mnt/etc/udev/rules.d/70-persistent-net.rules
345 fi
346
347 if [ "$yum_uuid" = "yes" -a "$package_management" = "yum" ]; then
348     rm -f $mnt/var/lib/yum/uuid
349 fi
350
351 #----------------------------------------------------------------------
352 # Clean up and close down.
353
354 # If we created any new files and the guest uses SELinux, then we have
355 # to relabel the filesystem on boot.  Could do with a better way to
356 # test "guest uses SELinux" (XXX).
357 case "$selinux_relabel/$created_files" in
358     yes/*)
359         touch $mnt/.autorelabel;;
360     auto/yes)
361         case "$type/$distro" in
362             linux/fedora|linux/rhel|linux/centos|linux/scientificlinux|linux/redhat-based)
363                 touch $mnt/.autorelabel
364                 ;;
365         esac
366         ;;
367 esac
368
369 sync
370
371 fusermount -u $tmpdir/mnt
372 rm -rf $tmpdir
373
374 trap - EXIT ERR
375
376 exit 0