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