summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--Makefile.am7
-rw-r--r--clone/Makefile.am51
-rw-r--r--clone/virt-sysprep.in408
-rw-r--r--configure.ac4
-rwxr-xr-xcontrib/make-check-on-installed.pl2
-rw-r--r--po-docs/ja/Makefile.am9
-rw-r--r--po-docs/podfiles4
-rw-r--r--po-docs/uk/Makefile.am9
-rw-r--r--src/guestfs.pod9
-rw-r--r--sysprep/Makefile.am150
-rw-r--r--sysprep/main.ml232
-rw-r--r--sysprep/sysprep_operation.ml183
-rw-r--r--sysprep/sysprep_operation.mli99
-rw-r--r--sysprep/sysprep_operation_cron_spool.ml34
-rw-r--r--sysprep/sysprep_operation_dhcp_client_state.ml39
-rw-r--r--sysprep/sysprep_operation_dhcp_server_state.ml34
-rw-r--r--sysprep/sysprep_operation_hostname.ml69
-rw-r--r--sysprep/sysprep_operation_logfiles.ml53
-rw-r--r--sysprep/sysprep_operation_mail_spool.ml39
-rw-r--r--sysprep/sysprep_operation_net_hwaddr.ml54
-rw-r--r--sysprep/sysprep_operation_random_seed.ml57
-rw-r--r--sysprep/sysprep_operation_rhn_systemid.ml40
-rw-r--r--sysprep/sysprep_operation_smolt_uuid.ml44
-rw-r--r--sysprep/sysprep_operation_ssh_hostkeys.ml51
-rw-r--r--sysprep/sysprep_operation_udev_persistent_net.ml46
-rw-r--r--sysprep/sysprep_operation_utmp.ml43
-rw-r--r--sysprep/sysprep_operation_yum_uuid.ml42
-rwxr-xr-xsysprep/test-virt-sysprep.sh (renamed from clone/test-virt-sysprep.sh)20
-rw-r--r--sysprep/utils.ml71
-rwxr-xr-xsysprep/virt-sysprep.pod (renamed from clone/virt-sysprep.pod)130
-rw-r--r--tests/extra/Makefile.am4
32 files changed, 1439 insertions, 607 deletions
diff --git a/.gitignore b/.gitignore
index 14d5c758..337ff751 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,9 +26,6 @@ cat/virt-ls
cat/virt-ls.1
ChangeLog
*.class
-clone/stamp-virt-sysprep.pod
-clone/virt-sysprep
-clone/virt-sysprep.1
*.cma
*.cmi
*.cmo
@@ -345,6 +342,12 @@ src/libguestfs.syms
src/.libs/libguestfs.so
src/stamp-guestfs.pod
stamp-h1
+sysprep/.depend
+sysprep/stamp-virt-sysprep.pod
+sysprep/sysprep-extra-options.pod
+sysprep/sysprep-operations.pod
+sysprep/virt-sysprep
+sysprep/virt-sysprep.1
*.swp
test1.img
test2.img
diff --git a/Makefile.am b/Makefile.am
index de2d4caf..5f104fa9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -91,7 +91,7 @@ SUBDIRS += csharp
# virt-resize (new version) and virt-sparsify are written in OCaml.
if HAVE_OCAML
-SUBDIRS += resize sparsify
+SUBDIRS += resize sparsify sysprep
endif
# Perl tools.
@@ -104,11 +104,6 @@ if HAVE_FUSE
SUBDIRS += fuse
endif
-# virt-tools in shell. This uses guestmount and virt-inspector.
-if HAVE_FUSE
-SUBDIRS += clone
-endif
-
# po-docs must come after tools, inspector.
if HAVE_PO4A
SUBDIRS += po-docs
diff --git a/clone/Makefile.am b/clone/Makefile.am
deleted file mode 100644
index 4d586c67..00000000
--- a/clone/Makefile.am
+++ /dev/null
@@ -1,51 +0,0 @@
-# libguestfs cloning tools
-# 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.
-
-include $(top_srcdir)/subdir-rules.mk
-
-EXTRA_DIST = \
- test-virt-sysprep.sh \
- virt-sysprep.pod
-
-CLEANFILES = stamp-virt-sysprep.pod
-
-bin_SCRIPTS = virt-sysprep
-
-# Manual pages and HTML files for the website.
-man_MANS = virt-sysprep.1
-noinst_DATA = $(top_builddir)/html/virt-sysprep.1.html
-
-virt-sysprep.1 $(top_builddir)/html/virt-sysprep.1.html: stamp-virt-sysprep.pod
-
-stamp-virt-sysprep.pod: virt-sysprep.pod
- $(top_builddir)/podwrapper.sh \
- --man virt-sysprep.1 \
- --html $(top_builddir)/html/virt-sysprep.1.html \
- $<
- touch $@
-
-# Tests.
-
-random_val := $(shell awk 'BEGIN{srand(); print 1+int(255*rand())}' < /dev/null)
-
-TESTS_ENVIRONMENT = \
- MALLOC_PERTURB_=$(random_val) \
- $(top_builddir)/run
-
-if ENABLE_APPLIANCE
-TESTS = test-virt-sysprep.sh
-endif ENABLE_APPLIANCE
diff --git a/clone/virt-sysprep.in b/clone/virt-sysprep.in
deleted file mode 100644
index d5055327..00000000
--- a/clone/virt-sysprep.in
+++ /dev/null
@@ -1,408 +0,0 @@
-#!/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|linux/rhel)
- 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|linux/rhel)
- 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
-
-# Unfortunately various unwanted processes jump into mountpoints.
-# tracker-miner-fs is the latest, previously it was something called
-# gvfs-gdu-volume-monitor. Therefore if the mountpoint is busy, try
-# to unmount it a few times. XXX
-count=10
-while ! fusermount -u $tmpdir/mnt && [ $count -gt 0 ]; do
- sleep 1
- ((count--))
-done
-if [ $count -eq 0 ]; then exit 1; fi
-
-rm -rf $tmpdir
-
-trap - EXIT ERR
-
-exit 0
diff --git a/configure.ac b/configure.ac
index b8cc8030..6d344602 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1149,8 +1149,6 @@ AC_CONFIG_HEADERS([config.h])
dnl http://www.mail-archive.com/automake@gnu.org/msg10204.html
AC_CONFIG_FILES([appliance/libguestfs-make-fixed-appliance],
[chmod +x appliance/libguestfs-make-fixed-appliance])
-AC_CONFIG_FILES([clone/virt-sysprep],
- [chmod +x clone/virt-sysprep])
AC_CONFIG_FILES([podwrapper.sh],
[chmod +x podwrapper.sh])
AC_CONFIG_FILES([run],
@@ -1159,7 +1157,6 @@ AC_CONFIG_FILES([Makefile
align/Makefile
appliance/Makefile
cat/Makefile
- clone/Makefile
csharp/Makefile
daemon/Makefile
df/Makefile
@@ -1201,6 +1198,7 @@ AC_CONFIG_FILES([Makefile
ruby/examples/Makefile
sparsify/Makefile
src/Makefile
+ sysprep/Makefile
test-tool/Makefile
tests/c-api/Makefile
tests/data/Makefile
diff --git a/contrib/make-check-on-installed.pl b/contrib/make-check-on-installed.pl
index dc12dc5e..83c408d4 100755
--- a/contrib/make-check-on-installed.pl
+++ b/contrib/make-check-on-installed.pl
@@ -80,7 +80,7 @@ my %mapping = (
'/bin/virt-rescue$' => "rescue",
'/bin/virt-resize$' => "resize",
'/bin/virt-sparsify$' => "sparsify",
- '/bin/virt-sysprep$' => "clone",
+ '/bin/virt-sysprep$' => "sysprep",
'/bin/virt-tar$' => "tools",
'/bin/virt-tar-in$' => "fish",
'/bin/virt-tar-out$' => "fish",
diff --git a/po-docs/ja/Makefile.am b/po-docs/ja/Makefile.am
index 2aa26bf8..a3350219 100644
--- a/po-docs/ja/Makefile.am
+++ b/po-docs/ja/Makefile.am
@@ -70,6 +70,15 @@ guestfish.1: guestfish.pod guestfish-actions.pod guestfish-commands.pod
--insert $(srcdir)/guestfish-commands.pod:@FISH_COMMANDS@ \
$<
+virt-sysprep.1: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.pod
+ $(top_builddir)/podwrapper.sh \
+ --man virt-sysprep.1 \
+ --insert sysprep-extra-options.pod:@EXTRA_OPTIONS@ \
+ --insert sysprep-operations.pod:@OPERATIONS@ \
+ --html $(top_builddir)/html/virt-sysprep.1.html \
+ $<
+ touch $@
+
%.1: %.pod
$(top_builddir)/podwrapper.sh --man $@ $<
diff --git a/po-docs/podfiles b/po-docs/podfiles
index d207ba32..a9139845 100644
--- a/po-docs/podfiles
+++ b/po-docs/podfiles
@@ -3,7 +3,6 @@
../cat/virt-cat.pod
../cat/virt-filesystems.pod
../cat/virt-ls.pod
-../clone/virt-sysprep.pod
../df/virt-df.pod
../edit/virt-edit.pod
../erlang/examples/guestfs-erlang.pod
@@ -32,6 +31,9 @@
../src/guestfs-availability.pod
../src/guestfs-structs.pod
../src/guestfs.pod
+../sysprep/sysprep-extra-options.pod
+../sysprep/sysprep-operations.pod
+../sysprep/virt-sysprep.pod
../test-tool/libguestfs-test-tool.pod
../tools/virt-list-filesystems.pl
../tools/virt-list-partitions.pl
diff --git a/po-docs/uk/Makefile.am b/po-docs/uk/Makefile.am
index 2aa26bf8..a3350219 100644
--- a/po-docs/uk/Makefile.am
+++ b/po-docs/uk/Makefile.am
@@ -70,6 +70,15 @@ guestfish.1: guestfish.pod guestfish-actions.pod guestfish-commands.pod
--insert $(srcdir)/guestfish-commands.pod:@FISH_COMMANDS@ \
$<
+virt-sysprep.1: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.pod
+ $(top_builddir)/podwrapper.sh \
+ --man virt-sysprep.1 \
+ --insert sysprep-extra-options.pod:@EXTRA_OPTIONS@ \
+ --insert sysprep-operations.pod:@OPERATIONS@ \
+ --html $(top_builddir)/html/virt-sysprep.1.html \
+ $<
+ touch $@
+
%.1: %.pod
$(top_builddir)/podwrapper.sh --man $@ $<
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 05f5c746..e2377b82 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -3008,11 +3008,6 @@ The libguestfs appliance, build scripts and so on.
The L<virt-cat(1)>, L<virt-filesystems(1)> and L<virt-ls(1)> commands
and documentation.
-=item C<clone>
-
-Tools for cloning virtual machines. Currently contains
-L<virt-sysprep(1)> command and documentation.
-
=item C<contrib>
Outside contributions, experimental parts.
@@ -3091,6 +3086,10 @@ L<virt-sparsify(1)> command and documentation.
Source code to the C library.
+=item C<sysprep>
+
+L<virt-sysprep(1)> command and documentation.
+
=item C<test-tool>
Test tool for end users to test if their qemu/kernel combination
diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am
new file mode 100644
index 00000000..034252e0
--- /dev/null
+++ b/sysprep/Makefile.am
@@ -0,0 +1,150 @@
+# libguestfs virt-sysprep tool
+# Copyright (C) 2012 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.
+
+include $(top_srcdir)/subdir-rules.mk
+
+SOURCES =
+
+EXTRA_DIST = \
+ $(SOURCES) \
+ test-virt-sysprep.sh \
+ virt-sysprep.pod
+
+CLEANFILES = stamp-virt-sysprep.pod
+
+if HAVE_OCAML
+
+# Alphabetical order.
+SOURCES += \
+ main.ml \
+ sysprep_operation.ml \
+ sysprep_operation.mli \
+ sysprep_operation_cron_spool.ml \
+ sysprep_operation_dhcp_client_state.ml \
+ sysprep_operation_dhcp_server_state.ml \
+ sysprep_operation_hostname.ml \
+ sysprep_operation_logfiles.ml \
+ sysprep_operation_mail_spool.ml \
+ sysprep_operation_net_hwaddr.ml \
+ sysprep_operation_random_seed.ml \
+ sysprep_operation_rhn_systemid.ml \
+ sysprep_operation_smolt_uuid.ml \
+ sysprep_operation_ssh_hostkeys.ml \
+ sysprep_operation_udev_persistent_net.ml \
+ sysprep_operation_utmp.ml \
+ sysprep_operation_yum_uuid.ml \
+ utils.ml
+
+# Note this list must be in dependency order.
+OBJECTS = \
+ utils.cmx \
+ sysprep_operation.cmx \
+ sysprep_operation_cron_spool.cmx \
+ sysprep_operation_dhcp_client_state.cmx \
+ sysprep_operation_dhcp_server_state.cmx \
+ sysprep_operation_hostname.cmx \
+ sysprep_operation_logfiles.cmx \
+ sysprep_operation_mail_spool.cmx \
+ sysprep_operation_net_hwaddr.cmx \
+ sysprep_operation_random_seed.cmx \
+ sysprep_operation_rhn_systemid.cmx \
+ sysprep_operation_smolt_uuid.cmx \
+ sysprep_operation_ssh_hostkeys.cmx \
+ sysprep_operation_udev_persistent_net.cmx \
+ sysprep_operation_utmp.cmx \
+ sysprep_operation_yum_uuid.cmx \
+ main.cmx
+
+bin_SCRIPTS = virt-sysprep
+
+# -I $(top_builddir)/src/.libs is a hack which forces corresponding -L
+# option to be passed to gcc, so we don't try linking against an
+# installed copy of libguestfs.
+OCAMLPACKAGES = -package unix -I $(top_builddir)/src/.libs -I $(top_builddir)/ocaml
+
+OCAMLCFLAGS = -g -warn-error CDEFLMPSUVYZX $(OCAMLPACKAGES)
+OCAMLOPTFLAGS = $(OCAMLCFLAGS)
+
+virt-sysprep: $(OBJECTS)
+ $(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) \
+ mlguestfs.cmxa -linkpkg $^ -cclib -lncurses -o $@
+
+.mli.cmi:
+ $(OCAMLFIND) ocamlc $(OCAMLCFLAGS) -c $< -o $@
+.ml.cmo:
+ $(OCAMLFIND) ocamlc $(OCAMLCFLAGS) -c $< -o $@
+.ml.cmx:
+ $(OCAMLFIND) ocamlopt $(OCAMLCFLAGS) -c $< -o $@
+
+# Manual pages and HTML files for the website.
+man_MANS = virt-sysprep.1
+noinst_DATA = $(top_builddir)/html/virt-sysprep.1.html
+
+virt-sysprep.1 $(top_builddir)/html/virt-sysprep.1.html: stamp-virt-sysprep.pod
+
+stamp-virt-sysprep.pod: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.pod
+ $(top_builddir)/podwrapper.sh \
+ --man virt-sysprep.1 \
+ --insert sysprep-extra-options.pod:@EXTRA_OPTIONS@ \
+ --insert sysprep-operations.pod:@OPERATIONS@ \
+ --html $(top_builddir)/html/virt-sysprep.1.html \
+ $<
+ touch $@
+
+sysprep-extra-options.pod: virt-sysprep
+ rm -f $@ $@-t
+ ./$< --dump-pod-options > $@-t
+ mv $@-t $@
+
+sysprep-operations.pod: virt-sysprep
+ rm -f $@ $@-t
+ ./$< --dump-pod > $@-t
+ mv $@-t $@
+
+# Tests.
+
+random_val := $(shell awk 'BEGIN{srand(); print 1+int(255*rand())}' < /dev/null)
+
+TESTS_ENVIRONMENT = \
+ MALLOC_PERTURB_=$(random_val) \
+ $(top_builddir)/run
+
+if ENABLE_APPLIANCE
+TESTS = test-virt-sysprep.sh
+endif ENABLE_APPLIANCE
+
+# Dependencies.
+depend: .depend
+
+.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
+ rm -f $@ $@-t
+ $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) $^ | \
+ $(SED) 's/ *$$//' | \
+ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
+ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
+ sort > $@-t
+ mv $@-t $@
+
+-include .depend
+
+endif
+
+.PHONY: depend docs
+
+# Parallel builds don't obey dependencies for some reason we
+# don't understand.
+.NOTPARALLEL:
diff --git a/sysprep/main.ml b/sysprep/main.ml
new file mode 100644
index 00000000..d06b0d2a
--- /dev/null
+++ b/sysprep/main.ml
@@ -0,0 +1,232 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Unix
+open Printf
+
+open Utils
+
+module G = Guestfs
+
+(* Finalize the list of operations modules. *)
+let () = Sysprep_operation.bake ()
+
+(* Command line argument parsing. *)
+let prog = Filename.basename Sys.executable_name
+
+let debug_gc, operations, g, selinux_relabel =
+ let debug_gc = ref false in
+ let domain = ref None in
+ let dryrun = ref false in
+ let files = ref [] in
+ let format = ref "auto" in
+ let libvirturi = ref "" in
+ let operations = ref None in
+ let selinux_relabel = ref `Auto in
+ let trace = ref false in
+ let verbose = ref false in
+
+ let display_version () =
+ let g = new G.guestfs () in
+ let version = g#version () in
+ printf "virt-sysprep %Ld.%Ld.%Ld%s\n"
+ version.G.major version.G.minor version.G.release version.G.extra;
+ exit 0
+ and add_file file =
+ let format = match !format with "auto" -> None | fmt -> Some fmt in
+ files := (file, format) :: !files
+ and set_domain dom =
+ if !domain <> None then (
+ eprintf "%s: --domain option can only be given once\n" prog;
+ exit 1
+ );
+ domain := Some dom
+ and dump_pod () =
+ Sysprep_operation.dump_pod ();
+ exit 0
+ and dump_pod_options () =
+ Sysprep_operation.dump_pod_options ();
+ exit 0
+ and set_enable ops =
+ if !operations <> None then (
+ eprintf "%s: --enable option can only be given once\n" prog;
+ exit 1
+ );
+ if ops = "" then (
+ eprintf "%s: you cannot pass an empty argument to --enable\n" prog;
+ exit 1
+ );
+ let ops = string_split "," ops in
+ let opset = List.fold_left (
+ fun opset op_name ->
+ try Sysprep_operation.add_to_set op_name opset
+ with Not_found ->
+ eprintf "%s: --enable: '%s' is not a known operation\n" prog op_name;
+ exit 1
+ ) Sysprep_operation.empty_set ops in
+ operations := Some opset
+ and force_selinux_relabel () =
+ selinux_relabel := `Force
+ and no_force_selinux_relabel () =
+ selinux_relabel := `Never
+ and list_operations () =
+ Sysprep_operation.list_operations ();
+ exit 0
+ in
+
+ let argspec = Arg.align [
+ "-a", Arg.String add_file, "file Add disk image file";
+ "--add", Arg.String add_file, "file Add disk image file";
+ "-c", Arg.Set_string libvirturi, "uri Set libvirt URI";
+ "--connect", Arg.Set_string libvirturi, "uri Set libvirt URI";
+ "--debug-gc", Arg.Set debug_gc, " Debug GC and memory allocations (internal)";
+ "-d", Arg.String set_domain, "domain Set libvirt guest name";
+ "--domain", Arg.String set_domain, "domain Set libvirt guest name";
+ "-n", Arg.Set dryrun, " Perform a dry run";
+ "--dryrun", Arg.Set dryrun, " Perform a dry run";
+ "--dry-run", Arg.Set dryrun, " Perform a dry run";
+ "--dump-pod", Arg.Unit dump_pod, " Dump POD (internal)";
+ "--dump-pod-options", Arg.Unit dump_pod_options, " Dump POD for options (internal)";
+ "--enable", Arg.String set_enable, "operations Enable specific operations";
+ "--format", Arg.Set_string format, "format Set format (default: auto)";
+ "--list-operations", Arg.Unit list_operations, " List supported operations";
+ "--selinux-relabel", Arg.Unit force_selinux_relabel, " Force SELinux relabel";
+ "--no-selinux-relabel", Arg.Unit no_force_selinux_relabel, " Never do SELinux relabel";
+ "-v", Arg.Set verbose, " Enable debugging messages";
+ "--verbose", Arg.Set verbose, " -\"-";
+ "-V", Arg.Unit display_version, " Display version and exit";
+ "--version", Arg.Unit display_version, " -\"-";
+ "-x", Arg.Set trace, " Enable tracing of libguestfs calls";
+ ] @ Sysprep_operation.extra_args () in
+ let anon_fun _ = raise (Arg.Bad "extra parameter on the command line") in
+ let usage_msg =
+ sprintf "\
+%s: reset or unconfigure a virtual machine so clones can be made
+
+ virt-sysprep [--options] -d domname
+
+ virt-sysprep [--options] -a disk.img [-a disk.img ...]
+
+A short summary of the options is given below. For detailed help please
+read the man page virt-sysprep(1).
+"
+ prog in
+ Arg.parse argspec anon_fun usage_msg;
+
+ (* Check -a and -d options. *)
+ let files = !files in
+ let domain = !domain in
+ let libvirturi = match !libvirturi with "" -> None | s -> Some s in
+ let add =
+ match files, domain with
+ | [], None ->
+ eprintf "%s: you must give either -a or -d options\n" prog;
+ eprintf "Read virt-sysprep(1) man page for further information.\n";
+ exit 1
+ | [], Some dom ->
+ fun (g : Guestfs.guestfs) readonly ->
+ let allowuuid = true in
+ let readonlydisk = "ignore" (* ignore CDs, data drives *) in
+ ignore (g#add_domain ~readonly ?libvirturi ~allowuuid ~readonlydisk dom)
+ | _, Some _ ->
+ eprintf "%s: you cannot give -a and -d options together\n" prog;
+ eprintf "Read virt-sysprep(1) man page for further information.\n";
+ exit 1
+ | files, None ->
+ fun g readonly ->
+ List.iter (
+ fun (file, format) ->
+ g#add_drive_opts ~readonly ?format file
+ ) files
+ in
+
+ (* Dereference the rest of the args. *)
+ let debug_gc = !debug_gc in
+ let dryrun = !dryrun in
+ let operations = !operations in
+ let selinux_relabel = !selinux_relabel in
+ let trace = !trace in
+ let verbose = !verbose in
+
+ (* Connect to libguestfs. *)
+ let g = new G.guestfs () in
+ if trace then g#set_trace true;
+ if verbose then g#set_verbose true;
+ add g dryrun;
+ g#launch ();
+
+ debug_gc, operations, g, selinux_relabel
+
+let () =
+ (* Inspection. *)
+ match Array.to_list (g#inspect_os ()) with
+ | [] ->
+ eprintf "%s: no operating systems were found in the guest image\n" prog;
+ exit 1
+ | roots ->
+ List.iter (
+ fun root ->
+ (* Mount up the disks, like guestfish -i.
+ * See [ocaml/examples/inspect_vm.ml].
+ *)
+ let mps = g#inspect_get_mountpoints root in
+ let cmp (a,_) (b,_) = compare (String.length a) (String.length b) in
+ let mps = List.sort cmp mps in
+ List.iter (
+ fun (mp, dev) ->
+ try g#mount dev mp
+ with Guestfs.Error msg -> eprintf "%s (ignored)\n" msg
+ ) mps;
+
+ (* Perform the operations. *)
+ let flags = Sysprep_operation.perform_operations ?operations g root in
+
+ (* Parse flags. *)
+ let relabel = ref false in
+ List.iter (function
+ | `Created_files -> relabel := true
+ ) flags;
+
+ (* SELinux relabel? *)
+ let relabel =
+ match selinux_relabel, !relabel with
+ | `Force, _ -> true
+ | `Never, _ -> false
+ | `Auto, relabel -> relabel in
+ if relabel then (
+ let typ = g#inspect_get_type root in
+ let distro = g#inspect_get_distro root in
+ match typ, distro with
+ | "linux", ("fedora"|"rhel"|"redhat-based"
+ |"centos"|"scientificlinux") ->
+ g#touch "/.autorelabel"
+ | _ -> ()
+ );
+
+ (* Unmount everything in this guest. *)
+ g#umount_all ()
+ ) roots
+
+(* Finished. *)
+let () =
+ g#close ();
+
+ if debug_gc then
+ Gc.compact ();
+
+ exit 0
diff --git a/sysprep/sysprep_operation.ml b/sysprep/sysprep_operation.ml
new file mode 100644
index 00000000..316b35be
--- /dev/null
+++ b/sysprep/sysprep_operation.ml
@@ -0,0 +1,183 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Printf
+
+type flag = [ `Created_files ]
+
+type operation = {
+ name : string;
+ pod_description : string;
+ extra_args : ((Arg.key * Arg.spec * Arg.doc) * string) list;
+ perform : Guestfs.guestfs -> string -> flag list;
+}
+
+let ops = ref []
+
+module OperationSet = Set.Make (
+ struct
+ type t = operation
+ let compare a b = compare a.name b.name
+ end
+)
+type set = OperationSet.t
+
+let empty_set = OperationSet.empty
+
+let add_to_set name set =
+ let op = List.find (fun { name = n } -> name = n) !ops in
+ OperationSet.add op set
+
+let register_operation op = ops := op :: !ops
+
+let baked = ref false
+let rec bake () =
+ let ops' = List.sort (fun { name = a } { name = b } -> compare a b) !ops in
+ check_no_dupes ops';
+ List.iter check ops';
+ ops := ops';
+ baked := true
+and check_no_dupes ops =
+ ignore (
+ List.fold_left (
+ fun opset op ->
+ if OperationSet.mem op opset then (
+ eprintf "virt-sysprep: duplicate operation name (%s)\n" op.name;
+ exit 1
+ );
+ add_to_set op.name opset
+ ) empty_set ops
+ )
+and check op =
+ let n = String.length op.name in
+ if n = 0 then (
+ eprintf "virt-sysprep: operation name is an empty string\n";
+ exit 1;
+ );
+ for i = 0 to n-1 do
+ match String.unsafe_get op.name i with
+ | 'a'..'z' | 'A'..'Z' | '0'..'9' | '-' -> ()
+ | c ->
+ eprintf "virt-sysprep: disallowed character (%c) in operation name\n" c;
+ exit 1
+ done;
+ let n = String.length op.pod_description in
+ if n = 0 then (
+ eprintf "virt-sysprep: operation %s has no POD\n" op.name;
+ exit 1
+ );
+ if op.pod_description.[n-1] = '\n' then (
+ eprintf "virt-sysprep: POD for %s must not end with newline\n" op.name;
+ exit 1
+ )
+
+let extra_args () =
+ assert !baked;
+
+ List.flatten (
+ List.map (fun { extra_args = extra_args } ->
+ List.map fst extra_args
+ ) !ops
+ )
+
+(* These internal functions are used to generate the man page. *)
+let dump_pod () =
+ assert !baked;
+
+ List.iter (
+ fun op ->
+ printf "=head2 B<%s>\n" op.name;
+ printf "\n";
+ printf "%s\n\n" op.pod_description
+ ) !ops
+
+(* Skip any leading '-' characters when comparing command line args. *)
+let skip_dashes str =
+ let n = String.length str in
+ let rec loop i =
+ if i >= n then assert false
+ else if str.[i] = '-' then loop (i+1)
+ else i
+ in
+ let i = loop 0 in
+ if i = 0 then str
+ else String.sub str i (n-i)
+
+let dump_pod_options () =
+ assert !baked;
+
+ let args = List.map (
+ fun { name = op_name; extra_args = extra_args } ->
+ List.map (fun ea -> op_name, ea) extra_args
+ ) !ops in
+ let args = List.flatten args in
+ let args = List.map (
+ fun (op_name, ((arg_name, spec, _), pod)) ->
+ match spec with
+ | Arg.Unit _
+ | Arg.Bool _
+ | Arg.Set _
+ | Arg.Clear _ ->
+ let heading = sprintf "B<%s>" arg_name in
+ arg_name, (op_name, heading, pod)
+ | Arg.String _
+ | Arg.Set_string _
+ | Arg.Int _
+ | Arg.Set_int _
+ | Arg.Float _
+ | Arg.Set_float _ ->
+ let heading = sprintf "B<%s> %s" arg_name (skip_dashes arg_name) in
+ arg_name, (op_name, heading, pod)
+ | Arg.Tuple _
+ | Arg.Symbol _
+ | Arg.Rest _ -> assert false (* XXX not implemented *)
+ ) args in
+
+ let args = List.sort (
+ fun (a, _) (b, _) ->
+ compare (skip_dashes a) (skip_dashes b)
+ ) args in
+
+ List.iter (
+ fun (arg_name, (op_name, heading, pod)) ->
+ printf "=item %s\n" heading;
+ printf "(see C<%s> below)\n" op_name;
+ printf "\n";
+ printf "%s\n\n" pod
+ ) args
+
+let list_operations () =
+ assert !baked;
+
+ (* For compatibility with old shell version, list just the operation
+ * names, sorted.
+ *)
+ List.iter (fun op -> print_endline op.name ) !ops
+
+let perform_operations ?operations g root =
+ assert !baked;
+
+ let ops =
+ match operations with
+ | None -> !ops (* all operations *)
+ | Some opset -> (* just the operation names listed *)
+ OperationSet.elements opset in
+
+ let flags = List.map (fun op -> op.perform g root) ops in
+
+ List.flatten flags
diff --git a/sysprep/sysprep_operation.mli b/sysprep/sysprep_operation.mli
new file mode 100644
index 00000000..5fe035f6
--- /dev/null
+++ b/sysprep/sysprep_operation.mli
@@ -0,0 +1,99 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+(** Structure used to describe sysprep operations. *)
+
+type flag = [ `Created_files ]
+
+type operation = {
+ name : string;
+ (** Operation name, also used to enable the operation on the command
+ line. Must contain only alphanumeric and '-' (dash)
+ character. *)
+
+ pod_description : string;
+ (** POD-format description, used for the man page. *)
+
+ extra_args : ((Arg.key * Arg.spec * Arg.doc) * string) list;
+ (** Extra command-line arguments, if any. eg. The [hostname]
+ operation has an extra [--hostname] parameter.
+
+ Each element of the list is the argspec (see {!Arg.spec} etc.)
+ and the corresponding full POD documentation.
+
+ You can decide the types of the arguments, whether they are
+ mandatory etc. *)
+
+ perform : Guestfs.guestfs -> string -> flag list;
+ (** The function which is called to perform this operation, when
+ enabled.
+
+ The parameters are [g] (libguestfs handle) and [root] (the
+ operating system root filesystem). Inspection has been performed
+ already on this handle so if the operation depends on OS type,
+ call [g#inspect_get_type], [g#inspect_get_distro] etc. in order to
+ determine that. The guest operating system's disks have been
+ mounted up, and this function must not unmount them.
+
+ In the rare case of a multiboot operating system, it is possible
+ for this function to be called multiple times.
+
+ On success, the function can return a list of flags (or an
+ empty list). See {!flag}.
+
+ On error the function should raise an exception. The function
+ also needs to be careful to {i suppress} exceptions for things
+ which are not errors, eg. deleting non-existent files. *)
+}
+
+val register_operation : operation -> unit
+(** Register an operation. *)
+
+val bake : unit -> unit
+(** 'Bake' is called after all modules have been registered. We
+ finalize the list of operations, sort it, and run some checks. *)
+
+val extra_args : unit -> (Arg.key * Arg.spec * Arg.doc) list
+(** Get the list of extra arguments for the command line. *)
+
+val dump_pod : unit -> unit
+(** Dump the perldoc (POD) for the manual page
+ (implements [--dump-pod]). *)
+
+val dump_pod_options : unit -> unit
+(** Dump the perldoc (POD) for the [extra_args]
+ (implements [--dump-pod-options]). *)
+
+val list_operations : unit -> unit
+(** List supported operations
+ (implements [--list-operations]). *)
+
+type set
+(** A (sub-)set of operations. *)
+
+val empty_set : set
+(** Empty set of operations. *)
+
+val add_to_set : string -> set -> set
+(** [add_to_set name set] adds the operation named [name] to [set].
+
+ Note that this will raise [Not_found] if [name] is not
+ a valid operation name. *)
+
+val perform_operations : ?operations:set -> Guestfs.guestfs -> string -> flag list
+(** Perform all operations, or the subset listed in the [operations] set. *)
diff --git a/sysprep/sysprep_operation_cron_spool.ml b/sysprep/sysprep_operation_cron_spool.ml
new file mode 100644
index 00000000..e67688bf
--- /dev/null
+++ b/sysprep/sysprep_operation_cron_spool.ml
@@ -0,0 +1,34 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let cron_spool_perform g root =
+ Array.iter g#rm_rf (g#glob_expand "/var/spool/cron/*");
+ []
+
+let cron_spool_op = {
+ name = "cron-spool";
+ pod_description = "Remove user at-jobs and cron-jobs.";
+ extra_args = [];
+ perform = cron_spool_perform;
+}
+
+let () = register_operation cron_spool_op
diff --git a/sysprep/sysprep_operation_dhcp_client_state.ml b/sysprep/sysprep_operation_dhcp_client_state.ml
new file mode 100644
index 00000000..e3e87cb2
--- /dev/null
+++ b/sysprep/sysprep_operation_dhcp_client_state.ml
@@ -0,0 +1,39 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let dhcp_client_state_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ = "linux" then (
+ List.iter (
+ fun glob -> Array.iter g#rm_rf (g#glob_expand glob)
+ ) [ "/var/lib/dhclient/*"; "/var/lib/dhcp/*" (* RHEL 3 *) ]
+ );
+ []
+
+let dhcp_client_state_op = {
+ name = "dhcp-client-state";
+ pod_description = "Remove DHCP client leases.";
+ extra_args = [];
+ perform = dhcp_client_state_perform;
+}
+
+let () = register_operation dhcp_client_state_op
diff --git a/sysprep/sysprep_operation_dhcp_server_state.ml b/sysprep/sysprep_operation_dhcp_server_state.ml
new file mode 100644
index 00000000..c5251ce4
--- /dev/null
+++ b/sysprep/sysprep_operation_dhcp_server_state.ml
@@ -0,0 +1,34 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let dhcp_server_state_perform g root =
+ Array.iter g#rm_rf (g#glob_expand "/var/lib/dhcpd/*");
+ []
+
+let dhcp_server_state_op = {
+ name = "dhcp-server-state";
+ pod_description = "Remove DHCP server leases.";
+ extra_args = [];
+ perform = dhcp_server_state_perform;
+}
+
+let () = register_operation dhcp_server_state_op
diff --git a/sysprep/sysprep_operation_hostname.ml b/sysprep/sysprep_operation_hostname.ml
new file mode 100644
index 00000000..1472a1c4
--- /dev/null
+++ b/sysprep/sysprep_operation_hostname.ml
@@ -0,0 +1,69 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Printf
+
+open Utils
+open Sysprep_operation
+
+module G = Guestfs
+
+let hostname = ref "localhost.localdomain"
+
+let hostname_perform g root =
+ let typ = g#inspect_get_type root in
+ let distro = g#inspect_get_distro root in
+ match typ, distro with
+ | "linux", ("fedora"|"rhel") ->
+ (* Replace HOSTNAME=... entry. The code assumes it's a small,
+ * plain text file.
+ *)
+ let filename = "/etc/sysconfig/network" in
+ let lines = Array.to_list (g#read_lines filename) in
+ let lines = List.filter (
+ fun line -> not (string_prefix line "HOSTNAME=")
+ ) lines in
+ let file =
+ String.concat "\n" lines ^
+ sprintf "\nHOSTNAME=%s\n" !hostname in
+ g#write filename file;
+ [ `Created_files ]
+
+ | "linux", ("debian"|"ubuntu") ->
+ g#write "/etc/hostname" !hostname;
+ [ `Created_files ]
+
+ | _ -> []
+
+let hostname_op = {
+ name = "hostname";
+ pod_description = "\
+Changes the hostname of the guest to the value given in the I<--hostname>
+parameter.
+
+If the I<--hostname> parameter is not given, then the hostname is changed
+to C<localhost.localdomain>.";
+ extra_args = [
+ ("--hostname", Arg.Set_string hostname, "hostname New hostname"),
+ "\
+Change the hostname. If not given, defaults to C<localhost.localdomain>."
+ ];
+ perform = hostname_perform;
+}
+
+let () = register_operation hostname_op
diff --git a/sysprep/sysprep_operation_logfiles.ml b/sysprep/sysprep_operation_logfiles.ml
new file mode 100644
index 00000000..910a9fb0
--- /dev/null
+++ b/sysprep/sysprep_operation_logfiles.ml
@@ -0,0 +1,53 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let logfiles_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ = "linux" then (
+ List.iter (
+ fun glob -> Array.iter g#rm_rf (g#glob_expand glob)
+ ) [
+ "/var/log/*.log*";
+ "/var/log/audit/*";
+ "/var/log/btmp*";
+ "/var/log/cron*";
+ "/var/log/dmesg*";
+ "/var/log/lastlog*";
+ "/var/log/maillog*";
+ "/var/log/mail/*";
+ "/var/log/messages*";
+ "/var/log/secure*";
+ "/var/log/spooler*";
+ "/var/log/tallylog*";
+ "/var/log/wtmp*";
+ ]
+ );
+ []
+
+let logfiles_op = {
+ name = "logfiles";
+ pod_description = "Remove many log files.";
+ extra_args = [];
+ perform = logfiles_perform;
+}
+
+let () = register_operation logfiles_op
diff --git a/sysprep/sysprep_operation_mail_spool.ml b/sysprep/sysprep_operation_mail_spool.ml
new file mode 100644
index 00000000..74f2d941
--- /dev/null
+++ b/sysprep/sysprep_operation_mail_spool.ml
@@ -0,0 +1,39 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let mail_spool_perform g root =
+ List.iter (
+ fun glob -> Array.iter g#rm_rf (g#glob_expand glob)
+ ) [
+ "/var/spool/mail/*";
+ "/var/mail/*";
+ ];
+ []
+
+let mail_spool_op = {
+ name = "mail-spool";
+ pod_description = "Remove email from the local mail spool directory.";
+ extra_args = [];
+ perform = mail_spool_perform;
+}
+
+let () = register_operation mail_spool_op
diff --git a/sysprep/sysprep_operation_net_hwaddr.ml b/sysprep/sysprep_operation_net_hwaddr.ml
new file mode 100644
index 00000000..8aa102a9
--- /dev/null
+++ b/sysprep/sysprep_operation_net_hwaddr.ml
@@ -0,0 +1,54 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Utils
+open Sysprep_operation
+
+module G = Guestfs
+
+let net_hwaddr_perform g root =
+ let typ = g#inspect_get_type root in
+ let distro = g#inspect_get_distro root in
+ match typ, distro with
+ | "linux", ("fedora"|"rhel") ->
+ let filenames = g#glob_expand "/etc/sysconfig/network-scripts/ifcfg-*" in
+ Array.iter (
+ fun filename ->
+ (* Replace HWADDR=... entry. *)
+ let lines = Array.to_list (g#read_lines filename) in
+ let lines = List.filter (
+ fun line -> not (string_prefix line "HWADDR=")
+ ) lines in
+ let file = String.concat "\n" lines ^ "\n" in
+ g#write filename file
+ ) filenames;
+
+ if filenames <> [||] then [ `Created_files ] else []
+
+ | _ -> []
+
+let net_hwaddr_op = {
+ name = "net-hwaddr";
+ pod_description = "\
+Remove HWADDR (hard-coded MAC address) configuration. For Fedora and
+Red Hat Enterprise Linux, this is removed from C<ifcfg-*> files.";
+ extra_args = [];
+ perform = net_hwaddr_perform;
+}
+
+let () = register_operation net_hwaddr_op
diff --git a/sysprep/sysprep_operation_random_seed.ml b/sysprep/sysprep_operation_random_seed.ml
new file mode 100644
index 00000000..989ffe82
--- /dev/null
+++ b/sysprep/sysprep_operation_random_seed.ml
@@ -0,0 +1,57 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let random_seed_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ = "linux" then (
+ let files = [
+ "/var/lib/random-seed"; (* Fedora *)
+ "/var/lib/urandom/random-seed"; (* Debian *)
+ ] in
+ List.iter (
+ fun file ->
+ if g#is_file file then (
+ (* Get 8 bytes of randomness from the host. *)
+ let chan = open_in "/dev/urandom" in
+ let buf = String.create 8 in
+ really_input chan buf 0 8;
+ close_in chan;
+
+ g#write file buf
+ )
+ ) files;
+ [ `Created_files ]
+ )
+ else []
+
+let random_seed_op = {
+ name = "random-seed";
+ pod_description = "\
+Write some random bytes from the host into the random seed file of the
+guest.
+
+See L</RANDOM SEED> below.";
+ extra_args = [];
+ perform = random_seed_perform;
+}
+
+let () = register_operation random_seed_op
diff --git a/sysprep/sysprep_operation_rhn_systemid.ml b/sysprep/sysprep_operation_rhn_systemid.ml
new file mode 100644
index 00000000..35849e41
--- /dev/null
+++ b/sysprep/sysprep_operation_rhn_systemid.ml
@@ -0,0 +1,40 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let rhn_systemid_perform g root =
+ let typ = g#inspect_get_type root in
+ let distro = g#inspect_get_distro root in
+
+ match typ, distro with
+ | "linux", "rhel" ->
+ (try g#rm "/etc/sysconfig/rhn/systemid" with G.Error _ -> ());
+ []
+ | _ -> []
+
+let rhn_systemid_op = {
+ name = "rhn-systemid";
+ pod_description = "Remove the RHN system ID.";
+ extra_args = [];
+ perform = rhn_systemid_perform;
+}
+
+let () = register_operation rhn_systemid_op
diff --git a/sysprep/sysprep_operation_smolt_uuid.ml b/sysprep/sysprep_operation_smolt_uuid.ml
new file mode 100644
index 00000000..a85aa9b3
--- /dev/null
+++ b/sysprep/sysprep_operation_smolt_uuid.ml
@@ -0,0 +1,44 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let smolt_uuid_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ = "linux" then (
+ let files = [ "/etc/sysconfig/hw-uuid";
+ "/etc/smolt/uuid";
+ "/etc/smolt/hw-uuid" ] in
+ List.iter (
+ fun file -> try g#rm file with G.Error _ -> ()
+ ) files;
+
+ []
+ )
+ else []
+
+let smolt_uuid_op = {
+ name = "smolt-uuid";
+ pod_description = "Remove the Smolt hardware UUID.";
+ extra_args = [];
+ perform = smolt_uuid_perform;
+}
+
+let () = register_operation smolt_uuid_op
diff --git a/sysprep/sysprep_operation_ssh_hostkeys.ml b/sysprep/sysprep_operation_ssh_hostkeys.ml
new file mode 100644
index 00000000..8da405b4
--- /dev/null
+++ b/sysprep/sysprep_operation_ssh_hostkeys.ml
@@ -0,0 +1,51 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let ssh_hostkeys_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ <> "windows" then (
+ let files = g#glob_expand "/etc/ssh/*_host_*" in
+ Array.iter g#rm files;
+ []
+ )
+ else []
+
+let ssh_hostkeys_op = {
+ name = "ssh-hostkeys";
+ pod_description = "\
+Remove the SSH host keys in the guest.
+
+The SSH host keys are regenerated (differently) next time the guest is
+booted.
+
+If, after cloning, the guest gets the same IP address, ssh will give
+you a stark warning about the host key changing:
+
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!";
+ extra_args = [];
+ perform = ssh_hostkeys_perform;
+}
+
+let () = register_operation ssh_hostkeys_op
diff --git a/sysprep/sysprep_operation_udev_persistent_net.ml b/sysprep/sysprep_operation_udev_persistent_net.ml
new file mode 100644
index 00000000..e54e1404
--- /dev/null
+++ b/sysprep/sysprep_operation_udev_persistent_net.ml
@@ -0,0 +1,46 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let udev_persistent_net_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ = "linux" then (
+ (try g#rm "/etc/udev/rules.d/70-persistent-net.rules"
+ with G.Error _ -> ());
+ []
+ )
+ else []
+
+let udev_persistent_net_op = {
+ name = "udev-persistent-net";
+ pod_description = "\
+Remove udev persistent net rules which map the guest's existing MAC
+address to a fixed ethernet device (eg. eth0).
+
+After a guest is cloned, the MAC address usually changes. Since the
+old MAC address occupies the old name (eg. eth0), this means the fresh
+MAC address is assigned to a new name (eg. eth1) and this is usually
+undesirable. Erasing the udev persistent net rules avoids this.";
+ extra_args = [];
+ perform = udev_persistent_net_perform;
+}
+
+let () = register_operation udev_persistent_net_op
diff --git a/sysprep/sysprep_operation_utmp.ml b/sysprep/sysprep_operation_utmp.ml
new file mode 100644
index 00000000..69867e15
--- /dev/null
+++ b/sysprep/sysprep_operation_utmp.ml
@@ -0,0 +1,43 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let utmp_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ <> "windows" then (
+ try g#rm "/var/run/utmp"
+ with G.Error _ -> ()
+ );
+ []
+
+let utmp_op = {
+ name = "utmp";
+ pod_description = "\
+Remove the utmp file.
+
+This file records who is currently logged in on a machine. In modern
+Linux distros it is stored in a ramdisk and hence not part of the
+virtual machine's disk, but it was stored on disk in older distros.";
+ extra_args = [];
+ perform = utmp_perform;
+}
+
+let () = register_operation utmp_op
diff --git a/sysprep/sysprep_operation_yum_uuid.ml b/sysprep/sysprep_operation_yum_uuid.ml
new file mode 100644
index 00000000..396cac54
--- /dev/null
+++ b/sysprep/sysprep_operation_yum_uuid.ml
@@ -0,0 +1,42 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let yum_uuid_perform g root =
+ let packager = g#inspect_get_package_management root in
+ if packager = "yum" then (
+ (try g#rm "/var/lib/yum/uuid" with G.Error _ -> ());
+ []
+ )
+ else []
+
+let yum_uuid_op = {
+ name = "yum-uuid";
+ pod_description = "\
+Remove the yum UUID.
+
+Yum creates a fresh UUID the next time it runs when it notices that the
+original UUID has been erased.";
+ extra_args = [];
+ perform = yum_uuid_perform;
+}
+
+let () = register_operation yum_uuid_op
diff --git a/clone/test-virt-sysprep.sh b/sysprep/test-virt-sysprep.sh
index 32173897..9b800542 100755
--- a/clone/test-virt-sysprep.sh
+++ b/sysprep/test-virt-sysprep.sh
@@ -24,26 +24,10 @@ if [ ! -w /dev/fuse ]; then
exit 0
fi
-rm -f test.img guestfish
+rm -f test.img
qemu-img create -f qcow2 -o backing_file=../tests/guests/fedora.img test.img
-# Provide alternate 'virt-inspector' and 'guestmount' binaries
-# that run the just-built programs.
-
-cat <<'EOF' > virt-inspector
-#!/bin/sh -
-../run ../inspector/virt-inspector "$@"
-EOF
-chmod +x virt-inspector
-cat <<'EOF' > guestmount
-#!/bin/sh -
-../run ../fuse/guestmount "$@"
-EOF
-chmod +x guestmount
-
-PATH=.:$PATH
-
./virt-sysprep -a test.img
-rm -f test.img virt-inspector guestmount
+rm -f test.img
diff --git a/sysprep/utils.ml b/sysprep/utils.ml
new file mode 100644
index 00000000..dfac57d4
--- /dev/null
+++ b/sysprep/utils.ml
@@ -0,0 +1,71 @@
+(* virt-sysprep
+ * Copyright (C) 2010-2012 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.
+ *)
+
+open Printf
+
+module G = Guestfs
+
+let (//) = Filename.concat
+
+let () = Random.self_init ()
+
+let string_prefix str prefix =
+ let n = String.length prefix in
+ String.length str >= n && String.sub str 0 n = prefix
+
+let rec string_find s sub =
+ let len = String.length s in
+ let sublen = String.length sub in
+ let rec loop i =
+ if i <= len-sublen then (
+ let rec loop2 j =
+ if j < sublen then (
+ if s.[i+j] = sub.[j] then loop2 (j+1)
+ else -1
+ ) else
+ i (* found *)
+ in
+ let r = loop2 0 in
+ if r = -1 then loop (i+1) else r
+ ) else
+ -1 (* not found *)
+ in
+ loop 0
+
+let rec string_split sep str =
+ let len = String.length str in
+ let seplen = String.length sep in
+ let i = string_find str sep in
+ if i = -1 then [str]
+ else (
+ let s' = String.sub str 0 i in
+ let s'' = String.sub str (i+seplen) (len-i-seplen) in
+ s' :: string_split sep s''
+ )
+
+let string_random8 =
+ let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in
+ fun () ->
+ String.concat "" (
+ List.map (
+ fun _ ->
+ let c = Random.int 36 in
+ let c = chars.[c] in
+ String.make 1 c
+ ) [1;2;3;4;5;6;7;8]
+ )
diff --git a/clone/virt-sysprep.pod b/sysprep/virt-sysprep.pod
index 5cab3eba..8120c78d 100755
--- a/clone/virt-sysprep.pod
+++ b/sysprep/virt-sysprep.pod
@@ -18,9 +18,6 @@ SSH host keys, removing persistent network MAC configuration, and
removing user accounts. Each step can be enabled or disabled as
required.
-Virt-sysprep is a simple shell script, allowing easy inspection or
-customization by the system administrator.
-
Virt-sysprep modifies the guest or disk image I<in place>. The guest
must be shut down. If you want to preserve the existing contents of
the guest, you I<must copy or clone the disk first>.
@@ -54,7 +51,7 @@ Display brief help.
Add I<file> which should be a disk image from a virtual machine.
The format of the disk image is auto-detected. To override this and
-force a particular format use the I<--format=..> option.
+force a particular format use the I<--format> option.
=item B<-c> URI
@@ -73,12 +70,19 @@ not used at all.
Add all the disks from the named libvirt guest. Domain UUIDs can be
used instead of names.
-=item B<--enable=...>
+=item B<-n>
+
+=item B<--dry-run>
+
+Perform a read-only "dry run" on the guest. This runs the sysprep
+operation, but throws away any changes to the disk at the end.
+
+=item B<--enable> operations
Choose which sysprep operations to perform. Give a comma-separated
list of operations, for example:
- --enable=ssh-hostkeys,udev-persistent-net
+ --enable ssh-hostkeys,udev-persistent-net
would enable ONLY C<ssh-hostkeys> and C<udev-persistent-net> operations.
@@ -92,22 +96,22 @@ version of virt-sysprep.
See L</OPERATIONS> below for a list and an explanation of each
operation.
-=item B<--format=raw|qcow2|..>
+=item B<--format> raw|qcow2|..
-=item B<--format>
+=item B<--format> auto
The default for the I<-a> option is to auto-detect the format of the
disk image. Using this forces the disk format for I<-a> options which
-follow on the command line. Using I<--format> with no argument
-switches back to auto-detection for subsequent I<-a> options.
+follow on the command line. Using I<--format auto> switches back to
+auto-detection for subsequent I<-a> options.
For example:
- virt-sysprep --format=raw -a disk.img
+ virt-sysprep --format raw -a disk.img
forces raw format (no auto-detection) for C<disk.img>.
- virt-sysprep --format=raw -a disk.img --format -a another.img
+ virt-sysprep --format raw -a disk.img --format auto -a another.img
forces raw format (no auto-detection) for C<disk.img> and reverts to
auto-detection for C<another.img>.
@@ -116,11 +120,6 @@ If you have untrusted raw-format guest disk images, you should use
this option to specify the disk format. This avoids a possible
security problem with malicious guests (CVE-2010-3851).
-=item B<--hostname> newhostname
-
-Change the hostname. See the L</hostname> operation below.
-If not given, defaults to C<localhost.localdomain>.
-
=item B<--list-operations>
List the operations supported by the virt-sysprep program.
@@ -151,6 +150,8 @@ Display version number and exit.
Enable tracing of libguestfs API calls.
+@EXTRA_OPTIONS@
+
=back
=head1 OPERATIONS
@@ -171,93 +172,7 @@ Future versions of virt-sysprep may add more operations. If you are
using virt-sysprep and want predictable behaviour, specify only the
operations that you want to have enabled.
-=head2 cron-spool
-
-Remove user at-jobs and cron-jobs.
-
-=head2 dhcp-client-state
-
-Remove DHCP client leases.
-
-=head2 dhcp-server-state
-
-Remove DHCP server leases.
-
-=head2 hostname
-
-Changes the hostname of the guest to the value given in the
-I<--hostname> parameter.
-
-If the I<--hostname> parameter is not given, then the hostname is
-changed to C<localhost.localdomain>.
-
-=head2 logfiles
-
-Remove many log files.
-
-=head2 mail-spool
-
-Remove email from the local mail spool directory.
-
-=head2 net-hwaddr
-
-Remove HWADDR (hard-coded MAC address) configuration. For Fedora and
-Red Hat Enterprise Linux, this is removed from C<ifcfg-*> files.
-
-=head2 random-seed
-
-Write some random bytes from the host into the random seed file of
-the guest.
-
-See L</RANDOM SEED> below.
-
-=head2 rhn-systemid
-
-Remove the RHN system ID.
-
-=head2 smolt-uuid
-
-Remove the Smolt hardware UUID.
-
-=head2 ssh-hostkeys
-
-Remove the SSH host keys in the guest.
-
-The SSH host keys are regenerated (differently) next time the guest is
-booted.
-
-If, after cloning, the guest gets the same IP address, ssh will give
-you a stark warning about the host key changing:
-
- @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
- @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
- @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
- IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
-
-=head2 udev-persistent-net
-
-Remove udev persistent net rules which map the guest's existing MAC
-address to a fixed ethernet device (eg. eth0).
-
-After a guest is cloned, the MAC address usually changes. Since the
-old MAC address occupies the old name (eg. eth0), this means the fresh
-MAC address is assigned to a new name (eg. eth1) and this is usually
-undesirable. Erasing the udev persistent net rules avoids this.
-
-=head2 utmp
-
-Remove the utmp file.
-
-This records who is currently logged in on a machine. In modern Linux
-distros it is stored in a ramdisk and hence not part of the virtual
-machine's disk, but it was stored on disk in older distros.
-
-=head2 yum-uuid
-
-Remove the yum UUID.
-
-Yum creates a fresh UUID the next time it runs when it notices that
-the original UUID has been erased.
+@OPERATIONS@
=head1 COPYING AND CLONING
@@ -430,8 +345,7 @@ thus is ideal for cloning guests from a template.
Although virt-sysprep removes some sensitive information from the
guest, it does not pretend to remove all of it. You should examine
-the L</OPERATIONS> above, and the implementation of the operations in
-the shell script. You should also examine the guest afterwards.
+the L</OPERATIONS> above and the guest afterwards.
Sensitive files are simply removed. The data they contained may still
exist on the disk, easily recovered with a hex editor or undelete
@@ -455,7 +369,7 @@ from a template too, which can be done by just enabling the
C<random-seed> operation:
cp template.img newguest.img
- virt-sysprep --enable=random-seed -a newguest.img
+ virt-sysprep --enable random-seed -a newguest.img
=head2 SELINUX RELABELLING
@@ -504,7 +418,7 @@ Richard W.M. Jones L<http://people.redhat.com/~rjones/>
=head1 COPYRIGHT
-Copyright (C) 2011 Red Hat Inc.
+Copyright (C) 2011-2012 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
diff --git a/tests/extra/Makefile.am b/tests/extra/Makefile.am
index b807048b..baba4a33 100644
--- a/tests/extra/Makefile.am
+++ b/tests/extra/Makefile.am
@@ -28,10 +28,8 @@
#
# XXX Not tested:
#
-# ../clone/virt-sysprep
-# - hard to test because it's a shell script
-#
# ../edit/virt-edit
+# ../sysprep/virt-sysprep
#
# Perl bindings
# ../edit/virt-edit -e