virt-sysprep: Add cron-spool, mail-spool, utmp.
[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     cron_spool=yes
135     dhcp_client_state=yes
136     dhcp_server_state=yes
137     hostname=yes
138     logfiles=yes
139     mail_spool=yes
140     net_hwaddr=yes
141     random_seed=yes
142     rhn_systemid=yes
143     smolt_uuid=yes
144     ssh_hostkeys=yes
145     udev_persistent_net=yes
146     utmp=yes
147     yum_uuid=yes
148 elif [ "$enable" = "list" ]; then
149     echo "cron-spool"
150     echo "dhcp-client-state"
151     echo "dhcp-server-state"
152     echo "hostname"
153     echo "logfiles"
154     echo "mail-spool"
155     echo "net-hwaddr"
156     echo "random-seed"
157     echo "rhn-systemid"
158     echo "smolt-uuid"
159     echo "ssh-hostkeys"
160     echo "udev-persistent-net"
161     echo "utmp"
162     echo "yum-uuid"
163     exit 0
164 else
165     for opt in $(echo "$enable" | sed 's/,/ /g'); do
166         case "$opt" in
167             cron-spool)            cron_spool=yes ;;
168             dhcp-client-state)     dhcp_client_state=yes ;;
169             dhcp-server-state)     dhcp_server_state=yes ;;
170             hostname)              hostname=yes ;;
171             logfiles)              logfiles=yes ;;
172             mail-spool)            mail_spool=yes ;;
173             net-hwaddr)            net_hwaddr=yes ;;
174             random-seed)           random_seed=yes ;;
175             rhn-systemid)          rhn_systemid=yes ;;
176             smolt-uuid)            smolt_uuid=yes ;;
177             ssh-hostkeys)          ssh_hostkeys=yes ;;
178             udev-persistent-net)   udev_persistent_net=yes ;;
179             utmp)                  utmp=yes ;;
180             yum-uuid)              yum_uuid=yes ;;
181             *)
182                 echo "error: unknown --enable feature: $opt"
183                 exit 1
184         esac
185     done
186 fi
187
188 # Make sure there were no extra parameters on the command line.
189 if [ $# -gt 0 ]; then
190     echo "error: $program: extra parameters on the command line"
191     echo
192     usage 1
193 fi
194
195 # Did the user specify at least one -a or -d option?
196 if [ $add_params -eq 0 ]; then
197     echo "error: $program: you need at least one -a or -d option"
198     echo
199     usage 1
200 fi
201
202 # end of command line parsing
203 #----------------------------------------------------------------------
204
205 set -e
206
207 if [ "$verbose" = "yes" ]; then
208     echo params: "${params[@]}"
209 fi
210
211 # Create a temporary directory for general purpose use during operations.
212 tmpdir="$(mktemp -d)"
213
214 cleanup ()
215 {
216     if [ -d $tmpdir/mnt ]; then
217         fusermount -u $tmpdir/mnt >/dev/null 2>&1 ||:
218     fi
219     rm -rf $tmpdir ||:
220 }
221 trap cleanup EXIT ERR
222
223 # Run virt-inspector and grab inspection information about this guest.
224 virt-inspector "${params[@]}" > $tmpdir/xml
225 xmlstarlet sel -t -c \
226     "string(/operatingsystems/operatingsystem[position()=1]/name)" \
227     $tmpdir/xml > $tmpdir/type
228 xmlstarlet sel -t -c \
229     "string(/operatingsystems/operatingsystem[position()=1]/distro)" \
230     $tmpdir/xml > $tmpdir/distro ||:
231 xmlstarlet sel -t -c \
232     "string(/operatingsystems/operatingsystem[position()=1]/package_format)" \
233     $tmpdir/xml > $tmpdir/package_format ||:
234 xmlstarlet sel -t -c \
235     "string(/operatingsystems/operatingsystem[position()=1]/package_management)" \
236     $tmpdir/xml > $tmpdir/package_management ||:
237
238 type="$(cat $tmpdir/type)"
239 distro="$(cat $tmpdir/distro)"
240 package_format="$(cat $tmpdir/package_format)"
241 package_management="$(cat $tmpdir/package_management)"
242
243 # Mount the disk.
244 mkdir $tmpdir/mnt
245 guestmount --rw -i "${params[@]}" $tmpdir/mnt
246
247 mnt="$tmpdir/mnt"
248
249 #----------------------------------------------------------------------
250 # The sysprep operations.
251
252 if [ "$cron_spool" = "yes" ]; then
253     rm -rf $mnt/var/spool/cron/*
254 fi
255
256 if [ "$dhcp_client_state" = "yes" ]; then
257     case "$type" in
258         linux)
259             rm -rf $mnt/var/lib/dhclient/*
260             # RHEL 3:
261             rm -rf $mnt/var/lib/dhcp/*
262             ;;
263     esac
264 fi
265
266 if [ "$dhcp_server_state" = "yes" ]; then
267     case "$type" in
268         linux)
269             rm -rf $mnt/var/lib/dhcpd/*
270             ;;
271     esac
272 fi
273
274 if [ "$hostname" = "yes" ]; then
275     case "$type/$distro" in
276         linux/fedora)
277             echo "HOSTNAME=$hostname_param" > $mnt/etc/sysconfig/network.new
278             sed '/^HOSTNAME=/d' < $mnt/etc/sysconfig/network >> $mnt/etc/sysconfig/network.new
279             mv -f $mnt/etc/sysconfig/network.new $mnt/etc/sysconfig/network
280             created_files=yes
281             ;;
282         linux/debian|linux/ubuntu)
283             echo "$hostname_param" > $mnt/etc/hostname
284             created_files=yes
285             ;;
286     esac
287 fi
288
289 if [ "$logfiles" = "yes" ]; then
290     case "$type" in
291         linux)
292             rm -rf $mnt/var/log/*.log*
293             rm -rf $mnt/var/log/audit/*
294             rm -rf $mnt/var/log/btmp*
295             rm -rf $mnt/var/log/cron*
296             rm -rf $mnt/var/log/dmesg*
297             rm -rf $mnt/var/log/lastlog*
298             rm -rf $mnt/var/log/maillog*
299             rm -rf $mnt/var/log/mail/*
300             rm -rf $mnt/var/log/messages*
301             rm -rf $mnt/var/log/secure*
302             rm -rf $mnt/var/log/spooler*
303             rm -rf $mnt/var/log/tallylog*
304             rm -rf $mnt/var/log/wtmp*
305             ;;
306     esac
307 fi
308
309 if [ "$mail_spool" = "yes" ]; then
310     rm -rf $mnt/var/spool/mail/*
311     rm -rf $mnt/var/mail/*
312 fi
313
314 if [ "$net_hwaddr" = "yes" ]; then
315     case "$type/$distro" in
316         linux/fedora)
317             if [ -d $mnt/etc/sysconfig/network-scripts ]; then
318                 rm_hwaddr ()
319                 {
320                     sed '/^HWADDR=/d' < "$1" > "$1.new"
321                     mv -f "$1.new" "$1"
322                 }
323                 export -f rm_hwaddr
324                 find $mnt/etc/sysconfig/network-scripts \
325                     -name 'ifcfg-*' -type f \
326                     -exec bash -c 'rm_hwaddr "$0"' {} \;
327                 created_files=yes
328             fi
329             ;;
330     esac
331 fi
332
333 if [ "$random_seed" = "yes" -a "$type" = "linux" ]; then
334     f=
335     if [ -f $mnt/var/lib/random-seed ]; then
336         # Fedora
337         f=$mnt/var/lib/random-seed
338     elif [ -f $mnt/var/lib/urandom/random-seed ]; then
339         # Debian
340         f=$mnt/var/lib/urandom/random-seed
341     fi
342     if [ -n "$f" ]; then
343         dd if=/dev/urandom of="$f" bs=8 count=1 conv=nocreat,notrunc 2>/dev/null
344     fi
345 fi
346
347 if [ "$rhn_systemid" = "yes" -a "$type/$distro" = "linux/rhel" ]; then
348     rm -f $mnt/etc/sysconfig/rhn/systemid
349 fi
350
351 if [ "$smolt_uuid" = "yes" -a "$type" = "linux" ]; then
352     rm -f $mnt/etc/sysconfig/hw-uuid
353     rm -f $mnt/etc/smolt/uuid
354     rm -f $mnt/etc/smolt/hw-uuid
355 fi
356
357 if [ "$ssh_hostkeys" = "yes" -a "$type" != "windows" ]; then
358     rm -rf $mnt/etc/ssh/*_host_*
359 fi
360
361 if [ "$udev_persistent_net" = "yes" -a "$type" = "linux" ]; then
362     rm -f $mnt/etc/udev/rules.d/70-persistent-net.rules
363 fi
364
365 if [ "$utmp" = "yes" -a "$type" != "windows" ]; then
366     rm -f $mnt/var/run/utmp
367 fi
368
369 if [ "$yum_uuid" = "yes" -a "$package_management" = "yum" ]; then
370     rm -f $mnt/var/lib/yum/uuid
371 fi
372
373 #----------------------------------------------------------------------
374 # Clean up and close down.
375
376 # If we created any new files and the guest uses SELinux, then we have
377 # to relabel the filesystem on boot.  Could do with a better way to
378 # test "guest uses SELinux" (XXX).
379 case "$selinux_relabel/$created_files" in
380     yes/*)
381         touch $mnt/.autorelabel;;
382     auto/yes)
383         case "$type/$distro" in
384             linux/fedora|linux/rhel|linux/centos|linux/scientificlinux|linux/redhat-based)
385                 touch $mnt/.autorelabel
386                 ;;
387         esac
388         ;;
389 esac
390
391 sync
392
393 fusermount -u $tmpdir/mnt
394 rm -rf $tmpdir
395
396 trap - EXIT ERR
397
398 exit 0