gnu: r-fansi: Update to 0.4.1.
[jackhill/guix/guix.git] / gnu / services / herd.scm
CommitLineData
240b57f0 1;;; GNU Guix --- Functional package management for GNU
bb64b2e7 2;;; Copyright © 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
5e03b122 3;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
240b57f0
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (gnu services herd)
240b57f0 21 #:use-module (srfi srfi-1)
183605c8 22 #:use-module (srfi srfi-9)
240b57f0 23 #:use-module (srfi srfi-11)
8bf92e39
LC
24 #:use-module (srfi srfi-34)
25 #:use-module (srfi srfi-35)
240b57f0 26 #:use-module (ice-9 match)
5e03b122 27 #:export (%shepherd-socket-file
6aeae5b9 28 shepherd-message-port
5e03b122
MO
29
30 shepherd-error?
8bf92e39
LC
31 service-not-found-error?
32 service-not-found-error-service
33 action-not-found-error?
34 action-not-found-error-service
35 action-not-found-error-action
36 action-exception-error?
37 action-exception-error-service
38 action-exception-error-action
39 action-exception-error-key
40 action-exception-error-arguments
41 unknown-shepherd-error?
42 unknown-shepherd-error-sexp
43
5c793753 44 live-service
183605c8
LC
45 live-service?
46 live-service-provision
47 live-service-requirement
48 live-service-running
5c793753 49 live-service-canonical-name
183605c8 50
147c5aa5 51 with-shepherd-action
8bf92e39 52 current-services
240b57f0
LC
53 unload-services
54 unload-service
55 load-services
4245ddcb 56 load-services/safe
0642838b
CB
57 start-service
58 stop-service))
240b57f0
LC
59
60;;; Commentary:
61;;;
62;;; This module provides an interface to the GNU Shepherd, similar to the
63;;; 'herd' command. Essentially it implements a subset of the (shepherd comm)
64;;; module, but focusing only on the parts relevant to 'guix system
65;;; reconfigure'.
66;;;
67;;; Code:
68
69(define %shepherd-socket-file
5e03b122 70 (make-parameter "/var/run/shepherd/socket"))
240b57f0 71
5e03b122 72(define* (open-connection #:optional (file (%shepherd-socket-file)))
240b57f0
LC
73 "Open a connection to the daemon, using the Unix-domain socket at FILE, and
74return the socket."
75 ;; The protocol is sexp-based and UTF-8-encoded.
76 (with-fluids ((%default-port-encoding "UTF-8"))
77 (let ((sock (socket PF_UNIX SOCK_STREAM 0))
78 (address (make-socket-address PF_UNIX file)))
79 (catch 'system-error
80 (lambda ()
81 (connect sock address)
bb64b2e7 82 (setvbuf sock 'block 1024)
240b57f0 83 sock)
1d6b7d58
LC
84 (lambda args
85 (close-port sock)
86 (apply throw args))))))
240b57f0
LC
87
88(define-syntax-rule (with-shepherd connection body ...)
89 "Evaluate BODY... with CONNECTION bound to an open socket to PID 1."
90 (let ((connection (open-connection)))
89a26478
LC
91 (dynamic-wind
92 (const #t)
93 (lambda ()
94 body ...)
95 (lambda ()
96 (close-port connection)))))
240b57f0 97
8bf92e39
LC
98(define-condition-type &shepherd-error &error
99 shepherd-error?)
100
101(define-condition-type &service-not-found-error &shepherd-error
102 service-not-found-error?
103 (service service-not-found-error-service))
104
105(define-condition-type &action-not-found-error &shepherd-error
106 action-not-found-error?
107 (service action-not-found-error-service)
108 (action action-not-found-error-action))
109
110(define-condition-type &action-exception-error &shepherd-error
111 action-exception-error?
112 (service action-exception-error-service)
113 (action action-exception-error-action)
114 (key action-exception-error-key)
115 (args action-exception-error-arguments))
116
117(define-condition-type &unknown-shepherd-error &shepherd-error
118 unknown-shepherd-error?
119 (sexp unknown-shepherd-error-sexp))
120
121(define (raise-shepherd-error error)
122 "Raise an error condition corresponding to ERROR, an sexp received by a
123shepherd client in reply to COMMAND, a command object. Return #t if ERROR
124does not denote an error."
240b57f0
LC
125 (match error
126 (('error ('version 0 x ...) 'service-not-found service)
8bf92e39
LC
127 (raise (condition (&service-not-found-error
128 (service service)))))
240b57f0 129 (('error ('version 0 x ...) 'action-not-found action service)
8bf92e39
LC
130 (raise (condition (&action-not-found-error
131 (service service)
132 (action action)))))
240b57f0
LC
133 (('error ('version 0 x ...) 'action-exception action service
134 key (args ...))
8bf92e39
LC
135 (raise (condition (&action-exception-error
136 (service service)
137 (action action)
138 (key key) (args args)))))
240b57f0 139 (('error . _)
8bf92e39 140 (raise (condition (&unknown-shepherd-error (sexp error)))))
240b57f0
LC
141 (#f ;not an error
142 #t)))
143
6aeae5b9
LC
144(define shepherd-message-port
145 ;; Port where messages coming from shepherd are printed.
146 (make-parameter (current-error-port)))
147
240b57f0 148(define (display-message message)
6aeae5b9 149 (format (shepherd-message-port) "shepherd: ~a~%" message))
240b57f0
LC
150
151(define* (invoke-action service action arguments cont)
152 "Invoke ACTION on SERVICE with ARGUMENTS. On success, call CONT with the
7d14082d
LC
153list of results (one result per instance with the name SERVICE). Otherwise
154return #f."
240b57f0
LC
155 (with-shepherd sock
156 (write `(shepherd-command (version 0)
157 (action ,action)
158 (service ,service)
159 (arguments ,arguments)
160 (directory ,(getcwd)))
161 sock)
162 (force-output sock)
163
164 (match (read sock)
dc7b3e56 165 (('reply ('version 0 _ ...) ('result result) ('error #f)
240b57f0
LC
166 ('messages messages))
167 (for-each display-message messages)
168 (cont result))
169 (('reply ('version 0 x ...) ('result y) ('error error)
170 ('messages messages))
171 (for-each display-message messages)
8bf92e39 172 (raise-shepherd-error error)
240b57f0
LC
173 #f)
174 (x
8bf92e39 175 ;; invalid reply
240b57f0
LC
176 #f))))
177
178(define-syntax-rule (with-shepherd-action service (action args ...)
179 result body ...)
147c5aa5
LC
180 "Invoke ACTION on SERVICE with the given ARGS, and evaluate BODY with RESULT
181bound to the action's result."
240b57f0
LC
182 (invoke-action service action (list args ...)
183 (lambda (result) body ...)))
184
185(define-syntax alist-let*
186 (syntax-rules ()
187 "Bind the given KEYs in EXP to the corresponding items in ALIST. ALIST
188is assumed to be a list of two-element tuples rather than a traditional list
189of pairs."
190 ((_ alist (key ...) exp ...)
191 (let ((key (and=> (assoc-ref alist 'key) car)) ...)
192 exp ...))))
193
183605c8
LC
194;; Information about live Shepherd services.
195(define-record-type <live-service>
196 (live-service provision requirement running)
197 live-service?
198 (provision live-service-provision) ;list of symbols
199 (requirement live-service-requirement) ;list of symbols
200 (running live-service-running)) ;#f | object
201
5c793753
JK
202(define (live-service-canonical-name service)
203 "Return the 'canonical name' of SERVICE."
204 (first (live-service-provision service)))
205
240b57f0 206(define (current-services)
183605c8
LC
207 "Return the list of currently defined Shepherd services, represented as
208<live-service> objects. Return #f if the list of services could not be
209obtained."
7d14082d
LC
210 (with-shepherd-action 'root ('status) results
211 ;; We get a list of results, one for each service with the name 'root'.
212 ;; In practice there's only one such service though.
213 (match results
214 ((services _ ...)
215 (match services
216 ((('service ('version 0 _ ...) _ ...) ...)
217 (map (lambda (service)
218 (alist-let* service (provides requires running)
219 (live-service provides requires running)))
220 services))
221 (x
222 #f))))))
240b57f0
LC
223
224(define (unload-service service)
225 "Unload SERVICE, a symbol name; return #t on success."
226 (with-shepherd-action 'root ('unload (symbol->string service)) result
7d14082d 227 (first result)))
240b57f0
LC
228
229(define (%load-file file)
230 "Load FILE in the Shepherd."
231 (with-shepherd-action 'root ('load file) result
7d14082d 232 (first result)))
240b57f0
LC
233
234(define (eval-there exp)
235 "Eval EXP in the Shepherd."
236 (with-shepherd-action 'root ('eval (object->string exp)) result
7d14082d 237 (first result)))
240b57f0
LC
238
239(define (load-services files)
240 "Load and register the services from FILES, where FILES contain code that
241returns a shepherd <service> object."
242 (eval-there `(register-services
243 ,@(map (lambda (file)
244 `(primitive-load ,file))
245 files))))
246
4245ddcb
CZ
247(define (load-services/safe files)
248 "This is like 'load-services', but make sure only the subset of FILES that
249can be safely reloaded is actually reloaded.
250
251This is done to accommodate the Shepherd < 0.15.0 where services lacked the
252'replacement' slot, and where 'register-services' would throw an exception
253when passed a service with an already-registered name."
254 (eval-there `(let* ((services (map primitive-load ',files))
255 (slots (map slot-definition-name
256 (class-slots <service>)))
257 (can-replace? (memq 'replacement slots)))
258 (define (registered? service)
259 (not (null? (lookup-services (canonical-name service)))))
260
261 (apply register-services
262 (if can-replace?
263 services
264 (remove registered? services))))))
265
ca0c43ec
MO
266(define* (start-service name #:optional (arguments '()))
267 (invoke-action name 'start arguments
268 (lambda (result)
269 result)))
240b57f0 270
0642838b
CB
271(define (stop-service name)
272 (with-shepherd-action name ('stop) result
273 result))
274
240b57f0
LC
275;; Local Variables:
276;; eval: (put 'alist-let* 'scheme-indent-function 2)
277;; eval: (put 'with-shepherd 'scheme-indent-function 1)
278;; eval: (put 'with-shepherd-action 'scheme-indent-function 3)
279;; End:
280
281;;; herd.scm ends here