1 ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
3 ;;; --- Wrapper over posix_spawn(3)
9 ((pid :initarg
:pid
:reader process-pid
)
10 (stdin :initform nil
:reader process-stdin
)
11 (stdout :initform nil
:reader process-stdout
)
12 (stderr :initform nil
:reader process-stderr
)))
14 (defmethod initialize-instance :after
((process process
) &key stdin stdout stderr
)
15 (with-slots ((in stdin
) (out stdout
) (err stderr
))
17 (setf in
(make-instance 'iolib.streams
:dual-channel-gray-stream
19 out
(make-instance 'iolib.streams
:dual-channel-gray-stream
21 err
(make-instance 'iolib.streams
:dual-channel-gray-stream
24 (defmethod close ((process process
) &key abort
)
25 (with-slots (pid stdin stdout stderr
)
27 (close stdin
:abort abort
)
28 (close stdout
:abort abort
)
29 (close stderr
:abort abort
)
30 (setf pid nil stdin nil stdout nil stderr nil
)))
32 (defmethod print-object ((o process
) s
)
33 (print-unreadable-object (o s
:type t
:identity t
)
34 (format s
":pid ~S" (process-pid o
))))
36 (defmacro with-posix-spawn-arguments
((attributes file-actions pid
) &body body
)
37 (with-gensyms (spawnattr-initialized-p file-actions-initialized-p
)
38 `(with-foreign-objects ((,attributes
'posix-spawnattr-t
)
39 (,file-actions
'posix-spawn-file-actions-t
)
41 (let (,spawnattr-initialized-p
42 ,file-actions-initialized-p
)
45 (posix-spawnattr-init ,attributes
)
46 (setf ,spawnattr-initialized-p t
)
47 (posix-spawn-file-actions-init ,file-actions
)
48 (setf ,file-actions-initialized-p t
)
50 (when ,spawnattr-initialized-p
51 (posix-spawnattr-destroy ,attributes
))
52 (when ,file-actions-initialized-p
53 (posix-spawn-file-actions-destroy ,file-actions
)))))))
55 (defun allocate-argv (argv argc program arglist
)
57 (setf (mem-aref argv
:pointer
0)
58 (foreign-string-alloc program
))
59 ;; copy program arguments
61 :for arg
:in arglist
:do
62 (setf (mem-aref argv
:pointer i
)
63 (foreign-string-alloc arg
)))
65 (setf (mem-aref argv
:pointer
(1- argc
)) (null-pointer)))
67 (defmacro with-argv
((argv program arguments
) &body body
)
69 `(let ((,argc
(+ 2 (length ,arguments
))))
70 (with-foreign-object (,argv
:pointer
,argc
)
73 (allocate-argv ,argv
,argc
,program
,arguments
)
75 (deallocate-null-ended-list ,argv
))))))
77 (defmacro with-3-pipes
((stdin-r stdin-w stdout-r stdout-w stderr-r stderr-w
)
79 `(multiple-value-bind (,stdin-r
,stdin-w
) (isys:pipe
)
80 (multiple-value-bind (,stdout-r
,stdout-w
) (isys:pipe
)
81 (multiple-value-bind (,stderr-r
,stderr-w
) (isys:pipe
)
82 (unwind-protect-case ()
84 ;; These are the FDs that we would use on the Lisp side
87 (isys:close
,stdout-r
)
88 (isys:close
,stderr-r
))
89 ;; These FDs are shared with the subprocess, must be closed always
92 (isys:close
,stdout-w
)
93 (isys:close
,stderr-w
)))))))
95 (defun create-process (program &optional arguments
&key
(search t
) environment
96 ;; path uid gid effective
98 (with-posix-spawn-arguments (attributes file-actions pid
)
99 (with-argv (argv program arguments
)
100 (with-c-environment (environment)
101 (with-3-pipes (stdin-r stdin-w stdout-r stdout-w stderr-r stderr-w
)
102 (posix-spawn-file-actions-adddup2 file-actions stdin-r
+stdin
+)
103 (posix-spawn-file-actions-adddup2 file-actions stdout-w
+stdout
+)
104 (posix-spawn-file-actions-adddup2 file-actions stderr-w
+stderr
+)
105 (with-foreign-string (cfile program
)
107 (posix-spawnp pid cfile file-actions attributes argv isys
:*environ
*)
108 (posix-spawn pid cfile file-actions attributes argv isys
:*environ
*)))
109 (make-instance 'process
:pid
(mem-ref pid
'pid-t
)
110 :stdin stdin-w
:stdout stdout-r
:stderr stderr-r
))))))
112 (defun run-program (program &optional arguments
&key
(search t
) environment
)
113 (flet ((slurp-stream-into-string (stream)
114 (with-output-to-string (s)
115 (loop :for c
:= (read-char stream nil nil
)
116 :while c
:do
(write-char c s
)))))
117 (let ((process (create-process program arguments
119 :environment environment
)))
120 (values (process-wait process
)
121 (slurp-stream-into-string (process-stdout process
))
122 (slurp-stream-into-string (process-stderr process
))))))
124 (defun process-wait (process)
125 (isys:waitpid
(process-pid process
) 0))
127 (defun process-kill (process signum
)
128 (isys:kill
(process-pid process
) signum
))