summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xguix/scripts/substitute.scm191
-rw-r--r--nix/libstore/build.cc4
-rw-r--r--nix/libstore/local-store.cc12
-rw-r--r--tests/substitute.scm4
4 files changed, 99 insertions, 112 deletions
diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm
index 79eaabd8fd5..48309f9b3a4 100755
--- a/guix/scripts/substitute.scm
+++ b/guix/scripts/substitute.scm
@@ -63,7 +63,7 @@
63 #:use-module (web uri) 63 #:use-module (web uri)
64 #:use-module (guix http-client) 64 #:use-module (guix http-client)
65 #:export (%allow-unauthenticated-substitutes? 65 #:export (%allow-unauthenticated-substitutes?
66 %error-to-file-descriptor-4? 66 %reply-file-descriptor
67 67
68 substitute-urls 68 substitute-urls
69 guix-substitute)) 69 guix-substitute))
@@ -279,29 +279,29 @@ Internal tool to substitute a pre-built binary to a local build.\n"))
279 "Evaluate EXP... Return its CPU usage as a fraction between 0 and 1." 279 "Evaluate EXP... Return its CPU usage as a fraction between 0 and 1."
280 (call-with-cpu-usage-monitoring (lambda () exp ...))) 280 (call-with-cpu-usage-monitoring (lambda () exp ...)))
281 281
282(define (display-narinfo-data narinfo) 282(define (display-narinfo-data port narinfo)
283 "Write to the current output port the contents of NARINFO in the format 283 "Write to PORT the contents of NARINFO in the format expected by the
284expected by the daemon." 284daemon."
285 (format #t "~a\n~a\n~a\n" 285 (format port "~a\n~a\n~a\n"
286 (narinfo-path narinfo) 286 (narinfo-path narinfo)
287 (or (and=> (narinfo-deriver narinfo) 287 (or (and=> (narinfo-deriver narinfo)
288 (cute string-append (%store-prefix) "/" <>)) 288 (cute string-append (%store-prefix) "/" <>))
289 "") 289 "")
290 (length (narinfo-references narinfo))) 290 (length (narinfo-references narinfo)))
291 (for-each (cute format #t "~a/~a~%" (%store-prefix) <>) 291 (for-each (cute format port "~a/~a~%" (%store-prefix) <>)
292 (narinfo-references narinfo)) 292 (narinfo-references narinfo))
293 293
294 (let-values (((uri compression file-size) 294 (let-values (((uri compression file-size)
295 (narinfo-best-uri narinfo 295 (narinfo-best-uri narinfo
296 #:fast-decompression? 296 #:fast-decompression?
297 %prefer-fast-decompression?))) 297 %prefer-fast-decompression?)))
298 (format #t "~a\n~a\n" 298 (format port "~a\n~a\n"
299 (or file-size 0) 299 (or file-size 0)
300 (or (narinfo-size narinfo) 0)))) 300 (or (narinfo-size narinfo) 0))))
301 301
302(define* (process-query command 302(define* (process-query port command
303 #:key cache-urls acl) 303 #:key cache-urls acl)
304 "Reply to COMMAND, a query as written by the daemon to this process's 304 "Reply on PORT to COMMAND, a query as written by the daemon to this process's
305standard input. Use ACL as the access-control list against which to check 305standard input. Use ACL as the access-control list against which to check
306authorized substitutes." 306authorized substitutes."
307 (define valid? 307 (define valid?
@@ -338,17 +338,17 @@ authorized substitutes."
338 #:open-connection open-connection-for-uri/cached 338 #:open-connection open-connection-for-uri/cached
339 #:make-progress-reporter make-progress-reporter))) 339 #:make-progress-reporter make-progress-reporter)))
340 (for-each (lambda (narinfo) 340 (for-each (lambda (narinfo)
341 (format #t "~a~%" (narinfo-path narinfo))) 341 (format port "~a~%" (narinfo-path narinfo)))
342 substitutable) 342 substitutable)
343 (newline))) 343 (newline port)))
344 (("info" paths ..1) 344 (("info" paths ..1)
345 ;; Reply info about PATHS if it's in CACHE-URLS. 345 ;; Reply info about PATHS if it's in CACHE-URLS.
346 (let ((substitutable (lookup-narinfos/diverse 346 (let ((substitutable (lookup-narinfos/diverse
347 cache-urls paths valid? 347 cache-urls paths valid?
348 #:open-connection open-connection-for-uri/cached 348 #:open-connection open-connection-for-uri/cached
349 #:make-progress-reporter make-progress-reporter))) 349 #:make-progress-reporter make-progress-reporter)))
350 (for-each display-narinfo-data substitutable) 350 (for-each (cut display-narinfo-data port <>) substitutable)
351 (newline))) 351 (newline port)))
352 (wtf 352 (wtf
353 (error "unknown `--query' command" wtf)))) 353 (error "unknown `--query' command" wtf))))
354 354
@@ -428,14 +428,14 @@ server certificates."
428 "Bind PORT with EXP... to a socket connected to URI." 428 "Bind PORT with EXP... to a socket connected to URI."
429 (call-with-cached-connection uri (lambda (port) exp ...))) 429 (call-with-cached-connection uri (lambda (port) exp ...)))
430 430
431(define* (process-substitution store-item destination 431(define* (process-substitution port store-item destination
432 #:key cache-urls acl 432 #:key cache-urls acl
433 deduplicate? print-build-trace?) 433 deduplicate? print-build-trace?)
434 "Substitute STORE-ITEM (a store file name) from CACHE-URLS, and write it to 434 "Substitute STORE-ITEM (a store file name) from CACHE-URLS, and write it to
435DESTINATION as a nar file. Verify the substitute against ACL, and verify its 435DESTINATION as a nar file. Verify the substitute against ACL, and verify its
436hash against what appears in the narinfo. When DEDUPLICATE? is true, and if 436hash against what appears in the narinfo. When DEDUPLICATE? is true, and if
437DESTINATION is in the store, deduplicate its files. Print a status line on 437DESTINATION is in the store, deduplicate its files. Print a status line to
438the current output port." 438PORT."
439 (define narinfo 439 (define narinfo
440 (lookup-narinfo cache-urls store-item 440 (lookup-narinfo cache-urls store-item
441 (if (%allow-unauthenticated-substitutes?) 441 (if (%allow-unauthenticated-substitutes?)
@@ -565,10 +565,10 @@ the current output port."
565 (let ((actual (get-hash))) 565 (let ((actual (get-hash)))
566 (if (bytevector=? actual expected) 566 (if (bytevector=? actual expected)
567 ;; Tell the daemon that we're done. 567 ;; Tell the daemon that we're done.
568 (format (current-output-port) "success ~a ~a~%" 568 (format port "success ~a ~a~%"
569 (narinfo-hash narinfo) (narinfo-size narinfo)) 569 (narinfo-hash narinfo) (narinfo-size narinfo))
570 ;; The actual data has a different hash than that in NARINFO. 570 ;; The actual data has a different hash than that in NARINFO.
571 (format (current-output-port) "hash-mismatch ~a ~a ~a~%" 571 (format port "hash-mismatch ~a ~a ~a~%"
572 (hash-algorithm-name algorithm) 572 (hash-algorithm-name algorithm)
573 (bytevector->nix-base32-string expected) 573 (bytevector->nix-base32-string expected)
574 (bytevector->nix-base32-string actual))))))) 574 (bytevector->nix-base32-string actual)))))))
@@ -682,28 +682,10 @@ default value."
682 (unless (string->uri uri) 682 (unless (string->uri uri)
683 (leave (G_ "~a: invalid URI~%") uri))) 683 (leave (G_ "~a: invalid URI~%") uri)))
684 684
685(define %error-to-file-descriptor-4? 685(define %reply-file-descriptor
686 ;; Whether to direct 'current-error-port' to file descriptor 4 like 686 ;; The file descriptor where replies to the daemon must be sent, or #f to
687 ;; 'guix-daemon' expects. 687 ;; use the current output port instead.
688 (make-parameter #t)) 688 (make-parameter 4))
689
690;; The daemon's agent code opens file descriptor 4 for us and this is where
691;; stderr should go.
692(define-syntax-rule (with-redirected-error-port exp ...)
693 "Evaluate EXP... with the current error port redirected to file descriptor 4
694if needed, as expected by the daemon's agent."
695 (let ((thunk (lambda () exp ...)))
696 (if (%error-to-file-descriptor-4?)
697 (parameterize ((current-error-port (fdopen 4 "wl")))
698 ;; Redirect diagnostics to file descriptor 4 as well.
699 (guix-warning-port (current-error-port))
700
701 ;; 'with-continuation-barrier' captures the initial value of
702 ;; 'current-error-port' to report backtraces in case of uncaught
703 ;; exceptions. Without it, backtraces would be printed to FD 2,
704 ;; thereby confusing the daemon.
705 (with-continuation-barrier thunk))
706 (thunk))))
707 689
708(define-command (guix-substitute . args) 690(define-command (guix-substitute . args)
709 (category internal) 691 (category internal)
@@ -719,68 +701,73 @@ if needed, as expected by the daemon's agent."
719 (define deduplicate? 701 (define deduplicate?
720 (find-daemon-option "deduplicate")) 702 (find-daemon-option "deduplicate"))
721 703
722 (with-redirected-error-port 704 (define reply-port
723 (mkdir-p %narinfo-cache-directory) 705 ;; Port used to reply to the daemon.
724 (maybe-remove-expired-cache-entries %narinfo-cache-directory 706 (if (%reply-file-descriptor)
725 cached-narinfo-files 707 (fdopen (%reply-file-descriptor) "wl")
726 #:entry-expiration 708 (current-output-port)))
727 cached-narinfo-expiration-time 709
728 #:cleanup-period 710 (mkdir-p %narinfo-cache-directory)
729 %narinfo-expired-cache-entry-removal-delay) 711 (maybe-remove-expired-cache-entries %narinfo-cache-directory
730 (check-acl-initialized) 712 cached-narinfo-files
731 713 #:entry-expiration
732 ;; Sanity-check SUBSTITUTE-URLS so we can provide a meaningful error 714 cached-narinfo-expiration-time
733 ;; message. 715 #:cleanup-period
734 (for-each validate-uri (substitute-urls)) 716 %narinfo-expired-cache-entry-removal-delay)
735 717 (check-acl-initialized)
736 ;; Attempt to install the client's locale so that messages are suitably 718
737 ;; translated. LC_CTYPE must be a UTF-8 locale; it's the case by default 719 ;; Sanity-check SUBSTITUTE-URLS so we can provide a meaningful error
738 ;; so don't change it. 720 ;; message.
739 (match (or (find-daemon-option "untrusted-locale") 721 (for-each validate-uri (substitute-urls))
740 (find-daemon-option "locale")) 722
741 (#f #f) 723 ;; Attempt to install the client's locale so that messages are suitably
742 (locale (false-if-exception (setlocale LC_MESSAGES locale)))) 724 ;; translated. LC_CTYPE must be a UTF-8 locale; it's the case by default
743 725 ;; so don't change it.
744 (catch 'system-error 726 (match (or (find-daemon-option "untrusted-locale")
745 (lambda () 727 (find-daemon-option "locale"))
746 (set-thread-name "guix substitute")) 728 (#f #f)
747 (const #t)) ;GNU/Hurd lacks 'prctl' 729 (locale (false-if-exception (setlocale LC_MESSAGES locale))))
748 730
749 (with-networking 731 (catch 'system-error
750 (with-error-handling ; for signature errors 732 (lambda ()
751 (match args 733 (set-thread-name "guix substitute"))
752 (("--query") 734 (const #t)) ;GNU/Hurd lacks 'prctl'
753 (let ((acl (current-acl))) 735
754 (let loop ((command (read-line))) 736 (with-networking
755 (or (eof-object? command) 737 (with-error-handling ; for signature errors
756 (begin 738 (match args
757 (process-query command 739 (("--query")
758 #:cache-urls (substitute-urls) 740 (let ((acl (current-acl)))
759 #:acl acl) 741 (let loop ((command (read-line)))
760 (loop (read-line))))))) 742 (or (eof-object? command)
761 (("--substitute") 743 (begin
762 ;; Download STORE-PATH and store it as a Nar in file DESTINATION. 744 (process-query reply-port command
763 ;; Specify the number of columns of the terminal so the progress 745 #:cache-urls (substitute-urls)
764 ;; report displays nicely. 746 #:acl acl)
765 (parameterize ((current-terminal-columns (client-terminal-columns))) 747 (loop (read-line)))))))
766 (let loop () 748 (("--substitute")
767 (match (read-line) 749 ;; Download STORE-PATH and store it as a Nar in file DESTINATION.
768 ((? eof-object?) 750 ;; Specify the number of columns of the terminal so the progress
769 #t) 751 ;; report displays nicely.
770 ((= string-tokenize ("substitute" store-path destination)) 752 (parameterize ((current-terminal-columns (client-terminal-columns)))
771 (process-substitution store-path destination 753 (let loop ()
772 #:cache-urls (substitute-urls) 754 (match (read-line)
773 #:acl (current-acl) 755 ((? eof-object?)
774 #:deduplicate? deduplicate? 756 #t)
775 #:print-build-trace? 757 ((= string-tokenize ("substitute" store-path destination))
776 print-build-trace?) 758 (process-substitution reply-port store-path destination
777 (loop)))))) 759 #:cache-urls (substitute-urls)
778 ((or ("-V") ("--version")) 760 #:acl (current-acl)
779 (show-version-and-exit "guix substitute")) 761 #:deduplicate? deduplicate?
780 (("--help") 762 #:print-build-trace?
781 (show-help)) 763 print-build-trace?)
782 (opts 764 (loop))))))
783 (leave (G_ "~a: unrecognized options~%") opts))))))) 765 ((or ("-V") ("--version"))
766 (show-version-and-exit "guix substitute"))
767 (("--help")
768 (show-help))
769 (opts
770 (leave (G_ "~a: unrecognized options~%") opts))))))
784 771
785;;; Local Variables: 772;;; Local Variables:
786;;; eval: (put 'with-timeout 'scheme-indent-function 1) 773;;; eval: (put 'with-timeout 'scheme-indent-function 1)
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 4f486f08220..5697ae5a436 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -3158,13 +3158,13 @@ void SubstitutionGoal::finished()
3158void SubstitutionGoal::handleChildOutput(int fd, const string & data) 3158void SubstitutionGoal::handleChildOutput(int fd, const string & data)
3159{ 3159{
3160 if (verbosity >= settings.buildVerbosity 3160 if (verbosity >= settings.buildVerbosity
3161 && fd == substituter->builderOut.readSide) { 3161 && fd == substituter->fromAgent.readSide) {
3162 writeToStderr(data); 3162 writeToStderr(data);
3163 /* Don't write substitution output to a log file for now. We 3163 /* Don't write substitution output to a log file for now. We
3164 probably should, though. */ 3164 probably should, though. */
3165 } 3165 }
3166 3166
3167 if (fd == substituter->fromAgent.readSide) { 3167 if (fd == substituter->builderOut.readSide) {
3168 /* DATA may consist of several lines. Process them one by one. */ 3168 /* DATA may consist of several lines. Process them one by one. */
3169 string input = data; 3169 string input = data;
3170 while (!input.empty()) { 3170 while (!input.empty()) {
diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index c304e2ddd16..675d1ba66f7 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -780,8 +780,8 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
780 }); 780 });
781} 781}
782 782
783/* Read a line from the substituter's stdout, while also processing 783/* Read a line from the substituter's reply file descriptor, while also
784 its stderr. */ 784 processing its stderr. */
785string LocalStore::getLineFromSubstituter(Agent & run) 785string LocalStore::getLineFromSubstituter(Agent & run)
786{ 786{
787 string res, err; 787 string res, err;
@@ -802,9 +802,9 @@ string LocalStore::getLineFromSubstituter(Agent & run)
802 } 802 }
803 803
804 /* Completely drain stderr before dealing with stdout. */ 804 /* Completely drain stderr before dealing with stdout. */
805 if (FD_ISSET(run.builderOut.readSide, &fds)) { 805 if (FD_ISSET(run.fromAgent.readSide, &fds)) {
806 char buf[4096]; 806 char buf[4096];
807 ssize_t n = read(run.builderOut.readSide, (unsigned char *) buf, sizeof(buf)); 807 ssize_t n = read(run.fromAgent.readSide, (unsigned char *) buf, sizeof(buf));
808 if (n == -1) { 808 if (n == -1) {
809 if (errno == EINTR) continue; 809 if (errno == EINTR) continue;
810 throw SysError("reading from substituter's stderr"); 810 throw SysError("reading from substituter's stderr");
@@ -822,9 +822,9 @@ string LocalStore::getLineFromSubstituter(Agent & run)
822 } 822 }
823 823
824 /* Read from stdout until we get a newline or the buffer is empty. */ 824 /* Read from stdout until we get a newline or the buffer is empty. */
825 else if (FD_ISSET(run.fromAgent.readSide, &fds)) { 825 else if (FD_ISSET(run.builderOut.readSide, &fds)) {
826 unsigned char c; 826 unsigned char c;
827 readFull(run.fromAgent.readSide, (unsigned char *) &c, 1); 827 readFull(run.builderOut.readSide, (unsigned char *) &c, 1);
828 if (c == '\n') { 828 if (c == '\n') {
829 if (!err.empty()) printMsg(lvlError, "substitute: " + err); 829 if (!err.empty()) printMsg(lvlError, "substitute: " + err);
830 return res; 830 return res;
diff --git a/tests/substitute.scm b/tests/substitute.scm
index 697abc46840..21b513e1d87 100644
--- a/tests/substitute.scm
+++ b/tests/substitute.scm
@@ -1,6 +1,6 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2014 Nikita Karetnikov <nikita@karetnikov.org> 2;;; Copyright © 2014 Nikita Karetnikov <nikita@karetnikov.org>
3;;; Copyright © 2014, 2015, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org> 3;;; Copyright © 2014, 2015, 2017, 2018, 2019, 2021 Ludovic Courtès <ludo@gnu.org>
4;;; 4;;;
5;;; This file is part of GNU Guix. 5;;; This file is part of GNU Guix.
6;;; 6;;;
@@ -198,7 +198,7 @@ a file for NARINFO."
198 198
199;; Never use file descriptor 4, unlike what happens when invoked by the 199;; Never use file descriptor 4, unlike what happens when invoked by the
200;; daemon. 200;; daemon.
201(%error-to-file-descriptor-4? #f) 201(%reply-file-descriptor #f)
202 202
203 203
204(test-equal "query narinfo without signature" 204(test-equal "query narinfo without signature"