summaryrefslogtreecommitdiffstats
path: root/src/febootstrap_yum_rpm.ml
diff options
context:
space:
mode:
Diffstat (limited to 'src/febootstrap_yum_rpm.ml')
-rw-r--r--src/febootstrap_yum_rpm.ml237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/febootstrap_yum_rpm.ml b/src/febootstrap_yum_rpm.ml
new file mode 100644
index 0000000..ca74f4a
--- /dev/null
+++ b/src/febootstrap_yum_rpm.ml
@@ -0,0 +1,237 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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
+ *)
+
+(* Yum and RPM support. *)
+
+open Unix
+open Printf
+
+open Febootstrap_package_handlers
+open Febootstrap_utils
+open Febootstrap_cmdline
+
+(* Create a temporary directory for use by all the functions in this file. *)
+let tmpdir = tmpdir ()
+
+let yum_rpm_detect () =
+ (file_exists "/etc/redhat-release" || file_exists "/etc/fedora-release") &&
+ Config.yum <> "no" && Config.rpm <> "no"
+
+let yum_rpm_resolve_dependencies_and_download names =
+ (* Liberate this data from python. *)
+ let tmpfile = tmpdir // "names.tmp" in
+ let py = sprintf "
+import yum
+import yum.misc
+import sys
+
+yb = yum.YumBase ()
+yb.preconf.debuglevel = %d
+yb.preconf.errorlevel = %d
+if %s:
+ yb.preconf.fn = %S
+yb.setCacheDir ()
+
+# Look up the base packages from the command line.
+deps = dict ()
+pkgs = yb.pkgSack.returnPackages (patterns=sys.argv[1:])
+for pkg in pkgs:
+ deps[pkg] = False
+
+# Recursively find all the dependencies.
+stable = False
+while not stable:
+ stable = True
+ for pkg in deps.keys():
+ if deps[pkg] == False:
+ deps[pkg] = []
+ stable = False
+ for r in pkg.requires:
+ ps = yb.whatProvides (r[0], r[1], r[2])
+ best = yb._bestPackageFromList (ps.returnPackages ())
+ if best.name != pkg.name:
+ deps[pkg].append (best)
+ if not deps.has_key (best):
+ deps[best] = False
+ deps[pkg] = yum.misc.unique (deps[pkg])
+
+# Write it to a file because yum spews garbage on stdout.
+f = open (%S, \"w\")
+for pkg in deps.keys ():
+ f.write (\"%%s %%s %%s %%s %%s\\n\" %%
+ (pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch))
+f.close ()
+"
+ (if verbose then 1 else 0)
+ (if verbose then 1 else 0)
+ (match yum_config with None -> "False" | Some _ -> "True")
+ (match yum_config with None -> "" | Some filename -> filename)
+ tmpfile in
+ run_python py names;
+ let chan = open_in tmpfile in
+ let lines = input_all_lines chan in
+ close_in chan;
+
+ (* Get fields. *)
+ let pkgs =
+ List.map (
+ fun line ->
+ match string_split " " line with
+ | [name; epoch; version; release; arch] ->
+ name, int_of_string epoch, version, release, arch
+ | _ ->
+ eprintf "febootstrap: bad output from python script: '%s'" line;
+ exit 1
+ ) lines in
+
+ (* Something of a hack for x86_64: exclude all i[3456]86 packages. *)
+ let pkgs =
+ if Config.host_cpu = "x86_64" then (
+ List.filter (
+ function (_, _, _, _, ("i386"|"i486"|"i586"|"i686")) -> false
+ | _ -> true
+ ) pkgs
+ )
+ else pkgs in
+
+ (* Drop the kernel package to save time. *)
+ let pkgs =
+ List.filter (function ("kernel",_,_,_,_) -> false | _ -> true) pkgs in
+
+ (* Exclude packages matching [--exclude] regexps on the command line. *)
+ let pkgs =
+ List.filter (
+ fun (name, _, _, _, _) ->
+ not (List.exists (fun re -> Str.string_match re name 0) excludes)
+ ) pkgs in
+
+ (* Sort the list of packages, and remove duplicates (by name).
+ * XXX This is not quite right: we really want to keep the latest
+ * package if duplicates are found, but that would require a full
+ * version compare function.
+ *)
+ let pkgs = List.sort (fun a b -> compare b a) pkgs in
+ let pkgs =
+ let cmp (name1, _, _, _, _) (name2, _, _, _, _) = compare name1 name2 in
+ uniq ~cmp pkgs in
+ let pkgs = List.sort compare pkgs in
+
+ (* Construct package names. *)
+ let pkgnames = List.map (
+ function
+ | name, 0, version, release, arch ->
+ sprintf "%s-%s-%s.%s" name version release arch
+ | name, epoch, version, release, arch ->
+ sprintf "%d:%s-%s-%s.%s" epoch name version release arch
+ ) pkgs in
+
+ if pkgnames = [] then (
+ eprintf "febootstrap: yum-rpm: error: no packages to download\n";
+ exit 1
+ );
+
+ let cmd = sprintf "yumdownloader%s%s --destdir %s %s"
+ (if verbose then "" else " --quiet")
+ (match yum_config with None -> ""
+ | Some filename -> sprintf " -c %s" filename)
+ (Filename.quote tmpdir)
+ (String.concat " " (List.map Filename.quote pkgnames)) in
+ run_command cmd;
+
+ (* Return list of package filenames. *)
+ List.map (
+ (* yumdownloader doesn't include epoch in the filename *)
+ fun (name, _, version, release, arch) ->
+ sprintf "%s/%s-%s-%s.%s.rpm" tmpdir name version release arch
+ ) pkgs
+
+let rec yum_rpm_list_files pkg =
+ (* Run rpm -qlp with some extra magic. *)
+ let cmd =
+ sprintf "rpm -q --qf '[%%{FILENAMES} %%{FILEFLAGS:fflags} %%{FILEMODES} %%{FILESIZES}\\n]' -p %s"
+ pkg in
+ let lines = run_command_get_lines cmd in
+
+ let files =
+ filter_map (
+ fun line ->
+ match string_split " " line with
+ | [filename; flags; mode; size] ->
+ let test_flag = String.contains flags in
+ let mode = int_of_string mode in
+ let size = int_of_string size in
+ if test_flag 'd' then None (* ignore documentation *)
+ else
+ Some (filename, {
+ ft_dir = mode land 0o40000 <> 0;
+ ft_ghost = test_flag 'g'; ft_config = test_flag 'c';
+ ft_mode = mode; ft_size = size;
+ })
+ | _ ->
+ eprintf "febootstrap: bad output from rpm command: '%s'" line;
+ exit 1
+ ) lines in
+
+ (* I've never understood why the base packages like 'filesystem' don't
+ * contain any /dev nodes at all. This leaves every program that
+ * bootstraps RPMs to create a varying set of device nodes themselves.
+ * This collection was copied from mock/backend.py.
+ *)
+ let files =
+ let b = Filename.basename pkg in
+ if string_prefix "filesystem-" b then (
+ let dirs = [ "/proc"; "/sys"; "/dev"; "/dev/pts"; "/dev/shm";
+ "/dev/mapper" ] in
+ let dirs =
+ List.map (fun name ->
+ name, { ft_dir = true; ft_ghost = false;
+ ft_config = false; ft_mode = 0o40755;
+ ft_size = 0 }) dirs in
+ let devs = [ "/dev/null"; "/dev/full"; "/dev/zero"; "/dev/random";
+ "/dev/urandom"; "/dev/tty"; "/dev/console";
+ "/dev/ptmx"; "/dev/stdin"; "/dev/stdout"; "/dev/stderr" ] in
+ (* No need to set the mode because these will go into hostfiles. *)
+ let devs =
+ List.map (fun name ->
+ name, { ft_dir = false; ft_ghost = false;
+ ft_config = false; ft_mode = 0o644;
+ ft_size = 0 }) devs in
+ dirs @ devs @ files
+ ) else files in
+
+ files
+
+let yum_rpm_get_file_from_package pkg file =
+ debug "extracting %s from %s ..." file (Filename.basename pkg);
+
+ let outfile = tmpdir // file in
+ let cmd =
+ sprintf "umask 0000; rpm2cpio %s | (cd %s && cpio --quiet -id .%s)"
+ (Filename.quote pkg) (Filename.quote tmpdir) (Filename.quote file) in
+ run_command cmd;
+ outfile
+
+let () =
+ let ph = {
+ ph_detect = yum_rpm_detect;
+ ph_resolve_dependencies_and_download =
+ yum_rpm_resolve_dependencies_and_download;
+ ph_list_files = yum_rpm_list_files;
+ ph_get_file_from_package = yum_rpm_get_file_from_package;
+ } in
+ register_package_handler "yum-rpm" ph