/[cmucl]/src/code/run-program.lisp
ViewVC logotype

Contents of /src/code/run-program.lisp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.26.32.3.2.1 - (show annotations)
Sun Nov 2 13:30:01 2008 UTC (5 years, 5 months ago) by rtoy
Branch: unicode-utf16-extfmt-branch
CVS Tags: unicode-utf16-extfmt-2009-03-27, unicode-snapshot-2009-05, unicode-snapshot-2009-06, unicode-utf16-extfmts-sync-2008-12, unicode-utf16-extfmt-2009-06-11
Changes since 1.26.32.3: +20 -31 lines
Sync to snapshot 2008-11.  But we didn't sync code/extfmts.lisp or
iso8859-1.lisp and utf-8.lisp.  (I don't think we want those changes,
yet.)
1 ;;; -*- Package: Extensions; Log: code.log -*-
2 ;;;
3 ;;; **********************************************************************
4 ;;; This code was written as part of the CMU Common Lisp project at
5 ;;; Carnegie Mellon University, and has been placed in the public domain.
6 ;;;
7 (ext:file-comment
8 "$Header: /tiger/var/lib/cvsroots/cmucl/src/code/run-program.lisp,v 1.26.32.3.2.1 2008/11/02 13:30:01 rtoy Exp $")
9 ;;;
10 ;;; **********************************************************************
11 ;;;
12 ;;; RUN-PROGRAM and friends. Facility for running unix programs from inside
13 ;;; a lisp.
14 ;;;
15 ;;; Written by Jim Healy and Bill Chiles, November 1987, using an earlier
16 ;;; version written by David McDonald.
17 ;;;
18 ;;; Completely re-written by William Lott, July 1989 - January 1990.
19 ;;;
20
21 (in-package "EXTENSIONS")
22
23 (export '(run-program process-status process-exit-code process-core-dumped
24 process-wait process-kill process-input process-output process-plist
25 process-pty process-error process-status-hook process-alive-p
26 process-close process-pid process-p))
27
28
29 ;;;; Import WAIT3 from unix.
30
31 (alien:def-alien-routine ("wait3" c-wait3) c-call:int
32 (status c-call:int :out)
33 (options c-call:int)
34 (rusage c-call:int))
35
36 (eval-when (load eval compile)
37 (defconstant wait-wnohang #-svr4 1 #+svr4 #o100)
38 (defconstant wait-wuntraced #-svr4 2 #+svr4 4)
39 (defconstant wait-wstopped #-svr4 #o177 #+svr4 wait-wuntraced))
40
41 (defun wait3 (&optional do-not-hang check-for-stopped)
42 "Return any available status information on child processed. "
43 (multiple-value-bind (pid status)
44 (c-wait3 (logior (if do-not-hang
45 wait-wnohang
46 0)
47 (if check-for-stopped
48 wait-wuntraced
49 0))
50 0)
51 (cond ((or (minusp pid)
52 (zerop pid))
53 nil)
54 ((eql (ldb (byte 8 0) status)
55 wait-wstopped)
56 (values pid
57 :stopped
58 (ldb (byte 8 8) status)))
59 ((zerop (ldb (byte 7 0) status))
60 (values pid
61 :exited
62 (ldb (byte 8 8) status)))
63 (t
64 (let ((signal (ldb (byte 7 0) status)))
65 (values pid
66 (if (or (eql signal unix:sigstop)
67 (eql signal unix:sigtstp)
68 (eql signal unix:sigttin)
69 (eql signal unix:sigttou))
70 :stopped
71 :signaled)
72 signal
73 (not (zerop (ldb (byte 1 7) status)))))))))
74
75
76
77 ;;;; Process control stuff.
78
79 (defvar *active-processes* nil
80 "List of process structures for all active processes.")
81
82 (defstruct (process (:print-function %print-process))
83 pid ; PID of child process.
84 %status ; Either :RUNNING, :STOPPED, :EXITED, or :SIGNALED.
85 exit-code ; Either exit code or signal
86 core-dumped ; T if a core image was dumped.
87 pty ; Stream to child's pty or nil.
88 input ; Stream to child's input or nil.
89 output ; Stream from child's output or nil.
90 error ; Stream from child's error output or nil.
91 status-hook ; Closure to call when PROC changes status.
92 plist ; Place for clients to stash tings.
93 cookie ; List of the number of pipes from the subproc.
94 )
95
96 (defun %print-process (proc stream depth)
97 (declare (ignore depth))
98 (format stream "#<process ~D ~S>"
99 (process-pid proc)
100 (process-status proc)))
101
102 ;;; PROCESS-STATUS -- Public.
103 ;;;
104 (defun process-status (proc)
105 "Return the current status of process. The result is one of :running,
106 :stopped, :exited, :signaled."
107 (declare (type process proc))
108 (get-processes-status-changes)
109 (process-%status proc))
110
111
112 ;;; PROCESS-WAIT -- Public.
113 ;;;
114 (defun process-wait (proc &optional check-for-stopped)
115 "Wait for PROC to quit running for some reason. Returns PROC."
116 (declare (type process proc))
117 (loop
118 (case (process-status proc)
119 (:running)
120 (:stopped
121 (when check-for-stopped
122 (return)))
123 (t
124 (when (zerop (car (process-cookie proc)))
125 (return))))
126 (system:serve-all-events 1))
127 proc)
128
129
130 #-hpux
131 ;;; FIND-CURRENT-FOREGROUND-PROCESS -- internal
132 ;;;
133 ;;; Finds the current foreground process group id.
134 ;;;
135 (defun find-current-foreground-process (proc)
136 (alien:with-alien ((result c-call:int))
137 (multiple-value-bind
138 (wonp error)
139 (unix:unix-ioctl (system:fd-stream-fd (ext:process-pty proc))
140 unix:TIOCGPGRP
141 (alien:alien-sap (alien:addr result)))
142 (unless wonp
143 (error "TIOCPGRP ioctl failed: ~S"
144 (unix:get-unix-error-msg error)))
145 result))
146 (process-pid proc))
147
148
149 ;;; PROCESS-KILL -- public
150 ;;;
151 ;;; Hand a process a signal.
152 ;;;
153 (defun process-kill (proc signal &optional (whom :pid))
154 "Hand SIGNAL to PROC. If whom is :pid, use the kill Unix system call. If
155 whom is :process-group, use the killpg Unix system call. If whom is
156 :pty-process-group deliver the signal to whichever process group is currently
157 in the foreground."
158 (declare (type process proc))
159 (let ((pid (ecase whom
160 ((:pid :process-group)
161 (process-pid proc))
162 (:pty-process-group
163 #-hpux
164 (find-current-foreground-process proc)))))
165 (multiple-value-bind
166 (okay errno)
167 (case whom
168 #+hpux
169 (:pty-process-group
170 (unix:unix-ioctl (system:fd-stream-fd (process-pty proc))
171 unix:TIOCSIGSEND
172 (system:int-sap
173 (unix:unix-signal-number signal))))
174 ((:process-group #-hpux :pty-process-group)
175 (unix:unix-killpg pid signal))
176 (t
177 (unix:unix-kill pid signal)))
178 (cond ((not okay)
179 (values nil errno))
180 ((and (eql pid (process-pid proc))
181 (= (unix:unix-signal-number signal) unix:sigcont))
182 (setf (process-%status proc) :running)
183 (setf (process-exit-code proc) nil)
184 (when (process-status-hook proc)
185 (funcall (process-status-hook proc) proc))
186 t)
187 (t
188 t)))))
189
190
191 ;;; PROCESS-ALIVE-P -- public
192 ;;;
193 ;;; Returns T if the process is still alive, NIL otherwise.
194 ;;;
195 (defun process-alive-p (proc)
196 "Returns T if the process is still alive, NIL otherwise."
197 (declare (type process proc))
198 (let ((status (process-status proc)))
199 (if (or (eq status :running)
200 (eq status :stopped))
201 t
202 nil)))
203
204 ;;; PROCESS-CLOSE -- public
205 ;;;
206 ;;; Close all the streams held open by PROC.
207 ;;;
208 (defun process-close (proc)
209 "Close all streams connected to PROC and stop maintaining the status slot."
210 (declare (type process proc))
211 (macrolet ((frob (stream abort)
212 `(when ,stream (close ,stream :abort ,abort))))
213 (frob (process-pty proc) t) ; Don't FLUSH-OUTPUT to dead process.
214 (frob (process-input proc) t) ; 'cause it will generate SIGPIPE.
215 (frob (process-output proc) nil)
216 (frob (process-error proc) nil))
217 (system:without-interrupts
218 (setf *active-processes* (delete proc *active-processes*)))
219 proc)
220
221 ;;; SIGCHLD-HANDLER -- Internal.
222 ;;;
223 ;;; This is the handler for sigchld signals that RUN-PROGRAM establishes.
224 ;;;
225 (defun sigchld-handler (ignore1 ignore2 ignore3)
226 (declare (ignore ignore1 ignore2 ignore3))
227 (get-processes-status-changes))
228
229 ;;; GET-PROCESSES-STATUS-CHANGES -- Internal.
230 ;;;
231 (defun get-processes-status-changes ()
232 (loop
233 (multiple-value-bind (pid what code core)
234 (wait3 t t)
235 (unless pid
236 (return))
237 (let ((proc (find pid *active-processes* :key #'process-pid)))
238 (when proc
239 (setf (process-%status proc) what)
240 (setf (process-exit-code proc) code)
241 (setf (process-core-dumped proc) core)
242 (when (process-status-hook proc)
243 (funcall (process-status-hook proc) proc))
244 (when (or (eq what :exited)
245 (eq what :signaled))
246 (system:without-interrupts
247 (setf *active-processes*
248 (delete proc *active-processes*)))))))))
249
250
251
252 ;;;; RUN-PROGRAM and close friends.
253
254 (defvar *close-on-error* nil
255 "List of file descriptors to close when RUN-PROGRAM exits due to an error.")
256 (defvar *close-in-parent* nil
257 "List of file descriptors to close when RUN-PROGRAM returns in the parent.")
258 (defvar *handlers-installed* nil
259 "List of handlers installed by RUN-PROGRAM.")
260
261
262 ;;; FIND-A-PTY -- internal
263 ;;;
264 ;;; Finds a pty that is not in use. Returns three values: the file descriptor
265 ;;; for the master side of the pty, the file descriptor for the slave side of
266 ;;; the pty, and the name of the tty device for the slave side.
267 ;;;
268 #-irix
269 (defun find-a-pty ()
270 "Returns the master fd, the slave fd, and the name of the tty"
271 (multiple-value-bind (error master-fd slave-fd)
272 (unix:unix-openpty nil nil nil)
273 (when (zerop error)
274 #-glibc2
275 (alien:with-alien ((stuff (alien:struct unix:sgttyb)))
276 (let ((sap (alien:alien-sap stuff)))
277 (unix:unix-ioctl slave-fd unix:TIOCGETP sap)
278 (setf (alien:slot stuff 'unix:sg-flags) #o300) ; EVENP|ODDP
279 (unix:unix-ioctl slave-fd unix:TIOCSETP sap)
280 (unix:unix-ioctl master-fd unix:TIOCGETP sap)
281 (setf (alien:slot stuff 'unix:sg-flags)
282 (logand (alien:slot stuff 'unix:sg-flags)
283 (lognot 8))) ; ~ECHO
284 (unix:unix-ioctl master-fd unix:TIOCSETP sap)))
285 (return-from find-a-pty
286 (values master-fd
287 slave-fd
288 (unix:unix-ttyname slave-fd))))
289 (error "Could not find a pty.")))
290
291 #+irix
292 (alien:def-alien-routine ("_getpty" c-getpty) c-call:c-string
293 (fildes c-call:int :out)
294 (oflag c-call:int)
295 (mode c-call:int)
296 (nofork c-call:int))
297
298 #+irix
299 (defun find-a-pty ()
300 "Returns the master fd, the slave fd, and the name of the tty"
301 (multiple-value-bind (line master-fd)
302 (c-getpty (logior unix:o_rdwr unix:o_ndelay) #o600 0)
303 (let* ((slave-name line)
304 (slave-fd (unix:unix-open slave-name unix:o_rdwr #o666)))
305 (when slave-fd
306 ; Maybe put a vhangup here?
307 #-glibc2
308 (alien:with-alien ((stuff (alien:struct unix:sgttyb)))
309 (let ((sap (alien:alien-sap stuff)))
310 (unix:unix-ioctl slave-fd unix:TIOCGETP sap)
311 (setf (alien:slot stuff 'unix:sg-flags) #o300) ; EVENP|ODDP
312 (unix:unix-ioctl slave-fd unix:TIOCSETP sap)
313 (unix:unix-ioctl master-fd unix:TIOCGETP sap)
314 (setf (alien:slot stuff 'unix:sg-flags)
315 (logand (alien:slot stuff 'unix:sg-flags)
316 (lognot 8))) ; ~ECHO
317 (unix:unix-ioctl master-fd unix:TIOCSETP sap)))
318 (return-from find-a-pty
319 (values master-fd
320 slave-fd
321 slave-name))))
322 (unix:unix-close master-fd))
323 (error "Could not find a pty."))
324
325 ;;; OPEN-PTY -- internal
326 ;;;
327 (defun open-pty (pty cookie)
328 (when pty
329 (multiple-value-bind
330 (master slave name)
331 (find-a-pty)
332 (push master *close-on-error*)
333 (push slave *close-in-parent*)
334 (when (streamp pty)
335 (multiple-value-bind (new-fd errno) (unix:unix-dup master)
336 (unless new-fd
337 (error "Could not UNIX:UNIX-DUP ~D: ~A"
338 master (unix:get-unix-error-msg errno)))
339 (push new-fd *close-on-error*)
340 (copy-descriptor-to-stream new-fd pty cookie)))
341 (values name
342 (system:make-fd-stream master :input t :output t)))))
343
344
345 (defmacro round-bytes-to-words (n)
346 `(logand (the fixnum (+ (the fixnum ,n) 3)) (lognot 3)))
347
348 (defun string-list-to-c-strvec (string-list)
349 ;;
350 ;; Make a pass over string-list to calculate the amount of memory
351 ;; needed to hold the strvec.
352 (let ((string-bytes 0)
353 ;; We need an extra for the null, and an extra 'cause exect clobbers
354 ;; argv[-1].
355 (vec-bytes (* #-alpha 4 #+alpha 8 (+ (length string-list) 2))))
356 (declare (fixnum string-bytes vec-bytes))
357 (dolist (s string-list)
358 (check-type s simple-string)
359 (incf string-bytes (round-bytes-to-words (1+ (length s)))))
360 ;;
361 ;; Now allocate the memory and fill it in.
362 (let* ((total-bytes (+ string-bytes vec-bytes))
363 (vec-sap (system:allocate-system-memory total-bytes))
364 (string-sap (sap+ vec-sap vec-bytes))
365 (i #-alpha 4 #+alpha 8))
366 (declare (type (and unsigned-byte fixnum) total-bytes i)
367 (type system:system-area-pointer vec-sap string-sap))
368 (dolist (s string-list)
369 (declare (simple-string s))
370 (let ((n (length s)))
371 ;;
372 ;; Blast the string into place
373 #-unicode
374 (kernel:copy-to-system-area (the simple-string s)
375 (* vm:vector-data-offset vm:word-bits)
376 string-sap 0
377 (* (1+ n) vm:byte-bits))
378 #+unicode
379 (progn
380 ;; FIXME: Do we need to apply some kind of transformation
381 ;; to convert Lisp unicode strings to C strings? Utf-8?
382 (dotimes (k n)
383 (setf (sap-ref-8 string-sap k)
384 (logand #xff (char-code (aref s k)))))
385 (setf (sap-ref-8 string-sap n) 0))
386 ;;
387 ;; Blast the pointer to the string into place
388 (setf (sap-ref-sap vec-sap i) string-sap)
389 (setf string-sap (sap+ string-sap (round-bytes-to-words (1+ n))))
390 (incf i #-alpha 4 #+alpha 8)))
391 ;; Blast in last null pointer
392 (setf (sap-ref-sap vec-sap i) (int-sap 0))
393 (values vec-sap (sap+ vec-sap #-alpha 4 #+alpha 8) total-bytes))))
394
395
396 (defmacro with-c-strvec ((var str-list) &body body)
397 (let ((sap (gensym "SAP-"))
398 (size (gensym "SIZE-")))
399 `(multiple-value-bind
400 (,sap ,var ,size)
401 (string-list-to-c-strvec ,str-list)
402 (unwind-protect
403 (progn
404 ,@body)
405 (system:deallocate-system-memory ,sap ,size)))))
406
407 (alien:def-alien-routine spawn c-call:int
408 (program c-call:c-string)
409 (argv (* c-call:c-string))
410 (envp (* c-call:c-string))
411 (pty-name c-call:c-string)
412 (stdin c-call:int)
413 (stdout c-call:int)
414 (stderr c-call:int))
415
416
417 ;;; RUN-PROGRAM -- public
418 ;;;
419 ;;; RUN-PROGRAM uses fork and execve to run a different program. Strange
420 ;;; stuff happens to keep the unix state of the world coherent.
421 ;;;
422 ;;; The child process needs to get its input from somewhere, and send its
423 ;;; output (both standard and error) to somewhere. We have to do different
424 ;;; things depending on where these somewheres really are.
425 ;;;
426 ;;; For input, there are five options:
427 ;;; - T: Just leave fd 0 alone. Pretty simple.
428 ;;; - "file": Read from the file. We need to open the file and pull the
429 ;;; descriptor out of the stream. The parent should close this stream after
430 ;;; the child is up and running to free any storage used in the parent.
431 ;;; - NIL: Same as "file", but use "/dev/null" as the file.
432 ;;; - :STREAM: Use unix-pipe to create two descriptors. Use system:make-fd-stream
433 ;;; to create the output stream on the writeable descriptor, and pass the
434 ;;; readable descriptor to the child. The parent must close the readable
435 ;;; descriptor for EOF to be passed up correctly.
436 ;;; - a stream: If it's a fd-stream, just pull the descriptor out of it.
437 ;;; Otherwise make a pipe as in :STREAM, and copy everything across.
438 ;;;
439 ;;; For output, there are n options:
440 ;;; - T: Leave descriptor 1 alone.
441 ;;; - "file": dump output to the file.
442 ;;; - NIL: dump output to /dev/null.
443 ;;; - :STREAM: return a stream that can be read from.
444 ;;; - a stream: if it's a fd-stream, use the descriptor in it. Otherwise, copy
445 ;;; stuff from output to stream.
446 ;;;
447 ;;; For error, there are all the same options as output plus:
448 ;;; - :OUTPUT: redirect to the same place as output.
449 ;;;
450 ;;; RUN-PROGRAM returns a process struct for the process if the fork worked,
451 ;;; and NIL if it did not.
452 ;;;
453 (defun run-program (program args
454 &key (env *environment-list*) (wait t) pty input
455 if-input-does-not-exist output (if-output-exists :error)
456 (error :output) (if-error-exists :error) status-hook)
457 "RUN-PROGRAM creates a new process and runs the unix program in the
458 file specified by the simple-string PROGRAM. ARGS are the standard
459 arguments that can be passed to a Unix program, for no arguments
460 use NIL (which means just the name of the program is passed as arg 0).
461
462 RUN-PROGRAM will either return NIL or a PROCESS structure. See the CMU
463 Common Lisp Users Manual for details about the PROCESS structure.
464
465 The keyword arguments have the following meanings:
466 :env -
467 An A-LIST mapping keyword environment variables to simple-string
468 values.
469 :wait -
470 If non-NIL (default), wait until the created process finishes. If
471 NIL, continue running Lisp until the program finishes.
472 :pty -
473 Either T, NIL, or a stream. Unless NIL, the subprocess is established
474 under a PTY. If :pty is a stream, all output to this pty is sent to
475 this stream, otherwise the PROCESS-PTY slot is filled in with a stream
476 connected to pty that can read output and write input.
477 :input -
478 Either T, NIL, a pathname, a stream, or :STREAM. If T, the standard
479 input for the current process is inherited. If NIL, /dev/null
480 is used. If a pathname, the file so specified is used. If a stream,
481 all the input is read from that stream and send to the subprocess. If
482 :STREAM, the PROCESS-INPUT slot is filled in with a stream that sends
483 its output to the process. Defaults to NIL.
484 :if-input-does-not-exist (when :input is the name of a file) -
485 can be one of:
486 :error - generate an error.
487 :create - create an empty file.
488 nil (default) - return nil from run-program.
489 :output -
490 Either T, NIL, a pathname, a stream, or :STREAM. If T, the standard
491 output for the current process is inherited. If NIL, /dev/null
492 is used. If a pathname, the file so specified is used. If a stream,
493 all the output from the process is written to this stream. If
494 :STREAM, the PROCESS-OUTPUT slot is filled in with a stream that can
495 be read to get the output. Defaults to NIL.
496 :if-output-exists (when :output is the name of a file) -
497 can be one of:
498 :error (default) - generates an error if the file already exists.
499 :supersede - output from the program supersedes the file.
500 :append - output from the program is appended to the file.
501 nil - run-program returns nil without doing anything.
502 :error and :if-error-exists -
503 Same as :output and :if-output-exists, except that :error can also be
504 specified as :output in which case all error output is routed to the
505 same place as normal output.
506 :status-hook -
507 This is a function the system calls whenever the status of the
508 process changes. The function takes the process as an argument."
509
510 ;; Make sure the interrupt handler is installed.
511 (system:enable-interrupt unix:sigchld #'sigchld-handler)
512 ;; Make sure all the args are okay.
513 (unless (every #'simple-string-p args)
514 (error "All args to program must be simple strings -- ~S." args))
515 ;; Pre-pend the program to the argument list.
516 (push (namestring program) args)
517 ;; Clear random specials used by GET-DESCRIPTOR-FOR to communicate cleanup
518 ;; info. Also, establish proc at this level so we can return it.
519 (let (*close-on-error* *close-in-parent* *handlers-installed* proc)
520 (unwind-protect
521 (let ((pfile (unix-namestring (merge-pathnames program "path:") t t))
522 (cookie (list 0)))
523 (unless pfile
524 (error "No such program: ~S" program))
525 (multiple-value-bind
526 (stdin input-stream)
527 (get-descriptor-for input cookie :direction :input
528 :if-does-not-exist if-input-does-not-exist)
529 (multiple-value-bind
530 (stdout output-stream)
531 (get-descriptor-for output cookie :direction :output
532 :if-does-not-exist :create
533 :if-exists if-output-exists)
534 (multiple-value-bind
535 (stderr error-stream)
536 (if (eq error :output)
537 (values stdout output-stream)
538 (get-descriptor-for error cookie :direction :output
539 :if-does-not-exist :create
540 :if-exists if-error-exists))
541 (multiple-value-bind (pty-name pty-stream)
542 (open-pty pty cookie)
543 ;; Make sure we are not notified about the child death before
544 ;; we have installed the process struct in *active-processes*
545 (system:without-interrupts
546 (with-c-strvec (argv args)
547 (with-c-strvec
548 (envp (mapcar #'(lambda (entry)
549 (concatenate
550 'string
551 (symbol-name (car entry))
552 "="
553 (cdr entry)))
554 env))
555 (let ((child-pid
556 (without-gcing
557 (spawn pfile argv envp pty-name
558 stdin stdout stderr))))
559 (when (< child-pid 0)
560 (error "Could not fork child process: ~A"
561 (unix:get-unix-error-msg)))
562 (setf proc (make-process :pid child-pid
563 :%status :running
564 :pty pty-stream
565 :input input-stream
566 :output output-stream
567 :error error-stream
568 :status-hook status-hook
569 :cookie cookie))
570 (push proc *active-processes*))))))))))
571 (dolist (fd *close-in-parent*)
572 (unix:unix-close fd))
573 (unless proc
574 (dolist (fd *close-on-error*)
575 (unix:unix-close fd))
576 (dolist (handler *handlers-installed*)
577 (system:remove-fd-handler handler))))
578 (when (and wait proc)
579 (process-wait proc))
580 proc))
581
582 ;;; COPY-DESCRIPTOR-TO-STREAM -- internal
583 ;;;
584 ;;; Installs a handler for any input that shows up on the file descriptor.
585 ;;; The handler reads the data and writes it to the stream.
586 ;;;
587 (defun copy-descriptor-to-stream (descriptor stream cookie)
588 (incf (car cookie))
589 (let ((string (make-string 256))
590 handler)
591 (setf handler
592 (system:add-fd-handler descriptor :input
593 #'(lambda (fd)
594 (declare (ignore fd))
595 (loop
596 (unless handler
597 (return))
598 (multiple-value-bind
599 (result readable/errno)
600 (unix:unix-select (1+ descriptor) (ash 1 descriptor)
601 0 0 0)
602 (cond ((null result)
603 (error "Could not select on sub-process: ~A"
604 (unix:get-unix-error-msg readable/errno)))
605 ((zerop result)
606 (return))))
607 (alien:with-alien ((buf (alien:array c-call:char 256)))
608 (multiple-value-bind
609 (count errno)
610 (unix:unix-read descriptor (alien-sap buf) 256)
611 (cond ((or (and (null count)
612 (eql errno unix:eio))
613 (eql count 0))
614 (system:remove-fd-handler handler)
615 (setf handler nil)
616 (decf (car cookie))
617 (unix:unix-close descriptor)
618 (return))
619 ((null count)
620 (system:remove-fd-handler handler)
621 (setf handler nil)
622 (decf (car cookie))
623 (error "Could not read input from sub-process: ~A"
624 (unix:get-unix-error-msg errno)))
625 (t
626 #-unicode
627 (kernel:copy-from-system-area
628 (alien-sap buf) 0
629 string (* vm:vector-data-offset vm:word-bits)
630 (* count vm:byte-bits))
631 #+unicode
632 (let ((sap (alien-sap buf)))
633 (dotimes (k count)
634 (setf (aref string k)
635 (code-char (sap-ref-8 sap k)))))
636 (write-string string stream
637 :end count)))))))))))
638
639 ;;; GET-DESCRIPTOR-FOR -- internal
640 ;;;
641 ;;; Find a file descriptor to use for object given the direction. Returns
642 ;;; the descriptor. If object is :STREAM, returns the created stream as the
643 ;;; second value.
644 ;;;
645 (defun get-descriptor-for (object cookie &rest keys &key direction
646 &allow-other-keys)
647 (cond ((eq object t)
648 ;; No new descriptor is needed.
649 (values -1 nil))
650 ((eq object nil)
651 ;; Use /dev/null.
652 (multiple-value-bind
653 (fd errno)
654 (unix:unix-open "/dev/null"
655 (case direction
656 (:input unix:o_rdonly)
657 (:output unix:o_wronly)
658 (t unix:o_rdwr))
659 #o666)
660 (unless fd
661 (error "Could not open \"/dev/null\": ~A"
662 (unix:get-unix-error-msg errno)))
663 (push fd *close-in-parent*)
664 (values fd nil)))
665 ((eq object :stream)
666 (multiple-value-bind
667 (read-fd write-fd)
668 (unix:unix-pipe)
669 (unless read-fd
670 (error "Could not create pipe: ~A"
671 (unix:get-unix-error-msg write-fd)))
672 (case direction
673 (:input
674 (push read-fd *close-in-parent*)
675 (push write-fd *close-on-error*)
676 (let ((stream (system:make-fd-stream write-fd :output t)))
677 (values read-fd stream)))
678 (:output
679 (push read-fd *close-on-error*)
680 (push write-fd *close-in-parent*)
681 (let ((stream (system:make-fd-stream read-fd :input t)))
682 (values write-fd stream)))
683 (t
684 (unix:unix-close read-fd)
685 (unix:unix-close write-fd)
686 (error "Direction must be either :INPUT or :OUTPUT, not ~S"
687 direction)))))
688 ((or (pathnamep object) (stringp object))
689 (with-open-stream (file (apply #'open object keys))
690 (multiple-value-bind
691 (fd errno)
692 (unix:unix-dup (system:fd-stream-fd file))
693 (cond (fd
694 (push fd *close-in-parent*)
695 (values fd nil))
696 (t
697 (error "Could not duplicate file descriptor: ~A"
698 (unix:get-unix-error-msg errno)))))))
699 ((system:fd-stream-p object)
700 (values (system:fd-stream-fd object) nil))
701 ((streamp object)
702 (ecase direction
703 (:input
704 (dotimes (count
705 256
706 (error "Could not open a temporary file in /tmp"))
707 (let* ((name (format nil "/tmp/.run-program-~D" count))
708 (fd (unix:unix-open name
709 (logior unix:o_rdwr
710 unix:o_creat
711 unix:o_excl)
712 #o666)))
713 (unix:unix-unlink name)
714 (when fd
715 (let ((newline (string #\Newline)))
716 (loop
717 (multiple-value-bind
718 (line no-cr)
719 (read-line object nil nil)
720 (unless line
721 (return))
722 (unix:unix-write fd line 0 (length line))
723 (if no-cr
724 (return)
725 (unix:unix-write fd newline 0 1)))))
726 (unix:unix-lseek fd 0 unix:l_set)
727 (push fd *close-in-parent*)
728 (return (values fd nil))))))
729 (:output
730 (multiple-value-bind (read-fd write-fd)
731 (unix:unix-pipe)
732 (unless read-fd
733 (error "Cound not create pipe: ~A"
734 (unix:get-unix-error-msg write-fd)))
735 (copy-descriptor-to-stream read-fd object cookie)
736 (push read-fd *close-on-error*)
737 (push write-fd *close-in-parent*)
738 (values write-fd nil)))))
739 (t
740 (error "Invalid option to run-program: ~S" object))))
741

  ViewVC Help
Powered by ViewVC 1.1.5