[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

system()



Someone here wanted a function like unix system(), but unfortunately, the
obvious use of run-program doesn't handle user-interrupts "correctly" (that
is, they interrupt the waiting lisp instead of the program), and certain programs
just don't work (I suppose because they get SIGTT{IN,OU} or something).

Appended is the process group hackery (oh what fun) and system function I
used to get around this problem.  Is unwind-protect safe in a :before-execve
hook?

-Miles

--
Miles Bader  --  HCRC, University of Edinburgh  --  Miles.Bader@ed.ac.uk
Ich bin ein Virus. Mach' mit und kopiere mich in Deine .signature.
94% of everything is grunge


(in-package :MACH) ; should be unix!
(export '(tcsetpgrp tcgetpgrp tty-process-group))

;;; tc[sg]etpgrp are posix names

(defun tcsetpgrp (fd pgrp)
  "Set the tty-process-group for the unix file-descriptor FD to PGRP."
  (system:with-stack-alien (byref (unsigned-byte 32) (system:long-words 1))
    (setf (system:alien-access (system:alien-value byref)) pgrp)
    (unix-ioctl fd
		tiocspgrp
		(system:alien-sap (system:alien-value byref)))))

(defun tcgetpgrp (fd)
  "Get the tty-process-group for the unix file-descriptor FD."
  (system:with-stack-alien (byref (unsigned-byte 32) (system:long-words 1))
    (and (unix-ioctl fd
		     tiocgpgrp
		     (system:alien-sap (system:alien-value byref)))
	 (system:alien-access (system:alien-value byref)))))

(defun tty-process-group (&optional fd)
  "Get the tty-process-group for the unix file-descriptor FD.  If not supplied,
  FD defaults to /dev/tty."
  (if fd
      (tcgetpgrp fd)
      (multiple-value-bind (tty-fd errno)
	  (unix-open "/dev/tty" o_rdwr 0)
	(cond (tty-fd
	       (prog1
		   (tcgetpgrp tty-fd)
		 (unix-close tty-fd)))
	      (t
	       (list tty-fd errno))))))

(defun %set-tty-process-group (pgrp &optional fd)
  "Set the tty-process-group for the unix file-descriptor FD to PGRP.  If not
  supplied, FD defaults to /dev/tty."
  (let ((old-sigs
	 (unix-sigblock
	  (sigmask :sigttou :sigttin :sigtstp :sigchld))))
    (declare (type (unsigned-byte 32) old-sigs))
    (unwind-protect
	(if fd
	    (tcsetpgrp fd pgrp)
	    (multiple-value-bind (tty-fd errno)
		(unix-open "/dev/tty" o_rdwr 0)
	      (cond (tty-fd
		     (prog1
			 (tcsetpgrp tty-fd pgrp)
		       (unix-close tty-fd)))
		    (t
		     (list tty-fd errno)))))
      (unix-sigsetmask old-sigs))))
  
(defsetf tty-process-group (&optional fd) (pgrp)
  "Set the tty-process-group for the unix file-descriptor FD to PGRP.  If not
  supplied, FD defaults to /dev/tty."
  `(%set-tty-process-group ,pgrp ,fd))

;;; ----------------
(in-package :EXTENSIONS)
(export '(system *default-interactive-shell*))

(defvar *default-interactive-shell* "csh")

;;; This should be an anonymous lambda, but I liked the name.
(defun assume-control ()
  (let ((pid (mach:unix-getpid)))
    (mach:unix-setpgrp pid pid)
    (setf (tty-process-group) pid)))

(defun system (&optional cmd
	       &key (input t) (output t) (error t) 
	            (env *environment-list*)
	            if-input-does-not-exist (if-output-exists :error)
		    (if-error-exists :error))
  "Execute CMD (which defaults to an interactive shell) in a subshell.  The
  keyword arguments have the same meaning as for RUN-PROGRAM (although by
  default, the command inherits all the standard file descriptors)."
  (let* ((pgrp (mach:unix-getpgrp (mach:unix-getpid))))
    (let ((proc
	   (run-program "/bin/sh"
			`("-c"
			  ,(or cmd
			       (let ((shell
				      (or (cdr (assoc :shell
						      *environment-list*))
					  *default-interactive-shell*)))
				 (concatenate 'string shell " -i"))))
			:input input
			:output output
			:error error
			:env env
			:if-input-does-not-exist if-input-does-not-exist
			:if-output-exists if-output-exists
			:if-error-exists if-error-exists
			:before-execve #'assume-control
			:wait nil)))

      (unwind-protect
	  (progn
	    ;; _ensure_ our child has the tty (this is redundant, since the
	    ;; child does it as well, but a comment in bash says that posix
	    ;; recommends doing it here too).
	    (setf (tty-process-group) (process-pid proc))
	    (process-wait proc))
	;; get the tty back!
	(setf (tty-process-group) pgrp))

      proc)))