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
;;; 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 Remco van 't Veer <remco@remworks.net>
;;;
@ -297,7 +297,6 @@ is deprecated; use '-D'~%"))
(with-error-handling
(let* ((opts (parse-options))
(store (open-connection))
(paths (filter-map (match-lambda
(('argument . arg) arg)
(_ #f))
@ -307,18 +306,20 @@ is deprecated; use '-D'~%"))
(leave (G_ "extraneous arguments: ~{~a ~}~%") paths)))
(define (list-relatives relatives)
(with-store store
(for-each (compose (lambda (path)
(for-each (cut simple-format #t "~a~%" <>)
(relatives store path)))
store-directory
symlink-target)
paths))
paths)))
(case (assoc-ref opts 'action)
((collect-garbage)
(assert-no-extra-arguments)
(let ((min-freed (assoc-ref opts 'min-freed))
(free-space (assoc-ref opts 'free-space)))
(with-store store
(match (assq 'delete-generations opts)
(#f #t)
((_ . pattern)
@ -331,15 +332,18 @@ is deprecated; use '-D'~%"))
(info (G_ "freed ~a~%") (number->size freed))))
(else
(let-values (((paths freed) (collect-garbage store)))
(info (G_ "freed ~a~%") (number->size freed)))))))
(info (G_ "freed ~a~%") (number->size freed))))))))
((list-roots)
(assert-no-extra-arguments)
(list-roots))
((list-busy)
;; Note: This is invoked by 'guix-daemon' so it must not open a
;; connection to the daemon.
(assert-no-extra-arguments)
(list-busy))
((delete)
(delete-paths store (map direct-store-path paths)))
(with-store store
(delete-paths store (map direct-store-path paths))))
((list-references)
(list-relatives references))
((list-requisites)
@ -351,22 +355,28 @@ is deprecated; use '-D'~%"))
(list-relatives valid-derivers))
((optimize)
(assert-no-extra-arguments)
(optimize-store store))
(with-store store
(optimize-store store)))
((verify)
(assert-no-extra-arguments)
(let ((options (assoc-ref opts 'verify-options)))
(exit
(with-store store
(verify-store store
#:check-contents? (memq 'contents options)
#:repair? (memq 'repair options)))))
#:repair? (memq 'repair options))))))
((list-failures)
(with-store store
(for-each (cut simple-format #t "~a~%" <>)
(query-failed-paths store)))
(query-failed-paths store))))
((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)
(for-each (cut simple-format #t "~a~%" <>)
(dead-paths store)))
(with-store store
(dead-paths store))))
((list-live)
(for-each (cut simple-format #t "~a~%" <>)
(live-paths store)))))))
(with-store store
(live-paths store))))))))