1 ;;;; Windows API bindings not needed for cold initialization.
2 (in-package "SB-WIN32")
4 ;;;; CreateProcess and surrounding data structures provide a way to implement
5 ;;;; RUN-PROGRAM while using handles rather than file descriptors.
7 (define-alien-type process-information
8 (struct process-information
9 (process-handle handle
)
10 (thread-handle handle
)
14 (define-alien-type startup-info
17 (reserved1 system-string
)
18 (desktop system-string
)
26 (fill-attribute dword
)
28 (show-window unsigned-short
)
29 (reserved2 unsigned-short
)
35 (defconstant +startf-use-std-handles
+ #x100
)
37 (define-alien-routine ("CreateProcessW" create-process
) lispbool
38 (application-name system-string
)
39 (command-line system-string
)
40 (process-security-attributes (* t
))
41 (thread-security-attributes (* t
))
42 (inherit-handles-p lispbool
)
43 (creation-flags dword
)
45 (current-directory system-string
)
47 (process-information (* t
)))
49 (defun search-path (partial-name)
50 "Searh executable using the system path"
51 (with-alien ((pathname-buffer pathname-buffer
))
52 (syscall (("SearchPath" t
) dword
60 (values (decode-system-string pathname-buffer
) result
))
62 max_path
(cast pathname-buffer
(* char
)) nil
)))
64 (define-alien-routine ("GetExitCodeProcess" get-exit-code-process
) int
65 (handle handle
) (exit-code dword
:out
))
67 (define-alien-routine ("GetExitCodeThread" get-exit-code-thread
) int
68 (handle handle
) (exit-code dword
:out
))
70 (defun mswin-spawn (program argv stdin stdout stderr searchp envp waitp
72 (let ((std-handles (multiple-value-list (get-std-handles)))
74 (flet ((maybe-std-handle (arg)
75 (let ((default (pop std-handles
)))
76 (case arg
(-1 default
) (otherwise (setf inheritp t
) arg
)))))
77 (with-alien ((process-information process-information
)
78 (startup-info startup-info
))
79 (sb-kernel:system-area-ub8-fill
80 0 (alien-sap startup-info
)
81 0 (alien-size startup-info
:bytes
))
82 (setf (slot startup-info
'cb
) (alien-size startup-info
:bytes
)
83 (slot startup-info
'stdin
) (maybe-std-handle stdin
)
84 (slot startup-info
'stdout
) (maybe-std-handle stdout
)
85 (slot startup-info
'stderr
) (maybe-std-handle stderr
)
86 (slot startup-info
'reserved1
) nil
87 (slot startup-info
'reserved2
) 0
88 (slot startup-info
'reserved3
) nil
89 (slot startup-info
'flags
) (if inheritp
+startf-use-std-handles
+ 0))
91 ;; KLUDGE: pass null image file name when searchp is true.
92 ;; This way, file extension gets resolved by OS if omitted.
93 (if (create-process (if searchp nil program
)
96 inheritp
0 envp directory
97 (alien-sap startup-info
)
98 (alien-sap process-information
))
99 (let ((child (slot process-information
'process-handle
)))
100 (close-handle (slot process-information
'thread-handle
))
104 (define-alien-routine ("SetConsoleCtrlHandler" set-console-ctrl-handler
) int
105 (callback (function (:stdcall int
) int
))
108 (defun windows-console-control-handler (event-code)
111 (flet ((interrupt-it ()
113 (sb-di::nth-interrupt-context
114 (1- sb-kernel
:*free-interrupt-context-index
*)))
115 (pc (sb-vm:context-pc context
)))
117 (let ((int (make-condition
118 'interactive-interrupt
120 :address
(sb-sys:sap-int pc
))))
121 ;; First SIGNAL, so that handlers can run.
123 (%break
'sigint int
))))))
124 (sb-thread:interrupt-thread
(sb-thread::foreground-thread
)
128 (defvar *console-control-handler
* #'windows-console-control-handler
)
129 (defvar *console-control-enabled
* nil
)
130 (defvar *console-control-spec
* nil
)
132 (sb-alien::define-alien-callback
*alien-console-control-handler
* (:stdcall int
)
134 (if (ignore-errors (funcall *console-control-handler
* event-code
)) 1 0))
136 (defun console-control-handler ()
137 "Get or set windows console control handler.
139 Boolean value: use default handler (NIL) or ignore event (T). Symbol
140 or function: designator for a function that receives an event code and
141 returns generalized boolean (false to fall back to other handlers,
142 true to stop searching)." *console-control-spec
*)
144 (defun (setf console-control-handler
) (new-handler)
145 (etypecase new-handler
147 (aver (plusp (set-console-ctrl-handler
148 (sap-alien (int-sap 0)
149 (function (:stdcall int
) int
))
150 (if new-handler
1 0))))
151 (setf *console-control-enabled
* nil
))
152 ((or symbol function
)
153 (setf *console-control-handler
* new-handler
)
154 (aver (plusp (set-console-ctrl-handler *alien-console-control-handler
* 1)))))
155 (setf *console-control-spec
* new-handler
))
157 (defun initialize-console-control-handler (&optional reset-to-default-p
)
158 (setf (console-control-handler)
159 (if reset-to-default-p
160 'windows-console-control-handler
161 (console-control-handler))))
163 (initialize-console-control-handler t
)
164 (pushnew 'initialize-console-control-handler sb-ext
:*init-hooks
*)
166 ;;;; I/O copying for run-program
167 ;;;; Anonymous pipes do not support overlapped I/O,
168 ;;;; named pipes are to be used instead.
170 (define-alien-type overlapped
173 (internal-high (* ulong
))
178 (define-alien-routine ("CreateNamedPipeW" create-named-pipe
) handle
182 (max-instances dword
)
183 (out-buffer-size dword
)
184 (in-buffer-size dword
)
185 (default-time-out dword
)
186 (security-attributes (* t
)))
188 (define-alien-routine ("WaitForSingleObject" wait-for-single-object
) dword
192 (define-alien-routine ("CreateEventW" create-event
) handle
193 (security-attributes (* t
))
194 (manual-reset lispbool
)
195 (initial-state lispbool
)
196 (name system-string
))
198 (define-alien-routine ("GetOverlappedResult" get-overlapped-result
) lispbool
201 (bytes-transferred dword
:out
)
204 (defun maybe-win32-error (result)
205 (when (minusp result
)
209 (defglobal **run-program-pipe-counter
** 0)
210 (declaim (type fixnum
**run-program-pipe-counter
**))
212 (defun make-named-pipe ()
213 (let* ((name (format nil
"\\\\.\\pipe\\SBCL-~a-~a"
214 (sb-unix:unix-getpid
)
215 (sb-ext:atomic-incf
**run-program-pipe-counter
**)))
216 (pipe (maybe-win32-error
217 (create-named-pipe name
218 (logior pipe-access-inbound file-flag-overlapped
)
221 (multiple-value-bind (fd error
) (sb-unix:unix-open name sb-unix
:o_wronly
224 (win32-error "open" error
))
227 (define-alien-routine ("win32_wait_for_multiple_objects_or_signal"
228 wait-for-multiple-objects-or-signal
)
241 (defconstant +copier-buffer
+ 256)
243 (defmacro zero-alien
(alien type
)
244 `(sb-kernel:system-area-ub8-fill
246 0 (alien-size ,type
:bytes
)))
248 (defun setup-copiers (copiers)
249 (let ((result (make-array (length copiers
))))
250 (loop for copier in copiers
253 (let ((overlapped (make-alien overlapped
))
254 (event (create-event nil t nil nil
)))
255 (setf (io-copier-event copier
) event
256 (io-copier-overlapped copier
) overlapped
257 (io-copier-buffer copier
) (make-alien char
+copier-buffer
+)
258 (svref result i
) copier
)
259 (zero-alien overlapped overlapped
)
260 (setf (slot overlapped
'event
) event
)))
263 (defun free-copier (copier)
264 (close-handle (io-copier-pipe copier
))
265 (when (io-copier-event copier
)
266 (close-handle (io-copier-event copier
)))
267 (when (io-copier-overlapped copier
)
268 (free-alien (io-copier-overlapped copier
)))
269 (when (io-copier-buffer copier
)
270 (free-alien (io-copier-buffer copier
))))
272 (defun win32-process-wait (process)
273 (let ((pid (process-pid process
))
274 (copiers (sb-impl::process-copiers process
)))
278 ;; Should be enough for stdout, stderr, pid,
279 ;; and the signal event
281 (let ((copiers (setup-copiers copiers
))
282 (count (length copiers
))
283 (lisp-buffer (make-array +copier-buffer
+ :element-type
'(unsigned-byte 8))))
284 (loop for i below count
285 do
(setf (deref events i
)
286 (io-copier-event (svref copiers i
))))
287 (setf (deref events count
) pid
)
288 (labels ((pending-or-error (operation
289 &optional
(error (get-last-error)))
290 (when (/= error error-io-pending
)
291 (win32-error operation error
)))
294 (read-file (io-copier-pipe copier
)
295 (io-copier-buffer copier
)
298 (io-copier-overlapped copier
)))
302 (let ((last-error (get-last-error)))
303 (unless (= last-error error-broken-pipe
)
304 (pending-or-error "ReadFile" last-error
))))))
306 (let* ((stream (io-copier-stream copier
))
307 (element-type (stream-element-type stream
)))
308 (multiple-value-bind (finished count
)
309 (get-overlapped-result (io-copier-pipe copier
)
310 (io-copier-overlapped copier
) nil
)
312 (loop for i below count
313 do
(setf (aref lisp-buffer i
)
314 (deref (io-copier-buffer copier
) i
)))
316 ((member element-type
'(base-char character
))
318 (octets-to-string lisp-buffer
321 (io-copier-external-format copier
))
327 (error 'simple-type-error
329 "Error using ~s for program output:~@
334 (type-error-expected-type c
)
336 (type-error-datum c
)))))
337 (write-sequence lisp-buffer stream
:end count
)))))
339 (let ((last-error (get-last-error)))
340 (unless (= last-error error-broken-pipe
)
341 (pending-or-error "ReadFile" last-error
)))))))))
342 (loop for copier across copiers
343 do
(try-read copier
))
344 (loop for event
= (wait-for-multiple-objects-or-signal (cast events
348 (cond ((= event wait-timeout
))
350 (let ((copier (svref copiers event
)))
353 ((= event count
) ;; PID event
355 ((= event wait-failed
)
356 (win32-error "WaitForMultipleObjects")))))))
357 (mapc #'free-copier copiers
)))
361 (wait-object-or-signal pid
))))))
362 (multiple-value-bind (ok code
) (get-exit-code-process pid
)
363 (when (and (plusp ok
) (/= code still-active
))
364 (setf (sb-impl::process-%status process
) :exited
365 (sb-impl::process-%exit-code process
) code
))))