Pass to --commands-from-file the name of a file containing customization commands in each line, as if they were specified as command line arguments.
This eases the reuse of commands among different builder/customize/sysprep invocations. --- builder/cmdline.ml | 3 +- customize/customize_run.ml | 5 +++ generator/customize.ml | 98 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/builder/cmdline.ml b/builder/cmdline.ml index bb7b1d0..1c6ab98 100644 --- a/builder/cmdline.ml +++ b/builder/cmdline.ml @@ -308,7 +308,8 @@ read the man page virt-builder(1). | `Delete _ | `Edit _ | `FirstbootCommand _ | `FirstbootPackages _ | `FirstbootScript _ | `Hostname _ | `Link _ | `Mkdir _ | `Password _ | `RootPassword _ | `Scrub _ | `SSHInject _ - | `Timezone _ | `Upload _ | `Write _ | `Chmod _ -> false + | `Timezone _ | `Upload _ | `Write _ | `Chmod _ + | `CommandsFromFile _ -> false ) ops.ops in if requires_execute_on_guest then error (f_"sorry, cannot run commands on a guest with a different architecture"); diff --git a/customize/customize_run.ml b/customize/customize_run.ml index 19b7a7d..fed905b 100644 --- a/customize/customize_run.ml +++ b/customize/customize_run.ml @@ -170,6 +170,11 @@ exec >>%s 2>&1 msg (f_"Running: %s") cmd; do_run ~display:cmd cmd + | `CommandsFromFile _ -> + (* Nothing to do, the files with customize commands are already + * read when their arguments are met. *) + () + | `Delete path -> msg (f_"Deleting: %s") path; g#rm_rf path diff --git a/generator/customize.ml b/generator/customize.ml index 82ecb79..d5e72ac 100644 --- a/generator/customize.ml +++ b/generator/customize.ml @@ -43,6 +43,7 @@ and op_type = | PasswordSelector of string (* password selector *) | UserPasswordSelector of string (* user:selector *) | SSHKeySelector of string (* user:selector *) +| StringFn of (string * string) (* string, function name *) let ops = [ { op_name = "chmod"; @@ -56,6 +57,29 @@ I<Note>: C<PERMISSIONS> by default would be decimal, unless you prefix it with C<0> to get octal, ie. use C<0700> not C<700>."; }; + { op_name = "commands-from-file"; + op_type = StringFn ("FILENAME", "customize_read_from_file"); + op_discrim = "`CommandsFromFile"; + op_shortdesc = "Read customize commands from file"; + op_pod_longdesc = "\ +Read the customize commands from a file, one (and its arguments) +each line. + +Each line contains a single customization command and its arguments, +for example: + + delete /some/file + install some-package + password some-user:password:its-new-password + +Empty lines are ignored, and lines starting with C<#> are comments +and are ignored as well. + +The commands are handled in the same order as they are in the file, +as if they were specified as I<--delete /some/file> on the command +line."; + }; + { op_name = "delete"; op_type = String "PATH"; op_discrim = "`Delete"; @@ -474,7 +498,7 @@ let rec argspec () = | target :: lns -> target, lns in - let argspec = [ + let rec argspec = [ "; List.iter ( @@ -569,6 +593,18 @@ let rec argspec () = pr " s_\"%s\" ^ \" \" ^ s_\"%s\"\n" v shortdesc; pr " ),\n"; pr " Some %S, %S;\n" v longdesc + | { op_type = StringFn (v, fn); 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 " %s s;\n" fn; + pr " ops := %s s :: !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 ( @@ -598,7 +634,57 @@ let rec argspec () = pr " Some %S, %S;\n" v longdesc ) flags; - pr " ] in + pr " ] + and customize_read_from_file filename = + let forbidden_commands = [ +"; + + List.iter ( + function + | { op_type = StringFn (_, _); op_name = name; } -> + pr " \"%s\";\n" name + | { op_type = Unit; } + | { op_type = String _; } + | { op_type = StringPair _; } + | { op_type = StringList _; } + | { op_type = TargetLinks _; } + | { op_type = PasswordSelector _; } + | { op_type = UserPasswordSelector _; } + | { op_type = SSHKeySelector _; } -> () + ) ops; + +pr " ] in + let lines = read_whole_file filename in + let lines = string_nsplit \"\\n\" lines in + let lines = List.filter ( + fun line -> + String.length line > 0 && line.[0] <> '#' + ) lines in + let cmds = List.map (fun line -> string_split \" \" line) lines in + (* Check for commands not allowed in files containing commands. *) + List.iter ( + fun (cmd, _) -> + if List.mem cmd forbidden_commands then + error (f_\"command '%%s' cannot be used in command files, see the man page\") + cmd + ) cmds; + List.iter ( + fun (cmd, arg) -> + try + let ((_, spec, _), _, _) = List.find ( + fun ((key, _, _), _, _) -> + key = \"--\" ^ cmd + ) argspec in + (match spec with + | Arg.Unit fn -> fn () + | Arg.String fn -> fn arg + | _ -> error \"INTERNAL error: spec not handled for %%s\" cmd + ) + with Not_found -> + error (f_\"command '%%s' not valid, see the man page\") + cmd + ) cmds + in argspec, get_ops " @@ -640,6 +726,8 @@ type ops = { op_name = name } -> pr " | %s of string * Ssh_key.ssh_key_selector\n (* --%s %s *)\n" discrim name v + | { op_type = StringFn (v, _); op_discrim = discrim; op_name = name } -> + pr " | %s of string\n (* --%s %s *)\n" discrim name v ) ops; pr "]\n"; @@ -665,7 +753,8 @@ let generate_customize_synopsis_pod () = | { op_type = Unit; op_name = n } -> n, sprintf "[--%s]" n | { op_type = String v | StringPair v | StringList v | TargetLinks v - | PasswordSelector v | UserPasswordSelector v | SSHKeySelector v; + | PasswordSelector v | UserPasswordSelector v | SSHKeySelector v + | StringFn (v, _); op_name = n } -> n, sprintf "[--%s %s]" n v ) ops @ @@ -705,7 +794,8 @@ let generate_customize_options_pod () = | { 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 | UserPasswordSelector v | SSHKeySelector v; + | PasswordSelector v | UserPasswordSelector v | SSHKeySelector v + | StringFn (v, _); op_name = n; op_pod_longdesc = ld } -> n, sprintf "B<--%s> %s" n v, ld ) ops @ -- 1.9.3 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://www.redhat.com/mailman/listinfo/libguestfs