services: herd: Move UI handling to 'guix system'.

This makes (gnu services herd) independent of (guix ui).

* gnu/services/herd.scm (&shepherd-error, &service-not-found-error)
(&action-not-found-error, &action-exception-error)
(&unknown-shepherd-error): New error condition types.
(report-action-error): Remove.
(raise-shepherd-error): New procedure.
(display-message): Do not use 'info' and '_'.
(invoke-action): Use 'raise-shepherd-error' instead of
'report-action-error'.  Do not use 'warning'.
(current-services): Do not use 'warning'.
* guix/scripts/system.scm (with-shepherd-error-handling): New macro.
(report-shepherd-error, call-with-service-upgrade-info): New
procedures.
(upgrade-shepherd-services): Use it.
master
Ludovic Courtès 2016-05-04 16:38:22 +02:00
parent af5640d1dd
commit 8bf92e3904
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
2 changed files with 151 additions and 67 deletions

View File

@ -17,12 +17,27 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services herd) (define-module (gnu services herd)
#:use-module (guix ui)
#:use-module (guix utils) #:use-module (guix utils)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-11) #:use-module (srfi srfi-11)
#:use-module (srfi srfi-34)
#:use-module (srfi srfi-35)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:export (current-services #:export (shepherd-error?
service-not-found-error?
service-not-found-error-service
action-not-found-error?
action-not-found-error-service
action-not-found-error-action
action-exception-error?
action-exception-error-service
action-exception-error-action
action-exception-error-key
action-exception-error-arguments
unknown-shepherd-error?
unknown-shepherd-error-sexp
current-services
unload-services unload-services
unload-service unload-service
load-services load-services
@ -61,31 +76,54 @@ return the socket."
(let ((connection (open-connection))) (let ((connection (open-connection)))
body ...)) body ...))
(define (report-action-error error) (define-condition-type &shepherd-error &error
"Report ERROR, an sexp received by a shepherd client in reply to COMMAND, a shepherd-error?)
command object."
(define-condition-type &service-not-found-error &shepherd-error
service-not-found-error?
(service service-not-found-error-service))
(define-condition-type &action-not-found-error &shepherd-error
action-not-found-error?
(service action-not-found-error-service)
(action action-not-found-error-action))
(define-condition-type &action-exception-error &shepherd-error
action-exception-error?
(service action-exception-error-service)
(action action-exception-error-action)
(key action-exception-error-key)
(args action-exception-error-arguments))
(define-condition-type &unknown-shepherd-error &shepherd-error
unknown-shepherd-error?
(sexp unknown-shepherd-error-sexp))
(define (raise-shepherd-error error)
"Raise an error condition corresponding to ERROR, an sexp received by a
shepherd client in reply to COMMAND, a command object. Return #t if ERROR
does not denote an error."
(match error (match error
(('error ('version 0 x ...) 'service-not-found service) (('error ('version 0 x ...) 'service-not-found service)
(report-error (_ "service '~a' could not be found~%") (raise (condition (&service-not-found-error
service)) (service service)))))
(('error ('version 0 x ...) 'action-not-found action service) (('error ('version 0 x ...) 'action-not-found action service)
(report-error (_ "service '~a' does not have an action '~a'~%") (raise (condition (&action-not-found-error
service action)) (service service)
(action action)))))
(('error ('version 0 x ...) 'action-exception action service (('error ('version 0 x ...) 'action-exception action service
key (args ...)) key (args ...))
(report-error (_ "exception caught while executing '~a' \ (raise (condition (&action-exception-error
on service '~a':~%") (service service)
action service) (action action)
(print-exception (current-error-port) #f key args)) (key key) (args args)))))
(('error . _) (('error . _)
(report-error (_ "something went wrong: ~s~%") (raise (condition (&unknown-shepherd-error (sexp error)))))
error))
(#f ;not an error (#f ;not an error
#t))) #t)))
(define (display-message message) (define (display-message message)
;; TRANSLATORS: Nothing to translate here. (format (current-error-port) "shepherd: ~a~%" message))
(info (_ "shepherd: ~a~%") message))
(define* (invoke-action service action arguments cont) (define* (invoke-action service action arguments cont)
"Invoke ACTION on SERVICE with ARGUMENTS. On success, call CONT with the "Invoke ACTION on SERVICE with ARGUMENTS. On success, call CONT with the
@ -107,10 +145,10 @@ result. Otherwise return #f."
(('reply ('version 0 x ...) ('result y) ('error error) (('reply ('version 0 x ...) ('result y) ('error error)
('messages messages)) ('messages messages))
(for-each display-message messages) (for-each display-message messages)
(report-action-error error) (raise-shepherd-error error)
#f) #f)
(x (x
(warning (_ "invalid shepherd reply~%")) ;; invalid reply
#f)))) #f))))
(define-syntax-rule (with-shepherd-action service (action args ...) (define-syntax-rule (with-shepherd-action service (action args ...)
@ -129,7 +167,8 @@ of pairs."
(define (current-services) (define (current-services)
"Return two lists: the list of currently running services, and the list of "Return two lists: the list of currently running services, and the list of
currently stopped services." currently stopped services. Return #f and #f if the list of services could
not be obtained."
(with-shepherd-action 'root ('status) services (with-shepherd-action 'root ('status) services
(match services (match services
((('service ('version 0 _ ...) _ ...) ...) ((('service ('version 0 _ ...) _ ...) ...)
@ -144,7 +183,6 @@ currently stopped services."
'() '()
services)) services))
(x (x
(warning (_ "failed to obtain list of shepherd services~%"))
(values #f #f))))) (values #f #f)))))
(define (unload-service service) (define (unload-service service)

View File

@ -236,6 +236,72 @@ BODY..., and restore them."
(with-monad %store-monad (with-monad %store-monad
(return #f))))) (return #f)))))
(define-syntax-rule (with-shepherd-error-handling body ...)
(warn-on-system-error
(guard (c ((shepherd-error? c)
(report-shepherd-error c)))
body ...)))
(define (report-shepherd-error error)
"Report ERROR, a '&shepherd-error' error condition object."
(cond ((service-not-found-error? error)
(report-error (_ "service '~a' could not be found~%")
(service-not-found-error-service error)))
((action-not-found-error? error)
(report-error (_ "service '~a' does not have an action '~a'~%")
(action-not-found-error-service error)
(action-not-found-error-action error)))
((action-exception-error? error)
(report-error (_ "exception caught while executing '~a' \
on service '~a':~%")
(action-exception-error-action error)
(action-exception-error-service error))
(print-exception (current-error-port) #f
(action-exception-error-key error)
(action-exception-error-arguments error)))
((unknown-shepherd-error? error)
(report-error (_ "something went wrong: ~s~%")
(unknown-shepherd-error-sexp error)))
((shepherd-error? error)
(report-error (_ "shepherd error~%")))
((not error) ;not an error
#t)))
(define (call-with-service-upgrade-info new-services mproc)
"Call MPROC, a monadic procedure in %STORE-MONAD, passing it the list of
names of services to load (upgrade), and the list of names of services to
unload."
(define (essential? service)
(memq service '(root shepherd)))
(define new-service-names
(map (compose first shepherd-service-provision)
new-services))
(let-values (((running stopped) (current-services)))
(if (and running stopped)
(let* ((to-load
;; Only load services that are either new or currently stopped.
(remove (lambda (service)
(memq (first (shepherd-service-provision service))
running))
new-services))
(to-unload
;; Unload services that are (1) no longer required, or (2) are
;; in TO-LOAD.
(remove essential?
(append (remove (lambda (service)
(memq service new-service-names))
(append running stopped))
(filter (lambda (service)
(memq service stopped))
(map shepherd-service-canonical-name
to-load))))))
(mproc to-load to-unload))
(with-monad %store-monad
(warning (_ "failed to obtain list of shepherd services~%"))
(return #f)))))
(define (upgrade-shepherd-services os) (define (upgrade-shepherd-services os)
"Upgrade the Shepherd (PID 1) by unloading obsolete services and loading new "Upgrade the Shepherd (PID 1) by unloading obsolete services and loading new
services specified in OS and not currently running. services specified in OS and not currently running.
@ -243,59 +309,35 @@ services specified in OS and not currently running.
This is currently very conservative in that it does not stop or unload any This is currently very conservative in that it does not stop or unload any
running service. Unloading or stopping the wrong service ('udev', say) could running service. Unloading or stopping the wrong service ('udev', say) could
bring the system down." bring the system down."
(define (essential? service)
(memq service '(root shepherd)))
(define new-services (define new-services
(service-parameters (service-parameters
(fold-services (operating-system-services os) (fold-services (operating-system-services os)
#:target-type shepherd-root-service-type))) #:target-type shepherd-root-service-type)))
(define new-service-names ;; Arrange to simply emit a warning if the service upgrade fails.
(map (compose first shepherd-service-provision) (with-shepherd-error-handling
new-services)) (call-with-service-upgrade-info new-services
(lambda (to-load to-unload)
(for-each (lambda (unload)
(info (_ "unloading service '~a'...~%") unload)
(unload-service unload))
to-unload)
;; Arrange to simply emit a warning if we cannot connect to the shepherd. (with-monad %store-monad
(warn-on-system-error (munless (null? to-load)
(let-values (((running stopped) (current-services))) (let ((to-load-names (map shepherd-service-canonical-name to-load))
(define to-load (to-start (filter shepherd-service-auto-start? to-load)))
;; Only load services that are either new or currently stopped. (info (_ "loading new services:~{ ~a~}...~%") to-load-names)
(remove (lambda (service) (mlet %store-monad ((files (mapm %store-monad shepherd-service-file
(memq (first (shepherd-service-provision service)) to-load)))
running)) ;; Here we assume that FILES are exactly those that were computed
new-services)) ;; as part of the derivation that built OS, which is normally the
(define to-unload ;; case.
;; Unload services that are (1) no longer required, or (2) are in (load-services (map derivation->output-path files))
;; TO-LOAD.
(remove essential?
(append (remove (lambda (service)
(memq service new-service-names))
(append running stopped))
(filter (lambda (service)
(memq service stopped))
(map shepherd-service-canonical-name
to-load)))))
(for-each (lambda (unload) (for-each start-service
(info (_ "unloading service '~a'...~%") unload) (map shepherd-service-canonical-name to-start))
(unload-service unload)) (return #t)))))))))
to-unload)
(with-monad %store-monad
(munless (null? to-load)
(let ((to-load-names (map shepherd-service-canonical-name to-load))
(to-start (filter shepherd-service-auto-start? to-load)))
(info (_ "loading new services:~{ ~a~}...~%") to-load-names)
(mlet %store-monad ((files (mapm %store-monad shepherd-service-file
to-load)))
;; Here we assume that FILES are exactly those that were computed
;; as part of the derivation that built OS, which is normally the
;; case.
(load-services (map derivation->output-path files))
(for-each start-service
(map shepherd-service-canonical-name to-start))
(return #t))))))))
(define* (switch-to-system os (define* (switch-to-system os
#:optional (profile %system-profile)) #:optional (profile %system-profile))
@ -839,4 +881,8 @@ argument list and OPTS is the option alist."
(parameterize ((%graft? (assoc-ref opts 'graft?))) (parameterize ((%graft? (assoc-ref opts 'graft?)))
(process-command command args opts))))) (process-command command args opts)))))
;;; Local Variables:
;;; eval: (put 'call-with-service-upgrade-info 'scheme-indent-function 1)
;;; End:
;;; system.scm ends here ;;; system.scm ends here