#!/bin/bash - # @configure_input@ # libguestfs virt-sysprep tool # Copyright (C) 2011 Red Hat Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. unset CDPATH program="virt-sysprep" version="@PACKAGE_VERSION@" # Uncomment this to see every shell command that is executed. #set -x TEMP=`getopt \ -o a:c:d:vVx \ --long help,add:,connect:,domain:,enable:,format::,hostname:,list-operations,selinux-relabel,no-selinux-relabel,verbose,version \ -n $program -- "$@"` if [ $? != 0 ]; then echo "$program: problem parsing the command line arguments" exit 1 fi eval set -- "$TEMP" # This array accumulates the arguments we pass through to guestmount. declare -a params i=0 verbose= add_params=0 enable= hostname_param=localhost.localdomain selinux_relabel=auto usage () { echo "Usage:" echo " $program [--options] -d domname" echo " $program [--options] -a disk.img [-a disk.img ...]" echo echo "Read $program(1) man page for more information." echo echo "NOTE: $program modifies the guest or disk image *in place*." exit $1 } while true; do case "$1" in -a|--add) params[i++]="-a" params[i++]="$2" ((add_params++)) shift 2;; -c|--connect) params[i++]="-c" params[i++]="$2" shift 2;; -d|--domain) params[i++]="-d" params[i++]="$2" ((add_params++)) shift 2;; --enable) if [ -n "$enable" ]; then echo "error: --enable option can only be given once" exit 1 fi enable="$2" shift 2;; --format) if [ -n "$2" ]; then params[i++]="--format=$2" else params[i++]="--format" fi shift 2;; --help) usage 0;; --hostname) hostname_param="$2" shift 2;; --list-operations) enable=list shift;; --selinux-relabel) selinux_relabel=yes shift;; --no-selinux-relabel) selinux_relabel=no shift;; -v|--verbose) params[i++]="-v" verbose=yes shift;; -V|--version) echo "$program $version" exit 0;; -x) # Can't pass the -x option directly to guestmount because # that stops guestmount from forking, which means we can't # coordinate with guestmount when it has finished # initializing. So instead set just the underlying option # in libguestfs by exporting LIBGUESTFS_TRACE. # Unfortunately (a) this omits FUSE calls, but don't worry # about that for now, and more importantly (b) trace # messages disappear into never-never land after the fork. export LIBGUESTFS_TRACE=1 shift;; --) shift break;; *) echo "Internal error!" exit 1;; esac done # Different sysprep operations that can be enabled. Default is to # enable all of these, although some of them are only done on certain # guest types (see details below). if [ -z "$enable" ]; then cron_spool=yes dhcp_client_state=yes dhcp_server_state=yes hostname=yes logfiles=yes mail_spool=yes net_hwaddr=yes random_seed=yes rhn_systemid=yes smolt_uuid=yes ssh_hostkeys=yes udev_persistent_net=yes utmp=yes yum_uuid=yes elif [ "$enable" = "list" ]; then echo "cron-spool" echo "dhcp-client-state" echo "dhcp-server-state" echo "hostname" echo "logfiles" echo "mail-spool" echo "net-hwaddr" echo "random-seed" echo "rhn-systemid" echo "smolt-uuid" echo "ssh-hostkeys" echo "udev-persistent-net" echo "utmp" echo "yum-uuid" exit 0 else for opt in $(echo "$enable" | sed 's/,/ /g'); do case "$opt" in cron-spool) cron_spool=yes ;; dhcp-client-state) dhcp_client_state=yes ;; dhcp-server-state) dhcp_server_state=yes ;; hostname) hostname=yes ;; logfiles) logfiles=yes ;; mail-spool) mail_spool=yes ;; net-hwaddr) net_hwaddr=yes ;; random-seed) random_seed=yes ;; rhn-systemid) rhn_systemid=yes ;; smolt-uuid) smolt_uuid=yes ;; ssh-hostkeys) ssh_hostkeys=yes ;; udev-persistent-net) udev_persistent_net=yes ;; utmp) utmp=yes ;; yum-uuid) yum_uuid=yes ;; *) echo "error: unknown --enable feature: $opt" exit 1 esac done fi # Make sure there were no extra parameters on the command line. if [ $# -gt 0 ]; then echo "error: $program: extra parameters on the command line" echo usage 1 fi # Did the user specify at least one -a or -d option? if [ $add_params -eq 0 ]; then echo "error: $program: you need at least one -a or -d option" echo usage 1 fi # end of command line parsing #---------------------------------------------------------------------- set -e if [ "$verbose" = "yes" ]; then echo params: "${params[@]}" fi # Create a temporary directory for general purpose use during operations. tmpdir="$(mktemp -d)" cleanup () { if [ -d $tmpdir/mnt ]; then fusermount -u $tmpdir/mnt >/dev/null 2>&1 ||: fi rm -rf $tmpdir ||: } trap cleanup EXIT ERR # Run virt-inspector and grab inspection information about this guest. virt-inspector "${params[@]}" > $tmpdir/xml virt-inspector --xpath \ "string(/operatingsystems/operatingsystem[position()=1]/name)" \ < $tmpdir/xml > $tmpdir/type virt-inspector --xpath \ "string(/operatingsystems/operatingsystem[position()=1]/distro)" \ < $tmpdir/xml > $tmpdir/distro ||: virt-inspector --xpath \ "string(/operatingsystems/operatingsystem[position()=1]/package_format)" \ < $tmpdir/xml > $tmpdir/package_format ||: virt-inspector --xpath \ "string(/operatingsystems/operatingsystem[position()=1]/package_management)" \ < $tmpdir/xml > $tmpdir/package_management ||: type="$(cat $tmpdir/type)" distro="$(cat $tmpdir/distro)" package_format="$(cat $tmpdir/package_format)" package_management="$(cat $tmpdir/package_management)" # Mount the disk. mkdir $tmpdir/mnt guestmount --rw -i "${params[@]}" $tmpdir/mnt mnt="$tmpdir/mnt" #---------------------------------------------------------------------- # The sysprep operations. if [ "$cron_spool" = "yes" ]; then rm -rf $mnt/var/spool/cron/* fi if [ "$dhcp_client_state" = "yes" ]; then case "$type" in linux) rm -rf $mnt/var/lib/dhclient/* # RHEL 3: rm -rf $mnt/var/lib/dhcp/* ;; esac fi if [ "$dhcp_server_state" = "yes" ]; then case "$type" in linux) rm -rf $mnt/var/lib/dhcpd/* ;; esac fi if [ "$hostname" = "yes" ]; then case "$type/$distro" in linux/fedora) echo "HOSTNAME=$hostname_param" > $mnt/etc/sysconfig/network.new sed '/^HOSTNAME=/d' < $mnt/etc/sysconfig/network >> $mnt/etc/sysconfig/network.new mv -f $mnt/etc/sysconfig/network.new $mnt/etc/sysconfig/network created_files=yes ;; linux/debian|linux/ubuntu) echo "$hostname_param" > $mnt/etc/hostname created_files=yes ;; esac fi if [ "$logfiles" = "yes" ]; then case "$type" in linux) rm -rf $mnt/var/log/*.log* rm -rf $mnt/var/log/audit/* rm -rf $mnt/var/log/btmp* rm -rf $mnt/var/log/cron* rm -rf $mnt/var/log/dmesg* rm -rf $mnt/var/log/lastlog* rm -rf $mnt/var/log/maillog* rm -rf $mnt/var/log/mail/* rm -rf $mnt/var/log/messages* rm -rf $mnt/var/log/secure* rm -rf $mnt/var/log/spooler* rm -rf $mnt/var/log/tallylog* rm -rf $mnt/var/log/wtmp* ;; esac fi if [ "$mail_spool" = "yes" ]; then rm -rf $mnt/var/spool/mail/* rm -rf $mnt/var/mail/* fi if [ "$net_hwaddr" = "yes" ]; then case "$type/$distro" in linux/fedora) if [ -d $mnt/etc/sysconfig/network-scripts ]; then rm_hwaddr () { sed '/^HWADDR=/d' < "$1" > "$1.new" mv -f "$1.new" "$1" } export -f rm_hwaddr find $mnt/etc/sysconfig/network-scripts \ -name 'ifcfg-*' -type f \ -exec bash -c 'rm_hwaddr "$0"' {} \; created_files=yes fi ;; esac fi if [ "$random_seed" = "yes" -a "$type" = "linux" ]; then f= if [ -f $mnt/var/lib/random-seed ]; then # Fedora f=$mnt/var/lib/random-seed elif [ -f $mnt/var/lib/urandom/random-seed ]; then # Debian f=$mnt/var/lib/urandom/random-seed fi if [ -n "$f" ]; then dd if=/dev/urandom of="$f" bs=8 count=1 conv=nocreat,notrunc 2>/dev/null fi fi if [ "$rhn_systemid" = "yes" -a "$type/$distro" = "linux/rhel" ]; then rm -f $mnt/etc/sysconfig/rhn/systemid fi if [ "$smolt_uuid" = "yes" -a "$type" = "linux" ]; then rm -f $mnt/etc/sysconfig/hw-uuid rm -f $mnt/etc/smolt/uuid rm -f $mnt/etc/smolt/hw-uuid fi if [ "$ssh_hostkeys" = "yes" -a "$type" != "windows" ]; then rm -rf $mnt/etc/ssh/*_host_* fi if [ "$udev_persistent_net" = "yes" -a "$type" = "linux" ]; then rm -f $mnt/etc/udev/rules.d/70-persistent-net.rules fi if [ "$utmp" = "yes" -a "$type" != "windows" ]; then rm -f $mnt/var/run/utmp fi if [ "$yum_uuid" = "yes" -a "$package_management" = "yum" ]; then rm -f $mnt/var/lib/yum/uuid fi #---------------------------------------------------------------------- # Clean up and close down. # If we created any new files and the guest uses SELinux, then we have # to relabel the filesystem on boot. Could do with a better way to # test "guest uses SELinux" (XXX). case "$selinux_relabel/$created_files" in yes/*) touch $mnt/.autorelabel;; auto/yes) case "$type/$distro" in linux/fedora|linux/rhel|linux/centos|linux/scientificlinux|linux/redhat-based) touch $mnt/.autorelabel ;; esac ;; esac sync fusermount -u $tmpdir/mnt rm -rf $tmpdir trap - EXIT ERR exit 0