repl: Add "-q".
[jackhill/guix/guix.git] / guix / scripts / substitute.scm
CommitLineData
f65cf81a 1;;; GNU Guix --- Functional package management for GNU
f4cde9ac 2;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
e9c6c584 3;;; Copyright © 2014 Nikita Karetnikov <nikita@karetnikov.org>
7ede577a 4;;; Copyright © 2018 Kyle Meyer <kyle@kyleam.com>
f65cf81a
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
2c74fde0 21(define-module (guix scripts substitute)
f65cf81a 22 #:use-module (guix ui)
f4cde9ac 23 #:use-module (guix store)
f65cf81a 24 #:use-module (guix utils)
958dd3ce 25 #:use-module (guix combinators)
fe0cff14 26 #:use-module (guix config)
c0cd1b3e 27 #:use-module (guix records)
2535635f 28 #:use-module ((guix serialization) #:select (restore-file))
ca719424 29 #:use-module (gcrypt hash)
895d1eda 30 #:use-module (guix base32)
e9c6c584 31 #:use-module (guix base64)
2ea2aac6 32 #:use-module (guix cache)
ca719424 33 #:use-module (gcrypt pk-crypto)
e9c6c584 34 #:use-module (guix pki)
d3a65203 35 #:use-module ((guix build utils) #:select (mkdir-p dump-port))
a85060ef 36 #:use-module ((guix build download)
8c348825 37 #:select (uri-abbreviation nar-uri-abbreviation
4fd06a4d
LC
38 (open-connection-for-uri
39 . guix:open-connection-for-uri)
a8be7b9a 40 store-path-abbreviation byte-count->string))
8c348825 41 #:use-module (guix progress)
8902d0f2
LC
42 #:use-module ((guix build syscalls)
43 #:select (set-thread-name))
b90ae065 44 #:autoload (guix lzlib) (lzlib-available?)
f65cf81a
LC
45 #:use-module (ice-9 rdelim)
46 #:use-module (ice-9 regex)
47 #:use-module (ice-9 match)
fe0cff14 48 #:use-module (ice-9 format)
4c7cacf1 49 #:use-module (ice-9 ftw)
a85060ef 50 #:use-module (ice-9 binary-ports)
3d3e93b3 51 #:use-module (ice-9 vlist)
e9c6c584 52 #:use-module (rnrs bytevectors)
f65cf81a
LC
53 #:use-module (srfi srfi-1)
54 #:use-module (srfi srfi-9)
55 #:use-module (srfi srfi-11)
eba783b7 56 #:use-module (srfi srfi-19)
f65cf81a 57 #:use-module (srfi srfi-26)
706e9e57 58 #:use-module (srfi srfi-34)
e9c6c584 59 #:use-module (srfi srfi-35)
f65cf81a 60 #:use-module (web uri)
9b7bd1b1 61 #:use-module (web http)
d3a65203
LC
62 #:use-module (web request)
63 #:use-module (web response)
3b8258c5 64 #:use-module (guix http-client)
e9c6c584 65 #:export (narinfo-signature->canonical-sexp
ea0c6e05
LC
66
67 narinfo?
68 narinfo-path
b90ae065 69 narinfo-uris
ea0c6e05 70 narinfo-uri-base
b90ae065
LC
71 narinfo-compressions
72 narinfo-file-hashes
73 narinfo-file-sizes
ea0c6e05
LC
74 narinfo-hash
75 narinfo-size
76 narinfo-references
77 narinfo-deriver
78 narinfo-system
79 narinfo-signature
80
81 narinfo-hash->sha256
4736d06f 82 narinfo-best-uri
ea0c6e05
LC
83
84 lookup-narinfos
55b2fc18 85 lookup-narinfos/diverse
e9c6c584
NK
86 read-narinfo
87 write-narinfo
218f6ecc 88
434138e2
LC
89 %allow-unauthenticated-substitutes?
90
218f6ecc 91 substitute-urls
2c74fde0 92 guix-substitute))
f65cf81a
LC
93
94;;; Comment:
95;;;
96;;; This is the "binary substituter". It is invoked by the daemon do check
97;;; for the existence of available "substitutes" (pre-built binaries), and to
98;;; actually use them as a substitute to building things locally.
99;;;
100;;; If possible, substitute a binary for the requested store path, using a Nix
101;;; "binary cache". This program implements the Nix "substituter" protocol.
102;;;
103;;; Code:
104
b547349d
LC
105(cond-expand
106 (guile-2.2
107 ;; Guile 2.2.2 has a bug whereby 'time-monotonic' objects have seconds and
108 ;; nanoseconds swapped (fixed in Guile commit 886ac3e). Work around it.
109 (define time-monotonic time-tai))
110 (else #t))
111
eba783b7 112(define %narinfo-cache-directory
f10dcbf1
LC
113 ;; A local cache of narinfos, to avoid going to the network. Most of the
114 ;; time, 'guix substitute' is called by guix-daemon as root and stores its
115 ;; cached data in /var/guix/…. However, when invoked from 'guix challenge'
116 ;; as a user, it stores its cache in ~/.cache.
117 (if (zero? (getuid))
118 (or (and=> (getenv "XDG_CACHE_HOME")
119 (cut string-append <> "/guix/substitute"))
120 (string-append %state-directory "/substitute/cache"))
f0e492f0 121 (string-append (cache-directory #:ensure? #f) "/substitute")))
eba783b7 122
434138e2
LC
123(define (warn-about-missing-authentication)
124 (warning (G_ "authentication and authorization of substitutes \
125disabled!~%"))
126 #t)
127
e9c6c584
NK
128(define %allow-unauthenticated-substitutes?
129 ;; Whether to allow unchecked substitutes. This is useful for testing
130 ;; purposes, and should be avoided otherwise.
434138e2
LC
131 (make-parameter
132 (and=> (getenv "GUIX_ALLOW_UNAUTHENTICATED_SUBSTITUTES")
133 (cut string-ci=? <> "yes"))
134 (lambda (value)
135 (when value
136 (warn-about-missing-authentication))
137 value)))
e9c6c584 138
eba783b7
LC
139(define %narinfo-ttl
140 ;; Number of seconds during which cached narinfo lookups are considered
23d60ba6
LC
141 ;; valid for substitute servers that do not advertise a TTL via the
142 ;; 'Cache-Control' response header.
5e6039a4 143 (* 36 3600))
eba783b7
LC
144
145(define %narinfo-negative-ttl
958fb14c 146 ;; Likewise, but for negative lookups---i.e., cached lookup failures (404).
eba783b7
LC
147 (* 3 3600))
148
958fb14c
LC
149(define %narinfo-transient-error-ttl
150 ;; Likewise, but for transient errors such as 504 ("Gateway timeout").
151 (* 10 60))
152
4c7cacf1
LC
153(define %narinfo-expired-cache-entry-removal-delay
154 ;; How often we want to remove files corresponding to expired cache entries.
155 (* 7 24 3600))
156
ce689ccf
LC
157(define fields->alist
158 ;; The narinfo format is really just like recutils.
159 recutils->alist)
f65cf81a 160
2207f731
LC
161(define %fetch-timeout
162 ;; Number of seconds after which networking is considered "slow".
8b79e2e6 163 5)
2207f731 164
bb7dcaea
LC
165(define %random-state
166 (seed->random-state (+ (ash (cdr (gettimeofday)) 32) (getpid))))
167
2207f731
LC
168(define-syntax-rule (with-timeout duration handler body ...)
169 "Run BODY; when DURATION seconds have expired, call HANDLER, and run BODY
170again."
171 (begin
172 (sigaction SIGALRM
173 (lambda (signum)
174 (sigaction SIGALRM SIG_DFL)
175 handler))
176 (alarm duration)
177 (call-with-values
178 (lambda ()
179 (let try ()
180 (catch 'system-error
181 (lambda ()
182 body ...)
183 (lambda args
c509bf8c
LC
184 ;; Before Guile v2.0.9-39-gfe51c7b, the SIGALRM triggers EINTR
185 ;; because of the bug at
bb7dcaea
LC
186 ;; <http://lists.gnu.org/archive/html/guile-devel/2013-06/msg00050.html>.
187 ;; When that happens, try again. Note: SA_RESTART cannot be
188 ;; used because of <http://bugs.gnu.org/14640>.
2207f731 189 (if (= EINTR (system-error-errno args))
bb7dcaea
LC
190 (begin
191 ;; Wait a little to avoid bursts.
192 (usleep (random 3000000 %random-state))
193 (try))
2207f731
LC
194 (apply throw args))))))
195 (lambda result
196 (alarm 0)
197 (sigaction SIGALRM SIG_DFL)
198 (apply values result)))))
199
cc27dbcf 200(define* (fetch uri #:key (buffered? #t) (timeout? #t))
fe0cff14 201 "Return a binary input port to URI and the number of bytes it's expected to
cc27dbcf 202provide."
f65cf81a
LC
203 (case (uri-scheme uri)
204 ((file)
b6952cad
LC
205 (let ((port (open-file (uri-path uri)
206 (if buffered? "rb" "r0b"))))
fe0cff14 207 (values port (stat:size (stat port)))))
9b7bd1b1 208 ((http https)
706e9e57 209 (guard (c ((http-get-error? c)
69daee23 210 (leave (G_ "download from '~a' failed: ~a, ~s~%")
cc27dbcf
LC
211 (uri->string (http-get-error-uri c))
212 (http-get-error-code c)
213 (http-get-error-reason c))))
706e9e57
LC
214 ;; Test this with:
215 ;; sudo tc qdisc add dev eth0 root netem delay 1500ms
216 ;; and then cancel with:
217 ;; sudo tc qdisc del dev eth0 root
218 (let ((port #f))
09d809db 219 (with-timeout (if timeout?
706e9e57
LC
220 %fetch-timeout
221 0)
222 (begin
69daee23 223 (warning (G_ "while fetching ~a: server is somewhat slow~%")
706e9e57 224 (uri->string uri))
1d84d7bf 225 (warning (G_ "try `--no-substitutes' if the problem persists~%")))
706e9e57
LC
226 (begin
227 (when (or (not port) (port-closed? port))
4fd06a4d
LC
228 (set! port (guix:open-connection-for-uri
229 uri #:verify-certificate? #f))
9b7bd1b1 230 (unless (or buffered? (not (file-port? port)))
76832d34 231 (setvbuf port 'none)))
166ba5b1
LC
232 (http-fetch uri #:text? #f #:port port
233 #:verify-certificate? #f))))))
204d34ff 234 (else
69daee23 235 (leave (G_ "unsupported substitute URI scheme: ~a~%")
204d34ff 236 (uri->string uri)))))
f65cf81a 237
074efd63 238\f
f65cf81a 239(define-record-type <narinfo>
b90ae065
LC
240 (%make-narinfo path uri-base uris compressions file-sizes file-hashes
241 nar-hash nar-size references deriver system
242 signature contents)
f65cf81a
LC
243 narinfo?
244 (path narinfo-path)
b90ae065
LC
245 (uri-base narinfo-uri-base) ;URI of the cache it originates from
246 (uris narinfo-uris) ;list of strings
247 (compressions narinfo-compressions) ;list of strings
248 (file-sizes narinfo-file-sizes) ;list of (integers | #f)
249 (file-hashes narinfo-file-hashes)
f65cf81a
LC
250 (nar-hash narinfo-hash)
251 (nar-size narinfo-size)
252 (references narinfo-references)
253 (deriver narinfo-deriver)
e9c6c584
NK
254 (system narinfo-system)
255 (signature narinfo-signature) ; canonical sexp
256 ;; The original contents of a narinfo file. This field is needed because we
257 ;; want to preserve the exact textual representation for verification purposes.
258 ;; See <https://lists.gnu.org/archive/html/guix-devel/2014-02/msg00340.html>
259 ;; for more information.
260 (contents narinfo-contents))
261
ea0c6e05
LC
262(define (narinfo-hash->sha256 hash)
263 "If the string HASH denotes a sha256 hash, return it as a bytevector.
264Otherwise return #f."
265 (and (string-prefix? "sha256:" hash)
266 (nix-base32-string->bytevector (string-drop hash 7))))
267
e9c6c584
NK
268(define (narinfo-signature->canonical-sexp str)
269 "Return the value of a narinfo's 'Signature' field as a canonical sexp."
270 (match (string-split str #\;)
e465d9e1 271 ((version host-name sig)
e9c6c584
NK
272 (let ((maybe-number (string->number version)))
273 (cond ((not (number? maybe-number))
69daee23 274 (leave (G_ "signature version must be a number: ~s~%")
e9c6c584
NK
275 version))
276 ;; Currently, there are no other versions.
277 ((not (= 1 maybe-number))
69daee23 278 (leave (G_ "unsupported signature version: ~a~%")
e9c6c584 279 maybe-number))
cdea30e0
LC
280 (else
281 (let ((signature (utf8->string (base64-decode sig))))
282 (catch 'gcry-error
283 (lambda ()
284 (string->canonical-sexp signature))
6ef3644e 285 (lambda (key proc err)
69daee23 286 (leave (G_ "signature is not a valid \
e4687a5e
LC
287s-expression: ~s~%")
288 signature))))))))
e9c6c584 289 (x
69daee23 290 (leave (G_ "invalid format of the signature field: ~a~%") x))))
f65cf81a 291
e9c6c584
NK
292(define (narinfo-maker str cache-url)
293 "Return a narinfo constructor for narinfos originating from CACHE-URL. STR
294must contain the original contents of a narinfo file."
b90ae065
LC
295 (lambda (path urls compressions file-hashes file-sizes
296 nar-hash nar-size references deriver system
297 signature)
fe0cff14 298 "Return a new <narinfo> object."
b90ae065
LC
299 (define len (length urls))
300 (%make-narinfo path cache-url
fe0cff14 301 ;; Handle the case where URL is a relative URL.
b90ae065
LC
302 (map (lambda (url)
303 (or (string->uri url)
304 (string->uri
305 (string-append cache-url "/" url))))
306 urls)
307 compressions
308 (match file-sizes
309 (() (make-list len #f))
310 ((lst ...) (map string->number lst)))
311 (match file-hashes
312 (() (make-list len #f))
313 ((lst ...) (map string->number lst)))
fe0cff14
LC
314 nar-hash
315 (and=> nar-size string->number)
316 (string-tokenize references)
317 (match deriver
318 ((or #f "") #f)
319 (_ deriver))
e9c6c584 320 system
cdea30e0
LC
321 (false-if-exception
322 (and=> signature narinfo-signature->canonical-sexp))
e9c6c584 323 str)))
f65cf81a 324
0561e9ae
LC
325(define* (read-narinfo port #:optional url
326 #:key size)
e9c6c584 327 "Read a narinfo from PORT. If URL is true, it must be a string used to
0561e9ae
LC
328build full URIs from relative URIs found while reading PORT. When SIZE is
329true, read at most SIZE bytes from PORT; otherwise, read as much as possible.
cdea30e0
LC
330
331No authentication and authorization checks are performed here!"
0561e9ae
LC
332 (let ((str (utf8->string (if size
333 (get-bytevector-n port size)
334 (get-bytevector-all port)))))
cdea30e0
LC
335 (alist->record (call-with-input-string str fields->alist)
336 (narinfo-maker str url)
337 '("StorePath" "URL" "Compression"
338 "FileHash" "FileSize" "NarHash" "NarSize"
339 "References" "Deriver" "System"
b90ae065
LC
340 "Signature")
341 '("URL" "Compression" "FileSize" "FileHash"))))
cdea30e0 342
e4687a5e
LC
343(define (narinfo-sha256 narinfo)
344 "Return the sha256 hash of NARINFO as a bytevector, or #f if NARINFO lacks a
345'Signature' field."
60b04024
LC
346 (define %mandatory-fields
347 ;; List of fields that must be signed. If they are not signed, the
348 ;; narinfo is considered unsigned.
349 '("StorePath" "NarHash" "References"))
350
e4687a5e 351 (let ((contents (narinfo-contents narinfo)))
8234fcf2 352 (match (string-contains contents "Signature:")
e4687a5e 353 (#f #f)
8234fcf2 354 (index
60b04024
LC
355 (let* ((above-signature (string-take contents index))
356 (signed-fields (match (call-with-input-string above-signature
357 fields->alist)
358 (((fields . values) ...) fields))))
359 (and (every (cut member <> signed-fields) %mandatory-fields)
360 (sha256 (string->utf8 above-signature))))))))
e4687a5e 361
a9468b42
LC
362(define* (valid-narinfo? narinfo #:optional (acl (current-acl))
363 #:key verbose?)
cdea30e0 364 "Return #t if NARINFO's signature is not valid."
434138e2 365 (or (%allow-unauthenticated-substitutes?)
e4687a5e 366 (let ((hash (narinfo-sha256 narinfo))
a9468b42 367 (signature (narinfo-signature narinfo))
b90ae065 368 (uri (uri->string (first (narinfo-uris narinfo)))))
e4687a5e
LC
369 (and hash signature
370 (signature-case (signature hash acl)
371 (valid-signature #t)
a9468b42
LC
372 (invalid-signature
373 (when verbose?
374 (format (current-error-port)
375 "invalid signature for substitute at '~a'~%"
376 uri))
377 #f)
378 (hash-mismatch
379 (when verbose?
380 (format (current-error-port)
381 "hash mismatch for substitute at '~a'~%"
382 uri))
383 #f)
384 (unauthorized-key
385 (when verbose?
386 (format (current-error-port)
387 "substitute at '~a' is signed by an \
388unauthorized party~%"
389 uri))
390 #f)
391 (corrupt-signature
392 (when verbose?
393 (format (current-error-port)
394 "corrupt signature for substitute at '~a'~%"
395 uri))
396 #f))))))
eba783b7
LC
397
398(define (write-narinfo narinfo port)
399 "Write NARINFO to PORT."
e9c6c584 400 (put-bytevector port (string->utf8 (narinfo-contents narinfo))))
eba783b7
LC
401
402(define (narinfo->string narinfo)
403 "Return the external representation of NARINFO."
404 (call-with-output-string (cut write-narinfo narinfo <>)))
405
00230df1 406(define (string->narinfo str cache-uri)
cdea30e0
LC
407 "Return the narinfo represented by STR. Assume CACHE-URI as the base URI of
408the cache STR originates form."
00230df1 409 (call-with-input-string str (cut read-narinfo <> cache-uri)))
eba783b7 410
895d1eda
LC
411(define (narinfo-cache-file cache-url path)
412 "Return the name of the local file that contains an entry for PATH. The
413entry is stored in a sub-directory specific to CACHE-URL."
30d4bc04
LC
414 ;; The daemon does not sanitize its input, so PATH could be something like
415 ;; "/gnu/store/foo". Gracefully handle that.
416 (match (store-path-hash-part path)
417 (#f
69daee23 418 (leave (G_ "'~a' does not name a store item~%") path))
30d4bc04
LC
419 ((? string? hash-part)
420 (string-append %narinfo-cache-directory "/"
421 (bytevector->base32-string (sha256 (string->utf8 cache-url)))
422 "/" hash-part))))
895d1eda
LC
423
424(define (cached-narinfo cache-url path)
425 "Check locally if we have valid info about PATH coming from CACHE-URL.
426Return two values: a Boolean indicating whether we have valid cached info, and
427that info, which may be either #f (when PATH is unavailable) or the narinfo
428for PATH."
eba783b7
LC
429 (define now
430 (current-time time-monotonic))
431
eba783b7 432 (define cache-file
895d1eda 433 (narinfo-cache-file cache-url path))
d3a65203
LC
434
435 (catch 'system-error
436 (lambda ()
437 (call-with-input-file cache-file
438 (lambda (p)
439 (match (read p)
1cf7e318 440 (('narinfo ('version 2)
d3a65203 441 ('cache-uri cache-uri)
5db5dff5 442 ('date date) ('ttl ttl) ('value #f))
d3a65203 443 ;; A cached negative lookup.
5db5dff5 444 (if (obsolete? date now ttl)
d3a65203
LC
445 (values #f #f)
446 (values #t #f)))
1cf7e318 447 (('narinfo ('version 2)
d3a65203 448 ('cache-uri cache-uri)
1cf7e318 449 ('date date) ('ttl ttl) ('value value))
d3a65203 450 ;; A cached positive lookup
1cf7e318 451 (if (obsolete? date now ttl)
d3a65203
LC
452 (values #f #f)
453 (values #t (string->narinfo value cache-uri))))
454 (('narinfo ('version v) _ ...)
455 (values #f #f))))))
456 (lambda _
457 (values #f #f))))
458
23d60ba6
LC
459(define (cache-narinfo! cache-url path narinfo ttl)
460 "Cache locally NARNIFO for PATH, which originates from CACHE-URL, with the
461given TTL (a number of seconds or #f). NARINFO may be #f, in which case it
462indicates that PATH is unavailable at CACHE-URL."
d3a65203
LC
463 (define now
464 (current-time time-monotonic))
eba783b7 465
cdea30e0 466 (define (cache-entry cache-uri narinfo)
1cf7e318 467 `(narinfo (version 2)
cdea30e0 468 (cache-uri ,cache-uri)
eba783b7 469 (date ,(time-second now))
23d60ba6
LC
470 (ttl ,(or ttl
471 (if narinfo %narinfo-ttl %narinfo-negative-ttl)))
eba783b7
LC
472 (value ,(and=> narinfo narinfo->string))))
473
895d1eda 474 (let ((file (narinfo-cache-file cache-url path)))
f10dcbf1
LC
475 (mkdir-p (dirname file))
476 (with-atomic-file-output file
477 (lambda (out)
478 (write (cache-entry cache-url narinfo) out))))
895d1eda 479
d3a65203
LC
480 narinfo)
481
482(define (narinfo-request cache-url path)
483 "Return an HTTP request for the narinfo of PATH at CACHE-URL."
484 (let ((url (string-append cache-url "/" (store-path-hash-part path)
f264e838
TGR
485 ".narinfo"))
486 (headers '((User-Agent . "GNU Guile"))))
487 (build-request (string->uri url) #:method 'GET #:headers headers)))
d3a65203 488
d213cc8c
LC
489(define (at-most max-length lst)
490 "If LST is shorter than MAX-LENGTH, return it; otherwise return its
491MAX-LENGTH first elements."
492 (let loop ((len 0)
493 (lst lst)
494 (result '()))
495 (match lst
496 (()
497 (reverse result))
498 ((head . tail)
499 (if (>= len max-length)
500 (reverse result)
501 (loop (+ 1 len) tail (cons head result)))))))
502
026ca50f 503(define* (http-multiple-get base-uri proc seed requests
166ba5b1 504 #:key port (verify-certificate? #t))
9b7bd1b1 505 "Send all of REQUESTS to the server at BASE-URI. Call PROC for each
f151298f
LC
506response, passing it the request object, the response, a port from which to
507read the response body, and the previous result, starting with SEED, à la
026ca50f
LC
508'fold'. Return the final result. When PORT is specified, use it as the
509initial connection on which HTTP requests are sent."
510 (let connect ((port port)
511 (requests requests)
f151298f 512 (result seed))
9e3f9ac3
LC
513 (define batch
514 (at-most 1000 requests))
515
d3a65203
LC
516 ;; (format (current-error-port) "connecting (~a requests left)..."
517 ;; (length requests))
4fd06a4d
LC
518 (let ((p (or port (guix:open-connection-for-uri
519 base-uri
520 #:verify-certificate?
521 verify-certificate?))))
9b7bd1b1
LC
522 ;; For HTTPS, P is not a file port and does not support 'setvbuf'.
523 (when (file-port? p)
76832d34 524 (setvbuf p 'block (expt 2 16)))
9b7bd1b1 525
9e3f9ac3 526 ;; Send BATCH in a row.
ec278439
LC
527 ;; XXX: Do our own caching to work around inefficiencies when
528 ;; communicating over TLS: <http://bugs.gnu.org/22966>.
529 (let-values (((buffer get) (open-bytevector-output-port)))
1d84d7bf
LC
530 ;; Inherit the HTTP proxying property from P.
531 (set-http-proxy-port?! buffer (http-proxy-port? p))
ec278439 532
d213cc8c 533 (for-each (cut write-request <> buffer)
9e3f9ac3 534 batch)
ec278439
LC
535 (put-bytevector p (get))
536 (force-output p))
d3a65203
LC
537
538 ;; Now start processing responses.
9e3f9ac3
LC
539 (let loop ((sent batch)
540 (processed 0)
541 (result result))
542 (match sent
d3a65203 543 (()
9e3f9ac3
LC
544 (match (drop requests processed)
545 (()
546 (reverse result))
547 (remainder
548 (connect port remainder result))))
d3a65203 549 ((head tail ...)
075d99f1
AP
550 (let* ((resp (read-response p))
551 (body (response-body-port resp))
f151298f 552 (result (proc head resp body result)))
d3a65203
LC
553 ;; The server can choose to stop responding at any time, in which
554 ;; case we have to try again. Check whether that is the case.
075d99f1 555 ;; Note that even upon "Connection: close", we can read from BODY.
d3a65203
LC
556 (match (assq 'connection (response-headers resp))
557 (('connection 'close)
f4cde9ac 558 (close-port p)
9e3f9ac3 559 (connect #f ;try again
e2922f52 560 (drop requests (+ 1 processed))
9e3f9ac3 561 result))
d3a65203 562 (_
9e3f9ac3 563 (loop tail (+ 1 processed) result)))))))))) ;keep going
d3a65203
LC
564
565(define (read-to-eof port)
566 "Read from PORT until EOF is reached. The data are discarded."
567 (dump-port port (%make-void-port "w")))
568
569(define (narinfo-from-file file url)
570 "Attempt to read a narinfo from FILE, using URL as the cache URL. Return #f
571if file doesn't exist, and the narinfo otherwise."
572 (catch 'system-error
573 (lambda ()
574 (call-with-input-file file
575 (cut read-narinfo <> url)))
576 (lambda args
577 (if (= ENOENT (system-error-errno args))
578 #f
579 (apply throw args)))))
580
4f5234be
LC
581(define %unreachable-hosts
582 ;; Set of names of unreachable hosts.
583 (make-hash-table))
584
585(define* (open-connection-for-uri/maybe uri
586 #:key
587 (verify-certificate? #f)
588 (time %fetch-timeout))
589 "Open a connection to URI and return a port to it, or, if connection failed,
590print a warning and return #f."
591 (define host
592 (uri-host uri))
593
594 (catch #t
595 (lambda ()
596 (guix:open-connection-for-uri uri
597 #:verify-certificate? verify-certificate?
598 #:timeout time))
599 (match-lambda*
600 (('getaddrinfo-error error)
601 (unless (hash-ref %unreachable-hosts host)
602 (hash-set! %unreachable-hosts host #t) ;warn only once
603 (warning (G_ "~a: host not found: ~a~%")
604 host (gai-strerror error)))
605 #f)
606 (('system-error . args)
607 (unless (hash-ref %unreachable-hosts host)
608 (hash-set! %unreachable-hosts host #t)
609 (warning (G_ "~a: connection failed: ~a~%") host
610 (strerror
611 (system-error-errno `(system-error ,@args)))))
612 #f)
613 (args
614 (apply throw args)))))
615
074efd63
LC
616(define (fetch-narinfos url paths)
617 "Retrieve all the narinfos for PATHS from the cache at URL and return them."
d3a65203 618 (define update-progress!
75a4d86f
LC
619 (let ((done 0)
620 (total (length paths)))
d3a65203 621 (lambda ()
4c97a368 622 (display "\r\x1b[K" (current-error-port)) ;erase current line
d3a65203
LC
623 (force-output (current-error-port))
624 (format (current-error-port)
2bf9351e 625 (G_ "updating substitutes from '~a'... ~5,1f%")
75a4d86f 626 url (* 100. (/ done total)))
d3a65203
LC
627 (set! done (+ 1 done)))))
628
3d3e93b3
LC
629 (define hash-part->path
630 (let ((mapping (fold (lambda (path result)
631 (vhash-cons (store-path-hash-part path) path
632 result))
633 vlist-null
634 paths)))
635 (lambda (hash)
636 (match (vhash-assoc hash mapping)
637 (#f #f)
638 ((_ . path) path)))))
639
f151298f 640 (define (handle-narinfo-response request response port result)
958fb14c
LC
641 (let* ((code (response-code response))
642 (len (response-content-length response))
23d60ba6
LC
643 (cache (response-cache-control response))
644 (ttl (and cache (assoc-ref cache 'max-age))))
4f5234be
LC
645 (update-progress!)
646
d3a65203
LC
647 ;; Make sure to read no more than LEN bytes since subsequent bytes may
648 ;; belong to the next response.
958fb14c
LC
649 (if (= code 200) ; hit
650 (let ((narinfo (read-narinfo port url #:size len)))
4f5234be
LC
651 (if (string=? (dirname (narinfo-path narinfo))
652 (%store-prefix))
653 (begin
654 (cache-narinfo! url (narinfo-path narinfo) narinfo ttl)
655 (cons narinfo result))
656 result))
958fb14c 657 (let* ((path (uri-path (request-uri request)))
a7a3b390
LC
658 (hash-part (basename
659 (string-drop-right path 8)))) ;drop ".narinfo"
958fb14c
LC
660 (if len
661 (get-bytevector-n port len)
662 (read-to-eof port))
3d3e93b3 663 (cache-narinfo! url (hash-part->path hash-part) #f
958fb14c
LC
664 (if (= 404 code)
665 ttl
666 %narinfo-transient-error-ttl))
958fb14c 667 result))))
d3a65203 668
4f5234be 669 (define (do-fetch uri)
ae4427e3 670 (case (and=> uri uri-scheme)
9b7bd1b1 671 ((http https)
ae4427e3 672 (let ((requests (map (cut narinfo-request url <>) paths)))
4f5234be
LC
673 (match (open-connection-for-uri/maybe uri)
674 (#f
675 '())
676 (port
677 (update-progress!)
678 ;; Note: Do not check HTTPS server certificates to avoid depending
679 ;; on the X.509 PKI. We can do it because we authenticate
680 ;; narinfos, which provides a much stronger guarantee.
681 (let ((result (http-multiple-get uri
682 handle-narinfo-response '()
683 requests
684 #:verify-certificate? #f
685 #:port port)))
686 (close-port port)
687 (newline (current-error-port))
688 result)))))
ae4427e3
LC
689 ((file #f)
690 (let* ((base (string-append (uri-path uri) "/"))
691 (files (map (compose (cut string-append base <> ".narinfo")
692 store-path-hash-part)
693 paths)))
694 (filter-map (cut narinfo-from-file <> url) files)))
695 (else
69daee23 696 (leave (G_ "~s: unsupported server URI scheme~%")
ae4427e3
LC
697 (if uri (uri-scheme uri) url)))))
698
4f5234be 699 (do-fetch (string->uri url)))
d3a65203
LC
700
701(define (lookup-narinfos cache paths)
702 "Return the narinfos for PATHS, invoking the server at CACHE when no
703information is available locally."
704 (let-values (((cached missing)
705 (fold2 (lambda (path cached missing)
706 (let-values (((valid? value)
895d1eda 707 (cached-narinfo cache path)))
d3a65203 708 (if valid?
a89dde1e
LC
709 (if value
710 (values (cons value cached) missing)
711 (values cached missing))
d3a65203
LC
712 (values cached (cons path missing)))))
713 '()
714 '()
715 paths)))
716 (if (null? missing)
717 cached
074efd63
LC
718 (let ((missing (fetch-narinfos cache missing)))
719 (append cached (or missing '()))))))
d3a65203 720
a9468b42
LC
721(define (equivalent-narinfo? narinfo1 narinfo2)
722 "Return true if NARINFO1 and NARINFO2 are equivalent--i.e., if they describe
723the same store item. This ignores unnecessary metadata such as the Nar URL."
724 (and (string=? (narinfo-hash narinfo1)
725 (narinfo-hash narinfo2))
726
727 ;; The following is not needed if all we want is to download a valid
728 ;; nar, but it's necessary if we want valid narinfo.
729 (string=? (narinfo-path narinfo1)
730 (narinfo-path narinfo2))
731 (equal? (narinfo-references narinfo1)
732 (narinfo-references narinfo2))
733
734 (= (narinfo-size narinfo1)
735 (narinfo-size narinfo2))))
736
737(define (lookup-narinfos/diverse caches paths authorized?)
55b2fc18 738 "Look up narinfos for PATHS on all of CACHES, a list of URLS, in that order.
a9468b42
LC
739That is, when a cache lacks an AUTHORIZED? narinfo, look it up in the next
740cache, and so on.
741
742Return a list of narinfos for PATHS or a subset thereof. The returned
743narinfos are either AUTHORIZED?, or they claim a hash that matches an
744AUTHORIZED? narinfo."
745 (define (select-hit result)
746 (lambda (path)
747 (match (vhash-fold* cons '() path result)
748 ((one)
749 one)
750 ((several ..1)
751 (let ((authorized (find authorized? (reverse several))))
752 (and authorized
753 (find (cut equivalent-narinfo? <> authorized)
754 several)))))))
755
55b2fc18
LC
756 (let loop ((caches caches)
757 (paths paths)
a9468b42
LC
758 (result vlist-null) ;path->narinfo vhash
759 (hits '())) ;paths
55b2fc18
LC
760 (match paths
761 (() ;we're done
a9468b42
LC
762 ;; Now iterate on all the HITS, and return exactly one match for each
763 ;; hit: the first narinfo that is authorized, or that has the same hash
764 ;; as an authorized narinfo, in the order of CACHES.
765 (filter-map (select-hit result) hits))
55b2fc18
LC
766 (_
767 (match caches
768 ((cache rest ...)
769 (let* ((narinfos (lookup-narinfos cache paths))
a9468b42
LC
770 (definite (map narinfo-path (filter authorized? narinfos)))
771 (missing (lset-difference string=? paths definite))) ;XXX: perf
772 (loop rest missing
773 (fold vhash-cons result
774 (map narinfo-path narinfos) narinfos)
775 (append definite hits))))
55b2fc18 776 (() ;that's it
a9468b42 777 (filter-map (select-hit result) hits)))))))
55b2fc18 778
a9468b42 779(define (lookup-narinfo caches path authorized?)
55b2fc18
LC
780 "Return the narinfo for PATH in CACHES, or #f when no substitute for PATH
781was found."
a9468b42 782 (match (lookup-narinfos/diverse caches (list path) authorized?)
55b2fc18
LC
783 ((answer) answer)
784 (_ #f)))
f65cf81a 785
2ea2aac6
LC
786(define (cached-narinfo-expiration-time file)
787 "Return the expiration time for FILE, which is a cached narinfo."
788 (catch 'system-error
789 (lambda ()
790 (call-with-input-file file
791 (lambda (port)
792 (match (read port)
793 (('narinfo ('version 2) ('cache-uri uri)
794 ('date date) ('ttl ttl) ('value #f))
5db5dff5 795 (+ date ttl))
2ea2aac6
LC
796 (('narinfo ('version 2) ('cache-uri uri)
797 ('date date) ('ttl ttl) ('value value))
798 (+ date ttl))
799 (x
800 0)))))
801 (lambda args
802 ;; FILE may have been deleted.
803 0)))
4c7cacf1 804
2ea2aac6 805(define (narinfo-cache-directories directory)
895d1eda 806 "Return the list of narinfo cache directories (one per cache URL.)"
2ea2aac6 807 (map (cut string-append directory "/" <>)
895d1eda
LC
808 (scandir %narinfo-cache-directory
809 (lambda (item)
810 (and (not (member item '("." "..")))
811 (file-is-directory?
812 (string-append %narinfo-cache-directory
813 "/" item)))))))
814
2ea2aac6
LC
815(define* (cached-narinfo-files #:optional
816 (directory %narinfo-cache-directory))
817 "Return the list of cached narinfo files under DIRECTORY."
818 (append-map (lambda (directory)
819 (map (cut string-append directory "/" <>)
820 (scandir directory
821 (lambda (file)
822 (= (string-length file) 32)))))
823 (narinfo-cache-directories directory)))
4c7cacf1 824
cf5d2ca3
LC
825(define-syntax with-networking
826 (syntax-rules ()
8c321299 827 "Catch DNS lookup errors and TLS errors and gracefully exit."
cf5d2ca3
LC
828 ;; Note: no attempt is made to catch other networking errors, because DNS
829 ;; lookup errors are typically the first one, and because other errors are
830 ;; a subset of `system-error', which is harder to filter.
831 ((_ exp ...)
8c321299 832 (catch #t
cf5d2ca3 833 (lambda () exp ...)
8c321299
LC
834 (match-lambda*
835 (('getaddrinfo-error error)
69daee23 836 (leave (G_ "host name lookup error: ~a~%")
8c321299
LC
837 (gai-strerror error)))
838 (('gnutls-error error proc . rest)
839 (let ((error->string (module-ref (resolve-interface '(gnutls))
840 'error->string)))
69daee23 841 (leave (G_ "TLS error in procedure '~a': ~a~%")
8c321299
LC
842 proc (error->string error))))
843 (args
844 (apply throw args)))))))
cf5d2ca3 845
f65cf81a 846\f
29479de5
LC
847;;;
848;;; Help.
849;;;
850
851(define (show-help)
69daee23 852 (display (G_ "Usage: guix substitute [OPTION]...
29479de5 853Internal tool to substitute a pre-built binary to a local build.\n"))
69daee23 854 (display (G_ "
29479de5
LC
855 --query report on the availability of substitutes for the
856 store file names passed on the standard input"))
69daee23 857 (display (G_ "
29479de5
LC
858 --substitute STORE-FILE DESTINATION
859 download STORE-FILE and store it as a Nar in file
860 DESTINATION"))
861 (newline)
69daee23 862 (display (G_ "
29479de5 863 -h, --help display this help and exit"))
69daee23 864 (display (G_ "
29479de5
LC
865 -V, --version display version information and exit"))
866 (newline)
867 (show-bug-report-information))
868
869
870\f
ef8f910f
LC
871;;;
872;;; Daemon/substituter protocol.
873;;;
874
875(define (display-narinfo-data narinfo)
9d2f48df 876 "Write to the current output port the contents of NARINFO in the format
ef8f910f
LC
877expected by the daemon."
878 (format #t "~a\n~a\n~a\n"
879 (narinfo-path narinfo)
880 (or (and=> (narinfo-deriver narinfo)
881 (cute string-append (%store-prefix) "/" <>))
882 "")
883 (length (narinfo-references narinfo)))
884 (for-each (cute format #t "~a/~a~%" (%store-prefix) <>)
885 (narinfo-references narinfo))
b90ae065 886
4736d06f 887 (let-values (((uri compression file-size) (narinfo-best-uri narinfo)))
b90ae065
LC
888 (format #t "~a\n~a\n"
889 (or file-size 0)
890 (or (narinfo-size narinfo) 0))))
ef8f910f
LC
891
892(define* (process-query command
55b2fc18 893 #:key cache-urls acl)
ef8f910f
LC
894 "Reply to COMMAND, a query as written by the daemon to this process's
895standard input. Use ACL as the access-control list against which to check
896authorized substitutes."
897 (define (valid? obj)
55b2fc18 898 (valid-narinfo? obj acl))
ef8f910f
LC
899
900 (match (string-tokenize command)
901 (("have" paths ..1)
55b2fc18 902 ;; Return the subset of PATHS available in CACHE-URLS.
a9468b42 903 (let ((substitutable (lookup-narinfos/diverse cache-urls paths valid?)))
ef8f910f
LC
904 (for-each (lambda (narinfo)
905 (format #t "~a~%" (narinfo-path narinfo)))
a9468b42 906 substitutable)
ef8f910f
LC
907 (newline)))
908 (("info" paths ..1)
55b2fc18 909 ;; Reply info about PATHS if it's in CACHE-URLS.
a9468b42
LC
910 (let ((substitutable (lookup-narinfos/diverse cache-urls paths valid?)))
911 (for-each display-narinfo-data substitutable)
ef8f910f
LC
912 (newline)))
913 (wtf
914 (error "unknown `--query' command" wtf))))
915
b90ae065
LC
916(define %compression-methods
917 ;; Known compression methods and a thunk to determine whether they're
918 ;; supported. See 'decompressed-port' in (guix utils).
919 `(("gzip" . ,(const #t))
920 ("lzip" . ,lzlib-available?)
921 ("xz" . ,(const #t))
922 ("bzip2" . ,(const #t))
923 ("none" . ,(const #t))))
924
925(define (supported-compression? compression)
926 "Return true if COMPRESSION, a string, denotes a supported compression
927method."
928 (match (assoc-ref %compression-methods compression)
929 (#f #f)
930 (supported? (supported?))))
931
932(define (compresses-better? compression1 compression2)
933 "Return true if COMPRESSION1 generally compresses better than COMPRESSION2;
934this is a rough approximation."
935 (match compression1
936 ("none" #f)
937 ("gzip" (string=? compression2 "none"))
938 (_ (or (string=? compression2 "none")
939 (string=? compression2 "gzip")))))
940
4736d06f 941(define (narinfo-best-uri narinfo)
b90ae065
LC
942 "Select the \"best\" URI to download NARINFO's nar, and return three values:
943the URI, its compression method (a string), and the compressed file size."
944 (define choices
945 (filter (match-lambda
946 ((uri compression file-size)
947 (supported-compression? compression)))
948 (zip (narinfo-uris narinfo)
949 (narinfo-compressions narinfo)
950 (narinfo-file-sizes narinfo))))
951
952 (define (file-size<? c1 c2)
953 (match c1
954 ((uri1 compression1 (? integer? file-size1))
955 (match c2
956 ((uri2 compression2 (? integer? file-size2))
957 (< file-size1 file-size2))
958 (_ #t)))
959 ((uri compression1 #f)
960 (match c2
961 ((uri2 compression2 _)
962 (compresses-better? compression1 compression2))))
963 (_ #f))) ;we can't tell
964
965 (match (sort choices file-size<?)
966 (((uri compression file-size) _ ...)
967 (values uri compression file-size))))
968
ef8f910f 969(define* (process-substitution store-item destination
dc0f74e5 970 #:key cache-urls acl print-build-trace?)
55b2fc18 971 "Substitute STORE-ITEM (a store file name) from CACHE-URLS, and write it to
ef8f910f 972DESTINATION as a nar file. Verify the substitute against ACL."
b90ae065
LC
973 (define narinfo
974 (lookup-narinfo cache-urls store-item
975 (cut valid-narinfo? <> acl)))
976
977 (unless narinfo
978 (leave (G_ "no valid substitute for '~a'~%")
979 store-item))
ef8f910f 980
b90ae065 981 (let-values (((uri compression file-size)
4736d06f 982 (narinfo-best-uri narinfo)))
ef8f910f
LC
983 ;; Tell the daemon what the expected hash of the Nar itself is.
984 (format #t "~a~%" (narinfo-hash narinfo))
985
dc0f74e5
LC
986 (unless print-build-trace?
987 (format (current-error-port)
988 (G_ "Downloading ~a...~%") (uri->string uri)))
989
ef8f910f
LC
990 (let*-values (((raw download-size)
991 ;; Note that Hydra currently generates Nars on the fly
992 ;; and doesn't specify a Content-Length, so
993 ;; DOWNLOAD-SIZE is #f in practice.
994 (fetch uri #:buffered? #f #:timeout? #f))
995 ((progress)
b90ae065
LC
996 (let* ((dl-size (or download-size
997 (and (equal? compression "none")
ef8f910f 998 (narinfo-size narinfo))))
dc0f74e5
LC
999 (reporter (if print-build-trace?
1000 (progress-reporter/trace
1001 destination
1002 (uri->string uri) dl-size
1003 (current-error-port))
1004 (progress-reporter/file
1005 (uri->string uri) dl-size
1006 (current-error-port)
1007 #:abbreviation nar-uri-abbreviation))))
79864851 1008 (progress-report-port reporter raw)))
ef8f910f 1009 ((input pids)
5efa0e4d
SB
1010 ;; NOTE: This 'progress' port of current process will be
1011 ;; closed here, while the child process doing the
1012 ;; reporting will close it upon exit.
b90ae065 1013 (decompressed-port (string->symbol compression)
ef8f910f
LC
1014 progress)))
1015 ;; Unpack the Nar at INPUT into DESTINATION.
1016 (restore-file input destination)
4220514b 1017 (close-port input)
5efa0e4d
SB
1018
1019 ;; Wait for the reporter to finish.
1020 (every (compose zero? cdr waitpid) pids)
ef8f910f 1021
79864851
SB
1022 ;; Skip a line after what 'progress-reporter/file' printed, and another
1023 ;; one to visually separate substitutions.
5efa0e4d 1024 (display "\n\n" (current-error-port)))))
ef8f910f
LC
1025
1026\f
f65cf81a
LC
1027;;;
1028;;; Entry point.
1029;;;
1030
cdea30e0
LC
1031(define (check-acl-initialized)
1032 "Warn if the ACL is uninitialized."
1033 (define (singleton? acl)
1034 ;; True if ACL contains just the user's public key.
1035 (and (file-exists? %public-key-file)
1036 (let ((key (call-with-input-file %public-key-file
1037 (compose string->canonical-sexp
2535635f 1038 read-string))))
00fe9333
LC
1039 (match acl
1040 ((thing)
1041 (equal? (canonical-sexp->string thing)
1042 (canonical-sexp->string key)))
1043 (_
1044 #f)))))
1045
1046 (let ((acl (acl->public-keys (current-acl))))
cdea30e0 1047 (when (or (null? acl) (singleton? acl))
69daee23 1048 (warning (G_ "ACL for archive imports seems to be uninitialized, \
cdea30e0
LC
1049substitutes may be unavailable\n")))))
1050
9176607e
LC
1051(define (daemon-options)
1052 "Return a list of name/value pairs denoting build daemon options."
1053 (define %not-newline
1054 (char-set-complement (char-set #\newline)))
1055
1056 (match (getenv "_NIX_OPTIONS")
1057 (#f ;should not happen when called by the daemon
1058 '())
1059 (newline-separated
1060 ;; Here we get something of the form "OPTION1=VALUE1\nOPTION2=VALUE2\n".
1061 (filter-map (lambda (option=value)
1062 (match (string-index option=value #\=)
1063 (#f ;invalid option setting
1064 #f)
1065 (equal-sign
1066 (cons (string-take option=value equal-sign)
1067 (string-drop option=value (+ 1 equal-sign))))))
1068 (string-tokenize newline-separated %not-newline)))))
1069
1070(define (find-daemon-option option)
1071 "Return the value of build daemon option OPTION, or #f if it could not be
1072found."
1073 (assoc-ref (daemon-options) option))
1074
218f6ecc 1075(define %default-substitute-urls
71e2065a
LC
1076 (match (and=> (or (find-daemon-option "untrusted-substitute-urls") ;client
1077 (find-daemon-option "substitute-urls")) ;admin
4938b0ee 1078 string-tokenize)
55b2fc18
LC
1079 ((urls ...)
1080 urls)
4938b0ee
LC
1081 (#f
1082 ;; This can only happen when this script is not invoked by the
1083 ;; daemon.
757e633d 1084 '("http://ci.guix.gnu.org"))))
9176607e 1085
218f6ecc
LC
1086(define substitute-urls
1087 ;; List of substitute URLs.
1088 (make-parameter %default-substitute-urls))
1089
b0a6a971
LC
1090(define (client-terminal-columns)
1091 "Return the number of columns in the client's terminal, if it is known, or a
1092default value."
1093 (or (and=> (or (find-daemon-option "untrusted-terminal-columns")
1094 (find-daemon-option "terminal-columns"))
85fc958d
LC
1095 (lambda (str)
1096 (let ((number (string->number str)))
1097 (and number (max 20 (- number 1))))))
b0a6a971
LC
1098 80))
1099
8a210507
LC
1100(define (validate-uri uri)
1101 (unless (string->uri uri)
69daee23 1102 (leave (G_ "~a: invalid URI~%") uri)))
8a210507 1103
2c74fde0 1104(define (guix-substitute . args)
f65cf81a 1105 "Implement the build daemon's substituter protocol."
dc0f74e5
LC
1106 (define print-build-trace?
1107 (match (or (find-daemon-option "untrusted-print-extended-build-trace")
1108 (find-daemon-option "print-extended-build-trace"))
1109 (#f #f)
1110 ((= string->number number) (> number 0))
1111 (_ #f)))
1112
eba783b7 1113 (mkdir-p %narinfo-cache-directory)
2ea2aac6
LC
1114 (maybe-remove-expired-cache-entries %narinfo-cache-directory
1115 cached-narinfo-files
1116 #:entry-expiration
1117 cached-narinfo-expiration-time
1118 #:cleanup-period
1119 %narinfo-expired-cache-entry-removal-delay)
00fe9333 1120 (check-acl-initialized)
d43eb499
LC
1121
1122 ;; Starting from commit 22144afa in Nix, we are allowed to bail out directly
1123 ;; when we know we cannot substitute, but we must emit a newline on stdout
1124 ;; when everything is alright.
218f6ecc 1125 (when (null? (substitute-urls))
55b2fc18 1126 (exit 0))
d43eb499
LC
1127
1128 ;; Say hello (see above.)
1129 (newline)
1130 (force-output (current-output-port))
1131
218f6ecc
LC
1132 ;; Sanity-check SUBSTITUTE-URLS so we can provide a meaningful error message.
1133 (for-each validate-uri (substitute-urls))
8a210507 1134
38f50f49
LC
1135 ;; Attempt to install the client's locale, mostly so that messages are
1136 ;; suitably translated.
1137 (match (or (find-daemon-option "untrusted-locale")
1138 (find-daemon-option "locale"))
1139 (#f #f)
1140 (locale (false-if-exception (setlocale LC_ALL locale))))
1141
7d058688
LC
1142 (catch 'system-error
1143 (lambda ()
1144 (set-thread-name "guix substitute"))
1145 (const #t)) ;GNU/Hurd lacks 'prctl'
8902d0f2 1146
cf5d2ca3 1147 (with-networking
cdea30e0
LC
1148 (with-error-handling ; for signature errors
1149 (match args
1150 (("--query")
ef8f910f 1151 (let ((acl (current-acl)))
cdea30e0
LC
1152 (let loop ((command (read-line)))
1153 (or (eof-object? command)
1154 (begin
ef8f910f 1155 (process-query command
218f6ecc 1156 #:cache-urls (substitute-urls)
ef8f910f 1157 #:acl acl)
cdea30e0
LC
1158 (loop (read-line)))))))
1159 (("--substitute" store-path destination)
1160 ;; Download STORE-PATH and add store it as a Nar in file DESTINATION.
b0a6a971
LC
1161 ;; Specify the number of columns of the terminal so the progress
1162 ;; report displays nicely.
1163 (parameterize ((current-terminal-columns (client-terminal-columns)))
1164 (process-substitution store-path destination
218f6ecc 1165 #:cache-urls (substitute-urls)
dc0f74e5
LC
1166 #:acl (current-acl)
1167 #:print-build-trace? print-build-trace?)))
7ede577a 1168 ((or ("-V") ("--version"))
2c74fde0 1169 (show-version-and-exit "guix substitute"))
cdea30e0
LC
1170 (("--help")
1171 (show-help))
1172 (opts
69daee23 1173 (leave (G_ "~a: unrecognized options~%") opts))))))
f65cf81a 1174
bb7dcaea 1175;;; Local Variables:
2207f731 1176;;; eval: (put 'with-timeout 'scheme-indent-function 1)
ae3b6bb0
LC
1177;;; End:
1178
2c74fde0 1179;;; substitute.scm ends here