posix.test 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. ;;;; posix.test --- Test suite for Guile POSIX functions. -*- scheme -*-
  2. ;;;;
  3. ;;;; Copyright 2003-2004, 2006-2007, 2010, 2012, 2015, 2017-2019, 2021-2023
  4. ;;;; Free Software Foundation, Inc.
  5. ;;;; Copyright 2021 Maxime Devos <maximedevos@telenet.be>
  6. ;;;;
  7. ;;;; This library is free software; you can redistribute it and/or
  8. ;;;; modify it under the terms of the GNU Lesser General Public
  9. ;;;; License as published by the Free Software Foundation; either
  10. ;;;; version 3 of the License, or (at your option) any later version.
  11. ;;;;
  12. ;;;; This library is distributed in the hope that it will be useful,
  13. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. ;;;; Lesser General Public License for more details.
  16. ;;;;
  17. ;;;; You should have received a copy of the GNU Lesser General Public
  18. ;;;; License along with this library; if not, write to the Free Software
  19. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. (define-module (test-suite test-posix)
  21. #:use-module (test-suite lib)
  22. #:use-module ((rnrs io ports) #:select (get-string-all)))
  23. ;; FIXME: The following exec tests are disabled since on an i386 debian with
  24. ;; glibc 2.3.2 they seem to interact badly with threads.test, the latter
  25. ;; dies with signal 32 (one of the SIGRTs). Don't know how or why, or who's
  26. ;; at fault (though it seems to happen with or without the recent memory
  27. ;; leak fix in these error cases).
  28. ;;
  29. ;; execl
  30. ;;
  31. ;; (with-test-prefix "execl"
  32. ;; (pass-if-exception "./nosuchprog" '(system-error . ".*")
  33. ;; (execl "./nosuchprog" "./nosuchprog" "some arg")))
  34. ;;
  35. ;; execlp
  36. ;;
  37. ;; (with-test-prefix "execlp"
  38. ;; (pass-if-exception "./nosuchprog" '(system-error . ".*")
  39. ;; (execlp "./nosuchprog" "./nosuchprog" "some arg")))
  40. ;;
  41. ;; execle
  42. ;;
  43. ;; (with-test-prefix "execle"
  44. ;; (pass-if-exception "./nosuchprog" '(system-error . ".*")
  45. ;; (execle "./nosuchprog" '() "./nosuchprog" "some arg"))
  46. ;; (pass-if-exception "./nosuchprog" '(system-error . ".*")
  47. ;; (execle "./nosuchprog" '("FOO=1" "BAR=2") "./nosuchprog" "some arg")))
  48. ;;
  49. ;; mkstemp
  50. ;;
  51. (with-test-prefix "mkstemp"
  52. ;; the temporary names used in the tests here are kept to 8 characters so
  53. ;; they'll work on a DOS 8.3 file system
  54. (define (string-copy str)
  55. (list->string (string->list str)))
  56. (pass-if-exception "number arg" exception:wrong-type-arg
  57. (mkstemp! 123))
  58. (pass-if "binary mode honored"
  59. (let* ((outport (mkstemp "T-XXXXXX" "wb"))
  60. (filename (port-filename outport)))
  61. (display "\n" outport)
  62. (close-port outport)
  63. (let* ((inport (open-input-file filename #:binary #t))
  64. (char1 (read-char inport))
  65. (char2 (read-char inport))
  66. (result (and (char=? char1 #\newline)
  67. (eof-object? char2))))
  68. (close-port inport)
  69. (delete-file filename)
  70. result))))
  71. ;;
  72. ;; putenv
  73. ;;
  74. (with-test-prefix "putenv"
  75. (pass-if "something"
  76. (putenv "FOO=something")
  77. (equal? "something" (getenv "FOO")))
  78. (pass-if "replacing"
  79. (putenv "FOO=one")
  80. (putenv "FOO=two")
  81. (equal? "two" (getenv "FOO")))
  82. (pass-if "empty"
  83. (putenv "FOO=")
  84. (equal? "" (getenv "FOO")))
  85. (pass-if "removing"
  86. (putenv "FOO=bar")
  87. (putenv "FOO")
  88. (not (getenv "FOO")))
  89. (pass-if "modifying string doesn't change env"
  90. (let ((s (string-copy "FOO=bar")))
  91. (putenv s)
  92. (string-set! s 5 #\x)
  93. (equal? "bar" (getenv "FOO")))))
  94. ;;
  95. ;; setenv
  96. ;;
  97. (with-test-prefix "setenv"
  98. (pass-if "something"
  99. (setenv "FOO" "something")
  100. (equal? "something" (getenv "FOO")))
  101. (pass-if "replacing"
  102. (setenv "FOO" "one")
  103. (setenv "FOO" "two")
  104. (equal? "two" (getenv "FOO")))
  105. (pass-if "empty"
  106. (setenv "FOO" "")
  107. (equal? "" (getenv "FOO")))
  108. (pass-if "removing"
  109. (setenv "FOO" "something")
  110. (setenv "FOO" #f)
  111. (not (getenv "FOO"))))
  112. ;;
  113. ;; unsetenv
  114. ;;
  115. (with-test-prefix "unsetenv"
  116. (pass-if "something"
  117. (putenv "FOO=something")
  118. (unsetenv "FOO")
  119. (not (getenv "FOO")))
  120. (pass-if "empty"
  121. (putenv "FOO=")
  122. (unsetenv "FOO")
  123. (not (getenv "FOO"))))
  124. ;;
  125. ;; ttyname
  126. ;;
  127. (with-test-prefix "ttyname"
  128. (pass-if-exception "non-tty argument" exception:system-error
  129. ;; This used to crash in 1.8.1 and earlier.
  130. (let ((file (false-if-exception
  131. (open-output-file "/dev/null"))))
  132. (if (not file)
  133. (throw 'unsupported)
  134. (ttyname file)))))
  135. ;;
  136. ;; utimes
  137. ;;
  138. (with-test-prefix "utime"
  139. (pass-if "valid argument (second resolution)"
  140. (let ((file "posix.test-utime"))
  141. (dynamic-wind
  142. (lambda ()
  143. (close-port (open-output-file file)))
  144. (lambda ()
  145. (let* ((accessed (+ (current-time) 3600))
  146. (modified (- accessed 1000)))
  147. (utime file accessed modified)
  148. (let ((info (stat file)))
  149. (and (= (stat:atime info) accessed)
  150. (= (stat:mtime info) modified)))))
  151. (lambda ()
  152. (delete-file file)))))
  153. (pass-if-equal "AT_SYMLINK_NOFOLLOW"
  154. '(1 1)
  155. (if (defined? 'AT_SYMLINK_NOFOLLOW)
  156. (let ((file "posix.test-utime"))
  157. (dynamic-wind
  158. (lambda ()
  159. (symlink "/dev/null" file))
  160. (lambda ()
  161. (utime file 1 1 0 0 AT_SYMLINK_NOFOLLOW)
  162. (let ((info (lstat file)))
  163. (list (stat:atime info) (stat:mtime info))))
  164. (lambda ()
  165. (delete-file file))))
  166. (throw 'unsupported)))
  167. (define (utime-unless-unsupported oops . arguments)
  168. (catch 'system-error
  169. (lambda ()
  170. (catch 'wrong-type-arg
  171. (lambda ()
  172. (apply utime arguments))
  173. (lambda _
  174. ;; 'futimens' is not supported on all platforms.
  175. (oops))))
  176. (lambda args
  177. ;; On some platforms, 'futimens' returns ENOSYS according to Gnulib.
  178. (if (= (system-error-errno args) ENOSYS)
  179. (oops)
  180. (apply throw args)))))
  181. (pass-if-equal "file port"
  182. '(1 1)
  183. (let ((file "posix.test-utime"))
  184. (false-if-exception (delete-file file))
  185. (close-port (open-output-file file))
  186. (define (delete)
  187. (delete-file file))
  188. (define (oops)
  189. (delete)
  190. (throw 'unsupported))
  191. (call-with-input-file file
  192. (lambda (port)
  193. (utime-unless-unsupported oops port 1 1 0 0)
  194. (define info (stat file))
  195. (delete)
  196. (list (stat:atime info) (stat:mtime info))))))
  197. ;; This causes an EBADF system error on GNU/Linux with the 5.10.46 kernel.
  198. #;
  199. (pass-if-equal "file port (port representing symbolic link)"
  200. '(1 1)
  201. (let ((file "posix.test-utime"))
  202. (unless (false-if-exception
  203. (begin (symlink "/should-be-irrelevant" file)
  204. #t))
  205. (display "cannot create symlink, a utime test skipped\n")
  206. (throw 'unresolved))
  207. (unless (and (defined? 'O_NOFOLLOW)
  208. (defined? 'O_PATH)
  209. (not (= 0 O_NOFOLLOW))
  210. (not (= 0 O_PATH)))
  211. (display "cannot open symlinks, a utime test skipped\n")
  212. (throw 'unresolved))
  213. (define (delete)
  214. (when port (close-port port))
  215. (false-if-exception (delete-file file)))
  216. (define (oops)
  217. (delete)
  218. (throw 'unsupported))
  219. (define port #f)
  220. (catch #t
  221. (lambda ()
  222. (set! port
  223. (open file (logior O_NOFOLLOW O_PATH)))
  224. (utime-unless-unsupported oops port 1 1 0 0))
  225. (lambda args
  226. (pk 'deleting file)
  227. (delete)
  228. (apply throw args)))
  229. (define info (lstat file))
  230. (delete)
  231. (list (stat:mtime info) (stat:atime info)))))
  232. ;;
  233. ;; affinity
  234. ;;
  235. (with-test-prefix "affinity"
  236. (pass-if "getaffinity"
  237. (if (defined? 'getaffinity)
  238. (> (bitvector-length (getaffinity (getpid))) 0)
  239. (throw 'unresolved)))
  240. (pass-if "setaffinity"
  241. (if (and (defined? 'setaffinity) (defined? 'getaffinity))
  242. (catch 'system-error
  243. (lambda ()
  244. (let ((mask (getaffinity (getpid))))
  245. (setaffinity (getpid) mask)
  246. (equal? mask (getaffinity (getpid)))))
  247. (lambda args
  248. ;; On some platforms such as sh4-linux-gnu, 'setaffinity'
  249. ;; returns ENOSYS.
  250. (let ((errno (system-error-errno args)))
  251. (if (= errno ENOSYS)
  252. (throw 'unresolved)
  253. (apply throw args)))))
  254. (throw 'unresolved))))
  255. ;;
  256. ;; pipe
  257. ;;
  258. (with-test-prefix "pipe"
  259. (pass-if-equal "in and out"
  260. "hi!\n"
  261. (let ((in+out (pipe)))
  262. (display "hi!\n" (cdr in+out))
  263. (close-port (cdr in+out))
  264. (let ((str (list->string (list (read-char (car in+out))
  265. (read-char (car in+out))
  266. (read-char (car in+out))
  267. (read-char (car in+out))))))
  268. (and (eof-object? (read-char (car in+out)))
  269. (begin
  270. (close-port (car in+out))
  271. str)))))
  272. (pass-if-equal "O_CLOEXEC"
  273. (list FD_CLOEXEC FD_CLOEXEC)
  274. (let* ((in+out (catch 'system-error
  275. (lambda ()
  276. (pipe O_CLOEXEC))
  277. (lambda args
  278. (if (= (system-error-errno args) ENOSYS)
  279. (throw 'unresolved)
  280. (apply throw args)))))
  281. (flags (list (fcntl (car in+out) F_GETFD)
  282. (fcntl (cdr in+out) F_GETFD))))
  283. (close-port (car in+out))
  284. (close-port (cdr in+out))
  285. flags)))
  286. ;;
  287. ;; system*
  288. ;;
  289. (with-test-prefix "system*"
  290. (pass-if "http://bugs.gnu.org/13166"
  291. ;; With Guile up to 2.0.7 included, the child process launched by
  292. ;; `system*' would remain alive after an `execvp' failure.
  293. (let ((me (getpid)))
  294. (and (not (zero? (system* "something-that-does-not-exist")))
  295. (= me (getpid)))))
  296. (pass-if-equal "exit code for nonexistent file"
  297. 127 ;aka. EX_NOTFOUND
  298. (status:exit-val (system* "something-that-does-not-exist")))
  299. (pass-if-equal "https://bugs.gnu.org/55596"
  300. 127
  301. ;; The parameterization below used to cause 'start_child' to close
  302. ;; fd 2 in the child process, which in turn would cause it to
  303. ;; segfault, leading to a wrong exit code.
  304. (parameterize ((current-output-port (current-error-port)))
  305. (status:exit-val (system* "something-that-does-not-exist"))))
  306. (pass-if-equal "https://bugs.gnu.org/52835"
  307. "bong\n"
  308. (let ((file (tmpnam)))
  309. ;; Redirect stdout and stderr to FILE.
  310. (define status
  311. (call-with-output-file file
  312. (lambda (port)
  313. (with-output-to-port port
  314. (lambda ()
  315. (with-error-to-port port
  316. (lambda ()
  317. (system* "sh" "-c" "echo bong >&2"))))))))
  318. (and (zero? (status:exit-val status))
  319. (call-with-input-file file get-string-all)))))
  320. ;;
  321. ;; spawn
  322. ;;
  323. (with-test-prefix "spawn"
  324. (pass-if-equal "basic"
  325. 0
  326. (cdr (waitpid (spawn "true" '("true")))))
  327. (pass-if-exception "non-file port argument" ;<https://bugs.gnu.org/61073>
  328. exception:wrong-type-arg
  329. (spawn "true" '("true")
  330. #:error (%make-void-port "w")))
  331. (pass-if-equal "uname with stdout redirect"
  332. (list 0 ;exit value
  333. (string-append (utsname:sysname (uname)) " "
  334. (utsname:machine (uname)) "\n"))
  335. (let* ((input+output (pipe))
  336. (pid (spawn "uname" '("uname" "-s" "-m")
  337. #:output (cdr input+output))))
  338. (close-port (cdr input+output))
  339. (let ((str (get-string-all (car input+output))))
  340. (close-port (car input+output))
  341. (list (cdr (waitpid pid)) str))))
  342. (pass-if-equal "wc with stdin and stdout redirects"
  343. "2\n"
  344. (let* ((a+b (pipe))
  345. (c+d (pipe))
  346. (pid (spawn "wc" '("wc" "-w")
  347. #:input (car a+b)
  348. #:output (cdr c+d))))
  349. (close-port (car a+b))
  350. (close-port (cdr c+d))
  351. (display "Hello world.\n" (cdr a+b))
  352. (close-port (cdr a+b))
  353. (let ((str (get-string-all (car c+d))))
  354. (close-port (car c+d))
  355. (waitpid pid)
  356. str)))
  357. (pass-if-equal "env with #:environment and #:output"
  358. "GNU=guile\n"
  359. (let* ((input+output (pipe))
  360. (pid (spawn "env" '("env")
  361. #:environment '("GNU=guile")
  362. #:output (cdr input+output))))
  363. (close-port (cdr input+output))
  364. (let ((str (get-string-all (car input+output))))
  365. (close-port (car input+output))
  366. (waitpid pid)
  367. str)))
  368. (pass-if-equal "ls /proc/self/fd"
  369. "0\n1\n2\n3\n" ;fourth FD is for /proc/self/fd
  370. (if (file-exists? "/proc/self/fd") ;Linux
  371. (let* ((input+output (pipe))
  372. (pid (spawn "ls" '("ls" "/proc/self/fd")
  373. #:output (cdr input+output))))
  374. (close-port (cdr input+output))
  375. (let ((str (get-string-all (car input+output))))
  376. (close-port (car input+output))
  377. (waitpid pid)
  378. str))
  379. (throw 'unresolved)))
  380. (pass-if-equal "file not found"
  381. ENOENT
  382. (catch 'system-error
  383. (lambda ()
  384. (spawn "this-does-not-exist" '("nope")
  385. #:search-path? #f))
  386. (lambda args
  387. (system-error-errno args)))))
  388. ;;
  389. ;; crypt
  390. ;;
  391. (with-test-prefix "crypt"
  392. (pass-if "basic usage"
  393. (if (not (defined? 'crypt))
  394. (throw 'unsupported)
  395. (string? (crypt "pass" "abcdefg"))))
  396. (pass-if "crypt invalid salt on glibc"
  397. (begin
  398. (unless (defined? 'crypt)
  399. (throw 'unsupported))
  400. (unless (string-contains %host-type "-gnu")
  401. (throw 'unresolved))
  402. (catch 'system-error
  403. (lambda ()
  404. ;; This used to deadlock on glibc while trying to throw to
  405. ;; 'system-error'. This test uses the special
  406. ;; interpretation of the salt that glibc does;
  407. ;; specifically, we pass a salt that's probably
  408. ;; syntactically invalid here. Note, whether it's invalid
  409. ;; or not is system-defined, so it's possible it just works.
  410. (string? (crypt "pass" "$X$abc")))
  411. (lambda _ #t)))))