diff options
| -rwxr-xr-x | guix/scripts/substitute.scm | 191 | ||||
| -rw-r--r-- | nix/libstore/build.cc | 4 | ||||
| -rw-r--r-- | nix/libstore/local-store.cc | 12 | ||||
| -rw-r--r-- | tests/substitute.scm | 4 |
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 |
| 284 | expected by the daemon." | 284 | daemon." |
| 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 |
| 305 | standard input. Use ACL as the access-control list against which to check | 305 | standard input. Use ACL as the access-control list against which to check |
| 306 | authorized substitutes." | 306 | authorized 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 |
| 435 | DESTINATION as a nar file. Verify the substitute against ACL, and verify its | 435 | DESTINATION as a nar file. Verify the substitute against ACL, and verify its |
| 436 | hash against what appears in the narinfo. When DEDUPLICATE? is true, and if | 436 | hash against what appears in the narinfo. When DEDUPLICATE? is true, and if |
| 437 | DESTINATION is in the store, deduplicate its files. Print a status line on | 437 | DESTINATION is in the store, deduplicate its files. Print a status line to |
| 438 | the current output port." | 438 | PORT." |
| 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 | ||
| 694 | if 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() | |||
| 3158 | void SubstitutionGoal::handleChildOutput(int fd, const string & data) | 3158 | void 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. */ |
| 785 | string LocalStore::getLineFromSubstituter(Agent & run) | 785 | string 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" |
