1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
(* virt-builder
* Copyright (C) 2013 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.
*)
open Std_utils
open Tools_utils
open Common_gettext.Gettext
open Utils
open Unix
open Printf
type uri = string
type filename = string
type t = {
curl : string;
tmpdir : string;
cache : Cache.t option; (* cache for templates *)
}
let create ~curl ~tmpdir ~cache = {
curl = curl;
tmpdir = tmpdir;
cache = cache;
}
let rec download t ?template ?progress_bar ?(proxy = Curl.SystemProxy) uri =
match template with
| None -> (* no cache, simple download *)
(* Create a temporary name. *)
let tmpfile = Filename.temp_file ~temp_dir:t.tmpdir "vbcache" ".txt" in
download_to t ?progress_bar ~proxy uri tmpfile;
(tmpfile, true)
| Some (name, arch, revision) ->
match t.cache with
| None ->
(* Not using the cache at all? *)
download t ?progress_bar ~proxy uri
| Some cache ->
let filename = Cache.cache_of_name cache name arch revision in
(* Is the requested template name + revision in the cache already?
* If not, download it.
*)
if not (Sys.file_exists filename) then
download_to t ?progress_bar ~proxy uri filename;
(filename, false)
and download_to t ?(progress_bar = false) ~proxy uri filename =
let parseduri =
try URI.parse_uri uri
with URI.Parse_failed ->
error (f_"error parsing URI '%s'. Look for error messages printed above.")
uri in
(* Note because there may be parallel virt-builder instances running
* and also to avoid partial downloads in the cache if the network
* fails, we download to a random name in the cache and then
* atomically rename it to the final filename.
*)
let filename_new = filename ^ "." ^ String.random8 () in
unlink_on_exit filename_new;
(match parseduri.URI.protocol with
(* Download (ie. copy) from a local file. *)
| "file" ->
let path = parseduri.URI.path in
let cmd = [ "cp" ] @
(if verbose () then [ "-v" ] else []) @
[ path; filename_new ] in
let r = run_command cmd in
if r <> 0 then
error (f_"cp (download) command failed copying ā%sā") path;
(* Any other protocol. *)
| _ ->
let common_args = [
"location", None; (* Follow 3xx redirects. *)
"url", Some uri; (* URI to download. *)
] in
let quiet_args = [ "silent", None; "show-error", None ] in
(* Get the status code first to ensure the file exists. *)
let curl_h =
let curl_args = ref common_args in
if not (verbose ()) then List.push_back_list curl_args quiet_args;
List.push_back_list curl_args [
"output", Some "/dev/null"; (* Write output to /dev/null. *)
"head", None; (* Request only HEAD. *)
"write-out", Some "%{http_code}" (* HTTP status code to stdout. *)
];
Curl.create ~curl:t.curl ~tmpdir:t.tmpdir !curl_args in
let lines = Curl.run curl_h in
if List.length lines < 1 then
error (f_"unexpected output from curl command, enable debug and look at previous messages");
let status_code = List.hd lines in
let bad_status_code = function
| "" -> true
| s when s.[0] = '4' -> true (* 4xx *)
| s when s.[0] = '5' -> true (* 5xx *)
| _ -> false
in
if bad_status_code status_code then
error (f_"failed to download %s: HTTP status code %s") uri status_code;
(* Now download the file. *)
let curl_h =
let curl_args = ref common_args in
List.push_back curl_args ("output", Some filename_new);
if not (verbose ()) then (
if progress_bar then List.push_back curl_args ("progress-bar", None)
else List.push_back_list curl_args quiet_args
);
Curl.create ~curl:t.curl ~tmpdir:t.tmpdir !curl_args in
ignore (Curl.run curl_h)
);
(* Rename the file if the download was successful. *)
rename filename_new filename
|