mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
399 lines
11 KiB
Bash
399 lines
11 KiB
Bash
#!/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., 675 Mass Ave, Cambridge, MA 02139, 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
|