X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Frun-program.lisp;h=1b9b310dff7ed770e7f57cb3c9d9fe8ddd55da90;hb=65b5ab7e713d04e0d76bc0ee196374f6e57b922f;hp=e1ab659ee8448ed8348c81db011cb6ef55e6989a;hpb=4721c800154f80478023a77a530d64cbfaf50901;p=sbcl.git diff --git a/src/code/run-program.lisp b/src/code/run-program.lisp index e1ab659..1b9b310 100644 --- a/src/code/run-program.lisp +++ b/src/code/run-program.lisp @@ -97,23 +97,23 @@ ;;;; Import wait3(2) from Unix. #-win32 -(define-alien-routine ("wait3" c-wait3) sb-alien:int +(define-alien-routine ("waitpid" c-waitpid) sb-alien:int + (pid sb-alien:int) (status sb-alien:int :out) - (options sb-alien:int) - (rusage sb-alien:int)) + (options sb-alien:int)) #-win32 -(defun wait3 (&optional do-not-hang check-for-stopped) +(defun waitpid (pid &optional do-not-hang check-for-stopped) #+sb-doc - "Return any available status information on child process. " + "Return any available status information on child process with PID." (multiple-value-bind (pid status) - (c-wait3 (logior (if do-not-hang - sb-unix:wnohang - 0) - (if check-for-stopped - sb-unix:wuntraced - 0)) - 0) + (c-waitpid pid + (logior (if do-not-hang + sb-unix:wnohang + 0) + (if check-for-stopped + sb-unix:wuntraced + 0))) (cond ((or (minusp pid) (zerop pid)) nil) @@ -154,7 +154,8 @@ ;;; accesses it, that's why we need without-interrupts. (defmacro with-active-processes-lock (() &body body) #-win32 - `(sb-thread::call-with-system-mutex (lambda () ,@body) *active-processes-lock*) + `(sb-thread::with-system-mutex (*active-processes-lock*) + ,@body) #+win32 `(progn ,@body)) @@ -248,7 +249,7 @@ PROCESS." (sb-sys:serve-all-events 1)) process) -#-(or hpux win32) +#-win32 ;;; Find the current foreground process group id. (defun find-current-foreground-process (proc) (with-alien ((result sb-alien:int)) @@ -273,18 +274,11 @@ PROCESS." ((:pid :process-group) (process-pid process)) (:pty-process-group - #-hpux (find-current-foreground-process process))))) (multiple-value-bind (okay errno) (case whom - #+hpux - (:pty-process-group - (sb-unix:unix-ioctl (sb-sys:fd-stream-fd (process-pty process)) - sb-unix:TIOCSIGSEND - (sb-sys:int-sap - signal))) - ((:process-group #-hpux :pty-process-group) + ((:process-group) (sb-unix:unix-killpg pid signal)) (t (sb-unix:unix-kill pid signal))) @@ -326,36 +320,28 @@ status slot." (setf *active-processes* (delete process *active-processes*))) process) -;;; the handler for SIGCHLD signals that RUN-PROGRAM establishes -#-win32 -(defun sigchld-handler (ignore1 ignore2 ignore3) - (declare (ignore ignore1 ignore2 ignore3)) - (get-processes-status-changes)) - (defun get-processes-status-changes () - #-win32 - (loop - (multiple-value-bind (pid what code core) - (wait3 t t) - (unless pid - (return)) - (let ((proc (with-active-processes-lock () - (find pid *active-processes* :key #'process-pid)))) - (when proc - (setf (process-%status proc) what) - (setf (process-exit-code proc) code) - (setf (process-core-dumped proc) core) - (when (process-status-hook proc) - (funcall (process-status-hook proc) proc)) - (when (position what #(:exited :signaled)) - (with-active-processes-lock () - (setf *active-processes* - (delete proc *active-processes*)))))))) - #+win32 (let (exited) (with-active-processes-lock () (setf *active-processes* - (delete-if (lambda (proc) + (delete-if #-win32 + (lambda (proc) + ;; Wait only on pids belonging to processes + ;; started by RUN-PROGRAM. There used to be a + ;; WAIT3 call here, but that makes direct + ;; WAIT, WAITPID usage impossible due to the + ;; race with the SIGCHLD signal handler. + (multiple-value-bind (pid what code core) + (waitpid (process-pid proc) t t) + (when pid + (setf (process-%status proc) what) + (setf (process-exit-code proc) code) + (setf (process-core-dumped proc) core) + (when (process-status-hook proc) + (push proc exited)) + t))) + #+win32 + (lambda (proc) (multiple-value-bind (ok code) (get-exit-code-process (process-pid proc)) (when (and (plusp ok) (/= code 259)) @@ -367,8 +353,8 @@ status slot." *active-processes*))) ;; Can't call the hooks before all the processes have been deal ;; with, as calling a hook may cause re-entry to - ;; GET-PROCESS-STATUS-CHANGES. That may be OK when using wait3, - ;; but in the Windows implementation is would be deeply bad. + ;; GET-PROCESS-STATUS-CHANGES. That may be OK when using waitpid, + ;; but in the Windows implementation it would be deeply bad. (dolist (proc exited) (let ((hook (process-status-hook proc))) (when hook @@ -390,7 +376,7 @@ status slot." ;;; Find an unused pty. Return three values: the file descriptor for ;;; the master side of the pty, the file descriptor for the slave side ;;; of the pty, and the name of the tty device for the slave side. -#-win32 +#-(or win32 openbsd) (progn (define-alien-routine ptsname c-string (fd int)) (define-alien-routine grantpt boolean (fd int)) @@ -437,9 +423,24 @@ status slot." slave-name))) (sb-unix:unix-close master-fd)))))) (error "could not find a pty"))) +#+openbsd +(progn + (define-alien-routine openpty int (amaster int :out) (aslave int :out) + (name (* char)) (termp (* t)) (winp (* t))) + (defun find-a-pty () + (with-alien ((name-buf (array char 16))) + (multiple-value-bind (return-val master-fd slave-fd) + (openpty (cast name-buf (* char)) nil nil) + (if (zerop return-val) + (values master-fd + slave-fd + (sb-alien::c-string-to-string (alien-sap name-buf) + (sb-impl::default-external-format) + 'character)) + (error "could not find a pty")))))) #-win32 -(defun open-pty (pty cookie) +(defun open-pty (pty cookie &key (external-format :default)) (when pty (multiple-value-bind (master slave name) @@ -451,7 +452,7 @@ status slot." (unless new-fd (error "couldn't SB-UNIX:UNIX-DUP ~W: ~A" master (strerror errno))) (push new-fd *close-on-error*) - (copy-descriptor-to-stream new-fd pty cookie))) + (copy-descriptor-to-stream new-fd pty cookie external-format))) (values name (sb-sys:make-fd-stream master :input t :output t :element-type :default @@ -489,7 +490,7 @@ status slot." ;; Put the pointer in the vector. (setf (sap-ref-sap vec-sap vec-index-offset) string-sap) ;; Advance string-sap for the next string. - (setf string-sap (sap+ string-sap (round-bytes-to-words (1+ size)))) + (setf string-sap (sap+ string-sap (round-bytes-to-words size))) (incf vec-index-offset bytes-per-word))) ;; Final null pointer. (setf (sap-ref-sap vec-sap vec-index-offset) (int-sap 0)) @@ -664,9 +665,6 @@ Users Manual for details about the PROCESS structure."#-win32" #-win32 (when (and env-p environment-p) (error "can't specify :ENV and :ENVIRONMENT simultaneously")) - ;; Make sure that the interrupt handler is installed. - #-win32 - (sb-sys:enable-interrupt sb-unix:sigchld #'sigchld-handler) ;; Prepend the program to the argument list. (push (namestring program) args) (labels (;; It's friendly to allow the caller to pass any string @@ -718,7 +716,8 @@ Users Manual for details about the PROCESS structure."#-win32" (values stdout output-stream) (get-descriptor-for ,@args)))) ,@body)) - (with-open-pty (((pty-name pty-stream) (pty cookie)) &body body) + (with-open-pty (((pty-name pty-stream) (pty cookie)) + &body body) #+win32 `(declare (ignore ,pty ,cookie)) #+win32 `(let (,pty-name ,pty-stream) ,@body) #-win32 `(multiple-value-bind (,pty-name ,pty-stream) @@ -750,34 +749,37 @@ Users Manual for details about the PROCESS structure."#-win32" ;; Make sure we are not notified about the child ;; death before we have installed the PROCESS ;; structure in *ACTIVE-PROCESSES*. - (with-active-processes-lock () - (with-args-vec (args-vec simple-args) - (with-environment-vec (environment-vec environment) - (let ((child - (without-gcing - (spawn progname args-vec - stdin stdout stderr - (if search 1 0) - environment-vec pty-name - (if wait 1 0))))) - (when (= child -1) - (error "couldn't fork child process: ~A" - (strerror))) - (setf proc (apply - #'make-process - :pid child - :input input-stream - :output output-stream - :error error-stream - :status-hook status-hook - :cookie cookie - #-win32 (list :pty pty-stream - :%status :running) - #+win32 (if wait - (list :%status :exited - :exit-code child) - (list :%status :running)))) - (push proc *active-processes*)))))))))) + (let (child) + (with-active-processes-lock () + (with-args-vec (args-vec simple-args) + (with-environment-vec (environment-vec environment) + (setq child (without-gcing + (spawn progname args-vec + stdin stdout stderr + (if search 1 0) + environment-vec pty-name + (if wait 1 0)))) + (unless (= child -1) + (setf proc + (apply + #'make-process + :pid child + :input input-stream + :output output-stream + :error error-stream + :status-hook status-hook + :cookie cookie + #-win32 (list :pty pty-stream + :%status :running) + #+win32 (if wait + (list :%status :exited + :exit-code child) + (list :%status :running)))) + (push proc *active-processes*))))) + ;; Report the error outside the lock. + (when (= child -1) + (error "couldn't fork child process: ~A" + (strerror))))))))) (dolist (fd *close-in-parent*) (sb-unix:unix-close fd)) (unless proc @@ -815,9 +817,11 @@ Users Manual for details about the PROCESS structure."#-win32" (ash 1 descriptor) 0 0 0) (cond ((null result) - (error "~@" - (strerror readable/errno))) + (if (eql sb-unix:eintr readable/errno) + (return) + (error "~@" + (strerror readable/errno)))) ((zerop result) (return)))) (multiple-value-bind (count errno) @@ -906,14 +910,10 @@ Users Manual for details about the PROCESS structure."#-win32" ;; run afoul of disk quotas or to choke on small /tmp file systems. (flet ((make-temp-fd () (multiple-value-bind (fd name/errno) - (sb-unix:unix-mkstemp "/tmp/.run-program-XXXXXX") + (sb-unix:sb-mkstemp "/tmp/.run-program-XXXXXX" #o0600) (unless fd (error "could not open a temporary file: ~A" (strerror name/errno))) - #-win32 #|FIXME: should say (logior s_irusr s_iwusr)|# - (unless (sb-unix:unix-chmod name/errno #o600) - (sb-unix:unix-close fd) - (error "failed to chmod the temporary file?!")) (unless (sb-unix:unix-unlink name/errno) (sb-unix:unix-close fd) (error "failed to unlink ~A" name/errno)) @@ -965,7 +965,11 @@ Users Manual for details about the PROCESS structure."#-win32" (error "Direction must be either :INPUT or :OUTPUT, not ~S." direction))))) ((or (pathnamep object) (stringp object)) - (with-open-stream (file (apply #'open object keys)) + ;; GET-DESCRIPTOR-FOR uses &allow-other-keys, so rather + ;; than munge the &rest list for OPEN, just disable keyword + ;; validation there. + (with-open-stream (file (apply #'open object :allow-other-keys t + keys)) (multiple-value-bind (fd errno) (sb-unix:unix-dup (sb-sys:fd-stream-fd file))