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.
This commit is contained in:
parent
ba2613bb4e
commit
4e0ea3eb28
|
@ -65,6 +65,7 @@
|
||||||
processes
|
processes
|
||||||
mkdtemp!
|
mkdtemp!
|
||||||
pivot-root
|
pivot-root
|
||||||
|
fcntl-flock
|
||||||
|
|
||||||
CLONE_CHILD_CLEARTID
|
CLONE_CHILD_CLEARTID
|
||||||
CLONE_CHILD_SETTID
|
CLONE_CHILD_SETTID
|
||||||
|
@ -637,6 +638,74 @@ system to PUT-OLD."
|
||||||
(list new-root put-old (strerror err))
|
(list new-root put-old (strerror err))
|
||||||
(list err)))))))
|
(list err)))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Advisory file locking.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(define %struct-flock
|
||||||
|
;; 'struct flock' from <fcntl.h>.
|
||||||
|
(list short ; l_type
|
||||||
|
short ; l_whence
|
||||||
|
size_t ; l_start
|
||||||
|
size_t ; l_len
|
||||||
|
int)) ; l_pid
|
||||||
|
|
||||||
|
(define F_SETLKW
|
||||||
|
;; On Linux-based systems, this is usually 7, but not always
|
||||||
|
;; (exceptions include SPARC.) On GNU/Hurd, it's 9.
|
||||||
|
(cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu
|
||||||
|
((string-contains %host-type "linux") 7) ; *-linux-gnu
|
||||||
|
(else 9))) ; *-gnu*
|
||||||
|
|
||||||
|
(define F_SETLK
|
||||||
|
;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6.
|
||||||
|
(cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu
|
||||||
|
((string-contains %host-type "linux") 6) ; *-linux-gnu
|
||||||
|
(else 8))) ; *-gnu*
|
||||||
|
|
||||||
|
(define F_xxLCK
|
||||||
|
;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
|
||||||
|
(cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu
|
||||||
|
((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu
|
||||||
|
((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu
|
||||||
|
(else #(1 2 3)))) ; *-gnu*
|
||||||
|
|
||||||
|
(define fcntl-flock
|
||||||
|
(let ((proc (syscall->procedure int "fcntl" `(,int ,int *))))
|
||||||
|
(lambda* (fd-or-port operation #:key (wait? #t))
|
||||||
|
"Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
|
||||||
|
must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is
|
||||||
|
true, block until the lock is acquired; otherwise, thrown an 'flock-error'
|
||||||
|
exception if it's already taken."
|
||||||
|
(define (operation->int op)
|
||||||
|
(case op
|
||||||
|
((read-lock) (vector-ref F_xxLCK 0))
|
||||||
|
((write-lock) (vector-ref F_xxLCK 1))
|
||||||
|
((unlock) (vector-ref F_xxLCK 2))
|
||||||
|
(else (error "invalid fcntl-flock operation" op))))
|
||||||
|
|
||||||
|
(define fd
|
||||||
|
(if (port? fd-or-port)
|
||||||
|
(fileno fd-or-port)
|
||||||
|
fd-or-port))
|
||||||
|
|
||||||
|
;; XXX: 'fcntl' is a vararg function, but here we happily use the
|
||||||
|
;; standard ABI; crossing fingers.
|
||||||
|
(let ((err (proc fd
|
||||||
|
(if wait?
|
||||||
|
F_SETLKW ; lock & wait
|
||||||
|
F_SETLK) ; non-blocking attempt
|
||||||
|
(make-c-struct %struct-flock
|
||||||
|
(list (operation->int operation)
|
||||||
|
SEEK_SET
|
||||||
|
0 0 ; whole file
|
||||||
|
0)))))
|
||||||
|
(or (zero? err)
|
||||||
|
|
||||||
|
;; Presumably we got EAGAIN or so.
|
||||||
|
(throw 'flock-error (errno)))))))
|
||||||
|
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Network interfaces.
|
;;; Network interfaces.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;;; GNU Guix --- Functional package management for GNU
|
;;; GNU Guix --- Functional package management for GNU
|
||||||
;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
|
;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
|
||||||
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
|
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
|
@ -18,8 +18,8 @@
|
||||||
;;; 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 (guix nar)
|
(define-module (guix nar)
|
||||||
#:use-module (guix utils)
|
|
||||||
#:use-module (guix serialization)
|
#:use-module (guix serialization)
|
||||||
|
#:use-module (guix build syscalls)
|
||||||
#:use-module ((guix build utils)
|
#:use-module ((guix build utils)
|
||||||
#:select (delete-file-recursively with-directory-excursion))
|
#:select (delete-file-recursively with-directory-excursion))
|
||||||
#:use-module (guix store)
|
#:use-module (guix store)
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#:use-module ((rnrs bytevectors) #:select (bytevector-u8-set!))
|
#:use-module ((rnrs bytevectors) #:select (bytevector-u8-set!))
|
||||||
#:use-module (guix combinators)
|
#:use-module (guix combinators)
|
||||||
#:use-module ((guix build utils) #:select (dump-port))
|
#:use-module ((guix build utils) #:select (dump-port))
|
||||||
#:use-module ((guix build syscalls) #:select (errno mkdtemp!))
|
#:use-module ((guix build syscalls) #:select (mkdtemp!))
|
||||||
#:use-module (ice-9 vlist)
|
#:use-module (ice-9 vlist)
|
||||||
#:use-module (ice-9 format)
|
#:use-module (ice-9 format)
|
||||||
#:autoload (ice-9 popen) (open-pipe*)
|
#:autoload (ice-9 popen) (open-pipe*)
|
||||||
|
@ -47,7 +47,6 @@
|
||||||
#:export (bytevector->base16-string
|
#:export (bytevector->base16-string
|
||||||
base16-string->bytevector
|
base16-string->bytevector
|
||||||
|
|
||||||
fcntl-flock
|
|
||||||
strip-keyword-arguments
|
strip-keyword-arguments
|
||||||
default-keyword-arguments
|
default-keyword-arguments
|
||||||
substitute-keyword-arguments
|
substitute-keyword-arguments
|
||||||
|
@ -338,78 +337,6 @@ This procedure returns #t on success."
|
||||||
(put-bytevector out post-bv))
|
(put-bytevector out post-bv))
|
||||||
#t))))))
|
#t))))))
|
||||||
|
|
||||||
|
|
||||||
;;;
|
|
||||||
;;; Advisory file locking.
|
|
||||||
;;;
|
|
||||||
|
|
||||||
(define %struct-flock
|
|
||||||
;; 'struct flock' from <fcntl.h>.
|
|
||||||
(list short ; l_type
|
|
||||||
short ; l_whence
|
|
||||||
size_t ; l_start
|
|
||||||
size_t ; l_len
|
|
||||||
int)) ; l_pid
|
|
||||||
|
|
||||||
(define F_SETLKW
|
|
||||||
;; On Linux-based systems, this is usually 7, but not always
|
|
||||||
;; (exceptions include SPARC.) On GNU/Hurd, it's 9.
|
|
||||||
(compile-time-value
|
|
||||||
(cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu
|
|
||||||
((string-contains %host-type "linux") 7) ; *-linux-gnu
|
|
||||||
(else 9)))) ; *-gnu*
|
|
||||||
|
|
||||||
(define F_SETLK
|
|
||||||
;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6.
|
|
||||||
(compile-time-value
|
|
||||||
(cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu
|
|
||||||
((string-contains %host-type "linux") 6) ; *-linux-gnu
|
|
||||||
(else 8)))) ; *-gnu*
|
|
||||||
|
|
||||||
(define F_xxLCK
|
|
||||||
;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
|
|
||||||
(compile-time-value
|
|
||||||
(cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu
|
|
||||||
((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu
|
|
||||||
((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu
|
|
||||||
(else #(1 2 3))))) ; *-gnu*
|
|
||||||
|
|
||||||
(define fcntl-flock
|
|
||||||
(let* ((ptr (dynamic-func "fcntl" (dynamic-link)))
|
|
||||||
(proc (pointer->procedure int ptr `(,int ,int *))))
|
|
||||||
(lambda* (fd-or-port operation #:key (wait? #t))
|
|
||||||
"Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
|
|
||||||
must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is
|
|
||||||
true, block until the lock is acquired; otherwise, thrown an 'flock-error'
|
|
||||||
exception if it's already taken."
|
|
||||||
(define (operation->int op)
|
|
||||||
(case op
|
|
||||||
((read-lock) (vector-ref F_xxLCK 0))
|
|
||||||
((write-lock) (vector-ref F_xxLCK 1))
|
|
||||||
((unlock) (vector-ref F_xxLCK 2))
|
|
||||||
(else (error "invalid fcntl-flock operation" op))))
|
|
||||||
|
|
||||||
(define fd
|
|
||||||
(if (port? fd-or-port)
|
|
||||||
(fileno fd-or-port)
|
|
||||||
fd-or-port))
|
|
||||||
|
|
||||||
;; XXX: 'fcntl' is a vararg function, but here we happily use the
|
|
||||||
;; standard ABI; crossing fingers.
|
|
||||||
(let ((err (proc fd
|
|
||||||
(if wait?
|
|
||||||
F_SETLKW ; lock & wait
|
|
||||||
F_SETLK) ; non-blocking attempt
|
|
||||||
(make-c-struct %struct-flock
|
|
||||||
(list (operation->int operation)
|
|
||||||
SEEK_SET
|
|
||||||
0 0 ; whole file
|
|
||||||
0)))))
|
|
||||||
(or (zero? err)
|
|
||||||
|
|
||||||
;; Presumably we got EAGAIN or so.
|
|
||||||
(throw 'flock-error (errno)))))))
|
|
||||||
|
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Keyword arguments.
|
;;; Keyword arguments.
|
||||||
|
|
|
@ -29,6 +29,10 @@
|
||||||
;; Test the (guix build syscalls) module, although there's not much that can
|
;; Test the (guix build syscalls) module, although there's not much that can
|
||||||
;; actually be tested without being root.
|
;; actually be tested without being root.
|
||||||
|
|
||||||
|
(define temp-file
|
||||||
|
(string-append "t-utils-" (number->string (getpid))))
|
||||||
|
|
||||||
|
|
||||||
(test-begin "syscalls")
|
(test-begin "syscalls")
|
||||||
|
|
||||||
(test-equal "mount, ENOENT"
|
(test-equal "mount, ENOENT"
|
||||||
|
@ -172,6 +176,88 @@
|
||||||
(status:exit-val status))))
|
(status:exit-val status))))
|
||||||
(eq? #t result))))))))
|
(eq? #t result))))))))
|
||||||
|
|
||||||
|
(false-if-exception (delete-file temp-file))
|
||||||
|
(test-equal "fcntl-flock wait"
|
||||||
|
42 ; the child's exit status
|
||||||
|
(let ((file (open-file temp-file "w0b")))
|
||||||
|
;; Acquire an exclusive lock.
|
||||||
|
(fcntl-flock file 'write-lock)
|
||||||
|
(match (primitive-fork)
|
||||||
|
(0
|
||||||
|
(dynamic-wind
|
||||||
|
(const #t)
|
||||||
|
(lambda ()
|
||||||
|
;; Reopen FILE read-only so we can have a read lock.
|
||||||
|
(let ((file (open-file temp-file "r0b")))
|
||||||
|
;; Wait until we can acquire the lock.
|
||||||
|
(fcntl-flock file 'read-lock)
|
||||||
|
(primitive-exit (read file)))
|
||||||
|
(primitive-exit 1))
|
||||||
|
(lambda ()
|
||||||
|
(primitive-exit 2))))
|
||||||
|
(pid
|
||||||
|
;; Write garbage and wait.
|
||||||
|
(display "hello, world!" file)
|
||||||
|
(force-output file)
|
||||||
|
(sleep 1)
|
||||||
|
|
||||||
|
;; Write the real answer.
|
||||||
|
(seek file 0 SEEK_SET)
|
||||||
|
(truncate-file file 0)
|
||||||
|
(write 42 file)
|
||||||
|
(force-output file)
|
||||||
|
|
||||||
|
;; Unlock, which should let the child continue.
|
||||||
|
(fcntl-flock file 'unlock)
|
||||||
|
|
||||||
|
(match (waitpid pid)
|
||||||
|
((_ . status)
|
||||||
|
(let ((result (status:exit-val status)))
|
||||||
|
(close-port file)
|
||||||
|
result)))))))
|
||||||
|
|
||||||
|
(test-equal "fcntl-flock non-blocking"
|
||||||
|
EAGAIN ; the child's exit status
|
||||||
|
(match (pipe)
|
||||||
|
((input . output)
|
||||||
|
(match (primitive-fork)
|
||||||
|
(0
|
||||||
|
(dynamic-wind
|
||||||
|
(const #t)
|
||||||
|
(lambda ()
|
||||||
|
(close-port output)
|
||||||
|
|
||||||
|
;; Wait for the green light.
|
||||||
|
(read-char input)
|
||||||
|
|
||||||
|
;; Open FILE read-only so we can have a read lock.
|
||||||
|
(let ((file (open-file temp-file "w0")))
|
||||||
|
(catch 'flock-error
|
||||||
|
(lambda ()
|
||||||
|
;; This attempt should throw EAGAIN.
|
||||||
|
(fcntl-flock file 'write-lock #:wait? #f))
|
||||||
|
(lambda (key errno)
|
||||||
|
(primitive-exit (pk 'errno errno)))))
|
||||||
|
(primitive-exit -1))
|
||||||
|
(lambda ()
|
||||||
|
(primitive-exit -2))))
|
||||||
|
(pid
|
||||||
|
(close-port input)
|
||||||
|
(let ((file (open-file temp-file "w0")))
|
||||||
|
;; Acquire an exclusive lock.
|
||||||
|
(fcntl-flock file 'write-lock)
|
||||||
|
|
||||||
|
;; Tell the child to continue.
|
||||||
|
(write 'green-light output)
|
||||||
|
(force-output output)
|
||||||
|
|
||||||
|
(match (waitpid pid)
|
||||||
|
((_ . status)
|
||||||
|
(let ((result (status:exit-val status)))
|
||||||
|
(fcntl-flock file 'unlock)
|
||||||
|
(close-port file)
|
||||||
|
result)))))))))
|
||||||
|
|
||||||
(test-assert "all-network-interface-names"
|
(test-assert "all-network-interface-names"
|
||||||
(match (all-network-interface-names)
|
(match (all-network-interface-names)
|
||||||
(((? string? names) ..1)
|
(((? string? names) ..1)
|
||||||
|
@ -303,3 +389,5 @@
|
||||||
0))
|
0))
|
||||||
|
|
||||||
(test-end)
|
(test-end)
|
||||||
|
|
||||||
|
(false-if-exception (delete-file temp-file))
|
||||||
|
|
|
@ -168,88 +168,6 @@
|
||||||
(call-with-decompressed-port 'xz (open-file temp-file "r0b")
|
(call-with-decompressed-port 'xz (open-file temp-file "r0b")
|
||||||
get-bytevector-all))))
|
get-bytevector-all))))
|
||||||
|
|
||||||
(false-if-exception (delete-file temp-file))
|
|
||||||
(test-equal "fcntl-flock wait"
|
|
||||||
42 ; the child's exit status
|
|
||||||
(let ((file (open-file temp-file "w0b")))
|
|
||||||
;; Acquire an exclusive lock.
|
|
||||||
(fcntl-flock file 'write-lock)
|
|
||||||
(match (primitive-fork)
|
|
||||||
(0
|
|
||||||
(dynamic-wind
|
|
||||||
(const #t)
|
|
||||||
(lambda ()
|
|
||||||
;; Reopen FILE read-only so we can have a read lock.
|
|
||||||
(let ((file (open-file temp-file "r0b")))
|
|
||||||
;; Wait until we can acquire the lock.
|
|
||||||
(fcntl-flock file 'read-lock)
|
|
||||||
(primitive-exit (read file)))
|
|
||||||
(primitive-exit 1))
|
|
||||||
(lambda ()
|
|
||||||
(primitive-exit 2))))
|
|
||||||
(pid
|
|
||||||
;; Write garbage and wait.
|
|
||||||
(display "hello, world!" file)
|
|
||||||
(force-output file)
|
|
||||||
(sleep 1)
|
|
||||||
|
|
||||||
;; Write the real answer.
|
|
||||||
(seek file 0 SEEK_SET)
|
|
||||||
(truncate-file file 0)
|
|
||||||
(write 42 file)
|
|
||||||
(force-output file)
|
|
||||||
|
|
||||||
;; Unlock, which should let the child continue.
|
|
||||||
(fcntl-flock file 'unlock)
|
|
||||||
|
|
||||||
(match (waitpid pid)
|
|
||||||
((_ . status)
|
|
||||||
(let ((result (status:exit-val status)))
|
|
||||||
(close-port file)
|
|
||||||
result)))))))
|
|
||||||
|
|
||||||
(test-equal "fcntl-flock non-blocking"
|
|
||||||
EAGAIN ; the child's exit status
|
|
||||||
(match (pipe)
|
|
||||||
((input . output)
|
|
||||||
(match (primitive-fork)
|
|
||||||
(0
|
|
||||||
(dynamic-wind
|
|
||||||
(const #t)
|
|
||||||
(lambda ()
|
|
||||||
(close-port output)
|
|
||||||
|
|
||||||
;; Wait for the green light.
|
|
||||||
(read-char input)
|
|
||||||
|
|
||||||
;; Open FILE read-only so we can have a read lock.
|
|
||||||
(let ((file (open-file temp-file "w0")))
|
|
||||||
(catch 'flock-error
|
|
||||||
(lambda ()
|
|
||||||
;; This attempt should throw EAGAIN.
|
|
||||||
(fcntl-flock file 'write-lock #:wait? #f))
|
|
||||||
(lambda (key errno)
|
|
||||||
(primitive-exit (pk 'errno errno)))))
|
|
||||||
(primitive-exit -1))
|
|
||||||
(lambda ()
|
|
||||||
(primitive-exit -2))))
|
|
||||||
(pid
|
|
||||||
(close-port input)
|
|
||||||
(let ((file (open-file temp-file "w0")))
|
|
||||||
;; Acquire an exclusive lock.
|
|
||||||
(fcntl-flock file 'write-lock)
|
|
||||||
|
|
||||||
;; Tell the child to continue.
|
|
||||||
(write 'green-light output)
|
|
||||||
(force-output output)
|
|
||||||
|
|
||||||
(match (waitpid pid)
|
|
||||||
((_ . status)
|
|
||||||
(let ((result (status:exit-val status)))
|
|
||||||
(fcntl-flock file 'unlock)
|
|
||||||
(close-port file)
|
|
||||||
result)))))))))
|
|
||||||
|
|
||||||
;; This is actually in (guix store).
|
;; This is actually in (guix store).
|
||||||
(test-equal "store-path-package-name"
|
(test-equal "store-path-package-name"
|
||||||
"bash-4.2-p24"
|
"bash-4.2-p24"
|
||||||
|
|
Loading…
Reference in New Issue