syscalls.scm 22 KB

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