;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2021, 2023 Andrew Tropin <andrew@trop.in> ;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz> ;;; Copyright © 2024 Ludovic Courtès <ludo@gnu.org> ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix is free software; you can redistribute it and/or modify it ;;; under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 3 of the License, or (at ;;; your option) any later version. ;;; ;;; GNU Guix is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. (define-module (gnu home services shepherd) #:use-module (gnu home services) #:use-module (gnu packages admin) #:use-module (gnu services shepherd) #:use-module (guix sets) #:use-module (guix gexp) #:use-module (guix records) #:use-module (srfi srfi-1) #:export (home-shepherd-service-type home-shepherd-configuration home-shepherd-configuration? home-shepherd-configuration-shepherd home-shepherd-configuration-auto-start? home-shepherd-configuration-daemonize? home-shepherd-configuration-silent? home-shepherd-configuration-services) #:re-export (shepherd-service shepherd-service? shepherd-service-documentation shepherd-service-provision shepherd-service-canonical-name shepherd-service-requirement shepherd-service-one-shot? shepherd-service-respawn? shepherd-service-start shepherd-service-stop shepherd-service-auto-start? shepherd-service-modules shepherd-action shepherd-configuration-action)) (define-record-type* <home-shepherd-configuration> home-shepherd-configuration make-home-shepherd-configuration home-shepherd-configuration? (shepherd home-shepherd-configuration-shepherd (default shepherd-1.0)) ;package (auto-start? home-shepherd-configuration-auto-start? (default #t)) (daemonize? home-shepherd-configuration-daemonize? (default #t)) (silent? home-shepherd-configuration-silent? (default #t)) (services home-shepherd-configuration-services (default '()))) (define (home-shepherd-configuration-file config) "Return the shepherd configuration file for SERVICES. SHEPHERD is used as shepherd package." (let* ((daemonize? (home-shepherd-configuration-daemonize? config)) (services (home-shepherd-configuration-services config)) (_ (assert-valid-graph services)) (files (map shepherd-service-file services)) ;; TODO: Add compilation of services, it can improve start ;; time. ;; (scm->go (cute scm->go <> shepherd)) ) (define config #~(begin (use-modules (srfi srfi-34) (system repl error-handling)) (define (make-user-module) ;; Copied from (shepherd support), where it's private. (let ((m (make-fresh-user-module))) (module-use! m (resolve-interface '(shepherd service))) m)) (register-services (map (lambda (file) (save-module-excursion (lambda () (set-current-module (make-user-module)) (load file)))) '#$files)) #$(and daemonize? #~(perform-service-action root-service 'daemonize)) (format #t "Starting services...~%") (let ((services-to-start '#$(append-map shepherd-service-provision (filter shepherd-service-auto-start? services)))) (start-in-the-background services-to-start) (redirect-port (open-input-file "/dev/null") (current-input-port))))) (scheme-file "shepherd.conf" config))) (define (launch-shepherd-gexp config) (let* ((shepherd (home-shepherd-configuration-shepherd config)) (silent? (home-shepherd-configuration-silent? config))) (if (home-shepherd-configuration-auto-start? config) (with-imported-modules '((guix build utils)) #~(unless (file-exists? (string-append (or (getenv "XDG_RUNTIME_DIR") (format #f "/run/user/~a" (getuid))) "/shepherd/socket")) (system* #$(file-append shepherd "/bin/shepherd") #$@(if silent? '("--silent") '()) "--config" #$(home-shepherd-configuration-file config)))) #~""))) (define (reload-configuration-gexp config) (let* ((shepherd (home-shepherd-configuration-shepherd config))) #~(system* #$(file-append shepherd "/bin/herd") "load" "root" #$(home-shepherd-configuration-file config)))) (define (ensure-shepherd-gexp config) #~(if (file-exists? (string-append (or (getenv "XDG_RUNTIME_DIR") (format #f "/run/user/~a" (getuid))) "/shepherd/socket")) #$(reload-configuration-gexp config) ;; Don't attempt to start user shepherd if the system is running the ;; activation script. /run/user/<uid> may not have been created ;; yet. But do otherwise so if the runtime dir does not exist an error ;; is logged. (unless (getenv "GUIX_SYSTEM_IS_RUNNING_HOME_ACTIVATE") #$(launch-shepherd-gexp config)))) (define (shepherd-xdg-configuration-files config) `(("shepherd/init.scm" ,(home-shepherd-configuration-file config)))) (define home-shepherd-service-type (service-type (name 'home-shepherd) (extensions (list (service-extension home-run-on-first-login-service-type launch-shepherd-gexp) (service-extension home-xdg-configuration-files-service-type shepherd-xdg-configuration-files) (service-extension home-activation-service-type ensure-shepherd-gexp) (service-extension home-profile-service-type (lambda (config) `(,(home-shepherd-configuration-shepherd config)))))) (compose concatenate) (extend (lambda (config extra-services) (home-shepherd-configuration (inherit config) (services (append (home-shepherd-configuration-services config) extra-services))))) (default-value (home-shepherd-configuration)) (description "Configure and install userland Shepherd."))) (define-service-type-mapping shepherd-root-service-type => home-shepherd-service-type)