syscalls.scm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2015 David Thompson <davet@gnu.org>
  4. ;;; Copyright © 2020 Simon South <simon@simonsouth.net>
  5. ;;; Copyright © 2020 Mathieu Othacehe <m.othacehe@gmail.com>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (test-syscalls)
  22. #:use-module (guix utils)
  23. #:use-module (guix build syscalls)
  24. #:use-module (gnu build linux-container)
  25. #:use-module (srfi srfi-1)
  26. #:use-module (srfi srfi-26)
  27. #:use-module (srfi srfi-64)
  28. #:use-module (system foreign)
  29. #:use-module ((ice-9 ftw) #:select (scandir))
  30. #:use-module (ice-9 match))
  31. ;; Test the (guix build syscalls) module, although there's not much that can
  32. ;; actually be tested without being root.
  33. (define temp-file
  34. (string-append "t-utils-" (number->string (getpid))))
  35. (test-begin "syscalls")
  36. (test-equal "mount, ENOENT"
  37. ENOENT
  38. (catch 'system-error
  39. (lambda ()
  40. (mount "/dev/null" "/does-not-exist" "ext2")
  41. #f)
  42. (compose system-error-errno list)))
  43. (test-assert "umount, ENOENT/EPERM"
  44. (catch 'system-error
  45. (lambda ()
  46. (umount "/does-not-exist")
  47. #f)
  48. (lambda args
  49. ;; Both return values have been encountered in the wild.
  50. (memv (system-error-errno args) (list EPERM ENOENT)))))
  51. (test-assert "mounts"
  52. ;; Check for one of the common mount points.
  53. (let ((mounts (mounts)))
  54. (any (match-lambda
  55. ((point . type)
  56. (let ((mount (find (lambda (mount)
  57. (string=? (mount-point mount) point))
  58. mounts)))
  59. (and mount
  60. (string=? (mount-type mount) type)))))
  61. '(("/proc" . "proc")
  62. ("/sys" . "sysfs")
  63. ("/dev/shm" . "tmpfs")))))
  64. (test-assert "mount-points"
  65. ;; Reportedly "/" is not always listed as a mount point, so check a few
  66. ;; others (see <http://bugs.gnu.org/20261>.)
  67. (any (cute member <> (mount-points))
  68. '("/" "/proc" "/sys" "/dev")))
  69. (false-if-exception (delete-file temp-file))
  70. (test-equal "utime with AT_SYMLINK_NOFOLLOW"
  71. '(0 0)
  72. (begin
  73. ;; Test libguile's utime with AT_SYMLINK_NOFOLLOW, which libguile does not
  74. ;; define as of Guile 2.2.4.
  75. (symlink "/nowhere" temp-file)
  76. (utime temp-file 0 0 0 0 AT_SYMLINK_NOFOLLOW)
  77. (let ((st (lstat temp-file)))
  78. (delete-file temp-file)
  79. ;; Note: 'utimensat' does not change 'ctime'.
  80. (list (stat:mtime st) (stat:atime st)))))
  81. (test-assert "swapon, ENOSYS/ENOENT/EPERM"
  82. (catch 'system-error
  83. (lambda ()
  84. (swapon "/does-not-exist")
  85. #f)
  86. (lambda args
  87. (memv (system-error-errno args) (list EPERM ENOENT ENOSYS)))))
  88. (test-assert "swapoff, ENOSYS/ENOENT/EINVAL/EPERM"
  89. (catch 'system-error
  90. (lambda ()
  91. (swapoff "/does-not-exist")
  92. #f)
  93. (lambda args
  94. (memv (system-error-errno args) (list EPERM EINVAL ENOENT ENOSYS)))))
  95. (test-assert "mkdtemp!"
  96. (let* ((tmp (or (getenv "TMPDIR") "/tmp"))
  97. (dir (mkdtemp! (string-append tmp "/guix-test-XXXXXX"))))
  98. (and (file-exists? dir)
  99. (begin
  100. (rmdir dir)
  101. #t))))
  102. (test-equal "statfs, ENOENT"
  103. ENOENT
  104. (catch 'system-error
  105. (lambda ()
  106. (statfs "/does-not-exist"))
  107. (compose system-error-errno list)))
  108. (test-assert "statfs"
  109. (let ((fs (statfs "/")))
  110. (and (file-system? fs)
  111. (> (file-system-block-size fs) 0)
  112. (>= (file-system-blocks-available fs) 0)
  113. (>= (file-system-blocks-free fs)
  114. (file-system-blocks-available fs)))))
  115. (define (user-namespace pid)
  116. (string-append "/proc/" (number->string pid) "/ns/user"))
  117. (define perform-container-tests?
  118. (and (user-namespace-supported?)
  119. (unprivileged-user-namespace-supported?)))
  120. (unless perform-container-tests?
  121. (test-skip 1))
  122. (test-assert "clone"
  123. (match (clone (logior CLONE_NEWUSER SIGCHLD))
  124. (0 (primitive-exit 42))
  125. (pid
  126. ;; Check if user namespaces are different.
  127. (and (not (equal? (readlink (user-namespace pid))
  128. (readlink (user-namespace (getpid)))))
  129. (match (waitpid pid)
  130. ((_ . status)
  131. (= 42 (status:exit-val status))))))))
  132. (unless perform-container-tests?
  133. (test-skip 1))
  134. (test-assert "setns"
  135. (match (clone (logior CLONE_NEWUSER SIGCHLD))
  136. (0 (primitive-exit 0))
  137. (clone-pid
  138. (match (pipe)
  139. ((in . out)
  140. (match (primitive-fork)
  141. (0
  142. (close in)
  143. ;; Join the user namespace.
  144. (call-with-input-file (user-namespace clone-pid)
  145. (lambda (port)
  146. (setns (port->fdes port) 0)))
  147. (write 'done out)
  148. (close out)
  149. (primitive-exit 0))
  150. (fork-pid
  151. (close out)
  152. ;; Wait for the child process to join the namespace.
  153. (read in)
  154. (let ((result (and (equal? (readlink (user-namespace clone-pid))
  155. (readlink (user-namespace fork-pid))))))
  156. ;; Clean up.
  157. (waitpid clone-pid)
  158. (waitpid fork-pid)
  159. result))))))))
  160. (when (not perform-container-tests?)
  161. (test-skip 1))
  162. (test-equal "pivot-root"
  163. 'success!
  164. (match (socketpair AF_UNIX SOCK_STREAM 0)
  165. ((parent . child)
  166. (match (clone (logior CLONE_NEWUSER CLONE_NEWNS SIGCHLD))
  167. (0
  168. (dynamic-wind
  169. (const #t)
  170. (lambda ()
  171. (close parent)
  172. (call-with-temporary-directory
  173. (lambda (root)
  174. (display "ready\n" child)
  175. (read child) ;wait for "go!"
  176. (let ((put-old (string-append root "/real-root")))
  177. (mount "none" root "tmpfs")
  178. (mkdir put-old)
  179. (call-with-output-file (string-append root "/test")
  180. (lambda (port)
  181. (display "testing\n" port)))
  182. (pivot-root root put-old)
  183. ;; The test file should now be located inside the root directory.
  184. (write (and (file-exists? "/test") 'success!) child)
  185. (close child)))))
  186. (lambda ()
  187. (primitive-exit 0))))
  188. (pid
  189. (close child)
  190. (match (read parent)
  191. ('ready
  192. ;; Set up the UID/GID mapping so that we can mkdir on the tmpfs:
  193. ;; <https://bugzilla.kernel.org/show_bug.cgi?id=183461>.
  194. (call-with-output-file (format #f "/proc/~d/setgroups" pid)
  195. (lambda (port)
  196. (display "deny" port)))
  197. (call-with-output-file (format #f "/proc/~d/uid_map" pid)
  198. (lambda (port)
  199. (format port "0 ~d 1" (getuid))))
  200. (call-with-output-file (format #f "/proc/~d/gid_map" pid)
  201. (lambda (port)
  202. (format port "0 ~d 1" (getgid))))
  203. (display "go!\n" parent)
  204. (let ((result (read parent)))
  205. (close parent)
  206. (and (zero? (match (waitpid pid)
  207. ((_ . status)
  208. (status:exit-val status))))
  209. result)))))))))
  210. (test-equal "scandir*, ENOENT"
  211. ENOENT
  212. (catch 'system-error
  213. (lambda ()
  214. (scandir* "/does/not/exist"))
  215. (lambda args
  216. (system-error-errno args))))
  217. (test-equal "scandir*, ASCII file names"
  218. (scandir (dirname (search-path %load-path "guix/base32.scm"))
  219. (const #t) string<?)
  220. (match (scandir* (dirname (search-path %load-path "guix/base32.scm")))
  221. (((names . properties) ...)
  222. names)))
  223. (test-equal "scandir*, UTF-8 file names"
  224. '("." ".." "α" "λ")
  225. (call-with-temporary-directory
  226. (lambda (directory)
  227. ;; Wrap 'creat' to make sure that we really pass a UTF-8-encoded file
  228. ;; name to the system call.
  229. (let ((creat (pointer->procedure int
  230. (dynamic-func "creat" (dynamic-link))
  231. (list '* int))))
  232. (creat (string->pointer (string-append directory "/α")
  233. "UTF-8")
  234. #o644)
  235. (creat (string->pointer (string-append directory "/λ")
  236. "UTF-8")
  237. #o644)
  238. (let ((locale (setlocale LC_ALL)))
  239. (dynamic-wind
  240. (lambda ()
  241. ;; Make sure that even in a C locale we get the right result.
  242. (setlocale LC_ALL "C"))
  243. (lambda ()
  244. (match (scandir* directory)
  245. (((names . properties) ...)
  246. names)))
  247. (lambda ()
  248. (setlocale LC_ALL locale))))))))
  249. (test-assert "scandir*, properties"
  250. (let ((directory (dirname (search-path %load-path "guix/base32.scm"))))
  251. (every (lambda (entry name)
  252. (match entry
  253. ((name2 . properties)
  254. (and (string=? name2 name)
  255. (let* ((full (string-append directory "/" name))
  256. (stat (lstat full))
  257. (inode (assoc-ref properties 'inode))
  258. (type (assoc-ref properties 'type)))
  259. (and (= inode (stat:ino stat))
  260. (or (eq? type 'unknown)
  261. (eq? type (stat:type stat)))))))))
  262. (scandir* directory)
  263. (scandir directory (const #t) string<?))))
  264. (false-if-exception (delete-file temp-file))
  265. (test-assert "getxattr, setxattr"
  266. (let ((key "user.translator")
  267. (value "/hurd/pfinet\0")
  268. (file (open-file temp-file "w0")))
  269. (catch 'system-error
  270. (lambda ()
  271. (setxattr temp-file key value)
  272. (string=? (getxattr temp-file key) value))
  273. (lambda args
  274. ;; Accept ENOTSUP, if the file-system does not support extended user
  275. ;; attributes.
  276. (memv (system-error-errno args) (list ENOTSUP))))))
  277. (false-if-exception (delete-file temp-file))
  278. (test-equal "fcntl-flock wait"
  279. 42 ; the child's exit status
  280. (let ((file (open-file temp-file "w0b")))
  281. ;; Acquire an exclusive lock.
  282. (fcntl-flock file 'write-lock)
  283. (match (primitive-fork)
  284. (0
  285. (dynamic-wind
  286. (const #t)
  287. (lambda ()
  288. ;; Reopen FILE read-only so we can have a read lock.
  289. (let ((file (open-file temp-file "r0b")))
  290. ;; Wait until we can acquire the lock.
  291. (fcntl-flock file 'read-lock)
  292. (primitive-exit (read file)))
  293. (primitive-exit 1))
  294. (lambda ()
  295. (primitive-exit 2))))
  296. (pid
  297. ;; Write garbage and wait.
  298. (display "hello, world!" file)
  299. (force-output file)
  300. (sleep 1)
  301. ;; Write the real answer.
  302. (seek file 0 SEEK_SET)
  303. (truncate-file file 0)
  304. (write 42 file)
  305. (force-output file)
  306. ;; Unlock, which should let the child continue.
  307. (fcntl-flock file 'unlock)
  308. (match (waitpid pid)
  309. ((_ . status)
  310. (let ((result (status:exit-val status)))
  311. (close-port file)
  312. result)))))))
  313. (test-equal "fcntl-flock non-blocking"
  314. EAGAIN ; the child's exit status
  315. (match (pipe)
  316. ((input . output)
  317. (match (primitive-fork)
  318. (0
  319. (dynamic-wind
  320. (const #t)
  321. (lambda ()
  322. (close-port output)
  323. ;; Wait for the green light.
  324. (read-char input)
  325. ;; Open FILE read-only so we can have a read lock.
  326. (let ((file (open-file temp-file "w0")))
  327. (catch 'flock-error
  328. (lambda ()
  329. ;; This attempt should throw EAGAIN.
  330. (fcntl-flock file 'write-lock #:wait? #f))
  331. (lambda (key errno)
  332. (primitive-exit (pk 'errno errno)))))
  333. (primitive-exit -1))
  334. (lambda ()
  335. (primitive-exit -2))))
  336. (pid
  337. (close-port input)
  338. (let ((file (open-file temp-file "w0")))
  339. ;; Acquire an exclusive lock.
  340. (fcntl-flock file 'write-lock)
  341. ;; Tell the child to continue.
  342. (write 'green-light output)
  343. (force-output output)
  344. (match (waitpid pid)
  345. ((_ . status)
  346. (let ((result (status:exit-val status)))
  347. (fcntl-flock file 'unlock)
  348. (close-port file)
  349. result)))))))))
  350. (test-equal "set-thread-name"
  351. "Syscall Test"
  352. (let ((name (thread-name)))
  353. (set-thread-name "Syscall Test")
  354. (let ((new-name (thread-name)))
  355. (set-thread-name name)
  356. new-name)))
  357. (test-assert "all-network-interface-names"
  358. (match (all-network-interface-names)
  359. (((? string? names) ..1)
  360. (member "lo" names))))
  361. (test-assert "network-interface-names"
  362. (match (remove (lambda (interface)
  363. ;; Ignore interface aliases since they don't show up in
  364. ;; (all-network-interface-names).
  365. (string-contains interface ":"))
  366. (network-interface-names))
  367. (((? string? names) ..1)
  368. (lset<= string=? names (all-network-interface-names)))))
  369. (test-assert "network-interface-flags"
  370. (let* ((sock (socket AF_INET SOCK_STREAM 0))
  371. (flags (network-interface-flags sock "lo")))
  372. (close-port sock)
  373. (and (not (zero? (logand flags IFF_LOOPBACK)))
  374. (not (zero? (logand flags IFF_UP))))))
  375. (test-equal "loopback-network-interface?"
  376. ENODEV
  377. (and (loopback-network-interface? "lo")
  378. (catch 'system-error
  379. (lambda ()
  380. (loopback-network-interface? "nonexistent")
  381. #f)
  382. (lambda args
  383. (system-error-errno args)))))
  384. (test-equal "loopback-network-interface-running?"
  385. ENODEV
  386. (and (network-interface-running? "lo")
  387. (catch 'system-error
  388. (lambda ()
  389. (network-interface-running? "nonexistent")
  390. #f)
  391. (lambda args
  392. (system-error-errno args)))))
  393. (test-skip (if (zero? (getuid)) 1 0))
  394. (test-assert "set-network-interface-flags"
  395. (let ((sock (socket AF_INET SOCK_STREAM 0)))
  396. (catch 'system-error
  397. (lambda ()
  398. (set-network-interface-flags sock "lo" IFF_UP))
  399. (lambda args
  400. (close-port sock)
  401. ;; We get EPERM with Linux 3.18ish and EACCES with 2.6.32.
  402. (memv (system-error-errno args) (list EPERM EACCES))))))
  403. (test-equal "network-interface-address lo"
  404. (make-socket-address AF_INET (inet-pton AF_INET "127.0.0.1") 0)
  405. (let* ((sock (socket AF_INET SOCK_STREAM 0))
  406. (addr (network-interface-address sock "lo")))
  407. (close-port sock)
  408. addr))
  409. (test-skip (if (zero? (getuid)) 1 0))
  410. (test-assert "set-network-interface-address"
  411. (let ((sock (socket AF_INET SOCK_STREAM 0)))
  412. (catch 'system-error
  413. (lambda ()
  414. (set-network-interface-address sock "nonexistent"
  415. (make-socket-address
  416. AF_INET
  417. (inet-pton AF_INET "127.12.14.15")
  418. 0)))
  419. (lambda args
  420. (close-port sock)
  421. ;; We get EPERM with Linux 3.18ish and EACCES with 2.6.32.
  422. (memv (system-error-errno args) (list EPERM EACCES))))))
  423. (test-equal "network-interface-netmask lo"
  424. (make-socket-address AF_INET (inet-pton AF_INET "255.0.0.0") 0)
  425. (let* ((sock (socket AF_INET SOCK_STREAM 0))
  426. (addr (network-interface-netmask sock "lo")))
  427. (close-port sock)
  428. addr))
  429. (test-skip (if (zero? (getuid)) 1 0))
  430. (test-assert "set-network-interface-netmask"
  431. (let ((sock (socket AF_INET SOCK_STREAM 0)))
  432. (catch 'system-error
  433. (lambda ()
  434. (set-network-interface-netmask sock "nonexistent"
  435. (make-socket-address
  436. AF_INET
  437. (inet-pton AF_INET "255.0.0.0")
  438. 0)))
  439. (lambda args
  440. (close-port sock)
  441. (memv (system-error-errno args) (list EPERM EACCES))))))
  442. (test-equal "network-interfaces returns one or more interfaces"
  443. '(#t #t #t)
  444. (match (network-interfaces)
  445. ((interfaces ..1)
  446. (list (every interface? interfaces)
  447. (every string? (map interface-name interfaces))
  448. (every (lambda (sockaddr)
  449. ;; Sometimes interfaces have no associated address.
  450. (or (vector? sockaddr)
  451. (not sockaddr)))
  452. (map interface-address interfaces))))))
  453. (test-equal "network-interfaces returns \"lo\""
  454. (list #t (make-socket-address AF_INET (inet-pton AF_INET "127.0.0.1") 0))
  455. (match (filter (lambda (interface)
  456. (string=? "lo" (interface-name interface)))
  457. (network-interfaces))
  458. ((loopbacks ..1)
  459. (list (every (lambda (lo)
  460. (not (zero? (logand IFF_LOOPBACK (interface-flags lo)))))
  461. loopbacks)
  462. (match (find (lambda (lo)
  463. (= AF_INET (sockaddr:fam (interface-address lo))))
  464. loopbacks)
  465. (#f #f)
  466. (lo (interface-address lo)))))))
  467. (test-skip (if (zero? (getuid)) 1 0))
  468. (test-assert "add-network-route/gateway"
  469. (let ((sock (socket AF_INET SOCK_STREAM 0))
  470. (gateway (make-socket-address AF_INET
  471. (inet-pton AF_INET "192.168.0.1")
  472. 0)))
  473. (catch 'system-error
  474. (lambda ()
  475. (add-network-route/gateway sock gateway))
  476. (lambda args
  477. (close-port sock)
  478. (memv (system-error-errno args) (list EPERM EACCES))))))
  479. (test-skip (if (zero? (getuid)) 1 0))
  480. (test-assert "delete-network-route"
  481. (let ((sock (socket AF_INET SOCK_STREAM 0))
  482. (destination (make-socket-address AF_INET INADDR_ANY 0)))
  483. (catch 'system-error
  484. (lambda ()
  485. (delete-network-route sock destination))
  486. (lambda args
  487. (close-port sock)
  488. (memv (system-error-errno args) (list EPERM EACCES))))))
  489. (test-equal "tcgetattr ENOTTY"
  490. ENOTTY
  491. (catch 'system-error
  492. (lambda ()
  493. (call-with-input-file "/dev/null"
  494. (lambda (port)
  495. (tcgetattr (fileno port)))))
  496. (compose system-error-errno list)))
  497. (test-skip (if (and (file-exists? "/proc/self/fd/0")
  498. (string-prefix? "/dev/pts/" (readlink "/proc/self/fd/0")))
  499. 0
  500. 2))
  501. (test-assert "tcgetattr"
  502. (let ((termios (tcgetattr 0)))
  503. (and (termios? termios)
  504. (> (termios-input-speed termios) 0)
  505. (> (termios-output-speed termios) 0))))
  506. (test-assert "tcsetattr"
  507. (let ((first (tcgetattr 0)))
  508. (tcsetattr 0 (tcsetattr-action TCSANOW) first)
  509. (equal? first (tcgetattr 0))))
  510. (test-assert "terminal-window-size ENOTTY"
  511. (call-with-input-file "/dev/null"
  512. (lambda (port)
  513. (catch 'system-error
  514. (lambda ()
  515. (terminal-window-size port))
  516. (lambda args
  517. ;; Accept EINVAL, which some old Linux versions might return.
  518. (memv (system-error-errno args)
  519. (list ENOTTY EINVAL)))))))
  520. (test-assert "terminal-columns"
  521. (> (terminal-columns) 0))
  522. (test-assert "terminal-columns non-file port"
  523. (> (terminal-columns (open-input-string "Join us now, share the software!"))
  524. 0))
  525. (test-assert "terminal-rows"
  526. (> (terminal-rows) 0))
  527. (test-assert "utmpx-entries"
  528. (match (utmpx-entries)
  529. (((? utmpx? entries) ...)
  530. (every (lambda (entry)
  531. (match (utmpx-user entry)
  532. ((? string?)
  533. ;; Ensure we have a valid PID for those entries where it
  534. ;; makes sense.
  535. (or (not (memv (utmpx-login-type entry)
  536. (list (login-type INIT_PROCESS)
  537. (login-type LOGIN_PROCESS)
  538. (login-type USER_PROCESS))))
  539. (> (utmpx-pid entry) 0)))
  540. (#f ;might be DEAD_PROCESS
  541. #t)))
  542. entries))))
  543. (test-assert "read-utmpx, EOF"
  544. (eof-object? (read-utmpx (%make-void-port "r"))))
  545. (unless (access? "/var/run/utmpx" O_RDONLY)
  546. (test-skip 1))
  547. (test-assert "read-utmpx"
  548. (let ((result (call-with-input-file "/var/run/utmpx" read-utmpx)))
  549. (or (utmpx? result) (eof-object? result))))
  550. (when (zero? (getuid))
  551. (test-skip 1))
  552. (test-equal "add-to-entropy-count"
  553. EPERM
  554. (call-with-output-file "/dev/urandom"
  555. (lambda (port)
  556. (catch 'system-error
  557. (lambda ()
  558. (add-to-entropy-count port 77)
  559. #f)
  560. (lambda args
  561. (system-error-errno args))))))
  562. (test-end)
  563. (false-if-exception (delete-file temp-file))