[Pkg-libvirt-commits] [libguestfs] 19/61: customize: Move virt-customize-related code to a separate directory.
Hilko Bengen
bengen at moszumanska.debian.org
Sat Mar 29 14:36:23 UTC 2014
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to branch experimental
in repository libguestfs.
commit 4b0b3589e854f0ed9174250490dc9c6ade94292f
Author: Richard W.M. Jones <rjones at redhat.com>
Date: Mon Mar 17 15:34:31 2014 +0000
customize: Move virt-customize-related code to a separate directory.
Split virt-builder into build and customize steps, so that we can spin
off a separate tool called 'virt-customize'. This commit does not in
fact create such a tool, but it moves all the common code into a
library, in the customize/ subdirectory of the source.
Although this is mostly refactoring, it does change the order in which
virt-builder command line arguments are processed, so they are now
processed in the order they appear, not the inflexible fixed order
used before.
---
.gitignore | 6 +
Makefile.am | 12 +-
builder/Makefile.am | 33 +-
builder/builder.ml | 333 +-----------
builder/cmdline.ml | 230 ++------
builder/virt-builder.pod | 262 +--------
configure.ac | 1 +
{mllib => customize}/Makefile.am | 137 ++---
{mllib => customize}/crypt-c.c | 0
{mllib => customize}/crypt.ml | 0
{mllib => customize}/crypt.mli | 0
customize/customize_run.ml | 328 +++++++++++
mllib/timezone.mli => customize/customize_run.mli | 14 +-
{mllib => customize}/firstboot.ml | 0
{mllib => customize}/firstboot.mli | 0
{mllib => customize}/hostname.ml | 0
{mllib => customize}/hostname.mli | 0
{mllib => customize}/password.ml | 0
{mllib => customize}/password.mli | 0
{mllib => customize}/perl_edit.ml | 0
{mllib => customize}/perl_edit.mli | 0
{mllib => customize}/random_seed.ml | 0
{mllib => customize}/random_seed.mli | 0
{mllib => customize}/timezone.ml | 0
{mllib => customize}/timezone.mli | 0
{mllib => customize}/urandom.ml | 0
{mllib => customize}/urandom.mli | 0
generator/Makefile.am | 2 +
generator/customize.ml | 634 ++++++++++++++++++++++
generator/main.ml | 6 +
mllib/Makefile.am | 26 -
po-docs/ja/Makefile.am | 9 +
po-docs/uk/Makefile.am | 9 +
po/POTFILES | 2 +-
po/POTFILES-ml | 8 -
src/guestfs.pod | 4 +
sysprep/Makefile.am | 23 +-
37 files changed, 1164 insertions(+), 915 deletions(-)
diff --git a/.gitignore b/.gitignore
index 317ddd5..a93eb95 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,6 +90,12 @@ Makefile.in
/config.sub
/configure
/csharp/Libguestfs.cs
+/customize/.depend
+/customize/customize_cmdline.ml
+/customize/customize_cmdline.mli
+/customize/customize-options.pod
+/customize/customize-synopsis.pod
+/customize/virt-customize
/daemon/actions.h
/daemon/errnostring.c
/daemon/errnostring-gperf.c
diff --git a/Makefile.am b/Makefile.am
index aa176db..5b8a82e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,10 +123,16 @@ endif
# Unconditional because nothing is built yet.
SUBDIRS += csharp
-# OCaml tools. Note 'mllib' contains random shared code used by
-# all of the OCaml tools.
+# OCaml tools. Note 'mllib' and 'customize' contain shared code used
+# by other OCaml tools, so these must come first.
if HAVE_OCAML
-SUBDIRS += mllib builder builder/website resize sparsify sysprep
+SUBDIRS += \
+ mllib \
+ customize \
+ builder builder/website \
+ resize \
+ sparsify \
+ sysprep
endif
# Perl tools.
diff --git a/builder/Makefile.am b/builder/Makefile.am
index ad791e9..a777942 100644
--- a/builder/Makefile.am
+++ b/builder/Makefile.am
@@ -77,26 +77,28 @@ if HAVE_OCAML
# Note this list must be in dependency order.
deps = \
$(top_builddir)/mllib/libdir.cmx \
+ $(top_builddir)/mllib/config.cmx \
$(top_builddir)/mllib/common_gettext.cmx \
$(top_builddir)/mllib/common_utils.cmx \
- $(top_builddir)/mllib/urandom.cmx \
- $(top_builddir)/mllib/random_seed.cmx \
- $(top_builddir)/mllib/hostname.cmx \
- $(top_builddir)/mllib/timezone.cmx \
- $(top_builddir)/mllib/firstboot.cmx \
- $(top_builddir)/mllib/perl_edit.cmx \
- $(top_builddir)/mllib/crypt-c.o \
- $(top_builddir)/mllib/crypt.cmx \
$(top_builddir)/mllib/fsync-c.o \
$(top_builddir)/mllib/fsync.cmx \
- $(top_builddir)/mllib/password.cmx \
$(top_builddir)/mllib/planner.cmx \
- $(top_builddir)/mllib/config.cmx \
- $(top_builddir)/fish/guestfish-uri.o \
$(top_builddir)/mllib/uri-c.o \
$(top_builddir)/mllib/uRI.cmx \
$(top_builddir)/mllib/mkdtemp-c.o \
$(top_builddir)/mllib/mkdtemp.cmx \
+ $(top_builddir)/customize/urandom.cmx \
+ $(top_builddir)/customize/random_seed.cmx \
+ $(top_builddir)/customize/hostname.cmx \
+ $(top_builddir)/customize/timezone.cmx \
+ $(top_builddir)/customize/firstboot.cmx \
+ $(top_builddir)/customize/perl_edit.cmx \
+ $(top_builddir)/customize/crypt-c.o \
+ $(top_builddir)/customize/crypt.cmx \
+ $(top_builddir)/customize/password.cmx \
+ $(top_builddir)/customize/customize_cmdline.cmx \
+ $(top_builddir)/customize/customize_run.cmx \
+ $(top_builddir)/fish/guestfish-uri.o \
index-scan.o \
index-struct.o \
index-parse.o \
@@ -135,7 +137,8 @@ OCAMLPACKAGES = \
-package str,unix \
-I $(top_builddir)/src/.libs \
-I $(top_builddir)/ocaml \
- -I $(top_builddir)/mllib
+ -I $(top_builddir)/mllib \
+ -I $(top_builddir)/customize
if HAVE_OCAML_PKG_GETTEXT
OCAMLPACKAGES += -package gettext-stub
endif
@@ -182,10 +185,12 @@ noinst_DATA += $(top_builddir)/html/virt-builder.1.html
virt-builder.1 $(top_builddir)/html/virt-builder.1.html: stamp-virt-builder.pod
-stamp-virt-builder.pod: virt-builder.pod
+stamp-virt-builder.pod: virt-builder.pod $(top_srcdir)/customize/customize-synopsis.pod $(top_srcdir)/customize/customize-options.pod
$(PODWRAPPER) \
--man virt-builder.1 \
--html $(top_builddir)/html/virt-builder.1.html \
+ --insert $(top_srcdir)/customize/customize-synopsis.pod:__CUSTOMIZE_SYNOPSIS__ \
+ --insert $(top_srcdir)/customize/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
--license GPLv2+ \
$<
touch $@
@@ -236,7 +241,7 @@ depend: .depend
.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
rm -f $@ $@-t
- $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \
+ $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \
$(SED) 's/ *$$//' | \
$(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
$(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
diff --git a/builder/builder.ml b/builder/builder.ml
index b3ca46a..81eb2d9 100644
--- a/builder/builder.ml
+++ b/builder/builder.ml
@@ -25,6 +25,7 @@ open Password
open Planner
open Cmdline
+open Customize_cmdline
open Unix
open Printf
@@ -38,12 +39,9 @@ let () = Random.self_init ()
let main () =
(* Command line argument parsing - see cmdline.ml. *)
let mode, arg,
- arch, attach, cache, check_signature, curl, debug, delete,
- delete_on_failure, edit, firstboot, run, format, gpg, hostname, install,
- list_format, links, memsize, mkdirs,
- network, output, password_crypto, quiet, root_password, scrub,
- scrub_logfile, selinux_relabel, size, smp, sources, sync, timezone,
- update, upload, writes =
+ arch, attach, cache, check_signature, curl, debug,
+ delete_on_failure, format, gpg, list_format, memsize,
+ network, ops, output, quiet, size, smp, sources, sync =
parse_cmdline () in
(* Timestamped messages in ordinary, non-debug non-quiet mode. *)
@@ -593,7 +591,7 @@ let main () =
(match smp with None -> () | Some smp -> g#set_smp smp);
g#set_network network;
- g#set_selinux selinux_relabel;
+ g#set_selinux ops.flags.selinux_relabel;
(* The output disk is being created, so use cache=unsafe here. *)
g#add_drive_opts ~format:output_format ~cachemode:"unsafe" output_filename;
@@ -626,313 +624,7 @@ let main () =
eprintf (f_"%s: no guest operating systems or multiboot OS found in this disk image\nThis is a failure of the source repository. Use -v for more information.\n") prog;
exit 1 in
- (* Set the random seed. *)
- msg (f_"Setting a random seed");
- if not (Random_seed.set_random_seed g root) then
- eprintf (f_"%s: warning: random seed could not be set for this type of guest\n%!") prog;
-
- (* Set the hostname. *)
- (match hostname with
- | None -> ()
- | Some hostname ->
- msg (f_"Setting the hostname: %s") hostname;
- if not (Hostname.set_hostname g root hostname) then
- eprintf (f_"%s: warning: hostname could not be set for this type of guest\n%!") prog
- );
-
- (* Set the timezone. *)
- (match timezone with
- | None -> ()
- | Some timezone ->
- msg (f_"Setting the timezone: %s") timezone;
- if not (Timezone.set_timezone ~prog g root timezone) then
- eprintf (f_"%s: warning: timezone could not be set for this type of guest\n%!") prog
- );
-
- (* Root password.
- * Note 'None' means that we randomize the root password.
- *)
- let () =
- match g#inspect_get_type root with
- | "linux" ->
- let password_map = Hashtbl.create 1 in
- let pw =
- match root_password with
- | Some pw ->
- msg (f_"Setting root password");
- pw
- | None ->
- msg (f_"Setting random root password [did you mean to use --root-password?]");
- parse_selector ~prog "random" in
- Hashtbl.replace password_map "root" pw;
- set_linux_passwords ~prog ?password_crypto g root password_map
- | _ ->
- eprintf (f_"%s: warning: root password could not be set for this type of guest\n%!") prog in
-
- (* Based on the guest type, choose a log file location. *)
- let logfile =
- match g#inspect_get_type root with
- | "windows" | "dos" ->
- if g#is_dir ~followsymlinks:true "/Temp" then "/Temp/builder.log"
- else "/builder.log"
- | _ ->
- if g#is_dir ~followsymlinks:true "/tmp" then "/tmp/builder.log"
- else "/builder.log" in
-
- (* Function to cat the log file, for debugging and error messages. *)
- let debug_logfile () =
- try
- (* XXX If stderr is redirected this actually truncates the
- * redirection file, which is pretty annoying to say the
- * least.
- *)
- g#download logfile "/dev/stderr"
- with exn ->
- eprintf (f_"%s: log file %s: %s (ignored)\n")
- prog logfile (Printexc.to_string exn) in
-
- (* Useful wrapper for scripts. *)
- let do_run ~display cmd =
- (* Add a prologue to the scripts:
- * - Pass environment variables through from the host.
- * - Send stdout and stderr to a log file so we capture all output
- * in error messages.
- * Also catch errors and dump the log file completely on error.
- *)
- let env_vars =
- filter_map (
- fun name ->
- try Some (sprintf "export %s=%s" name (quote (Sys.getenv name)))
- with Not_found -> None
- ) [ "http_proxy"; "https_proxy"; "ftp_proxy"; "no_proxy" ] in
- let env_vars = String.concat "\n" env_vars ^ "\n" in
-
- let cmd = sprintf "\
-exec >>%s 2>&1
-%s
-%s
-" (quote logfile) env_vars cmd in
-
- if debug then eprintf "running command:\n%s\n%!" cmd;
- try ignore (g#sh cmd)
- with
- Guestfs.Error msg ->
- debug_logfile ();
- eprintf (f_"%s: %s: command exited with an error\n") prog display;
- exit 1
- in
-
- (* http://distrowatch.com/dwres.php?resource=package-management *)
- let guest_install_command packages =
- let quoted_args = String.concat " " (List.map quote packages) in
- match g#inspect_get_package_management root with
- | "apt" ->
- (* http://unix.stackexchange.com/questions/22820 *)
- sprintf "
- export DEBIAN_FRONTEND=noninteractive
- apt_opts='-q -y -o Dpkg::Options::=--force-confnew'
- apt-get $apt_opts update
- apt-get $apt_opts install %s
- " quoted_args
- | "pisi" ->
- sprintf "pisi it %s" quoted_args
- | "pacman" ->
- sprintf "pacman -S %s" quoted_args
- | "urpmi" ->
- sprintf "urpmi %s" quoted_args
- | "yum" ->
- sprintf "yum -y install %s" quoted_args
- | "zypper" ->
- (* XXX Should we use -n option? *)
- sprintf "zypper in %s" quoted_args
- | "unknown" ->
- eprintf (f_"%s: --install is not supported for this guest operating system\n")
- prog;
- exit 1
- | pm ->
- eprintf (f_"%s: sorry, don't know how to use --install with the '%s' package manager\n")
- prog pm;
- exit 1
-
- and guest_update_command () =
- match g#inspect_get_package_management root with
- | "apt" ->
- (* http://unix.stackexchange.com/questions/22820 *)
- sprintf "
- export DEBIAN_FRONTEND=noninteractive
- apt_opts='-q -y -o Dpkg::Options::=--force-confnew'
- apt-get $apt_opts update
- apt-get $apt_opts upgrade
- "
- | "pisi" ->
- sprintf "pisi upgrade"
- | "pacman" ->
- sprintf "pacman -Su"
- | "urpmi" ->
- sprintf "urpmi --auto-select"
- | "yum" ->
- sprintf "yum -y update"
- | "zypper" ->
- sprintf "zypper update"
- | "unknown" ->
- eprintf (f_"%s: --update is not supported for this guest operating system\n")
- prog;
- exit 1
- | pm ->
- eprintf (f_"%s: sorry, don't know how to use --update with the '%s' package manager\n")
- prog pm;
- exit 1
- in
-
- (* Update core/template packages. *)
- if update then (
- msg (f_"Updating core packages");
-
- let cmd = guest_update_command () in
- do_run ~display:cmd cmd
- );
-
- (* Install packages. *)
- if install <> [] then (
- msg (f_"Installing packages: %s") (String.concat " " install);
-
- let cmd = guest_install_command install in
- do_run ~display:cmd cmd
- );
-
- (* Make directories. *)
- List.iter (
- fun dir ->
- msg (f_"Making directory: %s") dir;
- g#mkdir_p dir
- ) mkdirs;
-
- (* Write files. *)
- List.iter (
- fun (file, content) ->
- msg (f_"Writing: %s") file;
- g#write file content
- ) writes;
-
- (* Upload files. *)
- List.iter (
- fun (file, dest) ->
- msg (f_"Uploading: %s to %s") file dest;
- let dest =
- if g#is_dir ~followsymlinks:true dest then
- dest ^ "/" ^ Filename.basename file
- else
- dest in
- (* Do the file upload. *)
- g#upload file dest;
-
- (* Copy (some of) the permissions from the local file to the
- * uploaded file.
- *)
- let statbuf = stat file in
- let perms = statbuf.st_perm land 0o7777 (* sticky & set*id *) in
- g#chmod perms dest;
- let uid, gid = statbuf.st_uid, statbuf.st_gid in
- g#chown uid gid dest
- ) upload;
-
- (* Edit files. *)
- List.iter (
- fun (file, expr) ->
- msg (f_"Editing: %s") file;
-
- if not (g#is_file file) then (
- eprintf (f_"%s: error: %s is not a regular file in the guest\n")
- prog file;
- exit 1
- );
-
- Perl_edit.edit_file ~debug g file expr
- ) edit;
-
- (* Delete files. *)
- List.iter (
- fun file ->
- msg (f_"Deleting: %s") file;
- g#rm_rf file
- ) delete;
-
- (* Symbolic links. *)
- List.iter (
- fun (target, links) ->
- List.iter (
- fun link ->
- msg (f_"Linking: %s -> %s") link target;
- g#ln_sf target link
- ) links
- ) links;
-
- (* Scrub files. *)
- List.iter (
- fun file ->
- msg (f_"Scrubbing: %s") file;
- g#scrub_file file
- ) scrub;
-
- (* Firstboot scripts/commands/install. *)
- let () =
- let i = ref 0 in
- List.iter (
- fun op ->
- incr i;
- match op with
- | `Script script ->
- msg (f_"Installing firstboot script: [%d] %s") !i script;
- let cmd = read_whole_file script in
- Firstboot.add_firstboot_script g root !i cmd
- | `Command cmd ->
- msg (f_"Installing firstboot command: [%d] %s") !i cmd;
- Firstboot.add_firstboot_script g root !i cmd
- | `Packages pkgs ->
- msg (f_"Installing firstboot packages: [%d] %s") !i
- (String.concat " " pkgs);
- let cmd = guest_install_command pkgs in
- Firstboot.add_firstboot_script g root !i cmd
- ) firstboot in
-
- (* Run scripts. *)
- List.iter (
- function
- | `Script script ->
- msg (f_"Running: %s") script;
- let cmd = read_whole_file script in
- do_run ~display:script cmd
- | `Command cmd ->
- msg (f_"Running: %s") cmd;
- do_run ~display:cmd cmd
- ) run;
-
- if selinux_relabel then (
- msg (f_"SELinux relabelling");
- let cmd = sprintf "
- if load_policy && fixfiles restore; then
- rm -f /.autorelabel
- else
- touch /.autorelabel
- echo '%s: SELinux relabelling failed, will relabel at boot instead.'
- fi
- " prog in
- do_run ~display:"load_policy && fixfiles restore" cmd
- );
-
- (* Clean up the log file:
- *
- * If debugging, dump out the log file.
- * Then if asked, scrub the log file.
- *)
- if debug then debug_logfile ();
- if scrub_logfile && g#exists logfile then (
- msg (f_"Scrubbing the log file");
-
- (* Try various methods with decreasing complexity. *)
- try g#scrub_file logfile
- with _ -> g#rm_f logfile
- );
+ Customize_run.run ~prog ~debug ~quiet g root ops;
(* Collect some stats about the final output file.
* Notes:
@@ -976,19 +668,6 @@ exec >>%s 2>&1
(* Unmount everything and we're done! *)
msg (f_"Finishing off");
- (* Kill any daemons (eg. started by newly installed packages) using
- * the sysroot.
- * XXX How to make this nicer?
- * XXX fuser returns an error if it doesn't kill any processes, which
- * is not very useful.
- *)
- (try ignore (g#debug "sh" [| "fuser"; "-k"; "/sysroot" |])
- with exn ->
- if debug then
- eprintf (f_"%s: %s (ignored)\n") prog (Printexc.to_string exn)
- );
- g#ping_daemon (); (* tiny delay after kill *)
-
g#umount_all ();
g#shutdown ();
g#close ();
diff --git a/builder/cmdline.ml b/builder/cmdline.ml
index 2657906..7cd3342 100644
--- a/builder/cmdline.ml
+++ b/builder/cmdline.ml
@@ -21,9 +21,9 @@
open Common_gettext.Gettext
open Common_utils
-module G = Guestfs
+open Customize_cmdline
-open Password
+module G = Guestfs
open Unix
open Printf
@@ -62,67 +62,14 @@ let parse_cmdline () =
let curl = ref "curl" in
let debug = ref false in
- let delete = ref [] in
- let add_delete s = delete := s :: !delete in
-
let delete_on_failure = ref true in
- let edit = ref [] in
- let add_edit arg =
- let i =
- try String.index arg ':'
- with Not_found ->
- eprintf (f_"%s: invalid --edit format, see the man page.\n") prog;
- exit 1 in
- let len = String.length arg in
- let file = String.sub arg 0 i in
- let expr = String.sub arg (i+1) (len-(i+1)) in
- edit := (file, expr) :: !edit
- in
-
let fingerprints = ref [] in
let add_fingerprint arg = fingerprints := arg :: !fingerprints in
- let firstboot = ref [] in
- let add_firstboot s =
- if not (Sys.file_exists s) then (
- if not (String.contains s ' ') then
- eprintf (f_"%s: %s: %s: file not found\n") prog "--firstboot" s
- else
- eprintf (f_"%s: %s: %s: file not found [did you mean %s?]\n") prog "--firstboot" s "--firstboot-command";
- exit 1
- );
- firstboot := `Script s :: !firstboot
- in
- let add_firstboot_cmd s = firstboot := `Command s :: !firstboot in
- let add_firstboot_install pkgs =
- let pkgs = string_nsplit "," pkgs in
- firstboot := `Packages pkgs :: !firstboot
- in
-
let format = ref "" in
let gpg = ref "gpg" in
- let hostname = ref None in
- let set_hostname s = hostname := Some s in
-
- let install = ref [] in
- let add_install pkgs =
- let pkgs = string_nsplit "," pkgs in
- install := pkgs @ !install
- in
-
- let links = ref [] in
- let add_link arg =
- let target, lns =
- match string_nsplit ":" arg with
- | [] | [_] ->
- eprintf (f_"%s: invalid --link format, see the man page.\n") prog;
- exit 1
- | target :: lns -> target, lns in
- links := (target, lns) :: !links
- in
-
let list_format = ref `Short in
let list_set_long () = list_format := `Long in
let list_set_format arg =
@@ -137,44 +84,11 @@ let parse_cmdline () =
let memsize = ref None in
let set_memsize arg = memsize := Some arg in
- let mkdirs = ref [] in
- let add_mkdir arg = mkdirs := arg :: !mkdirs in
-
let network = ref true in
let output = ref "" in
- let password_crypto : password_crypto option ref = ref None in
- let set_password_crypto arg =
- password_crypto := Some (password_crypto_of_string ~prog arg)
- in
-
let quiet = ref false in
- let root_password = ref None in
- let set_root_password arg =
- let pw = parse_selector ~prog arg in
- root_password := Some pw
- in
-
- let run = ref [] in
- let add_run s =
- if not (Sys.file_exists s) then (
- if not (String.contains s ' ') then
- eprintf (f_"%s: %s: %s: file not found\n") prog "--run" s
- else
- eprintf (f_"%s: %s: %s: file not found [did you mean %s?]\n") prog "--run" s "--run-command";
- exit 1
- );
- run := `Script s :: !run
- in
- let add_run_cmd s = run := `Command s :: !run in
-
- let scrub = ref [] in
- let add_scrub s = scrub := s :: !scrub in
-
- let scrub_logfile = ref false in
- let selinux_relabel = ref false in
-
let size = ref None in
let set_size arg = size := Some (parse_size ~prog arg) in
@@ -186,43 +100,7 @@ let parse_cmdline () =
let sync = ref true in
- let timezone = ref None in
- let set_timezone s = timezone := Some s in
-
- let update = ref false in
-
- let upload = ref [] in
- let add_upload arg =
- let i =
- try String.index arg ':'
- with Not_found ->
- eprintf (f_"%s: invalid --upload format, see the man page.\n") prog;
- exit 1 in
- let len = String.length arg in
- let file = String.sub arg 0 i in
- if not (Sys.file_exists file) then (
- eprintf (f_"%s: --upload: %s: file not found\n") prog file;
- exit 1
- );
- let dest = String.sub arg (i+1) (len-(i+1)) in
- upload := (file, dest) :: !upload
- in
-
- let writes = ref [] in
- let add_write arg =
- let i =
- try String.index arg ':'
- with Not_found ->
- eprintf (f_"%s: invalid --write format, see the man page.\n") prog;
- exit 1 in
- let len = String.length arg in
- let file = String.sub arg 0 i in
- let content = String.sub arg (i+1) (len-(i+1)) in
- writes := (file, content) :: !writes
- in
-
- let ditto = " -\"-" in
- let argspec = Arg.align [
+ let argspec = [
"--arch", Arg.Set_string arch, "arch" ^ " " ^ s_"Set the output architecture";
"--attach", Arg.String attach_disk, "iso" ^ " " ^ s_"Attach data disk/ISO during install";
"--attach-format", Arg.String set_attach_format,
@@ -233,70 +111,60 @@ let parse_cmdline () =
" " ^ s_"Download all templates to the cache";
"--check-signature", Arg.Set check_signature,
" " ^ s_"Check digital signatures";
- "--check-signatures", Arg.Set check_signature, ditto;
+ "--check-signatures", Arg.Set check_signature,
+ " " ^ s_"Check digital signatures";
"--no-check-signature", Arg.Clear check_signature,
" " ^ s_"Disable digital signatures";
- "--no-check-signatures", Arg.Clear check_signature, ditto;
+ "--no-check-signatures", Arg.Clear check_signature,
+ " " ^ s_"Disable digital signatures";
"--curl", Arg.Set_string curl, "curl" ^ " " ^ s_"Set curl binary/command";
- "--delete", Arg.String add_delete, "name" ^ " " ^ s_"Delete a file or dir";
"--delete-cache", Arg.Unit delete_cache_mode,
" " ^ s_"Delete the template cache";
"--no-delete-on-failure", Arg.Clear delete_on_failure,
" " ^ s_"Don't delete output file on failure";
- "--edit", Arg.String add_edit, "file:expr" ^ " " ^ s_"Edit file with Perl expr";
"--fingerprint", Arg.String add_fingerprint,
"AAAA.." ^ " " ^ s_"Fingerprint of valid signing key";
- "--firstboot", Arg.String add_firstboot, "script" ^ " " ^ s_"Run script at first guest boot";
- "--firstboot-command", Arg.String add_firstboot_cmd, "cmd+args" ^ " " ^ s_"Run command at first guest boot";
- "--firstboot-install", Arg.String add_firstboot_install,
- "pkg,pkg" ^ " " ^ s_"Add package(s) to install at firstboot";
"--format", Arg.Set_string format, "raw|qcow2" ^ " " ^ s_"Output format (default: raw)";
"--get-kernel", Arg.Unit get_kernel_mode,
"image" ^ " " ^ s_"Get kernel from image";
"--gpg", Arg.Set_string gpg, "gpg" ^ " " ^ s_"Set GPG binary/command";
- "--hostname", Arg.String set_hostname, "hostname" ^ " " ^ s_"Set the hostname";
- "--install", Arg.String add_install, "pkg,pkg" ^ " " ^ s_"Add package(s) to install";
- "--link", Arg.String add_link, "target:link.." ^ " " ^ s_"Create symbolic links";
"-l", Arg.Unit list_mode, " " ^ s_"List available templates";
- "--list", Arg.Unit list_mode, ditto;
+ "--list", Arg.Unit list_mode, " " ^ s_"List available templates";
"--long", Arg.Unit list_set_long, " " ^ s_"Shortcut for --list-format short";
"--list-format", Arg.String list_set_format,
"short|long|json" ^ " " ^ s_"Set the format for --list (default: short)";
- "--no-logfile", Arg.Set scrub_logfile, " " ^ s_"Scrub build log file";
"--long-options", Arg.Unit display_long_options, " " ^ s_"List long options";
"-m", Arg.Int set_memsize, "mb" ^ " " ^ s_"Set memory size";
- "--memsize", Arg.Int set_memsize, "mb" ^ ditto;
- "--mkdir", Arg.String add_mkdir, "dir" ^ " " ^ s_"Create directory";
+ "--memsize", Arg.Int set_memsize, "mb" ^ " " ^ s_"Set memory size";
"--network", Arg.Set network, " " ^ s_"Enable appliance network (default)";
"--no-network", Arg.Clear network, " " ^ s_"Disable appliance network";
"--notes", Arg.Unit notes_mode, " " ^ s_"Display installation notes";
"-o", Arg.Set_string output, "file" ^ " " ^ s_"Set output filename";
- "--output", Arg.Set_string output, "file" ^ ditto;
- "--password-crypto", Arg.String set_password_crypto,
- "md5|sha256|sha512" ^ " " ^ s_"Set password crypto";
+ "--output", Arg.Set_string output, "file" ^ " " ^ s_"Set output filename";
"--print-cache", Arg.Unit print_cache_mode,
" " ^ s_"Print info about template cache";
"--quiet", Arg.Set quiet, " " ^ s_"No progress messages";
- "--root-password", Arg.String set_root_password,
- "..." ^ " " ^ s_"Set root password";
- "--run", Arg.String add_run, "script" ^ " " ^ s_"Run script in disk image";
- "--run-command", Arg.String add_run_cmd, "cmd+args" ^ " " ^ s_"Run command in disk image";
- "--scrub", Arg.String add_scrub, "name" ^ " " ^ s_"Scrub a file";
- "--selinux-relabel", Arg.Set selinux_relabel,
- " " ^ s_"Relabel files with correct SELinux labels";
"--size", Arg.String set_size, "size" ^ " " ^ s_"Set output disk size";
"--smp", Arg.Int set_smp, "vcpus" ^ " " ^ s_"Set number of vCPUs";
"--source", Arg.String add_source, "URL" ^ " " ^ s_"Set source URL";
"--no-sync", Arg.Clear sync, " " ^ s_"Do not fsync output file on exit";
- "--timezone",Arg.String set_timezone, "timezone" ^ " " ^ s_"Set the default timezone";
- "--update", Arg.Set update, " " ^ s_"Update core packages";
- "--upload", Arg.String add_upload, "file:dest" ^ " " ^ s_"Upload file to dest";
"-v", Arg.Set debug, " " ^ s_"Enable debugging messages";
- "--verbose", Arg.Set debug, ditto;
+ "--verbose", Arg.Set debug, " " ^ s_"Enable debugging messages";
"-V", Arg.Unit display_version, " " ^ s_"Display version and exit";
- "--version", Arg.Unit display_version, ditto;
- "--write", Arg.String add_write, "file:content" ^ " " ^ s_"Write file";
+ "--version", Arg.Unit display_version, " " ^ s_"Display version and exit";
] in
+ let customize_argspec, get_customize_ops =
+ Customize_cmdline.argspec ~prog () in
+ let customize_argspec =
+ List.map (fun (spec, _, _) -> spec) customize_argspec in
+ let argspec = argspec @ customize_argspec in
+ let argspec =
+ let cmp (arg1, _, _) (arg2, _, _) =
+ let arg1 = skip_dashes arg1 and arg2 = skip_dashes arg2 in
+ compare (String.lowercase arg1) (String.lowercase arg2)
+ in
+ List.sort cmp argspec in
+ let argspec = Arg.align argspec in
long_options := argspec;
let args = ref [] in
@@ -328,36 +196,20 @@ read the man page virt-builder(1).
let check_signature = !check_signature in
let curl = !curl in
let debug = !debug in
- let delete = List.rev !delete in
let delete_on_failure = !delete_on_failure in
- let edit = List.rev !edit in
let fingerprints = List.rev !fingerprints in
- let firstboot = List.rev !firstboot in
- let run = List.rev !run in
let format = match !format with "" -> None | s -> Some s in
let gpg = !gpg in
- let hostname = !hostname in
- let install = List.rev !install in
let list_format = !list_format in
- let links = List.rev !links in
let memsize = !memsize in
- let mkdirs = List.rev !mkdirs in
let network = !network in
+ let ops = get_customize_ops () in
let output = match !output with "" -> None | s -> Some s in
- let password_crypto = !password_crypto in
let quiet = !quiet in
- let root_password = !root_password in
- let scrub = List.rev !scrub in
- let scrub_logfile = !scrub_logfile in
- let selinux_relabel = !selinux_relabel in
let size = !size in
let smp = !smp in
let sources = List.rev !sources in
let sync = !sync in
- let timezone = !timezone in
- let update = !update in
- let upload = List.rev !upload in
- let writes = List.rev !writes in
(* Check options. *)
let arg =
@@ -442,7 +294,15 @@ read the man page virt-builder(1).
| arch ->
let target_arch = Architecture.filter_arch arch in
if Architecture.arch_is_compatible Architecture.current_arch target_arch <> true then (
- if install <> [] || run <> [] || update then (
+ let requires_execute_on_guest = List.exists (
+ function
+ | `Command _ | `InstallPackages _ | `Script _ | `Update -> true
+ | `Delete _ | `Edit _ | `FirstbootCommand _ | `FirstbootPackages _
+ | `FirstbootScript _ | `Hostname _ | `Link _ | `Mkdir _
+ | `RootPassword _ | `Scrub _ | `Timezone _ | `Upload _
+ | `Write _ -> false
+ ) ops.ops in
+ if requires_execute_on_guest then (
eprintf (f_"%s: sorry, cannot run commands on a guest with a different architecture\n")
prog;
exit 1
@@ -450,10 +310,20 @@ read the man page virt-builder(1).
);
target_arch in
+ (* If user didn't elect any root password, that means we set a random
+ * root password.
+ *)
+ let ops =
+ let has_set_root_password = List.exists (
+ function `RootPassword _ -> true | _ -> false
+ ) ops.ops in
+ if has_set_root_password then ops
+ else (
+ let pw = Password.parse_selector ~prog "random" in
+ { ops with ops = ops.ops @ [ `RootPassword pw ] }
+ ) in
+
mode, arg,
- arch, attach, cache, check_signature, curl, debug, delete,
- delete_on_failure, edit, firstboot, run, format, gpg, hostname, install,
- list_format, links, memsize, mkdirs,
- network, output, password_crypto, quiet, root_password, scrub,
- scrub_logfile, selinux_relabel, size, smp, sources, sync, timezone,
- update, upload, writes
+ arch, attach, cache, check_signature, curl, debug,
+ delete_on_failure, format, gpg, list_format, memsize,
+ network, ops, output, quiet, size, smp, sources, sync
diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod
index 7cf345c..2429f66 100644
--- a/builder/virt-builder.pod
+++ b/builder/virt-builder.pod
@@ -13,23 +13,8 @@ virt-builder - Build virtual machine images quickly
virt-builder os-version
[-o|--output DISKIMAGE] [--size SIZE] [--format raw|qcow2]
- [--arch ARCHITECTURE]
- [--attach ISOFILE]
- [--root-password SELECTOR]
- [--hostname HOSTNAME]
- [--timezone TIMEZONE]
- [--update]
- [--install PKG,[PKG...]]
- [--mkdir DIR]
- [--write FILE:CONTENT]
- [--upload FILE:DEST]
- [--link TARGET:LINK[:LINK]]
- [--edit FILE:EXPR]
- [--delete FILE] [--scrub FILE]
- [--selinux-relabel]
- [--run SCRIPT] [--run-command 'CMD ARGS ...']
- [--firstboot SCRIPT] [--firstboot-command 'CMD ARGS ...']
- [--firstboot-install PKG,[PKG...]]
+ [--arch ARCHITECTURE] [--attach ISOFILE]
+__CUSTOMIZE_SYNOPSIS__
virt-builder -l|--list [--long] [--list-format short|long|json]
@@ -261,15 +246,6 @@ curl parameters, for example to disable https certificate checks:
virt-builder --curl "curl --insecure" [...]
-=item B<--delete> FILE
-
-=item B<--delete> DIR
-
-Delete a file from the guest. Or delete a directory (and all its
-contents, recursively).
-
-See also: I<--upload>, I<--scrub>.
-
=item B<--delete-cache>
Delete the template cache. See L</CACHING>.
@@ -283,17 +259,6 @@ debug images.
The default is to delete the output file if virt-builder fails (or,
for example, some script that it runs fails).
-=item B<--edit> FILE:EXPR
-
-Edit C<FILE> using the Perl expression C<EXPR>.
-
-Be careful to properly quote the expression to prevent it from
-being altered by the shell.
-
-Note that this option is only available when Perl 5 is installed.
-
-See L<virt-edit(1)/NON-INTERACTIVE EDITING>.
-
=item B<--fingerprint> 'AAAA BBBB ...'
Check that the index and templates are signed by the key with the
@@ -305,33 +270,6 @@ URLs, then you can have either no fingerprint, one fingerprint or
multiple fingerprints. If you have multiple, then each must
correspond 1-1 with a source URL.
-=item B<--firstboot> SCRIPT
-
-=item B<--firstboot-command> 'CMD ARGS ...'
-
-Install C<SCRIPT> inside the guest, so that when the guest first boots
-up, the script runs (as root, late in the boot process).
-
-The script is automatically chmod +x after installation in the guest.
-
-The alternative version I<--firstboot-command> is the same, but it
-conveniently wraps the command up in a single line script for you.
-
-You can have multiple I<--firstboot> and I<--firstboot-command>
-options. They run in the same order that they appear on the command
-line.
-
-See also I<--run>.
-
-=item B<--firstboot-install> PKG[,PKG,...]
-
-Install the named packages (a comma-separated list). These are
-installed when the guest first boots using the guest's package manager
-(eg. apt, yum, etc.) and the guest's network connection.
-
-For an overview on the different ways to install packages, see
-L</INSTALLING PACKAGES>.
-
=item B<--format> qcow2
=item B<--format> raw
@@ -365,29 +303,6 @@ alternate home directory:
virt-builder --gpg "gpg --homedir /tmp" [...]
-=item B<--hostname> HOSTNAME
-
-Set the hostname of the guest to C<HOSTNAME>. You can use a
-dotted hostname.domainname (FQDN) if you want.
-
-=item B<--install> PKG[,PKG,...]
-
-Install the named packages (a comma-separated list). These are
-installed during the image build using the guest's package manager
-(eg. apt, yum, etc.) and the host's network connection.
-
-For an overview on the different ways to install packages, see
-L</INSTALLING PACKAGES>.
-
-See also I<--update>.
-
-=item B<--link TARGET:LINK>
-
-=item B<--link TARGET:LINK[:LINK...]>
-
-Create symbolic link(s) in the guest, starting at C<LINK> and
-pointing at C<TARGET>.
-
=item B<-l>
=item B<--list>
@@ -429,14 +344,6 @@ I<--long> is a shorthand for the C<long> format.
See also: I<--source>, I<--notes>, L</SOURCES OF TEMPLATES>.
-=item B<--no-logfile>
-
-Scrub C<builder.log> (log file from build commands) from the image
-after building is complete. If you don't want to reveal precisely how
-the image was built, use this option.
-
-See also: L</LOG FILE>.
-
=item B<-m> MB
=item B<--memsize> MB
@@ -449,13 +356,6 @@ The default can be found with this command:
guestfish get-memsize
-=item B<--mkdir> DIR
-
-Create a directory in the guest.
-
-This uses S<C<mkdir -p>> so any intermediate directories are created,
-and it also works if the directory already exists.
-
=item B<--network>
=item B<--no-network>
@@ -539,20 +439,6 @@ volume.
When used with I<--get-kernel>, this option specifies the output
directory.
-=item B<--password-crypto> password-crypto
-
-Set the password encryption to C<md5>, C<sha256> or C<sha512>.
-
-C<sha256> and C<sha512> require glibc E<ge> 2.7 (check crypt(3) inside
-the guest).
-
-C<md5> will work with relatively old Linux guests (eg. RHEL 3), but
-is not secure against modern attacks.
-
-The default is C<sha512> unless libguestfs detects an old guest that
-didn't have support for SHA-512, in which case it will use C<md5>.
-You can override libguestfs by specifying this option.
-
=item B<--print-cache>
Print information about the template cache. See L</CACHING>.
@@ -561,62 +447,6 @@ Print information about the template cache. See L</CACHING>.
Don't print ordinary progress messages.
-=item B<--root-password> SELECTOR
-
-Set the root password.
-
-See L</USERS AND PASSWORDS> below for the format of the C<SELECTOR>
-field, and also how to set up user accounts.
-
-Note if you I<don't> set I<--root-password> then the guest is given
-a I<random> root password.
-
-=item B<--run> SCRIPT
-
-=item B<--run-command> 'CMD ARGS ...'
-
-Run the shell script (or any program) called C<SCRIPT> on the disk
-image. The script runs virtualized inside a small appliance, chrooted
-into the guest filesystem.
-
-The script is automatically chmod +x.
-
-If libguestfs supports it then a limited network connection is
-available but it only allows outgoing network connections. You can
-also attach data disks (eg. ISO files) as another way to provide data
-(eg. software packages) to the script without needing a network
-connection (I<--attach>). You can also upload data files (I<--upload>).
-
-The alternative version I<--run-command> is the same, but it
-conveniently wraps the command up in a single line script for you.
-
-You can have multiple I<--run> and I<--run-command> options. They run
-in the same order that they appear on the command line.
-
-See also: I<--firstboot>, I<--attach>, I<--upload>.
-
-=item B<--scrub> FILE
-
-Scrub a file from the guest. This is like I<--delete> except that:
-
-=over 4
-
-=item *
-
-It scrubs the data so a guest could not recover it.
-
-=item *
-
-It cannot delete directories, only regular files.
-
-=back
-
-=item B<--selinux-relabel>
-
-Relabel files in the guest so that they have the correct SELinux label.
-
-You should only use this option for guests which support SELinux.
-
=item B<--size> SIZE
Select the size of the output disk, where the size can be specified
@@ -649,34 +479,6 @@ Note that you should not point I<--source> to sources that you don't
trust (unless the source is signed by someone you do trust). See also
the I<--no-network> option.
-=item B<--timezone> TIMEZONE
-
-Set the default timezone of the guest to C<TIMEZONE>. Use a location
-string like C<Europe/London>
-
-=item B<--update>
-
-Do the equivalent of C<yum update>, C<apt-get upgrade>, or whatever
-command is required to update the packages already installed in the
-template to their latest versions.
-
-See also I<--install>.
-
-=item B<--upload> FILE:DEST
-
-Upload local file C<FILE> to destination C<DEST> in the disk image.
-File owner and permissions from the original are preserved, so you
-should set them to what you want them to be in the disk image.
-
-C<DEST> could be the final filename. This can be used to rename
-the file on upload.
-
-If C<DEST> is a directory name (which must already exist in the guest)
-then the file is uploaded into that directory, and it keeps the same
-name as on the local filesystem.
-
-See also: I<--mkdir>, I<--delete>, I<--scrub>.
-
=item B<-v>
=item B<--verbose>
@@ -692,11 +494,11 @@ your bug report.
Display version number and exit.
-=item B<--write> FILE:CONTENT
+=back
-Write C<CONTENT> to C<FILE>.
+=head2 Customization options
-=back
+__CUSTOMIZE_OPTIONS__
=head1 REFERENCE
@@ -996,58 +798,8 @@ A new random seed is generated for the guest.
=item *
-The hostname and timezone are set (I<--hostname>, I<--timezone>).
-
-=item *
-
-The root password is changed (I<--root-password>).
-
-=item *
-
-Core packages are updated (I<--update>).
-
-=item *
-
-Packages are installed (I<--install>).
-
-=item *
-
-Directories are created (I<--mkdir>).
-
-=item *
-
-Files are written (I<--write>).
-
-=item *
-
-Files are uploaded (I<--upload>).
-
-=item *
-
-Files are edited (I<--edit>).
-
-=item *
-
-Files are deleted (I<--delete>, I<--scrub>).
-
-=item *
-
-Symbolic links are created (I<--link>).
-
-=item *
-
-Firstboot scripts are installed (I<--firstboot>,
-I<--firstboot-command>, I<--firstboot-install>).
-
-Note that although firstboot scripts are installed at this step, they
-do not run until the guest is booted first time. Firstboot scripts
-will run in the order they appear on the command line.
-
-=item *
-
-Scripts are run (I<--run>, I<--run-command>).
-
-Scripts run in the order they appear on the command line.
+Guest customization is performed, in the order specified on the
+command line.
=item *
diff --git a/configure.ac b/configure.ac
index 86cf440..d646b08 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1613,6 +1613,7 @@ AC_CONFIG_FILES([Makefile
builder/website/Makefile
cat/Makefile
csharp/Makefile
+ customize/Makefile
daemon/Makefile
df/Makefile
diff/Makefile
diff --git a/mllib/Makefile.am b/customize/Makefile.am
similarity index 64%
copy from mllib/Makefile.am
copy to customize/Makefile.am
index e275213..889fe1c 100644
--- a/mllib/Makefile.am
+++ b/customize/Makefile.am
@@ -1,5 +1,5 @@
-# libguestfs OCaml tools common code
-# Copyright (C) 2011-2014 Red Hat Inc.
+# virt-customize
+# Copyright (C) 2014 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
@@ -18,85 +18,65 @@
include $(top_srcdir)/subdir-rules.mk
EXTRA_DIST = \
- $(filter-out config.ml,$(SOURCES))
+ $(SOURCES)
CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o
+generator_built = \
+ customize_cmdline.mli \
+ customize_cmdline.ml \
+ customize-options.pod \
+ customize-synopsis.pod
+
# Alphabetical order.
SOURCES = \
- common_gettext.ml \
- common_utils.ml \
- common_utils_tests.ml \
- config.ml \
- crypt-c.c \
crypt.ml \
crypt.mli \
- firstboot.mli \
+ crypt-c.c \
+ customize_cmdline.ml \
+ customize_cmdline.mli \
+ customize_run.ml \
+ customize_run.mli \
firstboot.ml \
- fsync-c.c \
- fsync.mli \
- fsync.ml \
- mkdtemp.mli \
- mkdtemp.ml \
- mkdtemp-c.c \
- hostname.mli \
+ firstboot.mli \
hostname.ml \
- password.mli \
+ hostname.mli \
password.ml \
- perl_edit.mli \
+ password.mli \
perl_edit.ml \
- planner.mli \
- planner.ml \
- progress-c.c \
- progress.mli \
- progress.ml \
- random_seed.mli \
+ perl_edit.mli \
random_seed.ml \
- timezone.mli \
+ random_seed.mli \
timezone.ml \
- tty-c.c \
- tTY.mli \
- tTY.ml \
- urandom.mli \
+ timezone.mli \
urandom.ml \
- uri-c.c \
- uRI.mli \
- uRI.ml
+ urandom.mli
if HAVE_OCAML
-# Notes:
-# - We're not actually building a functioning program here, we're just
-# linking everything together to check all the modules build OK.
-# - This list must be in dependency order.
-ocaml_modules = config \
- libdir \
- common_gettext \
- common_utils \
+deps = \
+ $(top_builddir)/mllib/common_gettext.cmx \
+ $(top_builddir)/mllib/common_utils.cmx \
+ crypt-c.o
+
+if HAVE_OCAMLOPT
+OBJECTS = $(deps)
+else
+OBJECTS = $(patsubst %.cmx,%.cmo,$(deps))
+endif
+
+# This list must be in dependency order.
+ocaml_modules = \
+ crypt \
+ firstboot \
+ hostname \
urandom \
+ password \
+ perl_edit \
random_seed \
- hostname \
timezone \
- firstboot \
- perl_edit \
- tTY \
- fsync \
- progress \
- uRI \
- crypt \
- password \
- mkdtemp \
- planner
-
-OBJECTS = \
- $(top_builddir)/fish/guestfish-progress.o \
- $(top_builddir)/fish/guestfish-uri.o \
- tty-c.o \
- fsync-c.o \
- progress-c.o \
- uri-c.o \
- crypt-c.o \
- mkdtemp-c.o
+ customize_cmdline \
+ customize_run
if HAVE_OCAMLOPT
OBJECTS += $(patsubst %,%.cmx,$(ocaml_modules))
@@ -104,12 +84,18 @@ else
OBJECTS += $(patsubst %,%.cmo,$(ocaml_modules))
endif
-noinst_SCRIPTS = dummy
+# XXX virt-customize isn't a complete tool yet, so currently this is
+# just a dummy target binary.
+noinst_SCRIPTS = virt-customize
# -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 str,unix -I $(top_builddir)/src/.libs -I ../ocaml
+OCAMLPACKAGES = \
+ -package str,unix \
+ -I $(top_builddir)/src/.libs \
+ -I $(top_builddir)/ocaml \
+ -I $(top_builddir)/mllib
if HAVE_OCAML_PKG_GETTEXT
OCAMLPACKAGES += -package gettext-stub
endif
@@ -122,7 +108,7 @@ OCAMLCLIBS = \
-L../src/.libs -lutils \
-L../gnulib/lib/.libs -lgnu
-dummy: $(OBJECTS)
+virt-customize: $(OBJECTS)
if HAVE_OCAMLOPT
$(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) \
mlguestfs.cmxa -linkpkg $^ \
@@ -145,13 +131,6 @@ endif
.ml.cmx:
$(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) -c $< -o $@
-# This OCaml module has to be generated by make (configure will put
-# unexpanded prefix macro in).
-
-libdir.ml: Makefile
- echo 'let libdir = "$(libdir)"' > $@-t
- mv $@-t $@
-
# automake will decide we don't need C support in this file. Really
# we do, so we have to provide it ourselves.
@@ -167,21 +146,9 @@ DEFAULT_INCLUDES = \
# Tests.
-check_SCRIPTS = common_utils_tests
-
-if HAVE_OCAMLOPT
-common_utils_tests: common_gettext.cmx common_utils.cmx common_utils_tests.cmx
- $(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) \
- mlguestfs.cmxa -linkpkg $^ -cclib -lncurses -o $@
-else
-common_utils_tests: common_gettext.cmo common_utils.cmo common_utils_tests.cmo
- $(OCAMLFIND) ocamlc $(OCAMLCFLAGS) \
- mlguestfs.cma -linkpkg $^ -cclib -lncurses -custom -o $@
-endif
-
TESTS_ENVIRONMENT = $(top_builddir)/run --test
-TESTS = common_utils_tests
+TESTS =
check-valgrind:
$(MAKE) VG="$(top_builddir)/run @VG@" check
@@ -191,7 +158,7 @@ depend: .depend
.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
rm -f $@ $@-t
- $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) $^ | \
+ $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \
$(SED) 's/ *$$//' | \
$(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
$(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
diff --git a/mllib/crypt-c.c b/customize/crypt-c.c
similarity index 100%
rename from mllib/crypt-c.c
rename to customize/crypt-c.c
diff --git a/mllib/crypt.ml b/customize/crypt.ml
similarity index 100%
rename from mllib/crypt.ml
rename to customize/crypt.ml
diff --git a/mllib/crypt.mli b/customize/crypt.mli
similarity index 100%
rename from mllib/crypt.mli
rename to customize/crypt.mli
diff --git a/customize/customize_run.ml b/customize/customize_run.ml
new file mode 100644
index 0000000..dd98017
--- /dev/null
+++ b/customize/customize_run.ml
@@ -0,0 +1,328 @@
+(* virt-customize
+ * Copyright (C) 2014 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 Common_gettext.Gettext
+open Common_utils
+
+open Customize_cmdline
+open Password
+
+let quote = Filename.quote
+
+let run ~prog ~debug ~quiet (g : Guestfs.guestfs) root (ops : ops) =
+ (* Timestamped messages in ordinary, non-debug non-quiet mode. *)
+ let msg fs = make_message_function ~quiet fs in
+
+ (* Based on the guest type, choose a log file location. *)
+ let logfile =
+ match g#inspect_get_type root with
+ | "windows" | "dos" ->
+ if g#is_dir ~followsymlinks:true "/Temp" then "/Temp/builder.log"
+ else "/builder.log"
+ | _ ->
+ if g#is_dir ~followsymlinks:true "/tmp" then "/tmp/builder.log"
+ else "/builder.log" in
+
+ (* Function to cat the log file, for debugging and error messages. *)
+ let debug_logfile () =
+ try
+ (* XXX If stderr is redirected this actually truncates the
+ * redirection file, which is pretty annoying to say the
+ * least.
+ *)
+ g#download logfile "/dev/stderr"
+ with exn ->
+ eprintf (f_"%s: log file %s: %s (ignored)\n")
+ prog logfile (Printexc.to_string exn) in
+
+ (* Useful wrapper for scripts. *)
+ let do_run ~display cmd =
+ (* Add a prologue to the scripts:
+ * - Pass environment variables through from the host.
+ * - Send stdout and stderr to a log file so we capture all output
+ * in error messages.
+ * Also catch errors and dump the log file completely on error.
+ *)
+ let env_vars =
+ filter_map (
+ fun name ->
+ try Some (sprintf "export %s=%s" name (quote (Sys.getenv name)))
+ with Not_found -> None
+ ) [ "http_proxy"; "https_proxy"; "ftp_proxy"; "no_proxy" ] in
+ let env_vars = String.concat "\n" env_vars ^ "\n" in
+
+ let cmd = sprintf "\
+exec >>%s 2>&1
+%s
+%s
+" (quote logfile) env_vars cmd in
+
+ if debug then eprintf "running command:\n%s\n%!" cmd;
+ try ignore (g#sh cmd)
+ with
+ Guestfs.Error msg ->
+ debug_logfile ();
+ eprintf (f_"%s: %s: command exited with an error\n") prog display;
+ exit 1
+ in
+
+ (* http://distrowatch.com/dwres.php?resource=package-management *)
+ let guest_install_command packages =
+ let quoted_args = String.concat " " (List.map quote packages) in
+ match g#inspect_get_package_management root with
+ | "apt" ->
+ (* http://unix.stackexchange.com/questions/22820 *)
+ sprintf "
+ export DEBIAN_FRONTEND=noninteractive
+ apt_opts='-q -y -o Dpkg::Options::=--force-confnew'
+ apt-get $apt_opts update
+ apt-get $apt_opts install %s
+ " quoted_args
+ | "pisi" ->
+ sprintf "pisi it %s" quoted_args
+ | "pacman" ->
+ sprintf "pacman -S %s" quoted_args
+ | "urpmi" ->
+ sprintf "urpmi %s" quoted_args
+ | "yum" ->
+ sprintf "yum -y install %s" quoted_args
+ | "zypper" ->
+ (* XXX Should we use -n option? *)
+ sprintf "zypper in %s" quoted_args
+ | "unknown" ->
+ eprintf (f_"%s: --install is not supported for this guest operating system\n")
+ prog;
+ exit 1
+ | pm ->
+ eprintf (f_"%s: sorry, don't know how to use --install with the '%s' package manager\n")
+ prog pm;
+ exit 1
+
+ and guest_update_command () =
+ match g#inspect_get_package_management root with
+ | "apt" ->
+ (* http://unix.stackexchange.com/questions/22820 *)
+ sprintf "
+ export DEBIAN_FRONTEND=noninteractive
+ apt_opts='-q -y -o Dpkg::Options::=--force-confnew'
+ apt-get $apt_opts update
+ apt-get $apt_opts upgrade
+ "
+ | "pisi" ->
+ sprintf "pisi upgrade"
+ | "pacman" ->
+ sprintf "pacman -Su"
+ | "urpmi" ->
+ sprintf "urpmi --auto-select"
+ | "yum" ->
+ sprintf "yum -y update"
+ | "zypper" ->
+ sprintf "zypper update"
+ | "unknown" ->
+ eprintf (f_"%s: --update is not supported for this guest operating system\n")
+ prog;
+ exit 1
+ | pm ->
+ eprintf (f_"%s: sorry, don't know how to use --update with the '%s' package manager\n")
+ prog pm;
+ exit 1
+ in
+
+ (* Set the random seed. *)
+ msg (f_"Setting a random seed");
+ if not (Random_seed.set_random_seed g root) then
+ eprintf (f_"%s: warning: random seed could not be set for this type of guest\n%!") prog;
+
+ (* Used for numbering firstboot commands. *)
+ let i = ref 0 in
+
+ (* Store the passwords and set them all at the end. *)
+ let passwords = Hashtbl.create 13 in
+ let set_password user pw =
+ if Hashtbl.mem passwords user then (
+ eprintf (f_"%s: error: multiple --root-password/--password options set the password for user '%s' twice.\n")
+ prog user;
+ exit 1
+ );
+ Hashtbl.replace passwords user pw
+ in
+
+ (* Perform the remaining customizations in command-line order. *)
+ List.iter (
+ function
+ | `Command cmd ->
+ msg (f_"Running: %s") cmd;
+ do_run ~display:cmd cmd
+
+ | `Delete path ->
+ msg (f_"Deleting: %s") path;
+ g#rm_rf path
+
+ | `Edit (path, expr) ->
+ msg (f_"Editing: %s") path;
+
+ if not (g#is_file path) then (
+ eprintf (f_"%s: error: %s is not a regular file in the guest\n")
+ prog path;
+ exit 1
+ );
+
+ Perl_edit.edit_file ~debug g path expr
+
+ | `FirstbootCommand cmd ->
+ incr i;
+ msg (f_"Installing firstboot command: [%d] %s") !i cmd;
+ Firstboot.add_firstboot_script g root !i cmd
+
+ | `FirstbootPackages pkgs ->
+ incr i;
+ msg (f_"Installing firstboot packages: [%d] %s") !i
+ (String.concat " " pkgs);
+ let cmd = guest_install_command pkgs in
+ Firstboot.add_firstboot_script g root !i cmd
+
+ | `FirstbootScript script ->
+ incr i;
+ msg (f_"Installing firstboot script: [%d] %s") !i script;
+ let cmd = read_whole_file script in
+ Firstboot.add_firstboot_script g root !i cmd
+
+ | `Hostname hostname ->
+ msg (f_"Setting the hostname: %s") hostname;
+ if not (Hostname.set_hostname g root hostname) then
+ eprintf (f_"%s: warning: hostname could not be set for this type of guest\n%!")
+ prog
+
+ | `InstallPackages pkgs ->
+ msg (f_"Installing packages: %s") (String.concat " " pkgs);
+ let cmd = guest_install_command pkgs in
+ do_run ~display:cmd cmd
+
+ | `Link (target, links) ->
+ List.iter (
+ fun link ->
+ msg (f_"Linking: %s -> %s") link target;
+ g#ln_sf target link
+ ) links
+
+ | `Mkdir dir ->
+ msg (f_"Making directory: %s") dir;
+ g#mkdir_p dir
+
+ | `RootPassword pw ->
+ set_password "root" pw
+
+ | `Script script ->
+ msg (f_"Running: %s") script;
+ let cmd = read_whole_file script in
+ do_run ~display:script cmd
+
+ | `Scrub path ->
+ msg (f_"Scrubbing: %s") path;
+ g#scrub_file path
+
+ | `Timezone tz ->
+ msg (f_"Setting the timezone: %s") tz;
+ if not (Timezone.set_timezone ~prog g root tz) then
+ eprintf (f_"%s: warning: timezone could not be set for this type of guest\n%!")
+ prog
+
+ | `Update ->
+ msg (f_"Updating core packages");
+ let cmd = guest_update_command () in
+ do_run ~display:cmd cmd
+
+ | `Upload (path, dest) ->
+ msg (f_"Uploading: %s to %s") path dest;
+ let dest =
+ if g#is_dir ~followsymlinks:true dest then
+ dest ^ "/" ^ Filename.basename path
+ else
+ dest in
+ (* Do the file upload. *)
+ g#upload path dest;
+
+ (* Copy (some of) the permissions from the local file to the
+ * uploaded file.
+ *)
+ let statbuf = stat path in
+ let perms = statbuf.st_perm land 0o7777 (* sticky & set*id *) in
+ g#chmod perms dest;
+ let uid, gid = statbuf.st_uid, statbuf.st_gid in
+ g#chown uid gid dest
+
+ | `Write (path, content) ->
+ msg (f_"Writing: %s") path;
+ g#write path content
+ ) ops.ops;
+
+ (* Set all the passwords at the end. *)
+ if Hashtbl.length passwords > 0 then (
+ match g#inspect_get_type root with
+ | "linux" ->
+ msg (f_"Setting passwords");
+ let password_crypto = ops.flags.password_crypto in
+ set_linux_passwords ~prog ?password_crypto g root passwords
+
+ | _ ->
+ eprintf (f_"%s: warning: passwords could not be set for this type of guest\n%!")
+ prog
+ );
+
+ if ops.flags.selinux_relabel then (
+ msg (f_"SELinux relabelling");
+ let cmd = sprintf "
+ if load_policy && fixfiles restore; then
+ rm -f /.autorelabel
+ else
+ touch /.autorelabel
+ echo '%s: SELinux relabelling failed, will relabel at boot instead.'
+ fi
+ " prog in
+ do_run ~display:"load_policy && fixfiles restore" cmd
+ );
+
+ (* Clean up the log file:
+ *
+ * If debugging, dump out the log file.
+ * Then if asked, scrub the log file.
+ *)
+ if debug then debug_logfile ();
+ if ops.flags.scrub_logfile && g#exists logfile then (
+ msg (f_"Scrubbing the log file");
+
+ (* Try various methods with decreasing complexity. *)
+ try g#scrub_file logfile
+ with _ -> g#rm_f logfile
+ );
+
+ (* Kill any daemons (eg. started by newly installed packages) using
+ * the sysroot.
+ * XXX How to make this nicer?
+ * XXX fuser returns an error if it doesn't kill any processes, which
+ * is not very useful.
+ *)
+ (try ignore (g#debug "sh" [| "fuser"; "-k"; "/sysroot" |])
+ with exn ->
+ if debug then
+ eprintf (f_"%s: %s (ignored)\n") prog (Printexc.to_string exn)
+ );
+ g#ping_daemon () (* tiny delay after kill *)
diff --git a/mllib/timezone.mli b/customize/customize_run.mli
similarity index 68%
copy from mllib/timezone.mli
copy to customize/customize_run.mli
index ad0d4b2..0fa7683 100644
--- a/mllib/timezone.mli
+++ b/customize/customize_run.mli
@@ -1,4 +1,4 @@
-(* Set timezone in virt-sysprep and virt-builder.
+(* virt-customize
* Copyright (C) 2014 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -16,7 +16,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
-val set_timezone : prog:string -> Guestfs.guestfs -> string -> string -> bool
-(** [set_timezone ~prog g root "Europe/London"] sets the default timezone
- of the guest. Returns [true] if it was able to set the
- timezone or [false] if not. *)
+(* After command line arguments have been parsed, call this function
+ * to perform the operations on a guest handle.
+ *
+ * Note that inspection must have been done on the handle, and
+ * filesystems must be mounted up.
+ *)
+
+val run : prog:string -> debug:bool -> quiet:bool -> Guestfs.guestfs -> string -> Customize_cmdline.ops -> unit
diff --git a/mllib/firstboot.ml b/customize/firstboot.ml
similarity index 100%
rename from mllib/firstboot.ml
rename to customize/firstboot.ml
diff --git a/mllib/firstboot.mli b/customize/firstboot.mli
similarity index 100%
rename from mllib/firstboot.mli
rename to customize/firstboot.mli
diff --git a/mllib/hostname.ml b/customize/hostname.ml
similarity index 100%
rename from mllib/hostname.ml
rename to customize/hostname.ml
diff --git a/mllib/hostname.mli b/customize/hostname.mli
similarity index 100%
rename from mllib/hostname.mli
rename to customize/hostname.mli
diff --git a/mllib/password.ml b/customize/password.ml
similarity index 100%
rename from mllib/password.ml
rename to customize/password.ml
diff --git a/mllib/password.mli b/customize/password.mli
similarity index 100%
rename from mllib/password.mli
rename to customize/password.mli
diff --git a/mllib/perl_edit.ml b/customize/perl_edit.ml
similarity index 100%
rename from mllib/perl_edit.ml
rename to customize/perl_edit.ml
diff --git a/mllib/perl_edit.mli b/customize/perl_edit.mli
similarity index 100%
rename from mllib/perl_edit.mli
rename to customize/perl_edit.mli
diff --git a/mllib/random_seed.ml b/customize/random_seed.ml
similarity index 100%
rename from mllib/random_seed.ml
rename to customize/random_seed.ml
diff --git a/mllib/random_seed.mli b/customize/random_seed.mli
similarity index 100%
rename from mllib/random_seed.mli
rename to customize/random_seed.mli
diff --git a/mllib/timezone.ml b/customize/timezone.ml
similarity index 100%
rename from mllib/timezone.ml
rename to customize/timezone.ml
diff --git a/mllib/timezone.mli b/customize/timezone.mli
similarity index 100%
rename from mllib/timezone.mli
rename to customize/timezone.mli
diff --git a/mllib/urandom.ml b/customize/urandom.ml
similarity index 100%
rename from mllib/urandom.ml
rename to customize/urandom.ml
diff --git a/mllib/urandom.mli b/customize/urandom.mli
similarity index 100%
rename from mllib/urandom.mli
rename to customize/urandom.mli
diff --git a/generator/Makefile.am b/generator/Makefile.am
index c129747..e66644c 100644
--- a/generator/Makefile.am
+++ b/generator/Makefile.am
@@ -27,6 +27,7 @@ sources = \
c.ml \
checks.ml \
csharp.ml \
+ customize.ml \
daemon.ml \
docstrings.ml \
erlang.ml \
@@ -89,6 +90,7 @@ objects = \
golang.cmo \
bindtests.cmo \
errnostring.cmo \
+ customize.cmo \
main.cmo
EXTRA_DIST = $(sources) files-generated.txt
diff --git a/generator/customize.ml b/generator/customize.ml
new file mode 100644
index 0000000..c87eeba
--- /dev/null
+++ b/generator/customize.ml
@@ -0,0 +1,634 @@
+(* libguestfs
+ * Copyright (C) 2014 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
+ *)
+
+(* Please read generator/README first. *)
+
+open Printf
+
+open Docstrings
+open Pr
+
+(* Command-line arguments used by virt-customize, virt-builder and
+ * virt-sysprep.
+ *)
+
+type op = {
+ op_name : string; (* argument name, without "--" *)
+ op_type : op_type; (* argument value type *)
+ op_discrim : string; (* argument discriminator in OCaml code *)
+ op_shortdesc : string; (* single-line description *)
+ op_pod_longdesc : string; (* multi-line description *)
+}
+and op_type =
+| Unit (* no argument *)
+| String of string (* string *)
+| StringPair of string (* string:string *)
+| StringList of string (* string,string,... *)
+| TargetLinks of string (* target:link[:link...] *)
+| PasswordSelector of string (* password selector *)
+
+let ops = [
+ { op_name = "delete";
+ op_type = String "PATH";
+ op_discrim = "`Delete";
+ op_shortdesc = "Delete a file or directory";
+ op_pod_longdesc = "\
+Delete a file from the guest. Or delete a directory (and all its
+contents, recursively).
+
+See also: I<--upload>, I<--scrub>.";
+ };
+ { op_name = "edit";
+ op_type = StringPair "FILE:EXPR";
+ op_discrim = "`Edit";
+ op_shortdesc = "Edit file using Perl expression";
+ op_pod_longdesc = "\
+Edit C<FILE> using the Perl expression C<EXPR>.
+
+Be careful to properly quote the expression to prevent it from
+being altered by the shell.
+
+Note that this option is only available when Perl 5 is installed.
+
+See L<virt-edit(1)/NON-INTERACTIVE EDITING>.";
+ };
+ { op_name = "firstboot";
+ op_type = String "SCRIPT";
+ op_discrim = "`FirstbootScript";
+ op_shortdesc = "Run script at first guest boot";
+ op_pod_longdesc = "\
+Install C<SCRIPT> inside the guest, so that when the guest first boots
+up, the script runs (as root, late in the boot process).
+
+The script is automatically chmod +x after installation in the guest.
+
+The alternative version I<--firstboot-command> is the same, but it
+conveniently wraps the command up in a single line script for you.
+
+You can have multiple I<--firstboot> options. They run in the same
+order that they appear on the command line.
+
+See also I<--run>.";
+ };
+ { op_name = "firstboot-command";
+ op_type = String "'CMD+ARGS'";
+ op_discrim = "`FirstbootCommand";
+ op_shortdesc = "Run command at first guest boot";
+ op_pod_longdesc = "\
+Run command (and arguments) inside the guest when the guest first
+boots up (as root, late in the boot process).
+
+You can have multiple I<--firstboot> options. They run in the same
+order that they appear on the command line.
+
+See also I<--run>.";
+ };
+ { op_name = "firstboot-install";
+ op_type = StringList "PKG,PKG..";
+ op_discrim = "`FirstbootPackages";
+ op_shortdesc = "Add package(s) to install at first boot";
+ op_pod_longdesc = "\
+Install the named packages (a comma-separated list). These are
+installed when the guest first boots using the guest's package manager
+(eg. apt, yum, etc.) and the guest's network connection.
+
+For an overview on the different ways to install packages, see
+L<virt-builder(1)/INSTALLING PACKAGES>.";
+ };
+ { op_name = "hostname";
+ op_type = String "HOSTNAME";
+ op_discrim = "`Hostname";
+ op_shortdesc = "Set the hostname";
+ op_pod_longdesc = "\
+Set the hostname of the guest to C<HOSTNAME>. You can use a
+dotted hostname.domainname (FQDN) if you want.";
+ };
+ { op_name = "install";
+ op_type = StringList "PKG,PKG..";
+ op_discrim = "`InstallPackages";
+ op_shortdesc = "Add package(s) to install";
+ op_pod_longdesc = "\
+Install the named packages (a comma-separated list). These are
+installed during the image build using the guest's package manager
+(eg. apt, yum, etc.) and the host's network connection.
+
+For an overview on the different ways to install packages, see
+L<virt-builder(1)/INSTALLING PACKAGES>.
+
+See also I<--update>.";
+ };
+ { op_name = "link";
+ op_type = TargetLinks "TARGET:LINK[:LINK..]";
+ op_discrim = "`Link";
+ op_shortdesc = "Create symbolic links";
+ op_pod_longdesc = "\
+Create symbolic link(s) in the guest, starting at C<LINK> and
+pointing at C<TARGET>.";
+ };
+ { op_name = "mkdir";
+ op_type = String "DIR";
+ op_discrim = "`Mkdir";
+ op_shortdesc = "Create a directory";
+ op_pod_longdesc = "\
+Create a directory in the guest.
+
+This uses S<C<mkdir -p>> so any intermediate directories are created,
+and it also works if the directory already exists.";
+ };
+ { op_name = "root-password";
+ op_type = PasswordSelector "SELECTOR";
+ op_discrim = "`RootPassword";
+ op_shortdesc = "Set root password";
+ op_pod_longdesc = "\
+Set the root password.
+
+See L<virt-builder(1)/USERS AND PASSWORDS> for the format of
+the C<SELECTOR> field, and also how to set up user accounts.
+
+Note: In virt-builder, if you I<don't> set I<--root-password>
+then the guest is given a I<random> root password.";
+ };
+ { op_name = "run";
+ op_type = String "SCRIPT";
+ op_discrim = "`Script";
+ op_shortdesc = "Run script in disk image";
+ op_pod_longdesc = "\
+Run the shell script (or any program) called C<SCRIPT> on the disk
+image. The script runs virtualized inside a small appliance, chrooted
+into the guest filesystem.
+
+The script is automatically chmod +x.
+
+If libguestfs supports it then a limited network connection is
+available but it only allows outgoing network connections. You can
+also attach data disks (eg. ISO files) as another way to provide data
+(eg. software packages) to the script without needing a network
+connection (I<--attach>). You can also upload data files (I<--upload>).
+
+You can have multiple I<--run> options. They run
+in the same order that they appear on the command line.
+
+See also: I<--firstboot>, I<--attach>, I<--upload>.";
+ };
+ { op_name = "run-command";
+ op_type = String "'CMD+ARGS'";
+ op_discrim = "`Command";
+ op_shortdesc = "Run command in disk image";
+ op_pod_longdesc = "\
+Run the command and arguments on the disk image. The command runs
+virtualized inside a small appliance, chrooted into the guest filesystem.
+
+If libguestfs supports it then a limited network connection is
+available but it only allows outgoing network connections. You can
+also attach data disks (eg. ISO files) as another way to provide data
+(eg. software packages) to the script without needing a network
+connection (I<--attach>). You can also upload data files (I<--upload>).
+
+You can have multiple I<--run-command> options. They run
+in the same order that they appear on the command line.
+
+See also: I<--firstboot>, I<--attach>, I<--upload>.";
+ };
+ { op_name = "scrub";
+ op_type = String "FILE";
+ op_discrim = "`Scrub";
+ op_shortdesc = "Scrub a file";
+ op_pod_longdesc = "\
+Scrub a file from the guest. This is like I<--delete> except that:
+
+=over 4
+
+=item *
+
+It scrubs the data so a guest could not recover it.
+
+=item *
+
+It cannot delete directories, only regular files.
+
+=back";
+ };
+ { op_name = "timezone";
+ op_type = String "TIMEZONE";
+ op_discrim = "`Timezone";
+ op_shortdesc = "Set the default timezone";
+ op_pod_longdesc = "\
+Set the default timezone of the guest to C<TIMEZONE>. Use a location
+string like C<Europe/London>";
+ };
+ { op_name = "update";
+ op_type = Unit;
+ op_discrim = "`Update";
+ op_shortdesc = "Update core packages";
+ op_pod_longdesc = "\
+Do the equivalent of C<yum update>, C<apt-get upgrade>, or whatever
+command is required to update the packages already installed in the
+template to their latest versions.
+
+See also I<--install>.";
+ };
+ { op_name = "upload";
+ op_type = StringPair "FILE:DEST";
+ op_discrim = "`Upload";
+ op_shortdesc = "Upload local file to destination";
+ op_pod_longdesc = "\
+Upload local file C<FILE> to destination C<DEST> in the disk image.
+File owner and permissions from the original are preserved, so you
+should set them to what you want them to be in the disk image.
+
+C<DEST> could be the final filename. This can be used to rename
+the file on upload.
+
+If C<DEST> is a directory name (which must already exist in the guest)
+then the file is uploaded into that directory, and it keeps the same
+name as on the local filesystem.
+
+See also: I<--mkdir>, I<--delete>, I<--scrub>.";
+ };
+ { op_name = "write";
+ op_type = StringPair "FILE:CONTENT";
+ op_discrim = "`Write";
+ op_shortdesc = "Write file";
+ op_pod_longdesc = "\
+Write C<CONTENT> to C<FILE>.";
+ };
+]
+
+(* Flags. *)
+type flag = {
+ flag_name : string; (* argument name, without "--" *)
+ flag_type : flag_type; (* argument value type *)
+ flag_ml_var : string; (* variable name in OCaml code *)
+ flag_shortdesc : string; (* single-line description *)
+ flag_pod_longdesc : string; (* multi-line description *)
+}
+and flag_type =
+| FlagBool of bool (* boolean is the default value *)
+| FlagPasswordCrypto of string
+
+let flags = [
+ { flag_name = "no-logfile";
+ flag_type = FlagBool false;
+ flag_ml_var = "scrub_logfile";
+ flag_shortdesc = "Scrub build log file";
+ flag_pod_longdesc = "\
+Scrub C<builder.log> (log file from build commands) from the image
+after building is complete. If you don't want to reveal precisely how
+the image was built, use this option.
+
+See also: L</LOG FILE>.";
+ };
+ { flag_name = "password-crypto";
+ flag_type = FlagPasswordCrypto "md5|sha256|sha512";
+ flag_ml_var = "password_crypto";
+ flag_shortdesc = "Set password crypto";
+ flag_pod_longdesc = "\
+Set the password encryption to C<md5>, C<sha256> or C<sha512>.
+
+C<sha256> and C<sha512> require glibc E<ge> 2.7 (check crypt(3) inside
+the guest).
+
+C<md5> will work with relatively old Linux guests (eg. RHEL 3), but
+is not secure against modern attacks.
+
+The default is C<sha512> unless libguestfs detects an old guest that
+didn't have support for SHA-512, in which case it will use C<md5>.
+You can override libguestfs by specifying this option.";
+ };
+ { flag_name = "selinux-relabel";
+ flag_type = FlagBool false (* XXX - the default in virt-builder *);
+ flag_ml_var = "selinux_relabel";
+ flag_shortdesc = "Relabel files with correct SELinux labels";
+ flag_pod_longdesc = "\
+Relabel files in the guest so that they have the correct SELinux label.
+
+You should only use this option for guests which support SELinux.";
+ };
+]
+
+let rec generate_customize_cmdline_mli () =
+ generate_header OCamlStyle GPLv2plus;
+
+ pr "\
+(** Command line argument parsing, both for the virt-customize binary
+ and for the other tools that share the same code. *)
+
+";
+ generate_ops_struct_decl ();
+ pr "\n";
+
+ pr "\
+type argspec = Arg.key * Arg.spec * Arg.doc
+val argspec : prog:string -> unit -> (argspec * string option * string) list * (unit -> ops)
+(** This returns a pair [(list, get_ops)].
+
+ [list] is a list of the command line arguments, plus some extra data.
+
+ [get_ops] is a function you can call {i after} command line parsing
+ which will return the actual operations specified by the user on the
+ command line. *)"
+
+and generate_customize_cmdline_ml () =
+ generate_header OCamlStyle GPLv2plus;
+
+ pr "\
+(* Command line argument parsing, both for the virt-customize binary
+ * and for the other tools that share the same code.
+ *)
+
+open Printf
+
+open Common_utils
+open Common_gettext.Gettext
+
+";
+ generate_ops_struct_decl ();
+ pr "\n";
+
+ pr "\
+type argspec = Arg.key * Arg.spec * Arg.doc
+
+let rec argspec ~prog () =
+ let ops = ref [] in
+";
+ List.iter (
+ function
+ | { flag_type = FlagBool default; flag_ml_var = var } ->
+ pr " let %s = ref %b in\n" var default
+ | { flag_type = FlagPasswordCrypto _; flag_ml_var = var } ->
+ pr " let %s = ref None in\n" var
+ ) flags;
+ pr "\
+
+ let rec get_ops () = {
+ ops = List.rev !ops;
+ flags = get_flags ();
+ }
+ and get_flags () = {
+";
+ List.iter (fun { flag_ml_var = var } -> pr " %s = !%s;\n" var var) flags;
+ pr " }
+ in
+
+ let split_string_pair option_name arg =
+ let i =
+ try String.index arg ':'
+ with Not_found ->
+ eprintf (f_\"%%s: invalid format for '--%%s' parameter, see the man page.\\n\")
+ prog option_name;
+ exit 1 in
+ let len = String.length arg in
+ String.sub arg 0 i, String.sub arg (i+1) (len-(i+1))
+ in
+ let split_string_list arg =
+ string_nsplit \",\" arg
+ in
+ let split_links_list option_name arg =
+ match string_nsplit \":\" arg with
+ | [] | [_] ->
+ eprintf (f_\"%%s: invalid format for '--%%s' parameter, see the man page.\\n\")
+ prog option_name;
+ exit 1
+ | target :: lns -> target, lns
+ in
+
+ let argspec = [
+";
+
+ List.iter (
+ function
+ | { op_type = Unit; op_name = name; op_discrim = discrim;
+ op_shortdesc = shortdesc; op_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ pr " Arg.Unit (fun () -> ops := %s :: !ops),\n" discrim;
+ pr " \" \" ^ s_\"%s\"\n" shortdesc;
+ pr " ),\n";
+ pr " None, %S;\n" longdesc
+ | { op_type = String v; op_name = name; op_discrim = discrim;
+ op_shortdesc = shortdesc; op_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ pr " Arg.String (fun s -> ops := %s s :: !ops),\n" discrim;
+ pr " s_\"%s\" ^ \" \" ^ s_\"%s\"\n" v shortdesc;
+ pr " ),\n";
+ pr " Some %S, %S;\n" v longdesc
+ | { op_type = StringPair v; op_name = name; op_discrim = discrim;
+ op_shortdesc = shortdesc; op_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ pr " Arg.String (\n";
+ pr " fun s ->\n";
+ pr " let p = split_string_pair \"%s\" s in\n" name;
+ pr " ops := %s p :: !ops\n" discrim;
+ pr " ),\n";
+ pr " s_\"%s\" ^ \" \" ^ s_\"%s\"\n" v shortdesc;
+ pr " ),\n";
+ pr " Some %S, %S;\n" v longdesc
+ | { op_type = StringList v; op_name = name; op_discrim = discrim;
+ op_shortdesc = shortdesc; op_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ pr " Arg.String (\n";
+ pr " fun s ->\n";
+ pr " let ss = split_string_list s in\n";
+ pr " ops := %s ss :: !ops\n" discrim;
+ pr " ),\n";
+ pr " s_\"%s\" ^ \" \" ^ s_\"%s\"\n" v shortdesc;
+ pr " ),\n";
+ pr " Some %S, %S;\n" v longdesc
+ | { op_type = TargetLinks v; op_name = name; op_discrim = discrim;
+ op_shortdesc = shortdesc; op_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ pr " Arg.String (\n";
+ pr " fun s ->\n";
+ pr " let ss = split_links_list \"%s\" s in\n" name;
+ pr " ops := %s ss :: !ops\n" discrim;
+ pr " ),\n";
+ pr " s_\"%s\" ^ \" \" ^ s_\"%s\"\n" v shortdesc;
+ pr " ),\n";
+ pr " Some %S, %S;\n" v longdesc
+ | { op_type = PasswordSelector v; op_name = name; op_discrim = discrim;
+ op_shortdesc = shortdesc; op_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ pr " Arg.String (\n";
+ pr " fun s ->\n";
+ pr " let sel = Password.parse_selector ~prog s in\n";
+ pr " ops := %s sel :: !ops\n" discrim;
+ pr " ),\n";
+ pr " s_\"%s\" ^ \" \" ^ s_\"%s\"\n" v shortdesc;
+ pr " ),\n";
+ pr " Some %S, %S;\n" v longdesc
+ ) ops;
+
+ List.iter (
+ function
+ | { flag_type = FlagBool default; flag_ml_var = var; flag_name = name;
+ flag_shortdesc = shortdesc; flag_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ if default (* is true *) then
+ pr " Arg.Clear %s,\n" var
+ else
+ pr " Arg.Set %s,\n" var;
+ pr " \" \" ^ s_\"%s\"\n" shortdesc;
+ pr " ),\n";
+ pr " None, %S;\n" longdesc
+ | { flag_type = FlagPasswordCrypto v; flag_ml_var = var;
+ flag_name = name; flag_shortdesc = shortdesc;
+ flag_pod_longdesc = longdesc } ->
+ pr " (\n";
+ pr " \"--%s\",\n" name;
+ pr " Arg.String (\n";
+ pr " fun s ->\n";
+ pr " %s := Some (Password.password_crypto_of_string ~prog s)\n" var;
+ pr " ),\n";
+ pr " \"%s\" ^ \" \" ^ s_\"%s\"\n" v shortdesc;
+ pr " ),\n";
+ pr " Some %S, %S;\n" v longdesc
+ ) flags;
+
+ pr " ] in
+
+ argspec, get_ops
+"
+
+and generate_ops_struct_decl () =
+ pr "\
+type ops = {
+ ops : op list;
+ flags : flags;
+}
+";
+
+ (* Operations. *)
+ pr "and op = [\n";
+ List.iter (
+ function
+ | { op_type = Unit; op_discrim = discrim; op_name = name } ->
+ pr " | %s\n (* --%s *)\n" discrim name
+ | { op_type = String v; op_discrim = discrim; op_name = name } ->
+ pr " | %s of string\n (* --%s %s *)\n" discrim name v
+ | { op_type = StringPair v; op_discrim = discrim;
+ op_name = name } ->
+ pr " | %s of string * string\n (* --%s %s *)\n" discrim name v
+ | { op_type = StringList v; op_discrim = discrim;
+ op_name = name } ->
+ pr " | %s of string list\n (* --%s %s *)\n" discrim name v
+ | { op_type = TargetLinks v; op_discrim = discrim;
+ op_name = name } ->
+ pr " | %s of string * string list\n (* --%s %s *)\n" discrim name v
+ | { op_type = PasswordSelector v; op_discrim = discrim;
+ op_name = name } ->
+ pr " | %s of Password.password_selector\n (* --%s %s *)\n"
+ discrim name v
+ ) ops;
+ pr "]\n";
+
+ (* Flags. *)
+ pr "and flags = {\n";
+ List.iter (
+ function
+ | { flag_type = FlagBool _; flag_ml_var = var; flag_name = name } ->
+ pr " %s : bool;\n (* --%s *)\n" var name
+ | { flag_type = FlagPasswordCrypto v; flag_ml_var = var;
+ flag_name = name } ->
+ pr " %s : Password.password_crypto option;\n (* --%s %s *)\n"
+ var name v
+ ) flags;
+ pr "}\n"
+
+let generate_customize_synopsis_pod () =
+ (* generate_header PODStyle GPLv2plus; - NOT POSSIBLE *)
+
+ let options =
+ List.map (
+ function
+ | { op_type = Unit; op_name = n } ->
+ n, sprintf "[--%s]" n
+ | { op_type = String v | StringPair v | StringList v | TargetLinks v
+ | PasswordSelector v;
+ op_name = n } ->
+ n, sprintf "[--%s %s]" n v
+ ) ops @
+ List.map (
+ function
+ | { flag_type = FlagBool _; flag_name = n } ->
+ n, sprintf "[--%s]" n
+ | { flag_type = FlagPasswordCrypto v; flag_name = n } ->
+ n, sprintf "[--%s %s]" n v
+ ) flags in
+
+ (* Print the option names in the synopsis, line-wrapped. *)
+ let col = ref 4 in
+ pr " ";
+
+ List.iter (
+ fun (_, str) ->
+ let len = String.length str + 1 in
+ col := !col + len;
+ if !col >= 72 then (
+ col := 4 + len;
+ pr "\n "
+ );
+ pr " %s" str
+ ) options;
+ if !col > 4 then
+ pr "\n"
+
+let generate_customize_options_pod () =
+ generate_header PODStyle GPLv2plus;
+
+ pr "=over 4\n\n";
+
+ let pod =
+ List.map (
+ function
+ | { op_type = Unit; op_name = n; op_pod_longdesc = ld } ->
+ n, sprintf "B<--%s>" n, ld
+ | { op_type = String v | StringPair v | StringList v | TargetLinks v
+ | PasswordSelector v;
+ op_name = n; op_pod_longdesc = ld } ->
+ n, sprintf "B<--%s> %s" n v, ld
+ ) ops @
+ List.map (
+ function
+ | { flag_type = FlagBool _; flag_name = n; flag_pod_longdesc = ld } ->
+ n, sprintf "B<--%s>" n, ld
+ | { flag_type = FlagPasswordCrypto v;
+ flag_name = n; flag_pod_longdesc = ld } ->
+ n, sprintf "B<--%s> %s" n v, ld
+ ) flags in
+ let cmp (arg1, _, _) (arg2, _, _) =
+ compare (String.lowercase arg1) (String.lowercase arg2)
+ in
+ let pod = List.sort cmp pod in
+
+ List.iter (
+ fun (_, item, longdesc) ->
+ pr "\
+=item %s
+
+%s
+
+" item longdesc
+ ) pod;
+
+ pr "=back\n\n"
diff --git a/generator/main.ml b/generator/main.ml
index d1fa4d2..63ddb9a 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -46,6 +46,7 @@ open Gobject
open Golang
open Bindtests
open Errnostring
+open Customize
let perror msg = function
| Unix_error (err, _, _) ->
@@ -208,6 +209,11 @@ Run it from the top source directory using the command
generate_gobject_session_header;
output_to "gobject/src/session.c" generate_gobject_session_source;
+ output_to "customize/customize_cmdline.mli" generate_customize_cmdline_mli;
+ output_to "customize/customize_cmdline.ml" generate_customize_cmdline_ml;
+ output_to "customize/customize-synopsis.pod" generate_customize_synopsis_pod;
+ output_to "customize/customize-options.pod" generate_customize_options_pod;
+
(* Generate the list of files generated -- last. *)
printf "generated %d lines of code\n" (get_lines_generated ());
let files = List.sort compare (get_files_generated ()) in
diff --git a/mllib/Makefile.am b/mllib/Makefile.am
index e275213..fe215f8 100644
--- a/mllib/Makefile.am
+++ b/mllib/Makefile.am
@@ -28,37 +28,20 @@ SOURCES = \
common_utils.ml \
common_utils_tests.ml \
config.ml \
- crypt-c.c \
- crypt.ml \
- crypt.mli \
- firstboot.mli \
- firstboot.ml \
fsync-c.c \
fsync.mli \
fsync.ml \
mkdtemp.mli \
mkdtemp.ml \
mkdtemp-c.c \
- hostname.mli \
- hostname.ml \
- password.mli \
- password.ml \
- perl_edit.mli \
- perl_edit.ml \
planner.mli \
planner.ml \
progress-c.c \
progress.mli \
progress.ml \
- random_seed.mli \
- random_seed.ml \
- timezone.mli \
- timezone.ml \
tty-c.c \
tTY.mli \
tTY.ml \
- urandom.mli \
- urandom.ml \
uri-c.c \
uRI.mli \
uRI.ml
@@ -73,18 +56,10 @@ ocaml_modules = config \
libdir \
common_gettext \
common_utils \
- urandom \
- random_seed \
- hostname \
- timezone \
- firstboot \
- perl_edit \
tTY \
fsync \
progress \
uRI \
- crypt \
- password \
mkdtemp \
planner
@@ -95,7 +70,6 @@ OBJECTS = \
fsync-c.o \
progress-c.o \
uri-c.o \
- crypt-c.o \
mkdtemp-c.o
if HAVE_OCAMLOPT
diff --git a/po-docs/ja/Makefile.am b/po-docs/ja/Makefile.am
index e954f04..f17be96 100644
--- a/po-docs/ja/Makefile.am
+++ b/po-docs/ja/Makefile.am
@@ -107,6 +107,15 @@ guestfish.1: guestfish.pod guestfish-actions.pod guestfish-commands.pod guestfis
--insert $(srcdir)/guestfish-prepopts.pod:__PREPOPTS__ \
$<
+virt-builder.1: virt-builder.pod customize-synopsis.pod customize-options.pod
+ $(PODWRAPPER) \
+ --no-strict-checks \
+ --man $@ \
+ --license GPLv2+ \
+ --insert $(srcdir)/customize-synopsis.pod:__CUSTOMIZE_SYNOPSIS__ \
+ --insert $(srcdir)/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
+ $<
+
virt-sysprep.1: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.pod
$(PODWRAPPER) \
--no-strict-checks \
diff --git a/po-docs/uk/Makefile.am b/po-docs/uk/Makefile.am
index e954f04..f17be96 100644
--- a/po-docs/uk/Makefile.am
+++ b/po-docs/uk/Makefile.am
@@ -107,6 +107,15 @@ guestfish.1: guestfish.pod guestfish-actions.pod guestfish-commands.pod guestfis
--insert $(srcdir)/guestfish-prepopts.pod:__PREPOPTS__ \
$<
+virt-builder.1: virt-builder.pod customize-synopsis.pod customize-options.pod
+ $(PODWRAPPER) \
+ --no-strict-checks \
+ --man $@ \
+ --license GPLv2+ \
+ --insert $(srcdir)/customize-synopsis.pod:__CUSTOMIZE_SYNOPSIS__ \
+ --insert $(srcdir)/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
+ $<
+
virt-sysprep.1: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.pod
$(PODWRAPPER) \
--no-strict-checks \
diff --git a/po/POTFILES b/po/POTFILES
index ecdbae4..37dbbaa 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -11,6 +11,7 @@ cat/cat.c
cat/filesystems.c
cat/ls.c
cat/visit.c
+customize/crypt-c.c
daemon/9p.c
daemon/acl.c
daemon/augeas.c
@@ -239,7 +240,6 @@ inspector/inspector.c
java/com_redhat_et_libguestfs_GuestFS.c
lua/lua-guestfs.c
make-fs/make-fs.c
-mllib/crypt-c.c
mllib/fsync-c.c
mllib/mkdtemp-c.c
mllib/progress-c.c
diff --git a/po/POTFILES-ml b/po/POTFILES-ml
index ed96697..3870f3d 100644
--- a/po/POTFILES-ml
+++ b/po/POTFILES-ml
@@ -17,21 +17,13 @@ mllib/common_gettext.ml
mllib/common_utils.ml
mllib/common_utils_tests.ml
mllib/config.ml
-mllib/crypt.ml
-mllib/firstboot.ml
mllib/fsync.ml
-mllib/hostname.ml
mllib/libdir.ml
mllib/mkdtemp.ml
-mllib/password.ml
-mllib/perl_edit.ml
mllib/planner.ml
mllib/progress.ml
-mllib/random_seed.ml
mllib/tTY.ml
-mllib/timezone.ml
mllib/uRI.ml
-mllib/urandom.ml
resize/resize.ml
sparsify/cmdline.ml
sparsify/copying.ml
diff --git a/src/guestfs.pod b/src/guestfs.pod
index b3c32eb..e6e91f4 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -4272,6 +4272,10 @@ and documentation.
Outside contributions, experimental parts.
+=item C<customize>
+
+virt-customize mini-library.
+
=item C<daemon>
The daemon that runs inside the libguestfs appliance and carries out
diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am
index d20ad08..1bff338 100644
--- a/sysprep/Makefile.am
+++ b/sysprep/Makefile.am
@@ -88,20 +88,20 @@ if HAVE_OCAML
deps = \
$(top_builddir)/mllib/common_gettext.cmx \
$(top_builddir)/mllib/common_utils.cmx \
- $(top_builddir)/fish/guestfish-uri.o \
$(top_builddir)/mllib/uri-c.o \
$(top_builddir)/mllib/uRI.cmx \
- $(top_builddir)/mllib/crypt-c.o \
- $(top_builddir)/mllib/crypt.cmx \
- $(top_builddir)/mllib/urandom.cmx \
- $(top_builddir)/mllib/password.cmx \
- $(top_builddir)/mllib/random_seed.cmx \
- $(top_builddir)/mllib/hostname.cmx \
- $(top_builddir)/mllib/timezone.cmx \
- $(top_builddir)/mllib/firstboot.cmx \
$(top_builddir)/mllib/config.cmx \
$(top_builddir)/mllib/mkdtemp-c.o \
$(top_builddir)/mllib/mkdtemp.cmx \
+ $(top_builddir)/customize/crypt-c.o \
+ $(top_builddir)/customize/crypt.cmx \
+ $(top_builddir)/customize/urandom.cmx \
+ $(top_builddir)/customize/password.cmx \
+ $(top_builddir)/customize/random_seed.cmx \
+ $(top_builddir)/customize/hostname.cmx \
+ $(top_builddir)/customize/timezone.cmx \
+ $(top_builddir)/customize/firstboot.cmx \
+ $(top_builddir)/fish/guestfish-uri.o \
sysprep_operation.cmx \
$(patsubst %,sysprep_operation_%.cmx,$(operations)) \
main.cmx
@@ -121,7 +121,8 @@ OCAMLPACKAGES = \
-package str,unix \
-I $(top_builddir)/src/.libs \
-I $(top_builddir)/ocaml \
- -I $(top_builddir)/mllib
+ -I $(top_builddir)/mllib \
+ -I $(top_builddir)/customize
if HAVE_OCAML_PKG_GETTEXT
OCAMLPACKAGES += -package gettext-stub
endif
@@ -225,7 +226,7 @@ depend: .depend
.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
rm -f $@ $@-t
- $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \
+ $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \
$(SED) 's/ *$$//' | \
$(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
$(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-libvirt/libguestfs.git
More information about the Pkg-libvirt-commits
mailing list