2 * Copyright (C) 2009-2010 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 (* Yum and RPM support. *)
24 open Febootstrap_package_handlers
25 open Febootstrap_utils
26 open Febootstrap_cmdline
28 (* Create a temporary directory for use by all the functions in this file. *)
29 let tmpdir = tmpdir ()
31 let yum_rpm_detect () =
32 (file_exists "/etc/redhat-release" || file_exists "/etc/fedora-release") &&
33 Config.yum <> "no" && Config.rpm <> "no"
35 let yum_rpm_resolve_dependencies_and_download names =
36 (* Liberate this data from python. *)
37 let tmpfile = tmpdir // "names.tmp" in
46 print \"febootstrap_yum_rpm: running python code to query yum and resolve deps\"
49 yb.preconf.debuglevel = verbose
50 yb.preconf.errorlevel = verbose
56 print \"febootstrap_yum_rpm: looking up the base packages from the command line\"
58 pkgs = yb.pkgSack.returnPackages (patterns=sys.argv[1:])
63 print \"febootstrap_yum_rpm: recursively finding all the dependencies\"
67 for pkg in deps.keys():
68 if deps[pkg] == False:
72 print (\"febootstrap_yum_rpm: examining deps of %%s\" %%
74 for r in pkg.requires:
75 ps = yb.whatProvides (r[0], r[1], r[2])
76 best = yb._bestPackageFromList (ps.returnPackages ())
77 if best.name != pkg.name:
78 deps[pkg].append (best)
79 if not deps.has_key (best):
81 deps[pkg] = yum.misc.unique (deps[pkg])
83 # Write it to a file because yum spews garbage on stdout.
85 for pkg in deps.keys ():
86 f.write (\"%%s %%s %%s %%s %%s\\n\" %%
87 (pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch))
91 print \"febootstrap_yum_rpm: finished python code\"
93 (if verbose then 1 else 0)
94 (match yum_config with None -> "False" | Some _ -> "True")
95 (match yum_config with None -> "" | Some filename -> filename)
98 let chan = open_in tmpfile in
99 let lines = input_all_lines chan in
106 match string_split " " line with
107 | [name; epoch; version; release; arch] ->
108 name, int_of_string epoch, version, release, arch
110 eprintf "febootstrap: bad output from python script: '%s'" line;
114 (* Something of a hack for x86_64: exclude all i[3456]86 packages. *)
116 if Config.host_cpu = "x86_64" then (
118 function (_, _, _, _, ("i386"|"i486"|"i586"|"i686")) -> false
124 (* Drop the kernel package to save time. *)
126 List.filter (function ("kernel",_,_,_,_) -> false | _ -> true) pkgs in
128 (* Exclude packages matching [--exclude] regexps on the command line. *)
131 fun (name, _, _, _, _) ->
132 not (List.exists (fun re -> Str.string_match re name 0) excludes)
135 (* Sort the list of packages, and remove duplicates (by name).
136 * XXX This is not quite right: we really want to keep the latest
137 * package if duplicates are found, but that would require a full
138 * version compare function.
140 let pkgs = List.sort (fun a b -> compare b a) pkgs in
142 let cmp (name1, _, _, _, _) (name2, _, _, _, _) = compare name1 name2 in
144 let pkgs = List.sort compare pkgs in
146 (* Construct package names. *)
147 let pkgnames = List.map (
149 | name, 0, version, release, arch ->
150 sprintf "%s-%s-%s.%s" name version release arch
151 | name, epoch, version, release, arch ->
152 sprintf "%d:%s-%s-%s.%s" epoch name version release arch
155 if pkgnames = [] then (
156 eprintf "febootstrap: yum-rpm: error: no packages to download\n";
160 let cmd = sprintf "yumdownloader%s%s --destdir %s %s"
161 (if verbose then "" else " --quiet")
162 (match yum_config with None -> ""
163 | Some filename -> sprintf " -c %s" filename)
164 (Filename.quote tmpdir)
165 (String.concat " " (List.map Filename.quote pkgnames)) in
168 (* Return list of package filenames. *)
170 (* yumdownloader doesn't include epoch in the filename *)
171 fun (name, _, version, release, arch) ->
172 sprintf "%s/%s-%s-%s.%s.rpm" tmpdir name version release arch
175 let rec yum_rpm_list_files pkg =
176 (* Run rpm -qlp with some extra magic. *)
178 sprintf "rpm -q --qf '[%%{FILENAMES} %%{FILEFLAGS:fflags} %%{FILEMODES} %%{FILESIZES}\\n]' -p %s"
180 let lines = run_command_get_lines cmd in
185 match string_split " " line with
186 | [filename; flags; mode; size] ->
187 let test_flag = String.contains flags in
188 let mode = int_of_string mode in
189 let size = int_of_string size in
190 if test_flag 'd' then None (* ignore documentation *)
193 ft_dir = mode land 0o40000 <> 0;
194 ft_ghost = test_flag 'g'; ft_config = test_flag 'c';
195 ft_mode = mode; ft_size = size;
198 eprintf "febootstrap: bad output from rpm command: '%s'" line;
202 (* I've never understood why the base packages like 'filesystem' don't
203 * contain any /dev nodes at all. This leaves every program that
204 * bootstraps RPMs to create a varying set of device nodes themselves.
205 * This collection was copied from mock/backend.py.
208 let b = Filename.basename pkg in
209 if string_prefix "filesystem-" b then (
210 let dirs = [ "/proc"; "/sys"; "/dev"; "/dev/pts"; "/dev/shm";
213 List.map (fun name ->
214 name, { ft_dir = true; ft_ghost = false;
215 ft_config = false; ft_mode = 0o40755;
216 ft_size = 0 }) dirs in
217 let devs = [ "/dev/null"; "/dev/full"; "/dev/zero"; "/dev/random";
218 "/dev/urandom"; "/dev/tty"; "/dev/console";
219 "/dev/ptmx"; "/dev/stdin"; "/dev/stdout"; "/dev/stderr" ] in
220 (* No need to set the mode because these will go into hostfiles. *)
222 List.map (fun name ->
223 name, { ft_dir = false; ft_ghost = false;
224 ft_config = false; ft_mode = 0o644;
225 ft_size = 0 }) devs in
231 let yum_rpm_get_file_from_package pkg file =
232 debug "extracting %s from %s ..." file (Filename.basename pkg);
234 let outfile = tmpdir // file in
236 sprintf "umask 0000; rpm2cpio %s | (cd %s && cpio --quiet -id .%s)"
237 (Filename.quote pkg) (Filename.quote tmpdir) (Filename.quote file) in
243 ph_detect = yum_rpm_detect;
244 ph_resolve_dependencies_and_download =
245 yum_rpm_resolve_dependencies_and_download;
246 ph_list_files = yum_rpm_list_files;
247 ph_get_file_from_package = yum_rpm_get_file_from_package;
249 register_package_handler "yum-rpm" ph