Changeset 11440

Show
Ignore:
Timestamp:
11/27/08 12:14:20 (16 months ago)
Author:
gb
Message:

Try to address ticket:216 (:sharing in streams created by RUN-PROGRAM)
and ticket:318 (handling of internal pipes when :output and :error
are specified and disjoint.)

N.B. : the Windows code is half-implemented; need to check this in
to get it working on Windows.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/source/level-1/linux-files.lisp

    r11309 r11440  
    973973#-windows-target 
    974974(progn 
    975 (defun %execvp (argv) 
    976   (#_execvp (%get-ptr argv) argv) 
    977   (#_exit #$EX_OSERR)) 
    978  
    979 (defun exec-with-io-redirection (new-in new-out new-err argv) 
    980   (#_setpgid 0 0) 
    981   (if new-in (#_dup2 new-in 0)) 
    982   (if new-out (#_dup2 new-out 1)) 
    983   (if new-err (#_dup2 new-err 2)) 
    984   (do* ((fd 3 (1+ fd))) 
    985        ((= fd *max-os-open-files*) (%execvp argv)) 
    986     (declare (fixnum fd)) 
    987     (#_close fd))) 
    988  
    989  
    990  
    991  
    992  
    993 (defstruct external-process 
    994   pid 
    995   %status 
    996   %exit-code 
    997   pty 
    998   input 
    999   output 
    1000   error 
    1001   status-hook 
    1002   plist 
    1003   token 
    1004   core 
    1005   args 
    1006   (signal (make-semaphore)) 
    1007   (completed (make-semaphore)) 
    1008   watched-fd 
    1009   watched-stream 
    1010   ) 
    1011  
    1012 (defmethod print-object ((p external-process) stream) 
    1013   (print-unreadable-object (p stream :type t :identity t) 
    1014     (let* ((status (external-process-%status p))) 
    1015       (let* ((*print-length* 3)) 
    1016         (format stream "~a" (external-process-args p))) 
    1017       (format stream "[~d] (~a" (external-process-pid p) status) 
    1018       (unless (eq status :running) 
    1019         (format stream " : ~d" (external-process-%exit-code p))) 
    1020       (format stream ")")))) 
    1021  
    1022 (defun get-descriptor-for (object proc close-in-parent close-on-error 
    1023                                   &rest keys &key direction (element-type 'character) 
    1024                                   &allow-other-keys) 
    1025   (etypecase object 
    1026     ((eql t) 
    1027      (values nil nil close-in-parent close-on-error)) 
    1028     (null 
    1029      (let* ((null-device #+windows-target "nul" #-windows-target "/dev/null") 
    1030             (fd (fd-open null-device (case direction 
    1031                                        (:input #$O_RDONLY) 
    1032                                        (:output #$O_WRONLY) 
    1033                                        (t #$O_RDWR))))) 
    1034        (if (< fd 0) 
    1035          (signal-file-error fd null-device)) 
    1036        (values fd nil (cons fd close-in-parent) (cons fd close-on-error)))) 
    1037     ((eql :stream) 
    1038      (multiple-value-bind (read-pipe write-pipe) (pipe) 
    1039        (case direction 
    1040          (:input 
    1041           (values read-pipe 
    1042                   (make-fd-stream write-pipe 
    1043                                   :direction :output 
    1044                                   :element-type element-type 
    1045                                   :interactive nil 
    1046                                   :basic t 
    1047                                   :auto-close t) 
    1048                   (cons read-pipe close-in-parent) 
    1049                   (cons write-pipe close-on-error))) 
    1050          (:output 
    1051           (values write-pipe 
    1052                   (make-fd-stream read-pipe 
    1053                                   :direction :input 
    1054                                   :element-type element-type 
    1055                                   :interactive nil 
    1056                                   :basic t 
    1057                                   :auto-close t) 
    1058                   (cons write-pipe close-in-parent) 
    1059                   (cons read-pipe close-on-error))) 
    1060          (t 
    1061           (fd-close read-pipe) 
    1062           (fd-close write-pipe) 
    1063           (report-bad-arg direction '(member :input :output)))))) 
    1064     ((or pathname string) 
    1065      (with-open-stream (file (apply #'open object keys)) 
    1066        (let* ((fd (fd-dup (ioblock-device (stream-ioblock file t))))) 
     975  (defun %execvp (argv) 
     976    (#_execvp (%get-ptr argv) argv) 
     977    (#_exit #$EX_OSERR)) 
     978 
     979  (defun exec-with-io-redirection (new-in new-out new-err argv) 
     980    (#_setpgid 0 0) 
     981    (if new-in (#_dup2 new-in 0)) 
     982    (if new-out (#_dup2 new-out 1)) 
     983    (if new-err (#_dup2 new-err 2)) 
     984    (do* ((fd 3 (1+ fd))) 
     985         ((= fd *max-os-open-files*) (%execvp argv)) 
     986      (declare (fixnum fd)) 
     987      (#_close fd))) 
     988 
     989 
     990 
     991 
     992 
     993  (defstruct external-process 
     994    pid 
     995    %status 
     996    %exit-code 
     997    pty 
     998    input 
     999    output 
     1000    error 
     1001    status-hook 
     1002    plist 
     1003    token                                
     1004    core 
     1005    args 
     1006    (signal (make-semaphore)) 
     1007    (completed (make-semaphore)) 
     1008    watched-fds 
     1009    watched-streams 
     1010    ) 
     1011 
     1012  (defmethod print-object ((p external-process) stream) 
     1013    (print-unreadable-object (p stream :type t :identity t) 
     1014      (let* ((status (external-process-%status p))) 
     1015        (let* ((*print-length* 3)) 
     1016          (format stream "~a" (external-process-args p))) 
     1017        (format stream "[~d] (~a" (external-process-pid p) status) 
     1018        (unless (eq status :running) 
     1019          (format stream " : ~d" (external-process-%exit-code p))) 
     1020        (format stream ")")))) 
     1021 
     1022  (defun get-descriptor-for (object proc close-in-parent close-on-error 
     1023                                    &rest keys 
     1024                                    &key direction (element-type 'character) 
     1025                                    (sharing :private) 
     1026                                    &allow-other-keys) 
     1027    (etypecase object 
     1028      ((eql t) 
     1029       (values nil nil close-in-parent close-on-error)) 
     1030      (null 
     1031       (let* ((null-device #+windows-target "nul" #-windows-target "/dev/null") 
     1032              (fd (fd-open null-device (case direction 
     1033                                         (:input #$O_RDONLY) 
     1034                                         (:output #$O_WRONLY) 
     1035                                         (t #$O_RDWR))))) 
     1036         (if (< fd 0) 
     1037           (signal-file-error fd null-device)) 
     1038         (values fd nil (cons fd close-in-parent) (cons fd close-on-error)))) 
     1039      ((eql :stream) 
     1040       (multiple-value-bind (read-pipe write-pipe) (pipe) 
     1041         (case direction 
     1042           (:input 
     1043            (values read-pipe 
     1044                    (make-fd-stream write-pipe 
     1045                                    :direction :output 
     1046                                    :element-type element-type 
     1047                                    :interactive nil 
     1048                                    :sharing sharing 
     1049                                    :basic t 
     1050                                    :auto-close t) 
     1051                    (cons read-pipe close-in-parent) 
     1052                    (cons write-pipe close-on-error))) 
     1053           (:output 
     1054            (values write-pipe 
     1055                    (make-fd-stream read-pipe 
     1056                                    :direction :input 
     1057                                    :element-type element-type 
     1058                                    :interactive nil 
     1059                                    :basic t 
     1060                                    :sharing sharing 
     1061                                    :auto-close t) 
     1062                    (cons write-pipe close-in-parent) 
     1063                    (cons read-pipe close-on-error))) 
     1064           (t 
     1065            (fd-close read-pipe) 
     1066            (fd-close write-pipe) 
     1067            (report-bad-arg direction '(member :input :output)))))) 
     1068      ((or pathname string) 
     1069       (with-open-stream (file (apply #'open object keys)) 
     1070         (let* ((fd (fd-dup (ioblock-device (stream-ioblock file t))))) 
     1071           (values fd 
     1072                   nil 
     1073                   (cons fd close-in-parent) 
     1074                   (cons fd close-on-error))))) 
     1075      (fd-stream 
     1076       (let ((fd (fd-dup (ioblock-device (stream-ioblock object t))))) 
    10671077         (values fd 
    10681078                 nil 
    10691079                 (cons fd close-in-parent) 
    1070                  (cons fd close-on-error))))) 
    1071     (fd-stream 
    1072      (let ((fd (fd-dup (ioblock-device (stream-ioblock object t))))) 
    1073        (values fd 
    1074                nil 
    1075                (cons fd close-in-parent) 
    1076                (cons fd close-on-error)))) 
    1077     (stream 
    1078      (ecase direction 
    1079        (:input 
    1080         (with-cstrs ((template "/tmp/lisp-tempXXXXXX")) 
    1081           (let* ((fd (#_mkstemp template))) 
    1082             (if (< fd 0) 
    1083               (%errno-disp fd)) 
    1084             (#_unlink template) 
    1085             (loop 
     1080                 (cons fd close-on-error)))) 
     1081      (stream 
     1082       (ecase direction 
     1083         (:input 
     1084          (with-cstrs ((template "/tmp/lisp-tempXXXXXX")) 
     1085            (let* ((fd (#_mkstemp template))) 
     1086              (if (< fd 0) 
     1087                (%errno-disp fd)) 
     1088              (#_unlink template) 
     1089              (loop 
     1090                (multiple-value-bind (line no-newline) 
     1091                    (read-line object nil nil) 
     1092                  (unless line 
     1093                    (return)) 
     1094                  (let* ((len (length line))) 
     1095                    (%stack-block ((buf (1+ len))) 
     1096                      (%cstr-pointer line buf) 
     1097                      (fd-write fd buf len) 
     1098                      (if no-newline 
     1099                        (return)) 
     1100                      (setf (%get-byte buf) (char-code #\newline)) 
     1101                      (fd-write fd buf 1))))) 
     1102              (fd-lseek fd 0 #$SEEK_SET) 
     1103              (values fd nil (cons fd close-in-parent) (cons fd close-on-error))))) 
     1104         (:output 
     1105          (multiple-value-bind (read-pipe write-pipe) (pipe) 
     1106            (push read-pipe (external-process-watched-fds proc)) 
     1107            (push object (external-process-watched-streams proc)) 
     1108            (incf (car (external-process-token proc))) 
     1109            (values write-pipe 
     1110                    nil 
     1111                    (cons write-pipe close-in-parent) 
     1112                    (cons read-pipe close-on-error)))))))) 
     1113 
     1114  (let* ((external-processes ()) 
     1115         (external-processes-lock (make-lock))) 
     1116    (defun add-external-process (p) 
     1117      (with-lock-grabbed (external-processes-lock) 
     1118        (push p external-processes))) 
     1119    (defun remove-external-process (p) 
     1120      (with-lock-grabbed (external-processes-lock) 
     1121        (setq external-processes (delete p external-processes)))) 
     1122    ;; Likewise 
     1123    (defun external-processes () 
     1124      (with-lock-grabbed (external-processes-lock) 
     1125        (copy-list external-processes))) 
     1126    ) 
     1127 
     1128 
     1129  (defmacro wtermsig (status) 
     1130    `(ldb (byte 7 0) ,status)) 
     1131 
     1132  (defmacro wexitstatus (status) 
     1133    `(ldb (byte 8 8) (the fixnum ,status))) 
     1134 
     1135  (defmacro wstopsig (status) 
     1136    `(wexitstatus ,status)) 
     1137 
     1138  (defmacro wifexited (status) 
     1139    `(eql (wtermsig ,status) 0)) 
     1140 
     1141  (defmacro wifstopped (status) 
     1142    `(eql #x7f (ldb (byte 7 0) (the fixnum ,status)))) 
     1143 
     1144  (defun monitor-external-process (p) 
     1145    (let* ((in-fds (external-process-watched-fds p)) 
     1146           (out-streams (external-process-watched-streams p)) 
     1147           (token (external-process-token p)) 
     1148           (terminated) 
     1149           (changed) 
     1150           (maxfd 0) 
     1151           (pairs (pairlis in-fds out-streams))) 
     1152      (%stack-block ((in-fd-set *fd-set-size*)) 
     1153        (rlet ((tv #>timeval)) 
     1154          (loop 
     1155            (when changed 
     1156              (setq pairs (pairlis in-fds out-streams) 
     1157                    changed nil)) 
     1158            (when (and terminated (null pairs)) 
     1159              (signal-semaphore (external-process-completed p)) 
     1160              (return)) 
     1161            (when pairs 
     1162              (fd-zero in-fd-set) 
     1163              (setq maxfd 0) 
     1164              (dolist (p pairs) 
     1165                (let* ((fd (car p))) 
     1166                  (when (> fd maxfd) 
     1167                    (setq maxfd fd)) 
     1168                  (fd-set fd in-fd-set))) 
     1169              (setf (pref tv #>timeval.tv_sec) 1 
     1170                    (pref tv #>timeval.tv_usec) 0) 
     1171              (when (> (#_select (1+ maxfd) in-fd-set (%null-ptr) (%null-ptr) tv) 
     1172                       0) 
     1173                (dolist (p pairs) 
     1174                  (let* ((in-fd (car p)) 
     1175                         (out-stream (cdr p))) 
     1176                    (when (fd-is-set in-fd in-fd-set) 
     1177                      (%stack-block ((buf 1024)) 
     1178                        (let* ((n (fd-read in-fd buf 1024))) 
     1179                          (declare (fixnum n)) 
     1180                          (if (<= n 0) 
     1181                            (progn 
     1182                              (without-interrupts 
     1183                               (decf (car token)) 
     1184                               (fd-close in-fd) 
     1185                               (setq in-fds (delete in-fd in-fds) 
     1186                                     out-streams (delete out-stream out-streams) 
     1187                                     changed t))) 
     1188                            (let* ((string (make-string 1024))) 
     1189                              (declare (dynamic-extent string)) 
     1190                              (%str-from-ptr buf n string) 
     1191                              (write-sequence string out-stream :end n)))))))))) 
     1192            (let* ((statusflags (check-pid (external-process-pid p) 
     1193                                           (logior 
     1194                                            (if in-fds #$WNOHANG 0) 
     1195                                            #$WUNTRACED))) 
     1196                   (oldstatus (external-process-%status p))) 
     1197              (cond ((null statusflags) 
     1198                     (remove-external-process p) 
     1199                     (setq terminated t)) 
     1200                    ((eq statusflags t)) ; Running. 
     1201                    (t 
     1202                     (multiple-value-bind (status code core) 
     1203                         (cond ((wifstopped statusflags) 
     1204                                (values :stopped (wstopsig statusflags))) 
     1205                               ((wifexited statusflags) 
     1206                                (values :exited (wexitstatus statusflags))) 
     1207                               (t 
     1208                                (let* ((signal (wtermsig statusflags))) 
     1209                                  (declare (fixnum signal)) 
     1210                                  (values 
     1211                                   (if (or (= signal #$SIGSTOP) 
     1212                                           (= signal #$SIGTSTP) 
     1213                                           (= signal #$SIGTTIN) 
     1214                                           (= signal #$SIGTTOU)) 
     1215                                     :stopped 
     1216                                     :signaled) 
     1217                                   signal 
     1218                                   (logtest #-solaris-target #$WCOREFLAG 
     1219                                            #+solaris-target #$WCOREFLG 
     1220                                            statusflags))))) 
     1221                       (setf (external-process-%status p) status 
     1222                             (external-process-%exit-code p) code 
     1223                             (external-process-core p) core) 
     1224                       (let* ((status-hook (external-process-status-hook p))) 
     1225                         (when (and status-hook (not (eq oldstatus status))) 
     1226                           (funcall status-hook p))) 
     1227                       (when (or (eq status :exited) 
     1228                                 (eq status :signaled)) 
     1229                         (remove-external-process p) 
     1230                         (setq terminated t))))))))))) 
     1231       
     1232  (defun run-external-process (proc in-fd out-fd error-fd argv &optional env) 
     1233    (let* ((signaled nil)) 
     1234      (unwind-protect 
     1235           (let* ((child-pid (#-solaris-target #_fork #+solaris-target #_forkall))) 
     1236             (declare (fixnum child-pid)) 
     1237             (cond ((zerop child-pid) 
     1238                    ;; Running in the child; do an exec 
     1239                    (setq signaled t) 
     1240                    (dolist (pair env) 
     1241                      (setenv (string (car pair)) (cdr pair))) 
     1242                    (without-interrupts 
     1243                     (exec-with-io-redirection 
     1244                      in-fd out-fd error-fd argv))) 
     1245                   ((> child-pid 0) 
     1246                    ;; Running in the parent: success 
     1247                    (setf (external-process-pid proc) child-pid) 
     1248                    (add-external-process proc) 
     1249                    (signal-semaphore (external-process-signal proc)) 
     1250                    (setq signaled t) 
     1251                    (monitor-external-process proc)) 
     1252                   (t 
     1253                    ;; Fork failed 
     1254                    (setf (external-process-%status proc) :error 
     1255                          (external-process-%exit-code proc) (%get-errno)) 
     1256                    (signal-semaphore (external-process-signal proc)) 
     1257                    (setq signaled t)))) 
     1258        (unless signaled 
     1259          (setf (external-process-%status proc) :error 
     1260                (external-process-%exit-code proc) -1) 
     1261          (signal-semaphore (external-process-signal proc)))))) 
     1262 
     1263  (defparameter *silently-ignore-catastrophic-failure-in-run-program* 
     1264    #+ccl-0711 t #-ccl-0711 nil 
     1265    "If NIL, signal an error if run-program is unable to start the program. 
     1266If non-NIL, treat failure to start the same as failure from the program 
     1267itself, by setting the status and exit-code fields.") 
     1268 
     1269  (defun run-program (program args &key 
     1270                              (wait t) pty 
     1271                              input if-input-does-not-exist 
     1272                              output (if-output-exists :error) 
     1273                              (error :output) (if-error-exists :error) 
     1274                              status-hook (element-type 'character) 
     1275                              env 
     1276                              (sharing :private) 
     1277                              (silently-ignore-catastrophic-failures 
     1278                               *silently-ignore-catastrophic-failure-in-run-program*)) 
     1279    "Invoke an external program as an OS subprocess of lisp." 
     1280    (declare (ignore pty)) 
     1281    (unless (every #'(lambda (a) (typep a 'simple-string)) args) 
     1282      (error "Program args must all be simple strings : ~s" args)) 
     1283    (dolist (pair env) 
     1284      (destructuring-bind (var . val) pair 
     1285        (check-type var (or string symbol character)) 
     1286        (check-type val string))) 
     1287    (push (native-untranslated-namestring program) args) 
     1288    (let* ((token (list 0)) 
     1289           (in-fd nil) 
     1290           (in-stream nil) 
     1291           (out-fd nil) 
     1292           (out-stream nil) 
     1293           (error-fd nil) 
     1294           (error-stream nil) 
     1295           (close-in-parent nil) 
     1296           (close-on-error nil) 
     1297           (proc 
     1298            (make-external-process 
     1299             :pid nil 
     1300             :args args 
     1301             :%status :running 
     1302             :input nil 
     1303             :output nil 
     1304             :error nil 
     1305             :token token 
     1306             :status-hook status-hook))) 
     1307      (unwind-protect 
     1308           (progn 
     1309             (multiple-value-setq (in-fd in-stream close-in-parent close-on-error) 
     1310               (get-descriptor-for input proc  nil nil :direction :input 
     1311                                   :if-does-not-exist if-input-does-not-exist 
     1312                                   :element-type element-type 
     1313                                   :sharing sharing)) 
     1314             (multiple-value-setq (out-fd out-stream close-in-parent close-on-error) 
     1315               (get-descriptor-for output proc close-in-parent close-on-error 
     1316                                   :direction :output 
     1317                                   :if-exists if-output-exists 
     1318                                   :element-type element-type 
     1319                                   :sharing sharing)) 
     1320             (multiple-value-setq (error-fd error-stream close-in-parent close-on-error) 
     1321               (if (eq error :output) 
     1322                 (values out-fd out-stream close-in-parent close-on-error) 
     1323                 (get-descriptor-for error proc close-in-parent close-on-error 
     1324                                     :direction :output 
     1325                                     :if-exists if-error-exists 
     1326                                     :sharing sharing 
     1327                                     :element-type element-type))) 
     1328             (setf (external-process-input proc) in-stream 
     1329                   (external-process-output proc) out-stream 
     1330                   (external-process-error proc) error-stream) 
     1331             (call-with-string-vector 
     1332              #'(lambda (argv) 
     1333                  (process-run-function 
     1334                   (list :name 
     1335                         (format nil "Monitor thread for external process ~a" args) 
     1336                         :stack-size (ash 128 10) 
     1337                         :vstack-size (ash 128 10) 
     1338                         :tstack-size (ash 128 10)) 
     1339                   #'run-external-process proc in-fd out-fd error-fd argv env) 
     1340                  (wait-on-semaphore (external-process-signal proc))) 
     1341              args)) 
     1342        (dolist (fd close-in-parent) (fd-close fd)) 
     1343        (unless (external-process-pid proc) 
     1344          (dolist (fd close-on-error) (fd-close fd))) 
     1345        (when (and wait (external-process-pid proc)) 
     1346          (with-interrupts-enabled 
     1347              (wait-on-semaphore (external-process-completed proc))))) 
     1348      (unless (external-process-pid proc) 
     1349        ;; something is wrong 
     1350        (if (eq (external-process-%status proc) :error) 
     1351          ;; Fork failed 
     1352          (unless silently-ignore-catastrophic-failures 
     1353            (cerror "Pretend the program ran and failed" 'external-process-creation-failure :proc proc)) 
     1354          ;; Currently can't happen. 
     1355          (error "Bug: fork failed but status field not set?"))) 
     1356      proc)) 
     1357 
     1358 
     1359 
     1360  (defmacro wifsignaled (status) 
     1361    (let* ((statname (gensym))) 
     1362      `(let* ((,statname ,status)) 
     1363        (and (not (wifstopped ,statname)) (not (wifexited ,statname)))))) 
     1364 
     1365 
     1366  (defun check-pid (pid &optional (flags (logior  #$WNOHANG #$WUNTRACED))) 
     1367    (declare (fixnum pid)) 
     1368    (rlet ((status :signed)) 
     1369      (let* ((retval (ff-call-ignoring-eintr (#_waitpid pid status flags)))) 
     1370        (declare (fixnum retval)) 
     1371        (if (= retval pid) 
     1372          (pref status :signed) 
     1373          (zerop retval))))) 
     1374 
     1375 
     1376 
     1377 
     1378 
     1379  (defun external-process-wait (proc &optional check-stopped) 
     1380    (process-wait "external-process-wait" 
     1381                  #'(lambda () 
     1382                      (case (external-process-%status proc) 
     1383                        (:running) 
     1384                        (:stopped 
     1385                         (when check-stopped 
     1386                           t)) 
     1387                        (t 
     1388                         (when (zerop (car (external-process-token proc))) 
     1389                           t)))))) 
     1390 
     1391  (defun external-process-status (proc) 
     1392    "Return information about whether an OS subprocess is running; or, if 
     1393not, why not; and what its result code was if it completed." 
     1394    (require-type proc 'external-process) 
     1395    (values (external-process-%status proc) 
     1396            (external-process-%exit-code proc))) 
     1397 
     1398  (defun external-process-input-stream (proc) 
     1399    "Return the lisp stream which is used to write input to a given OS 
     1400subprocess, if it has one." 
     1401    (require-type proc 'external-process) 
     1402    (external-process-input proc)) 
     1403 
     1404  (defun external-process-output-stream (proc) 
     1405    "Return the lisp stream which is used to read output from a given OS 
     1406subprocess, if there is one." 
     1407    (require-type proc 'external-process) 
     1408    (external-process-output proc)) 
     1409 
     1410  (defun external-process-error-stream (proc) 
     1411    "Return the stream which is used to read error output from a given OS 
     1412subprocess, if it has one." 
     1413    (require-type proc 'external-process) 
     1414    (external-process-error proc)) 
     1415 
     1416  (defun external-process-id (proc) 
     1417    "Return the process id of an OS subprocess, a positive integer which 
     1418identifies it." 
     1419    (require-type proc 'external-process) 
     1420    (external-process-pid proc)) 
     1421   
     1422  (defun signal-external-process (proc signal) 
     1423    "Send the specified signal to the specified external process.  (Typically, 
     1424it would only be useful to call this function if the EXTERNAL-PROCESS was 
     1425created with :WAIT NIL.) Return T if successful; NIL if the process wasn't 
     1426created successfully, and signal an error otherwise." 
     1427    (require-type proc 'external-process) 
     1428    (let* ((pid (external-process-pid proc))) 
     1429      (when pid 
     1430        (let ((error (int-errno-call (#_kill pid signal)))) 
     1431          (or (eql error 0) 
     1432              (%errno-disp error)))))) 
     1433 
     1434  )                                     ; #-windows-target (progn 
     1435 
     1436#+windows-target 
     1437(progn 
     1438  (defun temp-file-name (prefix) 
     1439    "Returns a unique name for a temporary file, residing in system temp 
     1440space, and prefixed with PREFIX." 
     1441    (rlet ((buffer (:array :wchar_t #.#$MAX_PATH))) 
     1442      (#_GetTempPathW #$MAX_PATH buffer) 
     1443      (with-filename-cstrs ((c-prefix prefix))  
     1444        (#_GetTempFileNameW buffer c-prefix 0 buffer) 
     1445        (%get-native-utf-16-cstring buffer)))) 
     1446   
     1447  (defun get-descriptor-for (object proc close-in-parent close-on-error 
     1448                                    &rest keys 
     1449                                    &key 
     1450                                    direction (element-type 'character) 
     1451                                    (sharing :private) 
     1452                                    &allow-other-keys) 
     1453    (etypecase object 
     1454      ((eql t) 
     1455       (values nil nil close-in-parent close-on-error)) 
     1456      (null 
     1457       (let* ((null-device "nul") 
     1458              (fd (fd-open null-device (case direction 
     1459                                         (:input #$O_RDONLY) 
     1460                                         (:output #$O_WRONLY) 
     1461                                         (t #$O_RDWR))))) 
     1462         (if (< fd 0) 
     1463           (signal-file-error fd null-device)) 
     1464         (values fd nil (cons fd close-in-parent) (cons fd close-on-error)))) 
     1465      ((eql :stream) 
     1466       (multiple-value-bind (read-pipe write-pipe) (pipe) 
     1467         (case direction 
     1468           (:input 
     1469            (values read-pipe 
     1470                    (make-fd-stream (fd-uninheritable write-pipe :direction :output) 
     1471                                    :direction :output 
     1472                                    :element-type element-type 
     1473                                    :interactive nil 
     1474                                    :basic t 
     1475                                    :sharing sharing 
     1476                                    :auto-close t) 
     1477                    (cons read-pipe close-in-parent) 
     1478                    (cons write-pipe close-on-error))) 
     1479           (:output 
     1480            (values write-pipe 
     1481                    (make-fd-stream (fd-uninheritable read-pipe :direction :input) 
     1482                                    :direction :input 
     1483                                    :element-type element-type 
     1484                                    :interactive nil 
     1485                                    :basic t 
     1486                                    :sharing sharing 
     1487                                    :auto-close t) 
     1488                    (cons write-pipe close-in-parent) 
     1489                    (cons read-pipe close-on-error))) 
     1490           (t 
     1491            (fd-close read-pipe) 
     1492            (fd-close write-pipe) 
     1493            (report-bad-arg direction '(member :input :output)))))) 
     1494      ((or pathname string) 
     1495       (with-open-stream (file (apply #'open object keys)) 
     1496         (let* ((fd (fd-dup (ioblock-device (stream-ioblock file t)) :direction direction :inheritable t))) 
     1497           (values fd 
     1498                   nil 
     1499                   (cons fd close-in-parent) 
     1500                   (cons fd close-on-error))))) 
     1501      (fd-stream 
     1502       (let ((fd (fd-dup (ioblock-device (stream-ioblock object t))))) 
     1503         (values fd 
     1504                 nil 
     1505                 (cons fd close-in-parent) 
     1506                 (cons fd close-on-error)))) 
     1507      (stream 
     1508       (ecase direction 
     1509         (:input 
     1510          (let* ((tempname (temp-file-name "lisp-temp")) 
     1511                 (fd (fd-open tempname #$O_RDWR))) 
     1512            (if (< fd 0) 
     1513              (%errno-disp fd)) 
     1514            (loop 
    10861515              (multiple-value-bind (line no-newline) 
    10871516                  (read-line object nil nil) 
     
    10961525                    (setf (%get-byte buf) (char-code #\newline)) 
    10971526                    (fd-write fd buf 1))))) 
    1098             (fd-lseek fd 0 #$SEEK_SET) 
    1099             (values fd nil (cons fd close-in-parent) (cons fd close-on-error))))) 
    1100        (:output 
    1101         (multiple-value-bind (read-pipe write-pipe) (pipe) 
    1102           (setf (external-process-watched-fd proc) read-pipe 
    1103                 (external-process-watched-stream proc) object) 
    1104           (incf (car (external-process-token proc))) 
    1105           (values write-pipe 
    1106                   nil 
    1107                   (cons write-pipe close-in-parent) 
    1108                   (cons read-pipe close-on-error)))))))) 
    1109  
    1110 (let* ((external-processes ()) 
    1111        (external-processes-lock (make-lock))) 
    1112   (defun add-external-process (p) 
    1113     (with-lock-grabbed (external-processes-lock) 
    1114       (push p external-processes))) 
    1115   (defun remove-external-process (p) 
    1116     (with-lock-grabbed (external-processes-lock) 
    1117       (setq external-processes (delete p external-processes)))) 
    1118   ;; Likewise 
    1119   (defun external-processes () 
    1120     (with-lock-grabbed (external-processes-lock) 
    1121       (copy-list external-processes))) 
    1122   ) 
    1123  
    1124  
    1125 (defmacro wtermsig (status) 
    1126   `(ldb (byte 7 0) ,status)) 
    1127  
    1128 (defmacro wexitstatus (status) 
    1129   `(ldb (byte 8 8) (the fixnum ,status))) 
    1130  
    1131 (defmacro wstopsig (status) 
    1132   `(wexitstatus ,status)) 
    1133  
    1134 (defmacro wifexited (status) 
    1135   `(eql (wtermsig ,status) 0)) 
    1136  
    1137 (defmacro wifstopped (status) 
    1138   `(eql #x7f (ldb (byte 7 0) (the fixnum ,status)))) 
    1139  
    1140 (defun monitor-external-process (p) 
    1141   (let* ((in-fd (external-process-watched-fd p)) 
    1142          (out-stream (external-process-watched-stream p)) 
    1143          (token (external-process-token p)) 
    1144          (terminated)) 
    1145     (loop 
    1146       (when (and terminated (null in-fd)) 
    1147         (signal-semaphore (external-process-completed p)) 
    1148         (return)) 
    1149       (when in-fd 
    1150         (when (fd-input-available-p in-fd 1000) 
    1151           (%stack-block ((buf 1024)) 
    1152             (let* ((n (fd-read in-fd buf 1024))) 
    1153               (declare (fixnum n)) 
    1154               (if (<= n 0) 
    1155                 (progn 
    1156                   (without-interrupts 
    1157                    (decf (car token)) 
    1158                    (fd-close in-fd) 
    1159                    (setq in-fd nil))) 
    1160                 (let* ((string (make-string 1024))) 
    1161                   (declare (dynamic-extent string)) 
    1162                   (%str-from-ptr buf n string) 
    1163                   (write-sequence string out-stream :end n))))))) 
    1164       (let* ((statusflags (check-pid (external-process-pid p) 
    1165                                      (logior 
    1166                                       (if in-fd #$WNOHANG 0) 
    1167                                       #$WUNTRACED))) 
    1168              (oldstatus (external-process-%status p))) 
    1169         (cond ((null statusflags) 
    1170                (remove-external-process p) 
    1171                (setq terminated t)) 
    1172               ((eq statusflags t))      ; Running. 
    1173               (t 
    1174                (multiple-value-bind (status code core) 
    1175                    (cond ((wifstopped statusflags) 
    1176                           (values :stopped (wstopsig statusflags))) 
    1177                          ((wifexited statusflags) 
    1178                           (values :exited (wexitstatus statusflags))) 
    1179                          (t 
    1180                           (let* ((signal (wtermsig statusflags))) 
    1181                             (declare (fixnum signal)) 
    1182                             (values 
    1183                              (if (or (= signal #$SIGSTOP) 
    1184                                      (= signal #$SIGTSTP) 
    1185                                      (= signal #$SIGTTIN) 
    1186                                      (= signal #$SIGTTOU)) 
    1187                                :stopped 
    1188                                :signaled) 
    1189                              signal 
    1190                              (logtest #-solaris-target #$WCOREFLAG 
    1191                                       #+solaris-target #$WCOREFLG 
    1192                                       statusflags))))) 
    1193                  (setf (external-process-%status p) status 
    1194                        (external-process-%exit-code p) code 
    1195                        (external-process-core p) core) 
    1196                  (let* ((status-hook (external-process-status-hook p))) 
    1197                    (when (and status-hook (not (eq oldstatus status))) 
    1198                      (funcall status-hook p))) 
    1199                  (when (or (eq status :exited) 
    1200                            (eq status :signaled)) 
    1201                    (remove-external-process p) 
    1202                    (setq terminated t))))))))) 
    1203        
    1204 (defun run-external-process (proc in-fd out-fd error-fd argv &optional env) 
    1205   (let* ((signaled nil)) 
    1206     (unwind-protect 
    1207          (let* ((child-pid (#-solaris-target #_fork #+solaris-target #_forkall))) 
    1208            (declare (fixnum child-pid)) 
    1209            (cond ((zerop child-pid) 
    1210                   ;; Running in the child; do an exec 
    1211                   (setq signaled t) 
    1212                   (dolist (pair env) 
    1213                     (setenv (string (car pair)) (cdr pair))) 
    1214                   (without-interrupts 
    1215                     (exec-with-io-redirection 
    1216                      in-fd out-fd error-fd argv))) 
    1217                  ((> child-pid 0) 
    1218                   ;; Running in the parent: success 
    1219                   (setf (external-process-pid proc) child-pid) 
    1220                   (add-external-process proc) 
    1221                   (signal-semaphore (external-process-signal proc)) 
    1222                   (setq signaled t) 
    1223                   (monitor-external-process proc)) 
    1224                  (t 
    1225                   ;; Fork failed 
    1226                   (setf (external-process-%status proc) :error 
    1227                         (external-process-%exit-code proc) (%get-errno)) 
    1228                   (signal-semaphore (external-process-signal proc)) 
    1229                   (setq signaled t)))) 
    1230       (unless signaled 
    1231         (setf (external-process-%status proc) :error 
    1232               (external-process-%exit-code proc) -1) 
    1233         (signal-semaphore (external-process-signal proc)))))) 
    1234  
    1235 (defparameter *silently-ignore-catastrophic-failure-in-run-program* 
    1236   #+ccl-0711 t #-ccl-0711 nil 
    1237   "If NIL, signal an error if run-program is unable to start the program. 
    1238 If non-NIL, treat failure to start the same as failure from the program 
    1239 itself, by setting the status and exit-code fields.") 
    1240  
    1241 (defun run-program (program args &key 
    1242                             (wait t) pty 
    1243                             input if-input-does-not-exist 
    1244                             output (if-output-exists :error) 
    1245                             (error :output) (if-error-exists :error) 
    1246                             status-hook (element-type 'character) 
    1247                             env 
    1248                             (silently-ignore-catastrophic-failures 
    1249                              *silently-ignore-catastrophic-failure-in-run-program*)) 
    1250   "Invoke an external program as an OS subprocess of lisp." 
    1251   (declare (ignore pty)) 
    1252   (unless (every #'(lambda (a) (typep a 'simple-string)) args) 
    1253     (error "Program args must all be simple strings : ~s" args)) 
    1254   (dolist (pair env) 
    1255     (destructuring-bind (var . val) pair 
    1256       (check-type var (or string symbol character)) 
    1257       (check-type val string))) 
    1258   (push (native-untranslated-namestring program) args) 
    1259   (let* ((token (list 0)) 
    1260          (in-fd nil) 
    1261          (in-stream nil) 
    1262          (out-fd nil) 
    1263          (out-stream nil) 
    1264          (error-fd nil) 
    1265          (error-stream nil) 
    1266          (close-in-parent nil) 
    1267          (close-on-error nil) 
    1268          (proc 
    1269           (make-external-process 
    1270            :pid nil 
    1271            :args args 
    1272            :%status :running 
    1273            :input nil 
    1274            :output nil 
    1275            :error nil 
    1276            :token token 
    1277            :status-hook status-hook))) 
    1278     (unwind-protect 
    1279          (progn 
    1280            (multiple-value-setq (in-fd in-stream close-in-parent close-on-error) 
    1281              (get-descriptor-for input proc  nil nil :direction :input 
    1282                                  :if-does-not-exist if-input-does-not-exist 
    1283                                  :element-type element-type)) 
    1284            (multiple-value-setq (out-fd out-stream close-in-parent close-on-error) 
    1285              (get-descriptor-for output proc close-in-parent close-on-error 
    1286                                  :direction :output 
    1287                                  :if-exists if-output-exists 
    1288                                  :element-type element-type)) 
    1289            (multiple-value-setq (error-fd error-stream close-in-parent close-on-error) 
    1290              (if (eq error :output) 
    1291                (values out-fd out-stream close-in-parent close-on-error) 
    1292                (get-descriptor-for error proc close-in-parent close-on-error 
    1293                                    :direction :output 
    1294                                    :if-exists if-error-exists 
    1295                                    :element-type element-type))) 
    1296            (setf (external-process-input proc) in-stream 
    1297                  (external-process-output proc) out-stream 
    1298                  (external-process-error proc) error-stream) 
    1299            (call-with-string-vector 
    1300             #'(lambda (argv) 
    1301                 (process-run-function 
    1302                  (list :name 
    1303                        (format nil "Monitor thread for external process ~a" args) 
    1304                        :stack-size (ash 128 10) 
    1305                        :vstack-size (ash 128 10) 
    1306                        :tstack-size (ash 128 10)) 
    1307                  #'run-external-process proc in-fd out-fd error-fd argv env) 
    1308                 (wait-on-semaphore (external-process-signal proc))) 
    1309             args)) 
    1310       (dolist (fd close-in-parent) (fd-close fd)) 
    1311       (unless (external-process-pid proc) 
    1312         (dolist (fd close-on-error) (fd-close fd))) 
    1313       (when (and wait (external-process-pid proc)) 
    1314         (with-interrupts-enabled 
    1315           (wait-on-semaphore (external-process-completed proc))))) 
    1316     (unless (external-process-pid proc) 
    1317       ;; something is wrong 
    1318       (if (eq (external-process-%status proc) :error) 
    1319         ;; Fork failed 
    1320         (unless silently-ignore-catastrophic-failures 
    1321           (cerror "Pretend the program ran and failed" 'external-process-creation-failure :proc proc)) 
    1322         ;; Currently can't happen. 
    1323         (error "Bug: fork failed but status field not set?"))) 
    1324     proc)) 
    1325  
    1326  
    1327  
    1328 (defmacro wifsignaled (status) 
    1329   (let* ((statname (gensym))) 
    1330     `(let* ((,statname ,status)) 
    1331       (and (not (wifstopped ,statname)) (not (wifexited ,statname)))))) 
    1332  
    1333  
    1334 (defun check-pid (pid &optional (flags (logior  #$WNOHANG #$WUNTRACED))) 
    1335   (declare (fixnum pid)) 
    1336   (rlet ((status :signed)) 
    1337     (let* ((retval (ff-call-ignoring-eintr (#_waitpid pid status flags)))) 
    1338       (declare (fixnum retval)) 
    1339       (if (= retval pid) 
    1340         (pref status :signed) 
    1341         (zerop retval))))) 
    1342  
    1343  
    1344  
    1345  
    1346  
    1347 (defun external-process-wait (proc &optional check-stopped) 
    1348   (process-wait "external-process-wait" 
    1349                 #'(lambda () 
    1350                     (case (external-process-%status proc) 
    1351                       (:running) 
    1352                       (:stopped 
    1353                        (when check-stopped 
    1354                          t)) 
    1355                       (t 
    1356                        (when (zerop (car (external-process-token proc))) 
    1357                          t)))))) 
    1358  
    1359 (defun external-process-status (proc) 
    1360   "Return information about whether an OS subprocess is running; or, if 
     1527            (fd-lseek fd 0 #$SEEK_SET) 
     1528            (values fd nil (cons fd close-in-parent) (cons fd close-on-error)))) 
     1529         (:output 
     1530          (multiple-value-bind (read-pipe write-pipe) (pipe) 
     1531            (push read-pipe (external-process-watched-fds proc)) 
     1532            (push object (external-process-watched-streams proc)) 
     1533            (incf (car (external-process-token proc))) 
     1534            (values write-pipe 
     1535                    nil 
     1536                    (cons write-pipe close-in-parent) 
     1537                    (cons read-pipe close-on-error)))))))) 
     1538 
     1539  (defstruct external-process 
     1540    pid 
     1541    %status 
     1542    %exit-code 
     1543    pty 
     1544    input 
     1545    output 
     1546    error 
     1547    status-hook 
     1548    plist 
     1549    token 
     1550    core 
     1551    args 
     1552    (signal (make-semaphore)) 
     1553    (completed (make-semaphore)) 
     1554    watched-fds 
     1555    watched-streams 
     1556    ) 
     1557 
     1558  (defun external-process-status (proc) 
     1559    "Return information about whether an OS subprocess is running; or, if 
    13611560not, why not; and what its result code was if it completed." 
    1362   (require-type proc 'external-process) 
    1363   (values (external-process-%status proc) 
    1364           (external-process-%exit-code proc))) 
    1365  
    1366 (defun external-process-input-stream (proc) 
    1367   "Return the lisp stream which is used to write input to a given OS 
    1368 subprocess, if it has one." 
    1369   (require-type proc 'external-process) 
    1370   (external-process-input proc)) 
    1371  
    1372 (defun external-process-output-stream (proc) 
    1373   "Return the lisp stream which is used to read output from a given OS 
    1374 subprocess, if there is one." 
    1375   (require-type proc 'external-process) 
    1376   (external-process-output proc)) 
    1377  
    1378 (defun external-process-error-stream (proc) 
    1379   "Return the stream which is used to read error output from a given OS 
    1380 subprocess, if it has one." 
    1381   (require-type proc 'external-process) 
    1382   (external-process-error proc)) 
    1383  
    1384 (defun external-process-id (proc) 
    1385   "Return the process id of an OS subprocess, a positive integer which 
    1386 identifies it." 
    1387   (require-type proc 'external-process) 
    1388   (external-process-pid proc)) 
     1561    (require-type proc 'external-process) 
     1562    (values (external-process-%status proc) 
     1563            (external-process-%exit-code proc))) 
     1564 
     1565 
     1566  (defmethod print-object ((p external-process) stream) 
     1567    (print-unreadable-object (p stream :type t :identity t) 
     1568      (let* ((status (external-process-%status p))) 
     1569        (let* ((*print-length* 3)) 
     1570          (format stream "~a" (external-process-args p))) 
     1571        (format stream "[~d] (~a" (external-process-pid p) status) 
     1572        (unless (eq status :running) 
     1573          (format stream " : ~d" (external-process-%exit-code p))) 
     1574        (format stream ")")))) 
     1575 
     1576  (defun run-program (program args &key 
     1577                              (wait t) pty 
     1578                              input if-input-does-not-exist 
     1579                              output (if-output-exists :error) 
     1580                              (error :output) (if-error-exists :error) 
     1581                              status-hook (element-type 'character) 
     1582                              (sharing :private) 
     1583                              env) 
     1584    "Invoke an external program as an OS subprocess of lisp." 
     1585    (declare (ignore pty)) 
     1586    (unless (every #'(lambda (a) (typep a 'simple-string)) args) 
     1587      (error "Program args must all be simple strings : ~s" args)) 
     1588    (push program args) 
     1589    (let* ((token (list 0)) 
     1590           (in-fd nil) 
     1591           (in-stream nil) 
     1592           (out-fd nil) 
     1593           (out-stream nil) 
     1594           (error-fd nil) 
     1595           (error-stream nil) 
     1596           (close-in-parent nil) 
     1597           (close-on-error nil) 
     1598           (proc 
     1599            (make-external-process 
     1600             :pid nil 
     1601             :args args 
     1602             :%status :running 
     1603             :input nil 
     1604             :output nil 
     1605             :error nil 
     1606             :token token 
     1607             :status-hook status-hook))) 
     1608      (unwind-protect 
     1609           (progn 
     1610             (multiple-value-setq (in-fd in-stream close-in-parent close-on-error) 
     1611               (get-descriptor-for input proc  nil nil :direction :input 
     1612                                   :if-does-not-exist if-input-does-not-exist 
     1613                                   :sharing sharing 
     1614                                   :element-type element-type)) 
     1615             (multiple-value-setq (out-fd out-stream close-in-parent close-on-error) 
     1616               (get-descriptor-for output proc close-in-parent close-on-error 
     1617                                   :direction :output 
     1618                                   :if-exists if-output-exists 
     1619                                   :sharing sharing 
     1620                                   :element-type element-type)) 
     1621             (multiple-value-setq (error-fd error-stream close-in-parent close-on-error) 
     1622               (if (eq error :output) 
     1623                 (values out-fd out-stream close-in-parent close-on-error) 
     1624                 (get-descriptor-for error proc close-in-parent close-on-error 
     1625                                     :direction :output 
     1626                                     :if-exists if-error-exists 
     1627                                     :sharing sharing 
     1628                                     :element-type element-type))) 
     1629             (setf (external-process-input proc) in-stream 
     1630                   (external-process-output proc) out-stream 
     1631                   (external-process-error proc) error-stream) 
     1632             (process-run-function 
     1633              (format nil "Monitor thread for external process ~a" args) 
     1634                     
     1635              #'run-external-process proc in-fd out-fd error-fd env) 
     1636             (wait-on-semaphore (external-process-signal proc)) 
     1637             ) 
     1638        (dolist (fd close-in-parent) (fd-close fd)) 
     1639        (if (external-process-pid proc) 
     1640          (when (and wait (external-process-pid proc)) 
     1641            (with-interrupts-enabled 
     1642                (wait-on-semaphore (external-process-completed proc)))) 
     1643          (progn 
     1644            (dolist (fd close-on-error) (fd-close fd)) 
     1645            (error "Process execution failed")))) 
     1646      proc)) 
     1647 
     1648  (let* ((external-processes ()) 
     1649         (external-processes-lock (make-lock))) 
     1650    (defun add-external-process (p) 
     1651      (with-lock-grabbed (external-processes-lock) 
     1652        (push p external-processes))) 
     1653    (defun remove-external-process (p) 
     1654      (with-lock-grabbed (external-processes-lock) 
     1655        (setq external-processes (delete p external-processes)))) 
     1656    ;; Likewise 
     1657    (defun external-processes () 
     1658      (with-lock-grabbed (external-processes-lock) 
     1659        (copy-list external-processes))) 
     1660    ) 
     1661 
     1662 
     1663 
     1664 
     1665  (defun run-external-process (proc in-fd out-fd error-fd &optional env) 
     1666    (let* ((args (external-process-args proc)) 
     1667           (child-pid (exec-with-io-redirection in-fd out-fd error-fd args proc env))) 
     1668      (when child-pid 
     1669        (setf (external-process-pid proc) child-pid) 
     1670        (add-external-process proc) 
     1671        (signal-semaphore (external-process-signal proc)) 
     1672        (monitor-external-process proc)))) 
     1673 
     1674  (defun join-strings (strings) 
     1675    (reduce (lambda (left right) (concatenate 'string left " " right)) strings)) 
     1676 
     1677  (defun exec-with-io-redirection (new-in new-out new-err args proc &optional env) 
     1678    (declare (ignore env))              ; until we can do better. 
     1679    (with-filename-cstrs ((command (join-strings args))) 
     1680      (rletz ((proc-info #>PROCESS_INFORMATION) 
     1681              (si #>STARTUPINFO)) 
     1682        (setf (pref si #>STARTUPINFO.cb) (record-length #>STARTUPINFO)) 
     1683        (setf (pref si #>STARTUPINFO.dwFlags) 
     1684              (logior #$STARTF_USESTDHANDLES #$STARTF_USESHOWWINDOW)) 
     1685        (setf (pref si #>STARTUPINFO.wShowWindow) #$SW_HIDE) 
     1686        (setf (pref si #>STARTUPINFO.hStdInput) 
     1687              (if new-in 
     1688                (%int-to-ptr new-in) 
     1689                (#_GetStdHandle #$STD_INPUT_HANDLE))) 
     1690        (setf (pref si #>STARTUPINFO.hStdOutput) 
     1691              (if new-out 
     1692                (%int-to-ptr new-out) 
     1693                (#_GetStdHandle #$STD_OUTPUT_HANDLE))) 
     1694        (setf (pref si #>STARTUPINFO.hStdError) 
     1695              (if new-err 
     1696                (%int-to-ptr new-err) 
     1697                (#_GetStdHandle #$STD_ERROR_HANDLE))) 
     1698        (if (zerop (#_CreateProcessW (%null-ptr) 
     1699                                     command 
     1700                                     (%null-ptr) 
     1701                                     (%null-ptr) 
     1702                                     1 
     1703                                     #$CREATE_NEW_CONSOLE 
     1704                                     (%null-ptr) 
     1705                                     (%null-ptr) 
     1706                                     si 
     1707                                     proc-info)) 
     1708          (setf (external-process-%status proc) :error 
     1709                (external-process-%exit-code proc) (#_GetLastError)) 
     1710          (#_CloseHandle (pref proc-info #>PROCESS_INFORMATION.hThread))) 
     1711        (pref proc-info #>PROCESS_INFORMATION.hProcess)))) 
     1712 
     1713  (defun fd-uninheritable (fd &key direction) 
     1714    (let ((new-fd (fd-dup fd :direction direction))) 
     1715      (fd-close fd) 
     1716      new-fd)) 
     1717 
     1718  (defun monitor-external-process (p) 
     1719    (let* ((in-fds (external-process-watched-fds p)) 
     1720           (out-streams (external-process-watched-streams p)) 
     1721           (token (external-process-token p)) 
     1722           (terminated)) 
     1723      (loop 
     1724        (when terminated 
     1725          (without-interrupts 
     1726           (decf (car token)) 
     1727           (if in-fd (fd-close in-fd)) 
     1728           (setq in-fd nil) 
     1729           (rlet ((code #>DWORD)) 
     1730             (loop 
     1731               (#_GetExitCodeProcess (external-process-pid p) code) 
     1732               (unless (eql (pref code #>DWORD) #$STILL_ACTIVE) 
     1733                 (return))) 
     1734             (#_SleepEx 10 #$TRUE) 
     1735             (setf (external-process-%exit-code p) (pref code #>DWORD))) 
     1736           (#_CloseHandle (external-process-pid p)) 
     1737           (setf (external-process-pid p) nil) 
     1738           (setf (external-process-%status p) :exited) 
     1739           (let ((status-hook (external-process-status-hook p))) 
     1740             (when status-hook 
     1741               (funcall status-hook p))) 
     1742           (remove-external-process p) 
     1743           (signal-semaphore (external-process-completed p)) 
     1744           (return)))     
     1745        (if in-fd 
     1746          (rlet ((handles (:array #>HANDLE 2))) 
     1747            (setf (paref handles (:array #>HANDLE) 0) (external-process-pid p)) 
     1748            (setf (paref handles (:array #>HANDLE) 1) (#__get_osfhandle in-fd)) 
     1749            (let ((rc (ignoring-eintr 
     1750                       (let* ((code (#_WaitForMultipleObjectsEx 2 handles #$FALSE #$INFINITE #$true))) 
     1751                         (if (eql code #$WAIT_IO_COMPLETION) 
     1752                           (- #$EINTR) 
     1753                           code))))) 
     1754              (if (eq rc #$WAIT_OBJECT_0) 
     1755                (setf terminated t) 
     1756                (%stack-block ((buf 1024)) 
     1757                  (let* ((n (fd-read in-fd buf 1024))) 
     1758                    (declare (fixnum n)) 
     1759                    (if (<= n 0) 
     1760                      (setf terminated t) 
     1761                      (let* ((string (make-string 1024))) 
     1762                        (declare (dynamic-extent string)) 
     1763                        (%str-from-ptr buf n string) 
     1764                        (write-sequence string out-stream :end n)))))))) 
     1765          (progn 
     1766            (ignoring-eintr 
     1767             (let* ((code (#_WaitForSingleObjectEx (external-process-pid p) #$INFINITE #$true))) 
     1768               (if (eql code #$WAIT_IO_COMPLETION) 
     1769                 (- #$EINTR) 
     1770                 code))) 
     1771            (setf terminated t)))))) 
    13891772   
    1390 (defun signal-external-process (proc signal) 
    1391   "Send the specified signal to the specified external process.  (Typically, 
    1392 it would only be useful to call this function if the EXTERNAL-PROCESS was 
    1393 created with :WAIT NIL.) Return T if successful; NIL if the process wasn't 
    1394 created successfully, and signal an error otherwise." 
    1395   (require-type proc 'external-process) 
    1396   (let* ((pid (external-process-pid proc))) 
    1397     (when pid 
    1398       (let ((error (int-errno-call (#_kill pid signal)))) 
    1399         (or (eql error 0) 
    1400             (%errno-disp error)))))) 
    1401  
    1402 ) ; #-windows-target (progn 
    1403  
    1404 #+windows-target 
    1405 (progn 
    1406 (defun temp-file-name (prefix) 
    1407   "Returns a unique name for a temporary file, residing in system temp 
    1408 space, and prefixed with PREFIX." 
    1409   (rlet ((buffer (:array :wchar_t #.#$MAX_PATH))) 
    1410     (#_GetTempPathW #$MAX_PATH buffer) 
    1411     (with-filename-cstrs ((c-prefix prefix))  
    1412       (#_GetTempFileNameW buffer c-prefix 0 buffer) 
    1413       (%get-native-utf-16-cstring buffer)))) 
    1414    
    1415 (defun get-descriptor-for (object proc close-in-parent close-on-error 
    1416                                   &rest keys &key direction (element-type 'character) 
    1417                                   &allow-other-keys) 
    1418   (etypecase object 
    1419     ((eql t) 
    1420      (values nil nil close-in-parent close-on-error)) 
    1421     (null 
    1422      (let* ((null-device "nul") 
    1423             (fd (fd-open null-device (case direction 
    1424                                        (:input #$O_RDONLY) 
    1425                                        (:output #$O_WRONLY) 
    1426                                        (t #$O_RDWR))))) 
    1427        (if (< fd 0) 
    1428          (signal-file-error fd null-device)) 
    1429        (values fd nil (cons fd close-in-parent) (cons fd close-on-error)))) 
    1430     ((eql :stream) 
    1431      (multiple-value-bind (read-pipe write-pipe) (pipe) 
    1432        (case direction 
    1433          (:input 
    1434           (values read-pipe 
    1435                   (make-fd-stream (fd-uninheritable write-pipe :direction :output) 
    1436                                   :direction :output 
    1437                                   :element-type element-type 
    1438                                   :interactive nil 
    1439                                   :basic t 
    1440                                   :auto-close t) 
    1441                   (cons read-pipe close-in-parent) 
    1442                   (cons write-pipe close-on-error))) 
    1443          (:output 
    1444           (values write-pipe 
    1445                   (make-fd-stream (fd-uninheritable read-pipe :direction :input) 
    1446                                   :direction :input 
    1447                                   :element-type element-type 
    1448                                   :interactive nil 
    1449                                   :basic t 
    1450                                   :auto-close t) 
    1451                   (cons write-pipe close-in-parent) 
    1452                   (cons read-pipe close-on-error))) 
    1453          (t 
    1454           (fd-close read-pipe) 
    1455           (fd-close write-pipe) 
    1456           (report-bad-arg direction '(member :input :output)))))) 
    1457     ((or pathname string) 
    1458      (with-open-stream (file (apply #'open object keys)) 
    1459        (let* ((fd (fd-dup (ioblock-device (stream-ioblock file t)) :direction direction :inheritable t))) 
    1460          (values fd 
    1461                  nil 
    1462                  (cons fd close-in-parent) 
    1463                  (cons fd close-on-error))))) 
    1464     (fd-stream 
    1465      (let ((fd (fd-dup (ioblock-device (stream-ioblock object t))))) 
    1466        (values fd 
    1467                nil 
    1468                (cons fd close-in-parent) 
    1469                (cons fd close-on-error)))) 
    1470     (stream 
    1471      (ecase direction 
    1472        (:input 
    1473         (let* ((tempname (temp-file-name "lisp-temp")) 
    1474                (fd (fd-open tempname #$O_RDWR))) 
    1475           (if (< fd 0) 
    1476             (%errno-disp fd)) 
    1477           (loop 
    1478             (multiple-value-bind (line no-newline) 
    1479                 (read-line object nil nil) 
    1480               (unless line 
    1481                 (return)) 
    1482               (let* ((len (length line))) 
    1483                 (%stack-block ((buf (1+ len))) 
    1484                   (%cstr-pointer line buf) 
    1485                   (fd-write fd buf len) 
    1486                   (if no-newline 
    1487                     (return)) 
    1488                   (setf (%get-byte buf) (char-code #\newline)) 
    1489                   (fd-write fd buf 1))))) 
    1490           (fd-lseek fd 0 #$SEEK_SET) 
    1491           (values fd nil (cons fd close-in-parent) (cons fd close-on-error)))) 
    1492        (:output 
    1493         (multiple-value-bind (read-pipe write-pipe) (pipe) 
    1494           (setf (external-process-watched-fd proc) read-pipe 
    1495                 (external-process-watched-stream proc) object) 
    1496           (incf (car (external-process-token proc))) 
    1497           (values write-pipe 
    1498                   nil 
    1499                   (cons write-pipe close-in-parent) 
    1500                   (cons read-pipe close-on-error)))))))) 
    1501  
    1502 (defstruct external-process 
    1503   pid 
    1504   %status 
    1505   %exit-code 
    1506   pty 
    1507   input 
    1508   output 
    1509   error 
    1510   status-hook 
    1511   plist 
    1512   token 
    1513   core 
    1514   args 
    1515   (signal (make-semaphore)) 
    1516   (completed (make-semaphore)) 
    1517   watched-fd 
    1518   watched-stream 
    1519   ) 
    1520  
    1521 (defun external-process-status (proc) 
    1522   "Return information about whether an OS subprocess is running; or, if 
    1523 not, why not; and what its result code was if it completed." 
    1524   (require-type proc 'external-process) 
    1525   (values (external-process-%status proc) 
    1526           (external-process-%exit-code proc))) 
    1527  
    1528  
    1529 (defmethod print-object ((p external-process) stream) 
    1530   (print-unreadable-object (p stream :type t :identity t) 
    1531     (let* ((status (external-process-%status p))) 
    1532       (let* ((*print-length* 3)) 
    1533         (format stream "~a" (external-process-args p))) 
    1534       (format stream "[~d] (~a" (external-process-pid p) status) 
    1535       (unless (eq status :running) 
    1536         (format stream " : ~d" (external-process-%exit-code p))) 
    1537       (format stream ")")))) 
    1538  
    1539 (defun run-program (program args &key 
    1540                             (wait t) pty 
    1541                             input if-input-does-not-exist 
    1542                             output (if-output-exists :error) 
    1543                             (error :output) (if-error-exists :error) 
    1544                             status-hook (element-type 'character) 
    1545                             env) 
    1546   "Invoke an external program as an OS subprocess of lisp." 
    1547   (declare (ignore pty)) 
    1548   (unless (every #'(lambda (a) (typep a 'simple-string)) args) 
    1549     (error "Program args must all be simple strings : ~s" args)) 
    1550   (push program args) 
    1551   (let* ((token (list 0)) 
    1552          (in-fd nil) 
    1553          (in-stream nil) 
    1554          (out-fd nil) 
    1555          (out-stream nil) 
    1556          (error-fd nil) 
    1557          (error-stream nil) 
    1558          (close-in-parent nil) 
    1559          (close-on-error nil) 
    1560          (proc 
    1561           (make-external-process 
    1562            :pid nil 
    1563            :args args 
    1564            :%status :running 
    1565            :input nil 
    1566            :output nil 
    1567            :error nil 
    1568            :token token 
    1569            :status-hook status-hook))) 
    1570     (unwind-protect 
    1571          (progn 
    1572            (multiple-value-setq (in-fd in-stream close-in-parent close-on-error) 
    1573              (get-descriptor-for input proc  nil nil :direction :input 
    1574                                  :if-does-not-exist if-input-does-not-exist 
    1575                                  :element-type element-type)) 
    1576            (multiple-value-setq (out-fd out-stream close-in-parent close-on-error) 
    1577              (get-descriptor-for output proc close-in-parent close-on-error 
    1578                                  :direction :output 
    1579                                  :if-exists if-output-exists 
    1580                                  :element-type element-type)) 
    1581            (multiple-value-setq (error-fd error-stream close-in-parent close-on-error) 
    1582              (if (eq error :output) 
    1583                (values out-fd out-stream close-in-parent close-on-error) 
    1584                (get-descriptor-for error proc close-in-parent close-on-error 
    1585                                    :direction :output 
    1586                                    :if-exists if-error-exists 
    1587                                    :element-type element-type))) 
    1588            (setf (external-process-input proc) in-stream 
    1589                  (external-process-output proc) out-stream 
    1590                  (external-process-error proc) error-stream) 
    1591            (process-run-function 
    1592             (format nil "Monitor thread for external process ~a" args) 
    1593                      
    1594             #'run-external-process proc in-fd out-fd error-fd env) 
    1595            (wait-on-semaphore (external-process-signal proc)) 
    1596            ) 
    1597       (dolist (fd close-in-parent) (fd-close fd)) 
    1598       (if (external-process-pid proc) 
    1599         (when (and wait (external-process-pid proc)) 
    1600           (with-interrupts-enabled 
    1601               (wait-on-semaphore (external-process-completed proc)))) 
    1602         (progn 
    1603           (dolist (fd close-on-error) (fd-close fd)) 
    1604           (error "Process execution failed")))) 
    1605     proc)) 
    1606  
    1607 (let* ((external-processes ()) 
    1608        (external-processes-lock (make-lock))) 
    1609   (defun add-external-process (p) 
    1610     (with-lock-grabbed (external-processes-lock) 
    1611       (push p external-processes))) 
    1612   (defun remove-external-process (p) 
    1613     (with-lock-grabbed (external-processes-lock) 
    1614       (setq external-processes (delete p external-processes)))) 
    1615   ;; Likewise 
    1616   (defun external-processes () 
    1617     (with-lock-grabbed (external-processes-lock) 
    1618       (copy-list external-processes))) 
    1619   ) 
    1620  
    1621  
    1622  
    1623  
    1624 (defun run-external-process (proc in-fd out-fd error-fd &optional env) 
    1625   (let* ((args (external-process-args proc)) 
    1626          (child-pid (exec-with-io-redirection in-fd out-fd error-fd args proc env))) 
    1627     (when child-pid 
    1628       (setf (external-process-pid proc) child-pid) 
    1629       (add-external-process proc) 
    1630       (signal-semaphore (external-process-signal proc)) 
    1631       (monitor-external-process proc)))) 
    1632  
    1633 (defun join-strings (strings) 
    1634   (reduce (lambda (left right) (concatenate 'string left " " right)) strings)) 
    1635  
    1636 (defun exec-with-io-redirection (new-in new-out new-err args proc &optional env) 
    1637   (declare (ignore env))                ; until we can do better. 
    1638   (with-filename-cstrs ((command (join-strings args))) 
    1639     (rletz ((proc-info #>PROCESS_INFORMATION) 
    1640             (si #>STARTUPINFO)) 
    1641       (setf (pref si #>STARTUPINFO.cb) (record-length #>STARTUPINFO)) 
    1642       (setf (pref si #>STARTUPINFO.dwFlags) 
    1643             (logior #$STARTF_USESTDHANDLES #$STARTF_USESHOWWINDOW)) 
    1644       (setf (pref si #>STARTUPINFO.wShowWindow) #$SW_HIDE) 
    1645       (setf (pref si #>STARTUPINFO.hStdInput) 
    1646             (if new-in 
    1647               (%int-to-ptr new-in) 
    1648               (#_GetStdHandle #$STD_INPUT_HANDLE))) 
    1649       (setf (pref si #>STARTUPINFO.hStdOutput) 
    1650             (if new-out 
    1651               (%int-to-ptr new-out) 
    1652               (#_GetStdHandle #$STD_OUTPUT_HANDLE))) 
    1653       (setf (pref si #>STARTUPINFO.hStdError) 
    1654             (if new-err 
    1655               (%int-to-ptr new-err) 
    1656               (#_GetStdHandle #$STD_ERROR_HANDLE))) 
    1657       (if (zerop (#_CreateProcessW (%null-ptr) 
    1658                                    command 
    1659                                    (%null-ptr) 
    1660                                    (%null-ptr) 
    1661                                    1 
    1662                                    #$CREATE_NEW_CONSOLE 
    1663                                    (%null-ptr) 
    1664                                    (%null-ptr) 
    1665                                    si 
    1666                                    proc-info)) 
    1667         (setf (external-process-%status proc) :error 
    1668               (external-process-%exit-code proc) (#_GetLastError)) 
    1669         (#_CloseHandle (pref proc-info #>PROCESS_INFORMATION.hThread))) 
    1670       (pref proc-info #>PROCESS_INFORMATION.hProcess)))) 
    1671  
    1672 (defun fd-uninheritable (fd &key direction) 
    1673   (let ((new-fd (fd-dup fd :direction direction))) 
    1674     (fd-close fd) 
    1675     new-fd)) 
    1676  
    1677 (defun monitor-external-process (p) 
    1678   (let* ((in-fd (external-process-watched-fd p)) 
    1679          (out-stream (external-process-watched-stream p)) 
    1680          (token (external-process-token p)) 
    1681          (terminated)) 
    1682     (loop 
    1683       (when terminated 
    1684         (without-interrupts 
    1685          (decf (car token)) 
    1686          (if in-fd (fd-close in-fd)) 
    1687          (setq in-fd nil) 
    1688          (rlet ((code #>DWORD)) 
    1689            (loop 
    1690              (#_GetExitCodeProcess (external-process-pid p) code) 
    1691              (unless (eql (pref code #>DWORD) #$STILL_ACTIVE) 
    1692                (return))) 
    1693            (#_SleepEx 10 #$TRUE) 
    1694            (setf (external-process-%exit-code p) (pref code #>DWORD))) 
    1695          (#_CloseHandle (external-process-pid p)) 
    1696          (setf (external-process-pid p) nil) 
    1697          (setf (external-process-%status p) :exited) 
    1698          (let ((status-hook (external-process-status-hook p))) 
    1699            (when status-hook 
    1700              (funcall status-hook p))) 
    1701          (remove-external-process p) 
    1702          (signal-semaphore (external-process-completed p)) 
    1703          (return)))       
    1704       (if in-fd 
    1705         (rlet ((handles (:array #>HANDLE 2))) 
    1706           (setf (paref handles (:array #>HANDLE) 0) (external-process-pid p)) 
    1707           (setf (paref handles (:array #>HANDLE) 1) (#__get_osfhandle in-fd)) 
    1708           (let ((rc (ignoring-eintr 
    1709                      (let* ((code (#_WaitForMultipleObjectsEx 2 handles #$FALSE #$INFINITE #$true))) 
    1710                        (if (eql code #$WAIT_IO_COMPLETION) 
    1711                          (- #$EINTR) 
    1712                          code))))) 
    1713             (if (eq rc #$WAIT_OBJECT_0) 
    1714               (setf terminated t) 
    1715               (%stack-block ((buf 1024)) 
    1716                 (let* ((n (fd-read in-fd buf 1024))) 
    1717                   (declare (fixnum n)) 
    1718                   (if (<= n 0) 
    1719                     (setf terminated t) 
    1720                     (let* ((string (make-string 1024))) 
    1721                       (declare (dynamic-extent string)) 
    1722                       (%str-from-ptr buf n string) 
    1723                       (write-sequence string out-stream :end n)))))))) 
    1724         (progn 
    1725           (ignoring-eintr 
    1726            (let* ((code (#_WaitForSingleObjectEx (external-process-pid p) #$INFINITE #$true))) 
    1727              (if (eql code #$WAIT_IO_COMPLETION) 
    1728                (- #$EINTR) 
    1729                code))) 
    1730           (setf terminated t)))))) 
    1731    
    1732  
    1733 )                                   ; #+windows-target (progn 
     1773 
     1774  )                                     ; #+windows-target (progn 
    17341775 
    17351776;;; EOF on a TTY is transient, but I'm less sure of other cases.