--- v2v/Makefile.am | 8 +- v2v/output_qemu.ml | 88 +++++++------- v2v/qemu_command.ml | 101 --------------- v2v/qemuopts-c.c | 216 +++++++++++++++++++++++++++++++++ v2v/qemuopts.ml | 35 ++++++ v2v/{qemu_command.mli => qemuopts.mli} | 24 ++-- 6 files changed, 318 insertions(+), 154 deletions(-) delete mode 100644 v2v/qemu_command.ml create mode 100644 v2v/qemuopts-c.c create mode 100644 v2v/qemuopts.ml rename v2v/{qemu_command.mli => qemuopts.mli} (75%) diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 8df8ca0..5b6618a 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -57,7 +57,7 @@ SOURCES_MLI = \ parse_ovf_from_ova.mli \ parse_libvirt_xml.mli \ parse_vmx.mli \ - qemu_command.mli \ + qemuopts.mli \ target_bus_assignment.mli \ types.mli \ uefi.mli \ @@ -85,7 +85,7 @@ SOURCES_ML = \ parse_vmx.ml \ parse_libvirt_xml.ml \ create_libvirt_xml.ml \ - qemu_command.ml \ + qemuopts.ml \ input_libvirtxml.ml \ input_libvirt_other.ml \ input_libvirt_vcenter_https.ml \ @@ -111,6 +111,7 @@ SOURCES_ML = \ SOURCES_C = \ libvirt_utils-c.c \ + qemuopts-c.c \ utils-c.c if HAVE_OCAML @@ -122,6 +123,7 @@ virt_v2v_CPPFLAGS = \ -I. \ -I$(top_builddir) \ -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/common/qemuopts \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib virt_v2v_CFLAGS = \ @@ -140,6 +142,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/qemuopts/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ @@ -151,6 +154,7 @@ endif OCAMLCLIBS = \ -lutils \ + -lqemuopts \ $(LIBVIRT_LIBS) \ $(LIBXML2_LIBS) \ $(LIBINTL) \ diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml index 7721f0b..031279c 100644 --- a/v2v/output_qemu.ml +++ b/v2v/output_qemu.ml @@ -69,29 +69,31 @@ object let machine_q35 = secure_boot_required in let smm = secure_boot_required in - (* Construct the command line. Note that the [Qemu_command] + (* Construct the command line. Note that the [Qemuopts] * module deals with shell and qemu comma quoting. *) - let cmd = Qemu_command.create ~arch:guestcaps.gcaps_arch () in - let flag = Qemu_command.flag cmd - and arg = Qemu_command.arg cmd - and arg_noquote = Qemu_command.arg_noquote cmd - and commas = Qemu_command.commas cmd in + let cmd = Qemuopts.create () in + Qemuopts.set_binary_by_arch cmd (Some guestcaps.gcaps_arch); + + let flag = Qemuopts.flag cmd + and arg = Qemuopts.arg cmd + and arg_noquote = Qemuopts.arg_noquote cmd + and arg_list = Qemuopts.arg_list cmd in flag "-no-user-config"; flag "-nodefaults"; arg "-name" source.s_name; - commas "-machine" (if machine_q35 then ["q35"] else [] @ - if smm then ["smm=on"] else [] @ - ["accel=kvm:tcg"]); + arg_list "-machine" (if machine_q35 then ["q35"] else [] @ + if smm then ["smm=on"] else [] @ + ["accel=kvm:tcg"]); (match uefi_firmware with | None -> () | Some { Uefi.code = code } -> if secure_boot_required then - commas "-global" - ["driver=cfi.pflash01"; "property=secure"; "value=on"]; - commas "-drive" - ["if=pflash"; "format=raw"; "file=" ^ code; "readonly"]; + arg_list "-global" + ["driver=cfi.pflash01"; "property=secure"; "value=on"]; + arg_list "-drive" + ["if=pflash"; "format=raw"; "file=" ^ code; "readonly"]; arg_noquote "-drive" "if=pflash,format=raw,file=\"$uefi_vars\""; ); @@ -113,7 +115,7 @@ object (match source.s_cpu_threads with | None -> 1 | Some v -> v)); - commas "-smp" !a + arg_list "-smp" !a ) else arg "-smp" (string_of_int source.s_vcpu); @@ -123,17 +125,17 @@ object | BusSlotEmpty -> () | BusSlotTarget t -> - commas "-drive" ["file=" ^ t.target_file; "format=" ^ t.target_format; - "if=" ^ if_name; "index=" ^ string_of_int i; - "media=disk"] + arg_list "-drive" ["file=" ^ t.target_file; "format=" ^ t.target_format; + "if=" ^ if_name; "index=" ^ string_of_int i; + "media=disk"] | BusSlotRemovable { s_removable_type = CDROM } -> - commas "-drive" ["format=raw"; "if=" ^ if_name; - "index=" ^ string_of_int i; "media=cdrom"] + arg_list "-drive" ["format=raw"; "if=" ^ if_name; + "index=" ^ string_of_int i; "media=cdrom"] | BusSlotRemovable { s_removable_type = Floppy } -> - commas "-drive" ["format=raw"; "if=" ^ if_name; - "index=" ^ string_of_int i; "media=floppy"] + arg_list "-drive" ["format=raw"; "if=" ^ if_name; + "index=" ^ string_of_int i; "media=floppy"] in Array.iteri (make_disk "virtio") target_buses.target_virtio_blk_bus; Array.iteri (make_disk "ide") target_buses.target_ide_bus; @@ -142,17 +144,17 @@ object | BusSlotEmpty -> () | BusSlotTarget t -> - commas "-drive" ["file=" ^ t.target_file; "format=" ^ t.target_format; - "if=scsi"; "bus=0"; "unit=" ^ string_of_int i; - "media=disk"] + arg_list "-drive" ["file=" ^ t.target_file; "format=" ^ t.target_format; + "if=scsi"; "bus=0"; "unit=" ^ string_of_int i; + "media=disk"] | BusSlotRemovable { s_removable_type = CDROM } -> - commas "-drive" ["format=raw"; "if=scsi"; "bus=0"; - "unit=" ^ string_of_int i; "media=cdrom"] + arg_list "-drive" ["format=raw"; "if=scsi"; "bus=0"; + "unit=" ^ string_of_int i; "media=cdrom"] | BusSlotRemovable { s_removable_type = Floppy } -> - commas "-drive" ["format=raw"; "if=scsi"; "bus=0"; - "unit=" ^ string_of_int i; "media=floppy"] + arg_list "-drive" ["format=raw"; "if=scsi"; "bus=0"; + "unit=" ^ string_of_int i; "media=floppy"] in Array.iteri make_scsi target_buses.target_scsi_bus; @@ -167,12 +169,12 @@ object | RTL8139 -> "rtl8139" in iteri ( fun i nic -> - commas "-netdev" ["user"; "id=net" ^ string_of_int i]; - commas "-device" [net_bus; - sprintf "netdev=net%d%s" i - (match nic.s_mac with - | None -> "" - | Some mac -> "mac=" ^ mac)] + arg_list "-netdev" ["user"; "id=net" ^ string_of_int i]; + arg_list "-device" [net_bus; + sprintf "netdev=net%d%s" i + (match nic.s_mac with + | None -> "" + | Some mac -> "mac=" ^ mac)] ) source.s_nics; (* Add a display. *) @@ -185,11 +187,11 @@ object | VNC -> arg "-display" "vnc=:0" | Spice -> - commas "-spice" [sprintf "port=%d" - (match display.s_port with - | None -> 5900 - | Some p -> p); - "addr=127.0.0.1"] + arg_list "-spice" [sprintf "port=%d" + (match display.s_port with + | None -> 5900 + | Some p -> p); + "addr=127.0.0.1"] ); arg "-vga" (match guestcaps.gcaps_video with Cirrus -> "cirrus" | QXL -> "qxl") @@ -215,15 +217,15 @@ object (* Add the miscellaneous KVM devices. *) if guestcaps.gcaps_virtio_rng then ( - commas "-object" ["rng-random"; "filename=/dev/urandom"; "id=rng0"]; - commas "-device" ["virtio-rng-pci"; "rng=rng0"]; + arg_list "-object" ["rng-random"; "filename=/dev/urandom"; "id=rng0"]; + arg_list "-device" ["virtio-rng-pci"; "rng=rng0"]; ); if guestcaps.gcaps_virtio_balloon then arg "-balloon" "virtio" else arg "-balloon" "none"; if guestcaps.gcaps_isa_pvpanic then - commas "-device" ["pvpanic"; "ioport=0x505"]; + arg_list "-device" ["pvpanic"; "ioport=0x505"]; (* Add a serial console to Linux guests. *) if inspect.i_type = "linux" then @@ -244,7 +246,7 @@ object fpf "\n" ); - Qemu_command.to_chan cmd chan; + Qemuopts.to_chan cmd chan; close_out chan; Unix.chmod file 0o755; diff --git a/v2v/qemu_command.ml b/v2v/qemu_command.ml deleted file mode 100644 index ccdda8a..0000000 --- a/v2v/qemu_command.ml +++ /dev/null @@ -1,101 +0,0 @@ -(* virt-v2v - * Copyright (C) 2009-2017 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. - *) - -(** Generate a qemu command line, dealing with quoting. *) - -open Printf - -open Common_utils - -type t = { - cmd : string; - mutable args : arg list; (* list constructed in reverse order *) -} -and arg = - | Flag of string - | Arg of string * string * bool - | Commas of string * string list - -let create ?(arch = "x86_64") () = - { cmd = "qemu-system-" ^ arch; args = [] } - -let flag t k = - assert (String.is_prefix k "-"); - t.args <- Flag k :: t.args - -let arg t k v = - assert (String.is_prefix k "-"); - t.args <- Arg (k, v, true) :: t.args - -let arg_noquote t k v = - assert (String.is_prefix k "-"); - t.args <- Arg (k, v, false) :: t.args - -let commas t k vs = - assert (String.is_prefix k "-"); - List.iter (fun v -> assert (v <> "")) vs; - t.args <- Commas (k, vs) :: t.args - -let nl = " \\\n\t" - -(* If the value contains only simple characters then it doesn't - * need quoting. This keeps the output as similar as possible - * to the old code. - *) -let do_quoting str = - let len = String.length str in - let ret = ref false in - for i = 0 to len-1 do - let c = String.unsafe_get str i in - if not (Char.isalnum c) && - c <> '.' && c <> '-' && c <> '_' && - c <> '=' && c <> ',' && c <> ':' && c <> '/' - then - ret := true - done; - !ret - -let print_quoted_param chan k v = - if not (do_quoting v) then - fprintf chan "%s%s %s" nl k v - else - fprintf chan "%s%s %s" nl k (quote v) - -let to_chan t chan = - fprintf chan "%s" t.cmd; - List.iter ( - function - | Flag k -> - fprintf chan "%s%s" nl k - | Arg (k, v, true) -> - print_quoted_param chan k v - | Arg (k, v, false) -> - fprintf chan "%s%s %s" nl k v - | Commas (k, vs) -> - let vs = List.map (fun s -> String.replace s "," ",,") vs in - let v = String.concat "," vs in - print_quoted_param chan k v - ) (List.rev t.args); - fprintf chan "\n" - -let to_script t filename = - let chan = open_out filename in - fprintf chan "#!/bin/sh -\n"; - to_chan t chan; - close_out chan; - Unix.chmod filename 0o755 diff --git a/v2v/qemuopts-c.c b/v2v/qemuopts-c.c new file mode 100644 index 0000000..cfdb5f6 --- /dev/null +++ b/v2v/qemuopts-c.c @@ -0,0 +1,216 @@ +/* virt-v2v + * Copyright (C) 2009-2017 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 <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <caml/alloc.h> +#include <caml/custom.h> +#include <caml/fail.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> + +#ifdef HAVE_CAML_UNIXSUPPORT_H +#include <caml/unixsupport.h> +#else +#define Nothing ((value) 0) +extern void unix_error (int errcode, char * cmdname, value arg) Noreturn; +#endif + +#include "qemuopts.h" + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +#define Qopts_val(v) (*((struct qemuopts **)Data_custom_val(v))) + +static void +qopts_finalize (value qoptsv) +{ + struct qemuopts *qopts = Qopts_val (qoptsv); + + if (qopts) + qemuopts_free (qopts); +} + +static struct custom_operations qemuopts_custom_operations = { + (char *) "qemuopts_custom_operations", + qopts_finalize, + custom_compare_default, + custom_hash_default, + custom_serialize_default, + custom_deserialize_default +}; + +value +guestfs_int_qemuopts_create (value unitv) +{ + CAMLparam1 (unitv); + CAMLlocal1 (qoptsv); + struct qemuopts *qopts; + + qopts = qemuopts_create (); + if (qopts == NULL) + unix_error (errno, (char *) "qemuopts_create", Nothing); + + qoptsv = caml_alloc_custom (&qemuopts_custom_operations, + sizeof (struct qemuopts *), 0, 1); + Qopts_val (qoptsv) = qopts; + + CAMLreturn (qoptsv); +} + +value +guestfs_int_qemuopts_set_binary (value qoptsv, value strv) +{ + CAMLparam2 (qoptsv, strv); + struct qemuopts *qopts = Qopts_val (qoptsv); + + if (qemuopts_set_binary (qopts, String_val (strv)) == -1) + unix_error (errno, (char *) "qemuopts_set_binary", strv); + + CAMLreturn (Val_unit); +} + +value +guestfs_int_qemuopts_set_binary_by_arch (value qoptsv, value ostrv) +{ + CAMLparam2 (qoptsv, ostrv); + struct qemuopts *qopts = Qopts_val (qoptsv); + int r; + + if (ostrv != Val_int (0)) + r = qemuopts_set_binary_by_arch (qopts, NULL); + else + r = qemuopts_set_binary_by_arch (qopts, String_val (Field (ostrv, 0))); + + if (r == -1) + unix_error (errno, (char *) "qemuopts_set_binary_by_arch", Nothing); + + CAMLreturn (Val_unit); +} + +value +guestfs_int_qemuopts_flag (value qoptsv, value flagv) +{ + CAMLparam2 (qoptsv, flagv); + struct qemuopts *qopts = Qopts_val (qoptsv); + + if (qemuopts_add_flag (qopts, String_val (flagv)) == -1) + unix_error (errno, (char *) "qemuopts_add_flag", flagv); + + CAMLreturn (Val_unit); +} + +value +guestfs_int_qemuopts_arg (value qoptsv, value flagv, value valv) +{ + CAMLparam3 (qoptsv, flagv, valv); + struct qemuopts *qopts = Qopts_val (qoptsv); + + if (qemuopts_add_arg (qopts, String_val (flagv), String_val (valv)) == -1) + unix_error (errno, (char *) "qemuopts_add_arg", flagv); + + CAMLreturn (Val_unit); +} + +value +guestfs_int_qemuopts_arg_noquote (value qoptsv, value flagv, value valv) +{ + CAMLparam3 (qoptsv, flagv, valv); + struct qemuopts *qopts = Qopts_val (qoptsv); + + if (qemuopts_add_arg_noquote (qopts, + String_val (flagv), String_val (valv)) == -1) + unix_error (errno, (char *) "qemuopts_add_arg_noquote", flagv); + + CAMLreturn (Val_unit); +} + +value +guestfs_int_qemuopts_arg_list (value qoptsv, value flagv, value valuesv) +{ + CAMLparam3 (qoptsv, flagv, valuesv); + CAMLlocal1 (hd); + struct qemuopts *qopts = Qopts_val (qoptsv); + + if (qemuopts_start_arg_list (qopts, String_val (flagv)) == -1) + unix_error (errno, (char *) "qemuopts_start_arg_list", flagv); + + while (valuesv != Val_emptylist) { + hd = Field (valuesv, 0); + if (qemuopts_append_arg_list (qopts, String_val (hd)) == -1) + unix_error (errno, (char *) "qemuopts_append_arg_list", flagv); + valuesv = Field (valuesv, 1); + } + + if (qemuopts_end_arg_list (qopts) == -1) + unix_error (errno, (char *) "qemuopts_end_arg_list", flagv); + + CAMLreturn (Val_unit); +} + +value +guestfs_int_qemuopts_to_script (value qoptsv, value strv) +{ + CAMLparam2 (qoptsv, strv); + struct qemuopts *qopts = Qopts_val (qoptsv); + + if (qemuopts_to_script (qopts, String_val (strv)) == -1) + unix_error (errno, (char *) "qemuopts_to_script", strv); + + CAMLreturn (Val_unit); +} + +value +guestfs_int_qemuopts_to_chan (value qoptsv, value fdv) +{ + CAMLparam2 (qoptsv, fdv); + struct qemuopts *qopts = Qopts_val (qoptsv); + /* Note that Unix.file_descr is really just an int. */ + int fd = Int_val (fdv); + int fd2; + FILE *fp; + int saved_errno; + + /* Dup the file descriptor so we don't lose it in fclose. */ + fd2 = dup (fd); + if (fd2 == -1) + unix_error (errno, (char *) "qemuopts_to_channel: dup", Nothing); + + fp = fdopen (fd2, "w"); + if (fp == NULL) { + saved_errno = errno; + close (fd2); + unix_error (saved_errno, (char *) "qemuopts_to_channel: fdopen", Nothing); + } + + if (qemuopts_to_channel (qopts, fp) == -1) { + saved_errno = errno; + fclose (fp); + unix_error (saved_errno, (char *) "qemuopts_to_channel", Nothing); + } + + if (fclose (fp) == EOF) + unix_error (errno, (char *) "qemuopts_to_channel: fclose", Nothing); + + CAMLreturn (Val_unit); +} diff --git a/v2v/qemuopts.ml b/v2v/qemuopts.ml new file mode 100644 index 0000000..752e4d6 --- /dev/null +++ b/v2v/qemuopts.ml @@ -0,0 +1,35 @@ +(* virt-v2v + * Copyright (C) 2009-2017 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. + *) + +type t + +external create : unit -> t = "guestfs_int_qemuopts_create" +external set_binary : t -> string -> unit = "guestfs_int_qemuopts_set_binary" +external set_binary_by_arch : t -> string option -> unit = "guestfs_int_qemuopts_set_binary_by_arch" +external flag : t -> string -> unit = "guestfs_int_qemuopts_flag" +external arg : t -> string -> string -> unit = "guestfs_int_qemuopts_arg" +external arg_noquote : t -> string -> string -> unit = "guestfs_int_qemuopts_arg_noquote" +external arg_list : t -> string -> string list -> unit = "guestfs_int_qemuopts_arg_list" +external to_script : t -> string -> unit = "guestfs_int_qemuopts_to_script" + +external _to_chan : t -> Unix.file_descr -> unit = "guestfs_int_qemuopts_to_chan" + +let to_chan t chan = + flush chan; + let fd = Unix.descr_of_out_channel chan in + _to_chan t fd diff --git a/v2v/qemu_command.mli b/v2v/qemuopts.mli similarity index 75% rename from v2v/qemu_command.mli rename to v2v/qemuopts.mli index d993bd2..695acab 100644 --- a/v2v/qemu_command.mli +++ b/v2v/qemuopts.mli @@ -16,14 +16,22 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(** Generate a qemu command line, dealing with quoting. *) +(** OCaml bindings for the [common/qemuopts] library. *) type t -val create : ?arch:string -> unit -> t -(** Create an empty qemu command. If the optional [?arch] parameter - is supplied then the command will be [qemu-system-<arch>], - otherwise it will be [qemu-system-x86_64]. *) +val create : unit -> t +(** Create an empty qemu command line. + + In case of error, all these functions raise [Unix_error]. *) + +val set_binary : t -> string -> unit +(** Set the qemu binary name. *) + +val set_binary_by_arch : t -> string option -> unit +(** Set the qemu binary to [qemu-system-<arch>]. If [arch] is [None], + then this picks the right KVM binary for the current host + architecture. *) val flag : t -> string -> unit (** [flag t "-foo"] adds a parameter to the command line with no argument. *) @@ -34,13 +42,13 @@ val arg : t -> string -> string -> unit The value will shell-quoted if required, so you do not need to quote the string. However if the value is a comma-separated list (eg. [-drive file=foo,if=ide]) then do {b not} use this function, call - {!commas} instead. *) + {!arg_list} instead. *) val arg_noquote : t -> string -> string -> unit (** Like {!arg} except no quoting is done on the value. *) -val commas : t -> string -> string list -> unit -(** [commas t "-drive" ["file=foo"; "if=ide"]] adds a comma-separated +val arg_list : t -> string -> string list -> unit +(** [arg_list t "-drive" ["file=foo"; "if=ide"]] adds a comma-separated list of parameters to the command line [-drive file=foo,if=ide]. This does both qemu comma-quoting and shell-quoting as required. *) -- 2.9.3