Kindlier way to do network configuration.
[virt-p2v.git] / virt-p2v.ml
index 4b590af..23b59f0 100755 (executable)
@@ -325,9 +325,21 @@ let shget cmd =
   | WSIGNALED i -> failwith (sprintf "shget: command killed by signal %d" i)
   | WSTOPPED i -> failwith (sprintf "shget: command stopped by signal %d" i)
 
-(* Start an interactive shell. *)
+(* Start an interactive shell.  Need to juggle file descriptors a bit
+ * because bash write PS1 to stderr (currently directed to the logfile).
+ *)
 let shell () =
-  shfailok "PS1='\\u@\\h:\\w\\$ ' bash"
+  match fork () with
+  | 0 ->                               (* child, runs bash *)
+      close stderr;
+      dup2 stdout stderr;
+      (* Sys.command runs 'sh -c' which blows away PS1, so set it late. *)
+      ignore (
+       Sys.command "PS1='\\u@\\h:\\w\\$ ' /bin/bash --norc --noprofile -i"
+      )
+  | _ ->                               (* parent, waits *)
+      eprintf "waiting for subshell to exit\n%!";
+      ignore (wait ())
 
 (* Some true if is dir/file, Some false if not, None if not found. *)
 let is_dir path =
@@ -441,35 +453,46 @@ let snapshot =
     sh (sprintf "dmsetup create %s --table='0 %Ld snapshot /dev/mapper/%s_org %s n 64'"
          snapshot_dev sectors snapshot_dev ramdisk)
 
-(* Try to perform automatic network configuration, assuming a Fedora or RHEL-
- * like root filesystem mounted on /mnt/root.
+(* Try to perform automatic network configuration, assuming a Fedora or
+ * RHEL-like root filesystem mounted on /mnt/root.
  *)
 let auto_network state =
   (* Fedora gives an error if this file doesn't exist. *)
   sh "touch /etc/resolv.conf";
 
-  chdir "/etc/sysconfig";
-
-  sh "mv network network.saved";
-  sh "mv networking networking.saved";
-  sh "mv network-scripts network-scripts.saved";
-
-  (* Originally I symlinked these, but that causes dhclient to
-   * keep open /mnt/root (as its cwd is in network-scripts subdir).
-   * So now we will copy them recursively instead.
+(*
+  (* We can run /mnt/root/etc/init.d/network in a chroot environment,
+   * however this won't work directly because the architecture of the
+   * binaries under /mnt/root (eg. /mnt/root/sbin/ip) might not match
+   * the architecture of the live CD kernel.  In particular, a 32 bit
+   * live CD cannot run 64 bit binaries.  So we also have to bind-mount
+   * the live CD's /bin, /sbin, /lib etc. over the equivalents in
+   * /mnt/root.
    *)
-  sh "cp -r /mnt/root/etc/sysconfig/network .";
-  sh "cp -r /mnt/root/etc/sysconfig/networking .";
-  sh "cp -r /mnt/root/etc/sysconfig/network-scripts .";
-
+  let bind dir =
+    if is_dir dir = Some true then
+      sh ("mount -o bind " ^ quote dir ^ " " ^ quote ("/mnt/root" ^ dir))
+  in
+  let unbind dir =
+    if is_dir dir = Some true then sh ("umount -l " ^ quote ("/mnt/root" ^ dir))
+  in
+  let dirs = [
+    "/bin"; "/sbin"; "/lib"; "/lib64";
+    "/usr/bin"; "/usr/sbin"; "/usr/lib"; "/usr/lib64";
+    "/proc"; "/sys"
+  ] in
+  List.iter bind dirs;
+  let status = shwithstatus "chroot /mnt/root /etc/init.d/network start" in
+  List.iter unbind dirs;
+*)
+
+  (* Simpler way to do the above.
+   * NB. Lazy unmount is required because dhclient keeps its current
+   * directory open on /etc/sysconfig/network-scripts/
+   *)
+  sh "mount -o bind /mnt/root/etc /etc";
   let status = shwithstatus "/etc/init.d/network start" in
-
-  sh "rm -rf network networking network-scripts";
-  sh "mv network.saved network";
-  sh "mv networking.saved networking";
-  sh "mv network-scripts.saved network-scripts";
-
-  chdir "/tmp";
+  sh "umount -l /etc";
 
   (* Try to ping the remote host to see if this worked. *)
   shfailok ("ping -c 3 " ^ Option.map_default quote "" state.remote_host);
@@ -643,8 +666,8 @@ let rec main ttyname =
     (* Output of 'file' command for Linux swap file. *)
     let swap = Pcre.regexp "Linux.*swap.*file" in
     (* Contents of /etc/redhat-release. *)
-    let rhel = Pcre.regexp "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)" in
-    let fedora = Pcre.regexp "Fedora.*release (\\d+)\\.(\\d+)" in
+    let rhel = Pcre.regexp "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)(?:\\.(\\d+))?" in
+    let fedora = Pcre.regexp "Fedora.*release (\\d+)" in
     (* Contents of /etc/debian_version. *)
     let debian = Pcre.regexp "^(\\d+)\\.(\\d+)" in
     (* Output of 'file' on certain executables. *)
@@ -670,11 +693,13 @@ let rec main ttyname =
            match lines with
            | [] -> (* empty /etc/redhat-release ...? *)
                LinuxRoot (UnknownArch, OtherLinux)
-           | line::_ ->
+           | line::_ -> (* try to detect OS from /etc/redhat-release *)
                try
                  let subs = Pcre.exec ~rex:rhel line in
                  let major = int_of_string (Pcre.get_substring subs 1) in
-                 let minor = int_of_string (Pcre.get_substring subs 2) in
+                 let minor =
+                   try int_of_string (Pcre.get_substring subs 2)
+                   with Not_found -> 0 in
                  LinuxRoot (UnknownArch, RHEL (major, minor))
                with
                  Not_found | Failure "int_of_string" ->
@@ -694,7 +719,7 @@ let rec main ttyname =
            match lines with
            | [] -> (* empty /etc/debian_version ...? *)
                LinuxRoot (UnknownArch, OtherLinux)
-           | line::_ ->
+           | line::_ -> (* try to detect version from /etc/debian_version *)
                try
                  let subs = Pcre.exec ~rex:debian line in
                  let major = int_of_string (Pcre.get_substring subs 1) in