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-show-window
+ #x001
)
36 (defconstant +startf-use-std-handles
+ #x100
)
38 (defconstant +sw-hide
+ 0)
39 (defconstant +sw-show-normal
+ 1)
40 (defconstant +sw-show-minimized
+ 2)
41 (defconstant +sw-show-maximized
+ 3)
42 (defconstant +sw-show-no-activate
+ 4)
43 (defconstant +sw-show-min-no-active
+ 7)
44 (defconstant +sw-show-na
+ 8)
46 (define-alien-routine ("CreateProcessW" create-process
) lispbool
47 (application-name system-string
)
48 (command-line system-string
)
49 (process-security-attributes (* t
))
50 (thread-security-attributes (* t
))
51 (inherit-handles-p lispbool
)
52 (creation-flags dword
)
54 (current-directory system-string
)
56 (process-information (* t
)))
58 (defun search-path (partial-name)
59 "Searh executable using the system path"
60 (with-alien ((pathname-buffer pathname-buffer
))
61 (syscall (("SearchPath" t
) dword
69 (values (decode-system-string pathname-buffer
) result
))
71 max_path
(cast pathname-buffer
(* char
)) nil
)))
73 (define-alien-routine ("GetProcessId" get-process-id
) dword
76 (define-alien-routine ("GetExitCodeProcess" get-exit-code-process
) int
77 (handle handle
) (exit-code dword
:out
))
79 (define-alien-routine ("GetExitCodeThread" get-exit-code-thread
) int
80 (handle handle
) (exit-code dword
:out
))
82 (define-alien-routine ("TerminateProcess" terminate-process
) boolean
86 (defmacro zero-alien
(alien type
)
87 `(alien-funcall (extern-alien "memset" (function void system-area-pointer int unsigned
))
88 (alien-sap ,alien
) 0 (alien-size ,type
:bytes
)))
90 (defun mswin-spawn (program argv stdin stdout stderr searchp envp directory window preserve-handles
)
91 (let ((std-handles (multiple-value-list (get-std-handles)))
93 (flet ((maybe-std-handle (arg)
94 (let ((default (pop std-handles
)))
95 (case arg
(-1 default
) (otherwise (setf inheritp t
) arg
)))))
96 (when preserve-handles
98 (loop for handle in preserve-handles
99 do
(setf (inheritable-handle-p handle
) t
)))
100 (with-alien ((process-information process-information
)
101 (startup-info startup-info
))
102 (zero-alien startup-info startup-info
)
103 (setf (slot startup-info
'cb
) (alien-size startup-info
:bytes
)
104 (slot startup-info
'stdin
) (maybe-std-handle stdin
)
105 (slot startup-info
'stdout
) (maybe-std-handle stdout
)
106 (slot startup-info
'stderr
) (maybe-std-handle stderr
)
107 (slot startup-info
'reserved1
) nil
108 (slot startup-info
'reserved2
) 0
109 (slot startup-info
'reserved3
) nil
110 (slot startup-info
'show-window
) (ecase window
112 (:show-normal
+sw-show-normal
+)
113 (:show-maximized
+sw-show-maximized
+)
114 (:show-minimized
+sw-show-minimized
+)
115 (:show-no-activate
+sw-show-no-activate
+)
116 (:show-min-no-active
+sw-show-min-no-active
+)
117 (:show-na
+sw-show-na
+)
119 (slot startup-info
'flags
) (logior (if inheritp
+startf-use-std-handles
+ 0)
120 (if window
+startf-use-show-window
+ 0)))
123 ;; KLUDGE: pass null image file name when searchp is true.
124 ;; This way, file extension gets resolved by OS if omitted.
125 (if (create-process (if searchp nil program
)
128 inheritp
0 envp directory
129 (alien-sap startup-info
)
130 (alien-sap process-information
))
131 (let ((child (slot process-information
'process-handle
)))
132 (close-handle (slot process-information
'thread-handle
))
133 (values (get-process-id child
) child
))
136 (define-alien-routine ("SetConsoleCtrlHandler" set-console-ctrl-handler
) int
137 (callback (function (:stdcall int
) int
))
140 (defun windows-console-control-handler (event-code)
143 (flet ((interrupt-it ()
145 (sb-di::nth-interrupt-context
146 (1- sb-kernel
:*free-interrupt-context-index
*)))
147 (pc (sb-vm:context-pc context
)))
149 (let ((int (make-condition
150 'interactive-interrupt
152 :address
(sb-sys:sap-int pc
))))
153 ;; First SIGNAL, so that handlers can run.
155 (%break
'sigint int
))))))
156 (sb-thread:interrupt-thread
(sb-thread::foreground-thread
)
160 (defvar *console-control-handler
* #'windows-console-control-handler
)
161 (defvar *console-control-enabled
* nil
)
162 (defvar *console-control-spec
* nil
)
164 (define-alien-callable alien-console-control-handler
(:stdcall int
)
166 (if (ignore-errors (funcall *console-control-handler
* event-code
)) 1 0))
168 (defun console-control-handler ()
169 "Get or set windows console control handler.
171 Boolean value: use default handler (NIL) or ignore event (T). Symbol
172 or function: designator for a function that receives an event code and
173 returns generalized boolean (false to fall back to other handlers,
174 true to stop searching)." *console-control-spec
*)
176 (defun (setf console-control-handler
) (new-handler)
177 (etypecase new-handler
179 (aver (plusp (set-console-ctrl-handler
180 (sap-alien (int-sap 0)
181 (function (:stdcall int
) int
))
182 (if new-handler
1 0))))
183 (setf *console-control-enabled
* nil
))
184 ((or symbol function
)
185 (setf *console-control-handler
* new-handler
)
186 (aver (plusp (set-console-ctrl-handler (alien-callable-function 'alien-console-control-handler
) 1)))))
187 (setf *console-control-spec
* new-handler
))
189 (defun initialize-console-control-handler (&optional reset-to-default-p
)
190 (setf (console-control-handler)
191 (if reset-to-default-p
192 'windows-console-control-handler
193 (console-control-handler))))
195 (initialize-console-control-handler t
)
196 (pushnew 'initialize-console-control-handler sb-ext
:*init-hooks
*)
198 ;;;; I/O copying for run-program
199 ;;;; Anonymous pipes do not support overlapped I/O,
200 ;;;; named pipes are to be used instead.
202 (define-alien-type overlapped
205 (internal-high (* ulong
))
210 (define-alien-routine ("CreateNamedPipeW" create-named-pipe
) handle
214 (max-instances dword
)
215 (out-buffer-size dword
)
216 (in-buffer-size dword
)
217 (default-time-out dword
)
218 (security-attributes (* t
)))
220 (define-alien-routine ("WaitForSingleObject" wait-for-single-object
) dword
224 (define-alien-routine ("CreateEventW" create-event
) handle
225 (security-attributes (* t
))
226 (manual-reset lispbool
)
227 (initial-state lispbool
)
228 (name system-string
))
230 (define-alien-routine ("GetOverlappedResult" get-overlapped-result
) lispbool
233 (bytes-transferred dword
:out
)
236 (defun maybe-win32-error (result)
237 (when (minusp result
)
241 (defglobal **run-program-pipe-counter
** 0)
242 (declaim (type fixnum
**run-program-pipe-counter
**))
244 (defun make-named-pipe ()
245 (let* ((name (format nil
"\\\\.\\pipe\\SBCL-~a-~a"
246 (sb-unix:unix-getpid
)
247 (sb-ext:atomic-incf
**run-program-pipe-counter
**)))
248 (pipe (maybe-win32-error
249 (create-named-pipe name
250 (logior pipe-access-inbound file-flag-overlapped
)
253 (multiple-value-bind (fd error
) (sb-unix:unix-open name sb-unix
:o_wronly
256 (win32-error "open" error
))
259 (define-alien-routine ("win32_wait_for_multiple_objects_or_signal"
260 wait-for-multiple-objects-or-signal
)
273 (defconstant +copier-buffer
+ 256)
275 (defun setup-copiers (copiers)
276 (let ((result (make-array (length copiers
))))
277 (loop for copier in copiers
280 (let ((overlapped (make-alien overlapped
))
281 (event (create-event nil t nil nil
)))
282 (setf (io-copier-event copier
) event
283 (io-copier-overlapped copier
) overlapped
284 (io-copier-buffer copier
) (make-alien char
+copier-buffer
+)
285 (svref result i
) copier
)
286 (zero-alien overlapped overlapped
)
287 (setf (slot overlapped
'event
) event
)))
290 (defun free-copier (copier)
291 (close-handle (io-copier-pipe copier
))
292 (when (io-copier-event copier
)
293 (close-handle (io-copier-event copier
)))
294 (when (io-copier-overlapped copier
)
295 (free-alien (io-copier-overlapped copier
)))
296 (when (io-copier-buffer copier
)
297 (free-alien (io-copier-buffer copier
))))
299 (defun win32-process-wait (process)
300 (let ((handle (sb-impl::process-handle process
))
301 (copiers (sb-impl::process-copiers process
)))
306 ;; Should be enough for stdout, stderr, handle,
307 ;; and the signal event
309 (let ((copiers (setup-copiers copiers
))
310 (count (length copiers
))
311 (lisp-buffer (make-array +copier-buffer
+ :element-type
'(unsigned-byte 8))))
312 (loop for i below count
313 do
(setf (deref events i
)
314 (io-copier-event (svref copiers i
))))
315 (setf (deref events count
) handle
)
316 (labels ((pending-or-error (operation
317 &optional
(error (get-last-error)))
318 (when (/= error error-io-pending
)
319 (win32-error operation error
)))
322 (read-file (io-copier-pipe copier
)
323 (io-copier-buffer copier
)
326 (io-copier-overlapped copier
)))
330 (let ((last-error (get-last-error)))
331 (unless (= last-error error-broken-pipe
)
332 (pending-or-error "ReadFile" last-error
))))))
334 (let* ((stream (io-copier-stream copier
))
335 (element-type (stream-element-type stream
)))
336 (multiple-value-bind (finished count
)
337 (get-overlapped-result (io-copier-pipe copier
)
338 (io-copier-overlapped copier
) nil
)
340 (loop for i below count
341 do
(setf (aref lisp-buffer i
)
342 (deref (io-copier-buffer copier
) i
)))
344 ((member element-type
'(base-char character
))
346 (octets-to-string lisp-buffer
349 (io-copier-external-format copier
))
355 (error 'simple-type-error
357 "Error using ~s for program output:~@
362 (type-error-expected-type c
)
364 (type-error-datum c
)))))
365 (write-sequence lisp-buffer stream
:end count
)))))
367 (let ((last-error (get-last-error)))
368 (unless (= last-error error-broken-pipe
)
369 (pending-or-error "GetOverlappedResult" last-error
)))))))))
370 (loop for copier across copiers
371 do
(try-read copier
))
372 (loop for event
= (wait-for-multiple-objects-or-signal (cast events
376 (cond ((= event wait-timeout
))
378 (let ((copier (svref copiers event
)))
381 ((= event count
) ;; HANDLE event
383 ((= event wait-failed
)
384 (win32-error "WaitForMultipleObjects")))))))
385 (mapc #'free-copier copiers
)))
389 (wait-object-or-signal handle
))))))
390 (sb-impl::get-processes-status-changes
)))