popen.scm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. ;; popen emulation, for non-stdio based ports.
  2. ;;;; Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006, 2010, 2011, 2012,
  3. ;;;; 2013 Free Software Foundation, Inc.
  4. ;;;;
  5. ;;;; This library is free software; you can redistribute it and/or
  6. ;;;; modify it under the terms of the GNU Lesser General Public
  7. ;;;; License as published by the Free Software Foundation; either
  8. ;;;; version 3 of the License, or (at your option) any later version.
  9. ;;;;
  10. ;;;; This library is distributed in the hope that it will be useful,
  11. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;;; Lesser General Public License for more details.
  14. ;;;;
  15. ;;;; You should have received a copy of the GNU Lesser General Public
  16. ;;;; License along with this library; if not, write to the Free Software
  17. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. ;;;;
  19. (define-module (ice-9 popen)
  20. :use-module (ice-9 threads)
  21. :use-module (srfi srfi-9)
  22. :export (port/pid-table open-pipe* open-pipe close-pipe open-input-pipe
  23. open-output-pipe open-input-output-pipe))
  24. (eval-when (expand load eval)
  25. (load-extension (string-append "libguile-" (effective-version))
  26. "scm_init_popen"))
  27. (define-record-type <pipe-info>
  28. (make-pipe-info pid)
  29. pipe-info?
  30. (pid pipe-info-pid set-pipe-info-pid!))
  31. (define (make-rw-port read-port write-port)
  32. (make-soft-port
  33. (vector
  34. (lambda (c) (write-char c write-port))
  35. (lambda (s) (display s write-port))
  36. (lambda () (force-output write-port))
  37. (lambda () (read-char read-port))
  38. (lambda () (close-port read-port) (close-port write-port)))
  39. "r+"))
  40. ;; a guardian to ensure the cleanup is done correctly when
  41. ;; an open pipe is gc'd or a close-port is used.
  42. (define pipe-guardian (make-guardian))
  43. ;; a weak hash-table to store the process ids.
  44. ;; XXX use of this table is deprecated. It is no longer used here, and
  45. ;; is populated for backward compatibility only (since it is exported).
  46. (define port/pid-table (make-weak-key-hash-table 31))
  47. (define port/pid-table-mutex (make-mutex))
  48. (define (open-pipe* mode command . args)
  49. "Executes the program @var{command} with optional arguments
  50. @var{args} (all strings) in a subprocess.
  51. A port to the process (based on pipes) is created and returned.
  52. @var{mode} specifies whether an input, an output or an input-output
  53. port to the process is created: it should be the value of
  54. @code{OPEN_READ}, @code{OPEN_WRITE} or @code{OPEN_BOTH}."
  55. (call-with-values (lambda ()
  56. (apply open-process mode command args))
  57. (lambda (read-port write-port pid)
  58. (let ((port (or (and read-port write-port
  59. (make-rw-port read-port write-port))
  60. read-port
  61. write-port
  62. (%make-void-port mode)))
  63. (pipe-info (make-pipe-info pid)))
  64. ;; Guard the pipe-info instead of the port, so that we can still
  65. ;; call 'waitpid' even if 'close-port' is called (which clears
  66. ;; the port entry).
  67. (pipe-guardian pipe-info)
  68. (%set-port-property! port 'popen-pipe-info pipe-info)
  69. ;; XXX populate port/pid-table for backward compatibility.
  70. (with-mutex port/pid-table-mutex
  71. (hashq-set! port/pid-table port pid))
  72. port))))
  73. (define (open-pipe command mode)
  74. "Executes the shell command @var{command} (a string) in a subprocess.
  75. A port to the process (based on pipes) is created and returned.
  76. @var{mode} specifies whether an input, an output or an input-output
  77. port to the process is created: it should be the value of
  78. @code{OPEN_READ}, @code{OPEN_WRITE} or @code{OPEN_BOTH}."
  79. (open-pipe* mode "/bin/sh" "-c" command))
  80. (define (fetch-pipe-info port)
  81. (%port-property port 'popen-pipe-info))
  82. (define (close-process port pid)
  83. (close-port port)
  84. (cdr (waitpid pid)))
  85. (define (close-pipe p)
  86. "Closes the pipe created by @code{open-pipe}, then waits for the process
  87. to terminate and returns its status value, @xref{Processes, waitpid}, for
  88. information on how to interpret this value."
  89. (let ((pipe-info (fetch-pipe-info p)))
  90. (unless pipe-info
  91. (error "close-pipe: port not created by (ice-9 popen)"))
  92. (let ((pid (pipe-info-pid pipe-info)))
  93. (unless pid
  94. (error "close-pipe: pid has already been cleared"))
  95. ;; clear the pid to avoid repeated calls to 'waitpid'.
  96. (set-pipe-info-pid! pipe-info #f)
  97. (close-process p pid))))
  98. (define (reap-pipes)
  99. (let loop ()
  100. (let ((pipe-info (pipe-guardian)))
  101. (when pipe-info
  102. (let ((pid (pipe-info-pid pipe-info)))
  103. ;; maybe 'close-pipe' was already called.
  104. (when pid
  105. ;; clean up without reporting errors. also avoids blocking
  106. ;; the process: if the child isn't ready to be collected,
  107. ;; puts it back into the guardian's live list so it can be
  108. ;; tried again the next time the cleanup runs.
  109. (catch 'system-error
  110. (lambda ()
  111. (let ((pid/status (waitpid pid WNOHANG)))
  112. (if (zero? (car pid/status))
  113. (pipe-guardian pipe-info) ; not ready for collection
  114. (set-pipe-info-pid! pipe-info #f))))
  115. (lambda args #f))))
  116. (loop)))))
  117. (add-hook! after-gc-hook reap-pipes)
  118. (define (open-input-pipe command)
  119. "Equivalent to @code{open-pipe} with mode @code{OPEN_READ}"
  120. (open-pipe command OPEN_READ))
  121. (define (open-output-pipe command)
  122. "Equivalent to @code{open-pipe} with mode @code{OPEN_WRITE}"
  123. (open-pipe command OPEN_WRITE))
  124. (define (open-input-output-pipe command)
  125. "Equivalent to @code{open-pipe} with mode @code{OPEN_BOTH}"
  126. (open-pipe command OPEN_BOTH))