gc: Open a connection to the daemon only when strictly necessary.

Fixes guix/guix#1901.

Previously, ‘guix gc --list-busy’ (which is invoked by ‘guix-daemon’) would
open a connection to the daemon, which in turn attempts to create
/var/guix/profiles/per-user/$USER.  However, when ‘guix-daemon‘ is running as
an unprivileged user, creating that directory fails with EPERM.  Because of
this, garbage collection would always fail when running the unprivileged
daemon on Guix System.

* guix/scripts/gc.scm (guix-gc): Remove upfront call to ‘open-connection’.
Instead, use ‘with-store’ only for operations that require it.

Change-Id: I1fbfd97cf7ba9e3087f7287b4776ea2f6623400d
This commit is contained in:
Ludovic Courtès 2025-09-01 14:47:01 +02:00
parent 19deb9b658
commit 7445776b7e
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5

View file

@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2012-2013, 2015-2020, 2022 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2012-2013, 2015-2020, 2022, 2025 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2022 Efraim Flashner <efraim@flashner.co.il> ;;; Copyright © 2022 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2022 Remco van 't Veer <remco@remworks.net> ;;; Copyright © 2022 Remco van 't Veer <remco@remworks.net>
;;; ;;;
@ -297,7 +297,6 @@ is deprecated; use '-D'~%"))
(with-error-handling (with-error-handling
(let* ((opts (parse-options)) (let* ((opts (parse-options))
(store (open-connection))
(paths (filter-map (match-lambda (paths (filter-map (match-lambda
(('argument . arg) arg) (('argument . arg) arg)
(_ #f)) (_ #f))
@ -307,39 +306,44 @@ is deprecated; use '-D'~%"))
(leave (G_ "extraneous arguments: ~{~a ~}~%") paths))) (leave (G_ "extraneous arguments: ~{~a ~}~%") paths)))
(define (list-relatives relatives) (define (list-relatives relatives)
(for-each (compose (lambda (path) (with-store store
(for-each (cut simple-format #t "~a~%" <>) (for-each (compose (lambda (path)
(relatives store path))) (for-each (cut simple-format #t "~a~%" <>)
store-directory (relatives store path)))
symlink-target) store-directory
paths)) symlink-target)
paths)))
(case (assoc-ref opts 'action) (case (assoc-ref opts 'action)
((collect-garbage) ((collect-garbage)
(assert-no-extra-arguments) (assert-no-extra-arguments)
(let ((min-freed (assoc-ref opts 'min-freed)) (let ((min-freed (assoc-ref opts 'min-freed))
(free-space (assoc-ref opts 'free-space))) (free-space (assoc-ref opts 'free-space)))
(match (assq 'delete-generations opts) (with-store store
(#f #t) (match (assq 'delete-generations opts)
((_ . pattern) (#f #t)
(delete-generations store pattern))) ((_ . pattern)
(cond (delete-generations store pattern)))
(free-space (cond
(ensure-free-space store free-space)) (free-space
(min-freed (ensure-free-space store free-space))
(let-values (((paths freed) (collect-garbage store min-freed))) (min-freed
(info (G_ "freed ~a~%") (number->size freed)))) (let-values (((paths freed) (collect-garbage store min-freed)))
(else (info (G_ "freed ~a~%") (number->size freed))))
(let-values (((paths freed) (collect-garbage store))) (else
(info (G_ "freed ~a~%") (number->size freed))))))) (let-values (((paths freed) (collect-garbage store)))
(info (G_ "freed ~a~%") (number->size freed))))))))
((list-roots) ((list-roots)
(assert-no-extra-arguments) (assert-no-extra-arguments)
(list-roots)) (list-roots))
((list-busy) ((list-busy)
;; Note: This is invoked by 'guix-daemon' so it must not open a
;; connection to the daemon.
(assert-no-extra-arguments) (assert-no-extra-arguments)
(list-busy)) (list-busy))
((delete) ((delete)
(delete-paths store (map direct-store-path paths))) (with-store store
(delete-paths store (map direct-store-path paths))))
((list-references) ((list-references)
(list-relatives references)) (list-relatives references))
((list-requisites) ((list-requisites)
@ -351,22 +355,28 @@ is deprecated; use '-D'~%"))
(list-relatives valid-derivers)) (list-relatives valid-derivers))
((optimize) ((optimize)
(assert-no-extra-arguments) (assert-no-extra-arguments)
(optimize-store store)) (with-store store
(optimize-store store)))
((verify) ((verify)
(assert-no-extra-arguments) (assert-no-extra-arguments)
(let ((options (assoc-ref opts 'verify-options))) (let ((options (assoc-ref opts 'verify-options)))
(exit (exit
(verify-store store (with-store store
#:check-contents? (memq 'contents options) (verify-store store
#:repair? (memq 'repair options))))) #:check-contents? (memq 'contents options)
#:repair? (memq 'repair options))))))
((list-failures) ((list-failures)
(for-each (cut simple-format #t "~a~%" <>) (with-store store
(query-failed-paths store))) (for-each (cut simple-format #t "~a~%" <>)
(query-failed-paths store))))
((clear-failures) ((clear-failures)
(clear-failed-paths store (map direct-store-path paths))) (with-store store
(clear-failed-paths store (map direct-store-path paths))))
((list-dead) ((list-dead)
(for-each (cut simple-format #t "~a~%" <>) (for-each (cut simple-format #t "~a~%" <>)
(dead-paths store))) (with-store store
(dead-paths store))))
((list-live) ((list-live)
(for-each (cut simple-format #t "~a~%" <>) (for-each (cut simple-format #t "~a~%" <>)
(live-paths store))))))) (with-store store
(live-paths store))))))))