herd.scm 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016, 2017 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
  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. (define-module (gnu services herd)
  20. #:use-module (srfi srfi-1)
  21. #:use-module (srfi srfi-9)
  22. #:use-module (srfi srfi-11)
  23. #:use-module (srfi srfi-34)
  24. #:use-module (srfi srfi-35)
  25. #:use-module (ice-9 match)
  26. #:export (%shepherd-socket-file
  27. shepherd-error?
  28. service-not-found-error?
  29. service-not-found-error-service
  30. action-not-found-error?
  31. action-not-found-error-service
  32. action-not-found-error-action
  33. action-exception-error?
  34. action-exception-error-service
  35. action-exception-error-action
  36. action-exception-error-key
  37. action-exception-error-arguments
  38. unknown-shepherd-error?
  39. unknown-shepherd-error-sexp
  40. live-service?
  41. live-service-provision
  42. live-service-requirement
  43. live-service-running
  44. current-services
  45. unload-services
  46. unload-service
  47. load-services
  48. start-service
  49. stop-service))
  50. ;;; Commentary:
  51. ;;;
  52. ;;; This module provides an interface to the GNU Shepherd, similar to the
  53. ;;; 'herd' command. Essentially it implements a subset of the (shepherd comm)
  54. ;;; module, but focusing only on the parts relevant to 'guix system
  55. ;;; reconfigure'.
  56. ;;;
  57. ;;; Code:
  58. (define %shepherd-socket-file
  59. (make-parameter "/var/run/shepherd/socket"))
  60. (define* (open-connection #:optional (file (%shepherd-socket-file)))
  61. "Open a connection to the daemon, using the Unix-domain socket at FILE, and
  62. return the socket."
  63. ;; The protocol is sexp-based and UTF-8-encoded.
  64. (with-fluids ((%default-port-encoding "UTF-8"))
  65. (let ((sock (socket PF_UNIX SOCK_STREAM 0))
  66. (address (make-socket-address PF_UNIX file)))
  67. (catch 'system-error
  68. (lambda ()
  69. (connect sock address)
  70. (setvbuf sock _IOFBF 1024)
  71. sock)
  72. (lambda args
  73. (close-port sock)
  74. (apply throw args))))))
  75. (define-syntax-rule (with-shepherd connection body ...)
  76. "Evaluate BODY... with CONNECTION bound to an open socket to PID 1."
  77. (let ((connection (open-connection)))
  78. body ...))
  79. (define-condition-type &shepherd-error &error
  80. shepherd-error?)
  81. (define-condition-type &service-not-found-error &shepherd-error
  82. service-not-found-error?
  83. (service service-not-found-error-service))
  84. (define-condition-type &action-not-found-error &shepherd-error
  85. action-not-found-error?
  86. (service action-not-found-error-service)
  87. (action action-not-found-error-action))
  88. (define-condition-type &action-exception-error &shepherd-error
  89. action-exception-error?
  90. (service action-exception-error-service)
  91. (action action-exception-error-action)
  92. (key action-exception-error-key)
  93. (args action-exception-error-arguments))
  94. (define-condition-type &unknown-shepherd-error &shepherd-error
  95. unknown-shepherd-error?
  96. (sexp unknown-shepherd-error-sexp))
  97. (define (raise-shepherd-error error)
  98. "Raise an error condition corresponding to ERROR, an sexp received by a
  99. shepherd client in reply to COMMAND, a command object. Return #t if ERROR
  100. does not denote an error."
  101. (match error
  102. (('error ('version 0 x ...) 'service-not-found service)
  103. (raise (condition (&service-not-found-error
  104. (service service)))))
  105. (('error ('version 0 x ...) 'action-not-found action service)
  106. (raise (condition (&action-not-found-error
  107. (service service)
  108. (action action)))))
  109. (('error ('version 0 x ...) 'action-exception action service
  110. key (args ...))
  111. (raise (condition (&action-exception-error
  112. (service service)
  113. (action action)
  114. (key key) (args args)))))
  115. (('error . _)
  116. (raise (condition (&unknown-shepherd-error (sexp error)))))
  117. (#f ;not an error
  118. #t)))
  119. (define (display-message message)
  120. (format (current-error-port) "shepherd: ~a~%" message))
  121. (define* (invoke-action service action arguments cont)
  122. "Invoke ACTION on SERVICE with ARGUMENTS. On success, call CONT with the
  123. list of results (one result per instance with the name SERVICE). Otherwise
  124. return #f."
  125. (with-shepherd sock
  126. (write `(shepherd-command (version 0)
  127. (action ,action)
  128. (service ,service)
  129. (arguments ,arguments)
  130. (directory ,(getcwd)))
  131. sock)
  132. (force-output sock)
  133. (match (read sock)
  134. (('reply ('version 0 _ ...) ('result result) ('error #f)
  135. ('messages messages))
  136. (for-each display-message messages)
  137. (cont result))
  138. (('reply ('version 0 x ...) ('result y) ('error error)
  139. ('messages messages))
  140. (for-each display-message messages)
  141. (raise-shepherd-error error)
  142. #f)
  143. (x
  144. ;; invalid reply
  145. #f))))
  146. (define-syntax-rule (with-shepherd-action service (action args ...)
  147. result body ...)
  148. (invoke-action service action (list args ...)
  149. (lambda (result) body ...)))
  150. (define-syntax alist-let*
  151. (syntax-rules ()
  152. "Bind the given KEYs in EXP to the corresponding items in ALIST. ALIST
  153. is assumed to be a list of two-element tuples rather than a traditional list
  154. of pairs."
  155. ((_ alist (key ...) exp ...)
  156. (let ((key (and=> (assoc-ref alist 'key) car)) ...)
  157. exp ...))))
  158. ;; Information about live Shepherd services.
  159. (define-record-type <live-service>
  160. (live-service provision requirement running)
  161. live-service?
  162. (provision live-service-provision) ;list of symbols
  163. (requirement live-service-requirement) ;list of symbols
  164. (running live-service-running)) ;#f | object
  165. (define (current-services)
  166. "Return the list of currently defined Shepherd services, represented as
  167. <live-service> objects. Return #f if the list of services could not be
  168. obtained."
  169. (with-shepherd-action 'root ('status) results
  170. ;; We get a list of results, one for each service with the name 'root'.
  171. ;; In practice there's only one such service though.
  172. (match results
  173. ((services _ ...)
  174. (match services
  175. ((('service ('version 0 _ ...) _ ...) ...)
  176. (map (lambda (service)
  177. (alist-let* service (provides requires running)
  178. (live-service provides requires running)))
  179. services))
  180. (x
  181. #f))))))
  182. (define (unload-service service)
  183. "Unload SERVICE, a symbol name; return #t on success."
  184. (with-shepherd-action 'root ('unload (symbol->string service)) result
  185. (first result)))
  186. (define (%load-file file)
  187. "Load FILE in the Shepherd."
  188. (with-shepherd-action 'root ('load file) result
  189. (first result)))
  190. (define (eval-there exp)
  191. "Eval EXP in the Shepherd."
  192. (with-shepherd-action 'root ('eval (object->string exp)) result
  193. (first result)))
  194. (define (load-services files)
  195. "Load and register the services from FILES, where FILES contain code that
  196. returns a shepherd <service> object."
  197. (eval-there `(register-services
  198. ,@(map (lambda (file)
  199. `(primitive-load ,file))
  200. files))))
  201. (define (start-service name)
  202. (with-shepherd-action name ('start) result
  203. result))
  204. (define (stop-service name)
  205. (with-shepherd-action name ('stop) result
  206. result))
  207. ;; Local Variables:
  208. ;; eval: (put 'alist-let* 'scheme-indent-function 2)
  209. ;; eval: (put 'with-shepherd 'scheme-indent-function 1)
  210. ;; eval: (put 'with-shepherd-action 'scheme-indent-function 3)
  211. ;; End:
  212. ;;; herd.scm ends here