summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2023-06-06 11:41:39 +0200
committerLudovic Courtès <ludo@gnu.org>2023-06-06 11:54:39 +0200
commit181951207339508789b28ba7cb914f983319920f (patch)
treea9747a37eb4fa7cf7dadb481df64b7856644ca0e
parentdc0c5d56ee04d8a2b57f316be7f95b9aca244ab5 (diff)
services: 'modify-services' preserves service ordering.
Fixes <https://issues.guix.gnu.org/63921>. The regression was introduced in dbbc7e946131ba257728f1d05b96c4339b7ee88b, which changed the order of services. As a result, someone using 'modify-services' could find themselves with incorrect ordering of expressions in the "boot" script, whereby the cleanup expressions would come after (execl ".../shepherd"). This, in turn, would lead shepherd to error out at boot with EADDRINUSE on /var/run/shepherd/socket. * gnu/services.scm (%delete-service, %apply-clauses): Remove. (clause-alist): New macro. (apply-clauses): New procedure. (modify-services): Use it. Adjust docstring. * tests/services.scm ("modify-services: do nothing"): Remove 'sort' call. ("modify-services: delete service"): Likewise, and add 't4' service. ("modify-services: change value"): Remove 'sort' call and fix expected value.
-rw-r--r--gnu/services.scm93
-rw-r--r--tests/services.scm37
2 files changed, 80 insertions, 50 deletions
diff --git a/gnu/services.scm b/gnu/services.scm
index a990d297c9c..5410d319715 100644
--- a/gnu/services.scm
+++ b/gnu/services.scm
@@ -51,6 +51,7 @@
51 #:use-module (srfi srfi-26) 51 #:use-module (srfi srfi-26)
52 #:use-module (srfi srfi-34) 52 #:use-module (srfi srfi-34)
53 #:use-module (srfi srfi-35) 53 #:use-module (srfi srfi-35)
54 #:use-module (srfi srfi-71)
54 #:use-module (ice-9 vlist) 55 #:use-module (ice-9 vlist)
55 #:use-module (ice-9 match) 56 #:use-module (ice-9 match)
56 #:autoload (ice-9 pretty-print) (pretty-print) 57 #:autoload (ice-9 pretty-print) (pretty-print)
@@ -297,35 +298,65 @@ singleton service type NAME, of which the returned service is an instance."
297 (description "This is a simple service.")))) 298 (description "This is a simple service."))))
298 (service type value))) 299 (service type value)))
299 300
300(define (%delete-service kind services) 301(define-syntax clause-alist
301 (let loop ((found #f) 302 (syntax-rules (=> delete)
302 (return '()) 303 "Build an alist of clauses. Each element has the form (KIND PROC LOC)
303 (services services)) 304where PROC is the service transformation procedure to apply for KIND, and LOC
305is the source location information."
306 ((_ (delete kind) rest ...)
307 (cons (list kind
308 (lambda (service)
309 #f)
310 (current-source-location))
311 (clause-alist rest ...)))
312 ((_ (kind param => exp ...) rest ...)
313 (cons (list kind
314 (lambda (svc)
315 (let ((param (service-value svc)))
316 (service (service-kind svc)
317 (begin exp ...))))
318 (current-source-location))
319 (clause-alist rest ...)))
320 ((_)
321 '())))
322
323(define (apply-clauses clauses services)
324 "Apply CLAUSES, an alist as returned by 'clause-alist', to SERVICES, a list
325of services. Use each clause at most once; raise an error if a clause was not
326used."
327 (let loop ((services services)
328 (clauses clauses)
329 (result '()))
304 (match services 330 (match services
305 ('() 331 (()
306 (if found 332 (match clauses
307 (values return found) 333 (() ;all clauses fired, good
308 (raise (formatted-message 334 (reverse result))
335 (((kind _ properties) _ ...) ;one or more clauses didn't match
336 (raise (make-compound-condition
337 (condition
338 (&error-location
339 (location (source-properties->location properties))))
340 (formatted-message
309 (G_ "modify-services: service '~a' not found in service list") 341 (G_ "modify-services: service '~a' not found in service list")
310 (service-type-name kind))))) 342 (service-type-name kind)))))))
311 ((service . rest) 343 ((head . tail)
312 (if (eq? (service-kind service) kind) 344 (let ((service clauses
313 (loop service return rest) 345 (fold2 (lambda (clause service remainder)
314 (loop found (cons service return) rest)))))) 346 (match clause
315 347 ((kind proc properties)
316(define-syntax %apply-clauses 348 (if (eq? kind (service-kind service))
317 (syntax-rules (=> delete) 349 (values (proc service) remainder)
318 ((_ ((delete kind) . rest) services) 350 (values service
319 (%apply-clauses rest (%delete-service kind services))) 351 (cons clause remainder))))))
320 ((_ ((kind param => exp ...) . rest) services) 352 head
321 (call-with-values (lambda () (%delete-service kind services)) 353 '()
322 (lambda (svcs found) 354 clauses)))
323 (let ((param (service-value found))) 355 (loop tail
324 (cons (service (service-kind found) 356 (reverse clauses)
325 (begin exp ...)) 357 (if service
326 (%apply-clauses rest svcs)))))) 358 (cons service result)
327 ((_ () services) 359 result)))))))
328 services)))
329 360
330(define-syntax modify-services 361(define-syntax modify-services
331 (syntax-rules () 362 (syntax-rules ()
@@ -358,11 +389,9 @@ Consider this example:
358 389
359It changes the configuration of the GUIX-SERVICE-TYPE instance, and that of 390It changes the configuration of the GUIX-SERVICE-TYPE instance, and that of
360all the MINGETTY-SERVICE-TYPE instances, and it deletes instances of the 391all the MINGETTY-SERVICE-TYPE instances, and it deletes instances of the
361UDEV-SERVICE-TYPE. 392UDEV-SERVICE-TYPE."
362 393 ((_ services clauses ...)
363This is a shorthand for (filter-map (lambda (svc) ...) %base-services)." 394 (apply-clauses (clause-alist clauses ...) services))))
364 ((_ services . clauses)
365 (%apply-clauses clauses services))))
366 395
367 396
368;;; 397;;;
diff --git a/tests/services.scm b/tests/services.scm
index 8cdb1b2a314..20ff4d317e8 100644
--- a/tests/services.scm
+++ b/tests/services.scm
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015-2019, 2022 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2015-2019, 2022, 2023 Ludovic Courtès <ludo@gnu.org>
3;;; 3;;;
4;;; This file is part of GNU Guix. 4;;; This file is part of GNU Guix.
5;;; 5;;;
@@ -287,7 +287,7 @@
287 (x x)))) 287 (x x))))
288 288
289(test-equal "modify-services: do nothing" 289(test-equal "modify-services: do nothing"
290 '(1 2 3) 290 '(1 2 3) ;note: service order must be preserved
291 (let* ((t1 (service-type (name 't1) 291 (let* ((t1 (service-type (name 't1)
292 (extensions '()) 292 (extensions '())
293 (description ""))) 293 (description "")))
@@ -298,12 +298,11 @@
298 (extensions '()) 298 (extensions '())
299 (description ""))) 299 (description "")))
300 (services (list (service t1 1) (service t2 2) (service t3 3)))) 300 (services (list (service t1 1) (service t2 2) (service t3 3))))
301 (sort (map service-value 301 (map service-value
302 (modify-services services)) 302 (modify-services services))))
303 <)))
304 303
305(test-equal "modify-services: delete service" 304(test-equal "modify-services: delete service"
306 '(1) 305 '(1 4) ;note: service order must be preserved
307 (let* ((t1 (service-type (name 't1) 306 (let* ((t1 (service-type (name 't1)
308 (extensions '()) 307 (extensions '())
309 (description ""))) 308 (description "")))
@@ -313,12 +312,15 @@
313 (t3 (service-type (name 't3) 312 (t3 (service-type (name 't3)
314 (extensions '()) 313 (extensions '())
315 (description ""))) 314 (description "")))
316 (services (list (service t1 1) (service t2 2) (service t3 3)))) 315 (t4 (service-type (name 't4)
317 (sort (map service-value 316 (extensions '())
318 (modify-services services 317 (description "")))
319 (delete t3) 318 (services (list (service t1 1) (service t2 2)
320 (delete t2))) 319 (service t3 3) (service t4 4))))
321 <))) 320 (map service-value
321 (modify-services services
322 (delete t3)
323 (delete t2)))))
322 324
323(test-error "modify-services: delete non-existing service" 325(test-error "modify-services: delete non-existing service"
324 #t 326 #t
@@ -336,7 +338,7 @@
336 (delete t3)))) 338 (delete t3))))
337 339
338(test-equal "modify-services: change value" 340(test-equal "modify-services: change value"
339 '(2 11 33) 341 '(11 2 33) ;note: service order must be preserved
340 (let* ((t1 (service-type (name 't1) 342 (let* ((t1 (service-type (name 't1)
341 (extensions '()) 343 (extensions '())
342 (description ""))) 344 (description "")))
@@ -347,11 +349,10 @@
347 (extensions '()) 349 (extensions '())
348 (description ""))) 350 (description "")))
349 (services (list (service t1 1) (service t2 2) (service t3 3)))) 351 (services (list (service t1 1) (service t2 2) (service t3 3))))
350 (sort (map service-value 352 (map service-value
351 (modify-services services 353 (modify-services services
352 (t1 value => 11) 354 (t1 value => 11)
353 (t3 value => 33))) 355 (t3 value => 33)))))
354 <)))
355 356
356(test-error "modify-services: change value for non-existing service" 357(test-error "modify-services: change value for non-existing service"
357 #t 358 #t