diff options
Diffstat (limited to 'febootstrap_yum_rpm.ml')
-rw-r--r-- | febootstrap_yum_rpm.ml | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/febootstrap_yum_rpm.ml b/febootstrap_yum_rpm.ml new file mode 100644 index 0000000..43021cc --- /dev/null +++ b/febootstrap_yum_rpm.ml @@ -0,0 +1,222 @@ +(* 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 py = " +import yum +import yum.misc +import sys + +yb = yum.YumBase () +#yum.logginglevels.setDebugLevel(0) -- doesn't work? + +# Look up the base packages from the command line. +deps = dict () +pkgs = yb.pkgSack.returnPackages (patterns=sys.argv[2:]) +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 (sys.argv[1], \"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 () +" in + let tmpfile = tmpdir // "names.tmp" in + run_python py (tmpfile :: 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 --destdir %s %s" + (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}\\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] -> + let test_flag = String.contains flags in + let mode = int_of_string mode 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; + }) + | _ -> + 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 }) 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 }) 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 "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 |