summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2016-05-06 13:12:45 +0200
committerLudovic Courtès <ludo@gnu.org>2016-05-06 13:25:30 +0200
commit4e0ea3eb288c2143b44bf324c64047762c72d3b3 (patch)
treea261da4f5d972b0a90827347a3a987534ab80ac7
parentba2613bb4e47938044a3c96b92debf1bddcf0140 (diff)
utils: Move 'fcntl-flock' to (guix build syscalls).
* guix/utils.scm (%struct-flock, F_SETLKW, F_SETLK, F_xxLCK) (fcntl-flock): Move to... * guix/build/syscalls.scm: ... here. New variables. * guix/nar.scm: Adjust imports accordingly. * tests/utils.scm ("fcntl-flock wait", "fcntl-flock non-blocking"): Move to... * tests/syscalls.scm: ... here. New tests. (temp-file): New variable.
-rw-r--r--guix/build/syscalls.scm69
-rw-r--r--guix/nar.scm4
-rw-r--r--guix/utils.scm75
-rw-r--r--tests/syscalls.scm88
-rw-r--r--tests/utils.scm82
5 files changed, 160 insertions, 158 deletions
diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm
index a9cd6e93c89..86723c23c7c 100644
--- a/guix/build/syscalls.scm
+++ b/guix/build/syscalls.scm
@@ -65,6 +65,7 @@
65 processes 65 processes
66 mkdtemp! 66 mkdtemp!
67 pivot-root 67 pivot-root
68 fcntl-flock
68 69
69 CLONE_CHILD_CLEARTID 70 CLONE_CHILD_CLEARTID
70 CLONE_CHILD_SETTID 71 CLONE_CHILD_SETTID
@@ -639,6 +640,74 @@ system to PUT-OLD."
639 640
640 641
641;;; 642;;;
643;;; Advisory file locking.
644;;;
645
646(define %struct-flock
647 ;; 'struct flock' from <fcntl.h>.
648 (list short ; l_type
649 short ; l_whence
650 size_t ; l_start
651 size_t ; l_len
652 int)) ; l_pid
653
654(define F_SETLKW
655 ;; On Linux-based systems, this is usually 7, but not always
656 ;; (exceptions include SPARC.) On GNU/Hurd, it's 9.
657 (cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu
658 ((string-contains %host-type "linux") 7) ; *-linux-gnu
659 (else 9))) ; *-gnu*
660
661(define F_SETLK
662 ;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6.
663 (cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu
664 ((string-contains %host-type "linux") 6) ; *-linux-gnu
665 (else 8))) ; *-gnu*
666
667(define F_xxLCK
668 ;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
669 (cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu
670 ((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu
671 ((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu
672 (else #(1 2 3)))) ; *-gnu*
673
674(define fcntl-flock
675 (let ((proc (syscall->procedure int "fcntl" `(,int ,int *))))
676 (lambda* (fd-or-port operation #:key (wait? #t))
677 "Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
678must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is
679true, block until the lock is acquired; otherwise, thrown an 'flock-error'
680exception if it's already taken."
681 (define (operation->int op)
682 (case op
683 ((read-lock) (vector-ref F_xxLCK 0))
684 ((write-lock) (vector-ref F_xxLCK 1))
685 ((unlock) (vector-ref F_xxLCK 2))
686 (else (error "invalid fcntl-flock operation" op))))
687
688 (define fd
689 (if (port? fd-or-port)
690 (fileno fd-or-port)
691 fd-or-port))
692
693 ;; XXX: 'fcntl' is a vararg function, but here we happily use the
694 ;; standard ABI; crossing fingers.
695 (let ((err (proc fd
696 (if wait?
697 F_SETLKW ; lock & wait
698 F_SETLK) ; non-blocking attempt
699 (make-c-struct %struct-flock
700 (list (operation->int operation)
701 SEEK_SET
702 0 0 ; whole file
703 0)))))
704 (or (zero? err)
705
706 ;; Presumably we got EAGAIN or so.
707 (throw 'flock-error (errno)))))))
708
709
710;;;
642;;; Network interfaces. 711;;; Network interfaces.
643;;; 712;;;
644 713
diff --git a/guix/nar.scm b/guix/nar.scm
index 43e52107521..739d3d3a57c 100644
--- a/guix/nar.scm
+++ b/guix/nar.scm
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
3;;; Copyright © 2014 Mark H Weaver <mhw@netris.org> 3;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
4;;; 4;;;
5;;; This file is part of GNU Guix. 5;;; This file is part of GNU Guix.
@@ -18,8 +18,8 @@
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. 18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19 19
20(define-module (guix nar) 20(define-module (guix nar)
21 #:use-module (guix utils)
22 #:use-module (guix serialization) 21 #:use-module (guix serialization)
22 #:use-module (guix build syscalls)
23 #:use-module ((guix build utils) 23 #:use-module ((guix build utils)
24 #:select (delete-file-recursively with-directory-excursion)) 24 #:select (delete-file-recursively with-directory-excursion))
25 #:use-module (guix store) 25 #:use-module (guix store)
diff --git a/guix/utils.scm b/guix/utils.scm
index f18bbd19acf..d924e434bd1 100644
--- a/guix/utils.scm
+++ b/guix/utils.scm
@@ -34,7 +34,7 @@
34 #:use-module ((rnrs bytevectors) #:select (bytevector-u8-set!)) 34 #:use-module ((rnrs bytevectors) #:select (bytevector-u8-set!))
35 #:use-module (guix combinators) 35 #:use-module (guix combinators)
36 #:use-module ((guix build utils) #:select (dump-port)) 36 #:use-module ((guix build utils) #:select (dump-port))
37 #:use-module ((guix build syscalls) #:select (errno mkdtemp!)) 37 #:use-module ((guix build syscalls) #:select (mkdtemp!))
38 #:use-module (ice-9 vlist) 38 #:use-module (ice-9 vlist)
39 #:use-module (ice-9 format) 39 #:use-module (ice-9 format)
40 #:autoload (ice-9 popen) (open-pipe*) 40 #:autoload (ice-9 popen) (open-pipe*)
@@ -47,7 +47,6 @@
47 #:export (bytevector->base16-string 47 #:export (bytevector->base16-string
48 base16-string->bytevector 48 base16-string->bytevector
49 49
50 fcntl-flock
51 strip-keyword-arguments 50 strip-keyword-arguments
52 default-keyword-arguments 51 default-keyword-arguments
53 substitute-keyword-arguments 52 substitute-keyword-arguments
@@ -340,78 +339,6 @@ This procedure returns #t on success."
340 339
341 340
342;;; 341;;;
343;;; Advisory file locking.
344;;;
345
346(define %struct-flock
347 ;; 'struct flock' from <fcntl.h>.
348 (list short ; l_type
349 short ; l_whence
350 size_t ; l_start
351 size_t ; l_len
352 int)) ; l_pid
353
354(define F_SETLKW
355 ;; On Linux-based systems, this is usually 7, but not always
356 ;; (exceptions include SPARC.) On GNU/Hurd, it's 9.
357 (compile-time-value
358 (cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu
359 ((string-contains %host-type "linux") 7) ; *-linux-gnu
360 (else 9)))) ; *-gnu*
361
362(define F_SETLK
363 ;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6.
364 (compile-time-value
365 (cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu
366 ((string-contains %host-type "linux") 6) ; *-linux-gnu
367 (else 8)))) ; *-gnu*
368
369(define F_xxLCK
370 ;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
371 (compile-time-value
372 (cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu
373 ((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu
374 ((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu
375 (else #(1 2 3))))) ; *-gnu*
376
377(define fcntl-flock
378 (let* ((ptr (dynamic-func "fcntl" (dynamic-link)))
379 (proc (pointer->procedure int ptr `(,int ,int *))))
380 (lambda* (fd-or-port operation #:key (wait? #t))
381 "Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
382must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is
383true, block until the lock is acquired; otherwise, thrown an 'flock-error'
384exception if it's already taken."
385 (define (operation->int op)
386 (case op
387 ((read-lock) (vector-ref F_xxLCK 0))
388 ((write-lock) (vector-ref F_xxLCK 1))
389 ((unlock) (vector-ref F_xxLCK 2))
390 (else (error "invalid fcntl-flock operation" op))))
391
392 (define fd
393 (if (port? fd-or-port)
394 (fileno fd-or-port)
395 fd-or-port))
396
397 ;; XXX: 'fcntl' is a vararg function, but here we happily use the
398 ;; standard ABI; crossing fingers.
399 (let ((err (proc fd
400 (if wait?
401 F_SETLKW ; lock & wait
402 F_SETLK) ; non-blocking attempt
403 (make-c-struct %struct-flock
404 (list (operation->int operation)
405 SEEK_SET
406 0 0 ; whole file
407 0)))))
408 (or (zero? err)
409
410 ;; Presumably we got EAGAIN or so.
411 (throw 'flock-error (errno)))))))
412
413
414;;;
415;;; Keyword arguments. 342;;; Keyword arguments.
416;;; 343;;;
417 344
diff --git a/tests/syscalls.scm b/tests/syscalls.scm
index 0b73fb4b0cd..73fa8a7acfa 100644
--- a/tests/syscalls.scm
+++ b/tests/syscalls.scm
@@ -29,6 +29,10 @@
29;; Test the (guix build syscalls) module, although there's not much that can 29;; Test the (guix build syscalls) module, although there's not much that can
30;; actually be tested without being root. 30;; actually be tested without being root.
31 31
32(define temp-file
33 (string-append "t-utils-" (number->string (getpid))))
34
35
32(test-begin "syscalls") 36(test-begin "syscalls")
33 37
34(test-equal "mount, ENOENT" 38(test-equal "mount, ENOENT"
@@ -172,6 +176,88 @@
172 (status:exit-val status)))) 176 (status:exit-val status))))
173 (eq? #t result)))))))) 177 (eq? #t result))))))))
174 178
179(false-if-exception (delete-file temp-file))
180(test-equal "fcntl-flock wait"
181 42 ; the child's exit status
182 (let ((file (open-file temp-file "w0b")))
183 ;; Acquire an exclusive lock.
184 (fcntl-flock file 'write-lock)
185 (match (primitive-fork)
186 (0
187 (dynamic-wind
188 (const #t)
189 (lambda ()
190 ;; Reopen FILE read-only so we can have a read lock.
191 (let ((file (open-file temp-file "r0b")))
192 ;; Wait until we can acquire the lock.
193 (fcntl-flock file 'read-lock)
194 (primitive-exit (read file)))
195 (primitive-exit 1))
196 (lambda ()
197 (primitive-exit 2))))
198 (pid
199 ;; Write garbage and wait.
200 (display "hello, world!" file)
201 (force-output file)
202 (sleep 1)
203
204 ;; Write the real answer.
205 (seek file 0 SEEK_SET)
206 (truncate-file file 0)
207 (write 42 file)
208 (force-output file)
209
210 ;; Unlock, which should let the child continue.
211 (fcntl-flock file 'unlock)
212
213 (match (waitpid pid)
214 ((_ . status)
215 (let ((result (status:exit-val status)))
216 (close-port file)
217 result)))))))
218
219(test-equal "fcntl-flock non-blocking"
220 EAGAIN ; the child's exit status
221 (match (pipe)
222 ((input . output)
223 (match (primitive-fork)
224 (0
225 (dynamic-wind
226 (const #t)
227 (lambda ()
228 (close-port output)
229
230 ;; Wait for the green light.
231 (read-char input)
232
233 ;; Open FILE read-only so we can have a read lock.
234 (let ((file (open-file temp-file "w0")))
235 (catch 'flock-error
236 (lambda ()
237 ;; This attempt should throw EAGAIN.
238 (fcntl-flock file 'write-lock #:wait? #f))
239 (lambda (key errno)
240 (primitive-exit (pk 'errno errno)))))
241 (primitive-exit -1))
242 (lambda ()
243 (primitive-exit -2))))
244 (pid
245 (close-port input)
246 (let ((file (open-file temp-file "w0")))
247 ;; Acquire an exclusive lock.
248 (fcntl-flock file 'write-lock)
249
250 ;; Tell the child to continue.
251 (write 'green-light output)
252 (force-output output)
253
254 (match (waitpid pid)
255 ((_ . status)
256 (let ((result (status:exit-val status)))
257 (fcntl-flock file 'unlock)
258 (close-port file)
259 result)))))))))
260
175(test-assert "all-network-interface-names" 261(test-assert "all-network-interface-names"
176 (match (all-network-interface-names) 262 (match (all-network-interface-names)
177 (((? string? names) ..1) 263 (((? string? names) ..1)
@@ -303,3 +389,5 @@
303 0)) 389 0))
304 390
305(test-end) 391(test-end)
392
393(false-if-exception (delete-file temp-file))
diff --git a/tests/utils.scm b/tests/utils.scm
index a54482e94ce..6590ed91cf8 100644
--- a/tests/utils.scm
+++ b/tests/utils.scm
@@ -168,88 +168,6 @@
168 (call-with-decompressed-port 'xz (open-file temp-file "r0b") 168 (call-with-decompressed-port 'xz (open-file temp-file "r0b")
169 get-bytevector-all)))) 169 get-bytevector-all))))
170 170
171(false-if-exception (delete-file temp-file))
172(test-equal "fcntl-flock wait"
173 42 ; the child's exit status
174 (let ((file (open-file temp-file "w0b")))
175 ;; Acquire an exclusive lock.
176 (fcntl-flock file 'write-lock)
177 (match (primitive-fork)
178 (0
179 (dynamic-wind
180 (const #t)
181 (lambda ()
182 ;; Reopen FILE read-only so we can have a read lock.
183 (let ((file (open-file temp-file "r0b")))
184 ;; Wait until we can acquire the lock.
185 (fcntl-flock file 'read-lock)
186 (primitive-exit (read file)))
187 (primitive-exit 1))
188 (lambda ()
189 (primitive-exit 2))))
190 (pid
191 ;; Write garbage and wait.
192 (display "hello, world!" file)
193 (force-output file)
194 (sleep 1)
195
196 ;; Write the real answer.
197 (seek file 0 SEEK_SET)
198 (truncate-file file 0)
199 (write 42 file)
200 (force-output file)
201
202 ;; Unlock, which should let the child continue.
203 (fcntl-flock file 'unlock)
204
205 (match (waitpid pid)
206 ((_ . status)
207 (let ((result (status:exit-val status)))
208 (close-port file)
209 result)))))))
210
211(test-equal "fcntl-flock non-blocking"
212 EAGAIN ; the child's exit status
213 (match (pipe)
214 ((input . output)
215 (match (primitive-fork)
216 (0
217 (dynamic-wind
218 (const #t)
219 (lambda ()
220 (close-port output)
221
222 ;; Wait for the green light.
223 (read-char input)
224
225 ;; Open FILE read-only so we can have a read lock.
226 (let ((file (open-file temp-file "w0")))
227 (catch 'flock-error
228 (lambda ()
229 ;; This attempt should throw EAGAIN.
230 (fcntl-flock file 'write-lock #:wait? #f))
231 (lambda (key errno)
232 (primitive-exit (pk 'errno errno)))))
233 (primitive-exit -1))
234 (lambda ()
235 (primitive-exit -2))))
236 (pid
237 (close-port input)
238 (let ((file (open-file temp-file "w0")))
239 ;; Acquire an exclusive lock.
240 (fcntl-flock file 'write-lock)
241
242 ;; Tell the child to continue.
243 (write 'green-light output)
244 (force-output output)
245
246 (match (waitpid pid)
247 ((_ . status)
248 (let ((result (status:exit-val status)))
249 (fcntl-flock file 'unlock)
250 (close-port file)
251 result)))))))))
252
253;; This is actually in (guix store). 171;; This is actually in (guix store).
254(test-equal "store-path-package-name" 172(test-equal "store-path-package-name"
255 "bash-4.2-p24" 173 "bash-4.2-p24"