mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
This is not quite a neutral refactoring, because it means we now run the getenforce command every time virt-v2v starts up. However it's a trivial command that reads a single /sys file and it can't fail even if the command is missing or on platforms that know nothing about SELinux.
161 lines
5.0 KiB
OCaml
161 lines
5.0 KiB
OCaml
(* virt-v2v
|
||
* Copyright (C) 2009-2019 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.
|
||
*)
|
||
|
||
(* Utilities used in virt-v2v only. *)
|
||
|
||
open Printf
|
||
|
||
open Std_utils
|
||
open Tools_utils
|
||
open Common_gettext.Gettext
|
||
|
||
(* Is SELinux enabled and enforcing on the host? *)
|
||
let have_selinux =
|
||
0 = Sys.command "getenforce 2>/dev/null | grep -isq Enforcing"
|
||
|
||
(* URI quoting. *)
|
||
let uri_quote str =
|
||
let len = String.length str in
|
||
let xs = ref [] in
|
||
for i = 0 to len-1 do
|
||
xs :=
|
||
(match str.[i] with
|
||
| ('A'..'Z' | 'a'..'z' | '0'..'9' | '/' | '.' | '-') as c ->
|
||
String.make 1 c
|
||
| c ->
|
||
sprintf "%%%02x" (Char.code c)
|
||
) :: !xs
|
||
done;
|
||
String.concat "" (List.rev !xs)
|
||
|
||
(* Map guest architecture found by inspection to the architecture
|
||
* that KVM must emulate. Note for x86 we assume a 64 bit hypervisor.
|
||
*)
|
||
let kvm_arch = function
|
||
| "i386" | "i486" | "i586" | "i686"
|
||
| "x86_64" -> "x86_64"
|
||
| "unknown" -> "x86_64" (* most likely *)
|
||
| arch -> arch
|
||
|
||
(* Does qemu support the given sound card? *)
|
||
let qemu_supports_sound_card = function
|
||
| Types.AC97
|
||
| Types.ES1370
|
||
| Types.ICH6
|
||
| Types.ICH9
|
||
| Types.PCSpeaker
|
||
| Types.SB16
|
||
| Types.USBAudio
|
||
-> true
|
||
|
||
(* Find the UEFI firmware. *)
|
||
let find_uefi_firmware guest_arch =
|
||
let files =
|
||
(* The lists of firmware are actually defined in common/utils/uefi.c. *)
|
||
match guest_arch with
|
||
| "x86_64" -> Uefi.uefi_x86_64_firmware
|
||
| "aarch64" -> Uefi.uefi_aarch64_firmware
|
||
| arch ->
|
||
error (f_"don’t know how to convert UEFI guests for architecture %s")
|
||
guest_arch in
|
||
let rec loop = function
|
||
| [] ->
|
||
error (f_"cannot find firmware for UEFI guests.\n\nYou probably need to install OVMF (x86-64), or AAVMF (aarch64)")
|
||
| ({ Uefi.code; vars = vars_template } as ret) :: rest ->
|
||
if Sys.file_exists code && Sys.file_exists vars_template then ret
|
||
else loop rest
|
||
in
|
||
loop files
|
||
|
||
let error_unless_uefi_firmware guest_arch =
|
||
ignore (find_uefi_firmware guest_arch)
|
||
|
||
let compare_app2_versions app1 app2 =
|
||
let i = compare app1.Guestfs.app2_epoch app2.Guestfs.app2_epoch in
|
||
if i <> 0 then i
|
||
else (
|
||
let i =
|
||
compare_version app1.Guestfs.app2_version app2.Guestfs.app2_version in
|
||
if i <> 0 then i
|
||
else
|
||
compare_version app1.Guestfs.app2_release app2.Guestfs.app2_release
|
||
)
|
||
|
||
let du filename =
|
||
(* There's no OCaml binding for st_blocks, so run coreutils 'du'. *)
|
||
let cmd =
|
||
sprintf "du --block-size=1 %s | awk '{print $1}'" (quote filename) in
|
||
(* XXX This can call error and so exit, but it would be preferable
|
||
* to raise an exception here.
|
||
*)
|
||
let lines = external_command cmd in
|
||
match lines with
|
||
| line::_ -> Int64.of_string line
|
||
| [] -> invalid_arg filename
|
||
|
||
let qemu_img_supports_offset_and_size () =
|
||
(* We actually attempt to create a qcow2 file with a raw backing
|
||
* file that has an offset and size.
|
||
*)
|
||
let tmp = Filename.temp_file "v2vqemuimgtst" ".img" in
|
||
Unix.truncate tmp 1024;
|
||
|
||
let json = [
|
||
"file", JSON.Dict [
|
||
"driver", JSON.String "raw";
|
||
"offset", JSON.Int 512_L;
|
||
"size", JSON.Int 512_L;
|
||
"file", JSON.Dict [
|
||
"filename", JSON.String tmp
|
||
]
|
||
]
|
||
] in
|
||
|
||
let cmd =
|
||
sprintf "qemu-img info json:%s >/dev/null%s"
|
||
(quote (JSON.string_of_doc ~fmt:JSON.Compact json))
|
||
(if verbose () then "" else " 2>&1") in
|
||
debug "%s" cmd;
|
||
let r = 0 = Sys.command cmd in
|
||
Unix.unlink tmp;
|
||
debug "qemu-img supports \"offset\" and \"size\" in json URLs: %b" r;
|
||
r
|
||
|
||
let backend_is_libvirt () =
|
||
let backend = (open_guestfs ())#get_backend () in
|
||
let backend = fst (String.split ":" backend) in
|
||
backend = "libvirt"
|
||
|
||
(* When using the SSH driver in qemu (currently) this requires
|
||
* ssh-agent authentication. Give a clear error if this hasn't been
|
||
* set up (RHBZ#1139973). This might improve if we switch to libssh1.
|
||
*)
|
||
let error_if_no_ssh_agent () =
|
||
try ignore (Sys.getenv "SSH_AUTH_SOCK")
|
||
with Not_found ->
|
||
error (f_"ssh-agent authentication has not been set up ($SSH_AUTH_SOCK is not set). This is required by qemu to do passwordless ssh access. See the virt-v2v(1) man page for more information.")
|
||
|
||
(* Wait for a file to appear until a timeout. *)
|
||
let rec wait_for_file filename timeout =
|
||
if Sys.file_exists filename then true
|
||
else if timeout = 0 then false
|
||
else (
|
||
Unix.sleep 1;
|
||
wait_for_file filename (timeout-1)
|
||
)
|