X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=clone%2Fvirt-sysprep.in;h=144bad4213f2d604baea14790735baa7091172dc;hp=b98e4b062920f2fd4ff093f577a0a6e9fc4eba12;hb=c019d073a4f218b538d5176500b014e83dbce84f;hpb=17b26be2325348da1c7f6edf28397b8e45e9e499 diff --git a/clone/virt-sysprep.in b/clone/virt-sysprep.in index b98e4b0..144bad4 100644 --- a/clone/virt-sysprep.in +++ b/clone/virt-sysprep.in @@ -21,9 +21,12 @@ 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,verbose,version \ + --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" @@ -31,18 +34,15 @@ if [ $? != 0 ]; then fi eval set -- "$TEMP" -# This array accumulates the arguments we pass through to guestfish. -declare -a guestfish -guestfish[0]="guestfish" -guestfish[1]="--rw" -guestfish[2]="--listen" -guestfish[3]="-i" -i=4 +# 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 () { @@ -59,17 +59,17 @@ usage () while true; do case "$1" in -a|--add) - guestfish[i++]="-a" - guestfish[i++]="$2" + params[i++]="-a" + params[i++]="$2" ((add_params++)) shift 2;; -c|--connect) - guestfish[i++]="-c" - guestfish[i++]="$2" + params[i++]="-c" + params[i++]="$2" shift 2;; -d|--domain) - guestfish[i++]="-d" - guestfish[i++]="$2" + params[i++]="-d" + params[i++]="$2" ((add_params++)) shift 2;; --enable) @@ -81,9 +81,9 @@ while true; do shift 2;; --format) if [ -n "$2" ]; then - guestfish[i++]="--format=$2" + params[i++]="--format=$2" else - guestfish[i++]="--format" + params[i++]="--format" fi shift 2;; --help) @@ -94,15 +94,29 @@ while true; do --list-operations) enable=list shift;; + --selinux-relabel) + selinux_relabel=yes + shift;; + --no-selinux-relabel) + selinux_relabel=no + shift;; -v|--verbose) - guestfish[i++]="-v" + params[i++]="-v" verbose=yes shift;; -V|--version) echo "$program $version" exit 0;; -x) - guestfish[i++]="-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 @@ -117,23 +131,53 @@ done # 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 @@ -161,143 +205,194 @@ fi set -e if [ "$verbose" = "yes" ]; then - echo command: "${guestfish[@]}" + echo params: "${params[@]}" fi # Create a temporary directory for general purpose use during operations. tmpdir="$(mktemp -d)" -# Call guestfish. -GUESTFISH_PID= -eval $("${guestfish[@]}") -if [ -z "$GUESTFISH_PID" ]; then - echo "$program: guestfish didn't start up, see error messages above" - exit 1 -fi - -# Helper. -gf="guestfish --remote --" - cleanup () { - $gf exit >/dev/null 2>&1 ||: - rm -rf "$tmpdir" ||: + if [ -d $tmpdir/mnt ]; then + fusermount -u $tmpdir/mnt >/dev/null 2>&1 ||: + fi + rm -rf $tmpdir ||: } trap cleanup EXIT ERR -# Launch back-end, inspect for operating systems, and get the guest -# root disk. -root=$($gf inspect-get-roots) +# Run virt-inspector and grab inspection information about this guest. +virt-inspector "${params[@]}" > $tmpdir/xml +xmlstarlet sel -t -c \ + "string(/operatingsystems/operatingsystem[position()=1]/name)" \ + $tmpdir/xml > $tmpdir/type +xmlstarlet sel -t -c \ + "string(/operatingsystems/operatingsystem[position()=1]/distro)" \ + $tmpdir/xml > $tmpdir/distro ||: +xmlstarlet sel -t -c \ + "string(/operatingsystems/operatingsystem[position()=1]/package_format)" \ + $tmpdir/xml > $tmpdir/package_format ||: +xmlstarlet sel -t -c \ + "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" -if [ "$root" = "" ]; then - echo "$program: no operating system was found on this disk" - exit 1 -fi +#---------------------------------------------------------------------- +# The sysprep operations. -if [ "$verbose" = "yes" ]; then - echo root: "$root" +if [ "$cron_spool" = "yes" ]; then + rm -rf $mnt/var/spool/cron/* fi -# Get the guest type. -type="$($gf -inspect-get-type $root)" - -if [ "$type" = "linux" ]; then - distro="$($gf -inspect-get-distro $root)" +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 [ "$type" = "windows" ]; then - systemroot="$($gf -inspect-get-windows-systemroot $root)" +if [ "$dhcp_server_state" = "yes" ]; then + case "$type" in + linux) + rm -rf $mnt/var/lib/dhcpd/* + ;; + esac fi -#---------------------------------------------------------------------- -# Useful functions. - -# erase_line filename regex -# -# Erase line(s) in a file that match the given regex. -erase_line () -{ - $gf download "$1" "$tmpdir/file" - sed "/$2/d" < "$tmpdir/file" > "$tmpdir/file.1" - $gf upload "$tmpdir/file.1" "$1" -} - -# prepend_line filename line -# -# Prepend a line to a file (this is better than appending, because it -# works even when the original file isn't terminated with a newline). -prepend_line () -{ - $gf download "$1" "$tmpdir/file" - echo "$2" > "$tmpdir/file.1" - cat "$tmpdir/file.1" "$tmpdir/file" >> "$tmpdir/file.2" - $gf upload "$tmpdir/file.2" "$1" -} - -# rm_files wildcard -# -# Remove files. Doesn't fail if no files exist. Note the wildcard -# parameter cannot contain spaces or characters that need special -# quoting. -rm_files () -{ - files=$($gf glob-expand "$1") - for f in $files; do - $gf rm "$f" - done -} - -# rm_file filename -# -# Remove a single file. No error if the file doesn't exist or is not -# a file. -rm_file () -{ - t=$($gf is-file "$1") - if [ "$t" = "true" ]; then - $gf rm "$1" - fi -} - -#---------------------------------------------------------------------- -# The sysprep operations. - if [ "$hostname" = "yes" ]; then case "$type/$distro" in linux/fedora) - erase_line /etc/sysconfig/network "^HOSTNAME=" - prepend_line /etc/sysconfig/network "HOSTNAME=$hostname_param" + 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) - $gf write /etc/hostname "$hostname_param" + 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) - # XXX these filenames can have spaces and untrusted chars in them! - files=$($gf glob-expand '/etc/sysconfig/network-scripts/ifcfg-*') - for f in $files; do - erase_line "$f" "^HWADDR=" - done + 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_files "/etc/ssh/*_host_*" + rm -rf $mnt/etc/ssh/*_host_* fi if [ "$udev_persistent_net" = "yes" -a "$type" = "linux" ]; then - rm_file /etc/udev/rules.d/70-persistent-net.rules + 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. -$gf umount-all -$gf sync -$gf exit +# 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