febootstrap: Use contents of installed Debian packages instead of downloading and...
authorHilko Bengen <bengen@hilluzination.de>
Mon, 12 Sep 2011 21:58:09 +0000 (23:58 +0200)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 23 Sep 2011 18:28:18 +0000 (19:28 +0100)
This also adds --use-installed switch (which for now only works for
Debian).

src/febootstrap.ml
src/febootstrap.pod
src/febootstrap_cmdline.ml
src/febootstrap_cmdline.mli
src/febootstrap_debian.ml
src/febootstrap_package_handlers.ml
src/febootstrap_package_handlers.mli
src/febootstrap_pacman.ml
src/febootstrap_yum_rpm.ml

index 7e48206..3afb1bb 100644 (file)
@@ -67,7 +67,7 @@ let () =
     List.flatten (
       List.map (
         fun pkg ->
-          let files = ph.ph_list_files pkg in
+          let files = ph.ph_list_files ~use_installed pkg in
           List.map (fun (filename, ft) -> filename, ft, pkg) files
       ) packages
     ) in
@@ -320,7 +320,7 @@ let () =
        * original file from the package.
        *)
       else if config then (
-        let outfile = ph.ph_get_file_from_package pkg path in
+        let outfile = ph.ph_get_file_from_package ~use_installed pkg path in
 
         (* Note that the output config file might not be a regular file. *)
         let statbuf = lstat outfile in
index ac97f48..68ad367 100644 (file)
@@ -96,6 +96,18 @@ output directory then they will be overwritten.
 Don't remove temporary files and directories on exit.  This is useful
 for debugging.
 
+=item B<--use-installed>
+
+If packages are already installed, use the contents (from the local
+filesystem) instead of downloading them.
+
+Note that this can cause malformed appliances if local files have been
+changed from what was originally in the package.  This is particularly
+a problem for configuration files.
+
+However this option is useful in some controlled situations: for
+example when using febootstrap inside a freshly installed chroot.
+
 =item B<-v>
 
 =item B<--verbose>
index 667e297..fc18bbd 100644 (file)
@@ -23,6 +23,7 @@ let names_mode = ref false
 let outputdir = ref "."
 let packages = ref []
 let save_temps = ref false
+let use_installed = ref false
 let verbose = ref false
 let warnings = ref true
 let yum_config = ref None
@@ -50,6 +51,8 @@ let argspec = Arg.align [
     " Don't delete temporary files and directories on exit.";
   "--save-temps", Arg.Set save_temps,
     " Don't delete temporary files and directories on exit.";
+  "--use-installed", Arg.Set use_installed,
+    " Inspect already installed packages for determining contents.";
   "-v", Arg.Set verbose,
     " Enable verbose output";
   "--verbose", Arg.Set verbose,
@@ -89,6 +92,7 @@ let names_mode = !names_mode
 let outputdir = !outputdir
 let packages = List.rev !packages
 let save_temps = !save_temps
+let use_installed = !use_installed
 let verbose = !verbose
 let warnings = !warnings
 let yum_config = !yum_config
index d948d80..a545012 100644 (file)
@@ -38,6 +38,9 @@ val packages : string list
 val save_temps : bool
   (** True if [--save-temps] was given on the command line. *)
 
+val use_installed : bool
+  (** True if [--use-installed] was given on the command line *)
+
 val verbose : bool
   (** True if [--verbose] was given on the command line.
       See also {!debug}. *)
index 23f3593..7482ed4 100644 (file)
@@ -28,6 +28,9 @@ open Febootstrap_cmdline
 (* Create a temporary directory for use by all the functions in this file. *)
 let tmpdir = tmpdir ()
 
+let installed_pkgs =
+  run_command_get_lines "dpkg-query --show --showformat='${Package}\\n'"
+
 let debian_detect () =
   file_exists "/etc/debian_version" &&
     Config.aptitude <> "no" && Config.apt_cache <> "no" && Config.dpkg <> "no"
@@ -51,18 +54,29 @@ let rec debian_resolve_dependencies_and_download names =
         not (List.exists (fun re -> Str.string_match re name 0) excludes)
     ) pkgs in
 
+  let present_pkgs, download_pkgs = List.partition (
+    fun pkg -> List.exists ((=) pkg) installed_pkgs
+  ) pkgs in
+
+  debug "wanted packages (present / download): %s / %s\n"
+    (String.concat " " present_pkgs)
+    (String.concat " " download_pkgs);
+
   (* Download the packages. *)
-  let cmd =
-    sprintf "umask 0000; cd %s && %s download %s"
-      (Filename.quote tmpdir)
-      Config.aptitude
-      (String.concat " " (List.map Filename.quote pkgs)) in
-  run_command cmd;
+  if (List.length download_pkgs > 0)
+  then (
+    let cmd =
+      sprintf "umask 0000; cd %s && %s download %s"
+        (Filename.quote tmpdir)
+        Config.aptitude
+        (String.concat " " (List.map Filename.quote download_pkgs)) in
+    run_command cmd
+  );
 
   (* Find out what aptitude downloaded. *)
   let files = Sys.readdir tmpdir in
 
-  let pkgs = List.map (
+  let download_pkgs = List.map (
     fun pkg ->
       (* Look for 'pkg_*.deb' in the list of files. *)
       let pre = pkg ^ "_" in
@@ -79,9 +93,9 @@ let rec debian_resolve_dependencies_and_download names =
        exit 1
       with
          Exit -> !r
-  ) pkgs in
+  ) download_pkgs in
 
-  List.sort compare pkgs
+  List.sort compare (List.append present_pkgs download_pkgs)
 
 (* On Ubuntu 10.04 LTS, apt-cache depends --recurse is broken.  It
  * doesn't return the full list of dependencies.  Therefore recurse
@@ -106,7 +120,7 @@ and workaround_broken_apt_cache_depends_recurse names =
   else
     names
 
-let debian_list_files pkg =
+let debian_list_files_downloaded pkg =
   debug "unpacking %s ..." pkg;
 
   (* We actually need to extract the file in order to get the
@@ -147,9 +161,38 @@ let debian_list_files pkg =
 
   files
 
+let debian_list_files_installed pkg =
+  debug "using installed package %s ..." pkg;
+  let cmd = sprintf "dpkg-query --listfiles %s" pkg in
+  let lines = run_command_get_lines cmd in
+  (* filter out lines not directly describing fs objects such as
+     "package diverts others to: /path/to/..." *)
+  let lines = List.filter (
+    fun l -> l.[0] = '/' && l.[1] != '.'
+  ) lines in
+  let files = List.map (
+    fun path ->
+      let statbuf = lstat path in
+      let is_dir = statbuf.st_kind = S_DIR in
+      let config = statbuf.st_kind = S_REG && string_prefix "/etc/" path in
+      let mode = statbuf.st_perm in
+      (path, { ft_dir = is_dir; ft_config = config; ft_mode = mode;
+              ft_ghost = false; ft_size = statbuf.st_size })
+  ) lines in
+  files
+
+let debian_list_files ?(use_installed=false) pkg =
+  if use_installed && List.exists ((=) pkg) installed_pkgs then
+    debian_list_files_installed pkg
+  else
+    debian_list_files_downloaded pkg
+
 (* Easy because we already unpacked the archive above. *)
-let debian_get_file_from_package pkg file =
-  tmpdir // pkg ^ ".d" // file
+let debian_get_file_from_package ?(use_installed=false) pkg file =
+  if use_installed && List.exists (fun p -> p = pkg) installed_pkgs then
+    file
+  else
+    tmpdir // pkg ^ ".d" // file
 
 let () =
   let ph = {
index ad3a233..f627d2f 100644 (file)
@@ -25,8 +25,8 @@ open Febootstrap_cmdline
 type package_handler = {
   ph_detect : unit -> bool;
   ph_resolve_dependencies_and_download : string list -> string list;
-  ph_list_files : string -> (string * file_type) list;
-  ph_get_file_from_package : string -> string -> string
+  ph_list_files : ?use_installed:bool -> string -> (string * file_type) list;
+  ph_get_file_from_package : ?use_installed:bool -> string -> string -> string
 }
 and file_type = {
   ft_dir : bool;
index c28d81f..ebf0386 100644 (file)
@@ -31,11 +31,11 @@ type package_handler = {
 
       Note this should also process the [excludes] list. *)
 
-  ph_list_files : string -> (string * file_type) list;
+  ph_list_files : ?use_installed:bool -> string -> (string * file_type) list;
   (** [ph_list_files pkg] lists the files and file metadata in the
       package called [pkg] (a package file). *)
 
-  ph_get_file_from_package : string -> string -> string;
+  ph_get_file_from_package : ?use_installed:bool -> string -> string -> string;
   (** [ph_get_file_from_package pkg file] extracts the
       single named file [file] from [pkg].  The path of the
       extracted file is returned. *)
index 6691ebe..657f4d7 100644 (file)
@@ -71,7 +71,10 @@ let pacman_resolve_dependencies_and_download names =
 
   List.sort compare pkgs
 
-let pacman_list_files pkg =
+let pacman_list_files ?(use_installed=false) pkg =
+  if use_installed then
+    failwith "pacman driver doesn't support --use-installed";
+
   debug "unpacking %s ..." pkg;
 
   (* We actually need to extract the file in order to get the
@@ -117,7 +120,10 @@ let pacman_list_files pkg =
   files
 
 (* Easy because we already unpacked the archive above. *)
-let pacman_get_file_from_package pkg file =
+let pacman_get_file_from_package ?(use_installed=false) pkg file =
+  if use_installed then
+    failwith "pacman driver doesn't support --use-installed";
+
   tmpdir // pkg ^ ".d" // file
 
 let () =
index 028492a..815c5ba 100644 (file)
@@ -172,7 +172,10 @@ if verbose:
       sprintf "%s/%s-%s-%s.%s.rpm" tmpdir name version release arch
   ) pkgs
 
-let rec yum_rpm_list_files pkg =
+let rec yum_rpm_list_files ?(use_installed=false) pkg =
+  if use_installed then
+    failwith "yum_rpm driver doesn't support --use-installed";
+
   (* Run rpm -qlp with some extra magic. *)
   let cmd =
     sprintf "rpm -q --qf '[%%{FILENAMES} %%{FILEFLAGS:fflags} %%{FILEMODES} %%{FILESIZES}\\n]' -p %s"
@@ -228,7 +231,10 @@ let rec yum_rpm_list_files pkg =
 
   files
 
-let yum_rpm_get_file_from_package pkg file =
+let yum_rpm_get_file_from_package ?(use_installed=false) pkg file =
+  if use_installed then
+    failwith "yum_rpm driver doesn't support --use-installed";
+
   debug "extracting %s from %s ..." file (Filename.basename pkg);
 
   let outfile = tmpdir // file in