Stelian,
> I added to iolib.os a different
> implementation that calls posix_spawn(3)
Great, I agree that posix_spawn is a better solution than fork/exec.
> The implementation is currently not very complete
I rewrite your code, so that it now passes my tests.
I tested it on Linux with SBCL, Clozure CL and CLISP.
My code in fork iolib:
http://gitorious.org/~archimag/iolib/archimag-iolib/blobs/master/src/os/create-process-unix.lisp
My tests: http://gitorious.org/iolib-test/iolib-test/blobs/master/process.lisp
Andrey
diff --git a/src/os/create-process-unix.lisp b/src/os/create-process-unix.lisp
index 4a3f5ef..442be26 100644
--- a/src/os/create-process-unix.lisp
+++ b/src/os/create-process-unix.lisp
@@ -9,26 +9,50 @@
((pid :initarg :pid :reader process-pid)
(stdin :initform nil :reader process-stdin)
(stdout :initform nil :reader process-stdout)
- (stderr :initform nil :reader process-stderr)))
+ (stderr :initform nil :reader process-stderr)
+ (terminated-p :initform nil )))
(defmethod initialize-instance :after ((process process) &key stdin stdout stderr)
(with-slots ((in stdin) (out stdout) (err stderr))
process
- (setf in (make-instance 'iolib.streams:dual-channel-gray-stream
- :output-fd stdin)
- out (make-instance 'iolib.streams:dual-channel-gray-stream
- :input-fd stdout)
- err (make-instance 'iolib.streams:dual-channel-gray-stream
- :input-fd stderr))))
+ (setf in (if stdin
+ (make-instance 'iolib.streams:dual-channel-gray-stream
+ :output-fd stdin))
+ out (if stdout
+ (make-instance 'iolib.streams:dual-channel-gray-stream
+ :input-fd stdout))
+ err (if stderr
+ (make-instance 'iolib.streams:dual-channel-gray-stream
+ :input-fd stderr)))))
+
+(defun process-wait (process)
+ (setf (slot-value process 'terminated-p)
+ (isys:waitpid (process-pid process) 0)))
+
+(defun process-kill (process signum)
+ (isys:kill (process-pid process) signum))
+
+(defun close-process-stream (stream &key abort)
+ (when stream
+ (close stream :abort abort)
+ (isys:close (or (iolib.streams:output-fd-of stream)
+ (iolib.streams:input-fd-of stream)))))
(defmethod close ((process process) &key abort)
(with-slots (pid stdin stdout stderr)
process
- (close stdin :abort abort)
- (close stdout :abort abort)
- (close stderr :abort abort)
+ (close-process-stream stdin :abort abort)
+ (close-process-stream stdout :abort abort)
+ (close-process-stream stderr :abort abort)
+ #-clisp (unless (slot-value process 'terminated-p)
+ (isys:waitpid pid 0))
(setf pid nil stdin nil stdout nil stderr nil)))
+(defun process-stdin-close (process)
+ (close-process-stream (process-stdin process))
+ (setf (slot-value process 'stdin)
+ nil))
+
(defmethod print-object ((o process) s)
(print-unreadable-object (o s :type t :identity t)
(format s ":pid ~S" (process-pid o))))
@@ -74,55 +98,92 @@
,@body)
(deallocate-null-ended-list ,argv))))))
-(defmacro with-3-pipes ((stdin-r stdin-w stdout-r stdout-w stderr-r stderr-w)
- &body body)
- `(multiple-value-bind (,stdin-r ,stdin-w) (isys:pipe)
- (multiple-value-bind (,stdout-r ,stdout-w) (isys:pipe)
- (multiple-value-bind (,stderr-r ,stderr-w) (isys:pipe)
- (unwind-protect-case ()
- (progn ,@body)
- ;; These are the FDs that we would use on the Lisp side
- (:abort
- (isys:close ,stdin-w)
- (isys:close ,stdout-r)
- (isys:close ,stderr-r))
- ;; These FDs are shared with the subprocess, must be closed always
- (:always
- (isys:close ,stdin-r)
- (isys:close ,stdout-w)
- (isys:close ,stderr-w)))))))
-
-(defun create-process (program &optional arguments &key (search t) environment
- ;; path uid gid effective
- )
- (with-posix-spawn-arguments (attributes file-actions pid)
- (with-argv (argv program arguments)
- (with-c-environment (environment)
- (with-3-pipes (stdin-r stdin-w stdout-r stdout-w stderr-r stderr-w)
- (posix-spawn-file-actions-adddup2 file-actions stdin-r +stdin+)
- (posix-spawn-file-actions-adddup2 file-actions stdout-w +stdout+)
- (posix-spawn-file-actions-adddup2 file-actions stderr-w +stderr+)
- (with-foreign-string (cfile program)
- (if search
- (posix-spawnp pid cfile file-actions attributes argv isys:*environ*)
- (posix-spawn pid cfile file-actions attributes argv isys:*environ*)))
- (make-instance 'process :pid (mem-ref pid 'pid-t)
- :stdin stdin-w :stdout stdout-r :stderr stderr-r))))))
-
-(defun run-program (program &optional arguments &key (search t) environment)
+
+(defun create-process (program arguments &key stdin stdout stderr union-stdout-stderr environment search)
+ (flet ((create-pipe (flag)
+ (if flag
+ (multiple-value-bind (rd wr) (isys:pipe)
+ (cons rd wr))))
+ (pipe-rd (pipe) (car pipe))
+ (pipe-wr (pipe) (cdr pipe))
+ (close-fd (fd)
+ (when fd
+ (isys:close fd))))
+ (let ((p-stdin (create-pipe stdin))
+ (p-stdout (create-pipe (or stdout
+ union-stdout-stderr)))
+ (p-stderr (create-pipe (if (not union-stdout-stderr)
+ stderr))))
+ (unwind-protect-case ()
+ (with-posix-spawn-arguments (attributes file-actions pid)
+ (with-argv (argv program arguments)
+ (with-c-environment (environment)
+ (when stdin
+ (posix-spawn-file-actions-addclose file-actions (pipe-wr p-stdin))
+ (posix-spawn-file-actions-adddup2 file-actions (pipe-rd p-stdin) +stdin+)
+ (posix-spawn-file-actions-addclose file-actions (pipe-rd p-stdin)))
+ (cond
+ (union-stdout-stderr
+ (posix-spawn-file-actions-addclose file-actions (pipe-rd p-stdout))
+ (posix-spawn-file-actions-adddup2 file-actions (pipe-wr p-stdout) +stdout+)
+ (posix-spawn-file-actions-adddup2 file-actions (pipe-wr p-stdout) +stderr+)
+ (posix-spawn-file-actions-addclose file-actions (pipe-wr p-stdout)))
+ (t (when stdout
+ (posix-spawn-file-actions-addclose file-actions (pipe-rd p-stdout))
+ (posix-spawn-file-actions-adddup2 file-actions (pipe-wr p-stdout) +stdout+)
+ (posix-spawn-file-actions-addclose file-actions (pipe-wr p-stdout)))
+ (when stderr
+ (posix-spawn-file-actions-addclose file-actions (pipe-rd p-stderr))
+ (posix-spawn-file-actions-adddup2 file-actions (pipe-wr p-stderr) +stderr+)
+ (posix-spawn-file-actions-addclose file-actions (pipe-wr p-stderr)))))
+ (with-foreign-string (cfile program)
+ (if search
+ (posix-spawnp pid cfile file-actions attributes argv isys:*environ*)
+ (posix-spawn pid cfile file-actions attributes argv isys:*environ*)))
+ (make-instance 'process
+ :pid (mem-ref pid 'pid-t)
+ :stdin (pipe-wr p-stdin)
+ :stdout (pipe-rd p-stdout)
+ :stderr (pipe-rd p-stderr)))))
+ ;; These are the FDs that we would use on the Lisp side
+ (:abort
+ (close-fd (pipe-wr p-stdin))
+ (close-fd (pipe-rd p-stdout))
+ (close-fd (pipe-rd p-stderr)))
+ ;; These FDs are shared with the subprocess, must be closed always
+ (:always
+ (close-fd (pipe-rd p-stdin))
+ (close-fd (pipe-wr p-stdout))
+ (close-fd (pipe-wr p-stderr)))))))
+
+(defun create-process-sh (cmd &key stdin stdout stderr union-stdout-stderr environment)
+ (create-process "/bin/sh"
+ (list "-c" cmd)
+ :stdin stdin
+ :stdout stdout
+ :stderr stderr
+ :union-stdout-stderr union-stdout-stderr
+ :environment environment
+ :search nil))
+
+(defmacro with-child-process ((process cmd &key stdin stdout stderr union-stdout-stderr environment) &body body)
+ `(let ((,process (create-process-sh ,cmd
+ :stdin ,stdin
+ :stdout ,stdout
+ :stderr ,stderr
+ :union-stdout-stderr ,union-stdout-stderr
+ :environment ,environment)))
+ (unwind-protect
+ (progn ,@body)
+ (close ,process))))
+
+(defun popen (cmd &key environment)
(flet ((slurp-stream-into-string (stream)
(with-output-to-string (s)
(loop :for c := (read-char stream nil nil)
- :while c :do (write-char c s)))))
- (let ((process (create-process program arguments
- :search search
- :environment environment)))
- (values (process-wait process)
- (slurp-stream-into-string (process-stdout process))
- (slurp-stream-into-string (process-stderr process))))))
+ :while c :do (write-char c s)))))
+ (with-child-process (proc cmd :stdout t :stderr t :environment environment)
+ (values (slurp-stream-into-string (process-stdout proc))
+ (slurp-stream-into-string (process-stderr proc))))))
-(defun process-wait (process)
- (isys:waitpid (process-pid process) 0))
-(defun process-kill (process signum)
- (isys:kill (process-pid process) signum))
diff --git a/src/os/pkgdcl.lisp b/src/os/pkgdcl.lisp
index 65b36b5..046ff31 100644
--- a/src/os/pkgdcl.lisp
+++ b/src/os/pkgdcl.lisp
@@ -22,10 +22,12 @@
#:process
#:process-pid
#:process-stdin
+ #:process-stdin-close
#:process-stdout
#:process-stderr
#:create-process
- #:run-program
+ #:with-child-process
+ #:popen
#:process-wait
#:process-kill
_______________________________________________
IOLib-devel mailing list
[email protected]
http://common-lisp.net/cgi-bin/mailman/listinfo/iolib-devel