Use OperatingSystem executeCommand:... instead of popen().
authorStefan Vogel <sv@exept.de>
Mon, 09 Sep 1996 12:30:27 +0200
changeset 1648 faa891d2c1b0
parent 1647 6ccae4646a4a
child 1649 74626d4c1d96
Use OperatingSystem executeCommand:... instead of popen().
PipeStr.st
PipeStream.st
--- a/PipeStr.st	Sat Sep 07 14:11:39 1996 +0200
+++ b/PipeStr.st	Mon Sep 09 12:30:27 1996 +0200
@@ -11,7 +11,7 @@
 "
 
 NonPositionableExternalStream subclass:#PipeStream
-	instanceVariableNames:'commandString'
+	instanceVariableNames:'commandString pid exitStatus exitSema'
 	classVariableNames:'BrokenPipeSignal'
 	poolDictionaries:''
 	category:'Streams-External'
@@ -130,7 +130,7 @@
 %}
 ! !
 
-!PipeStream class methodsFor:'documentation'!
+!PipeStream  class methodsFor:'documentation'!
 
 copyright
 "
@@ -184,7 +184,7 @@
 "
 ! !
 
-!PipeStream class methodsFor:'initialization'!
+!PipeStream  class methodsFor:'initialization'!
 
 initialize
     "setup the signal"
@@ -196,7 +196,7 @@
     ]
 ! !
 
-!PipeStream class methodsFor:'instance creation'!
+!PipeStream  class methodsFor:'instance creation'!
 
 readingFrom:commandString
     "create and return a new pipeStream which can read from the unix command
@@ -204,7 +204,16 @@
 
     ^ (self basicNew) readingFrom:commandString
 
-    "PipeStream readingFrom:'ls -l'"
+    "
+        PipeStream readingFrom:'ls -l'
+
+        |s|
+        s := PipeStream readingFrom:'sh -c sleep\ 600'.
+        (Delay forSeconds:2) wait.
+        s shutDown
+    "
+
+    "Modified: 24.4.1996 / 09:09:25 / stefan"
 !
 
 writingTo:commandString
@@ -216,7 +225,7 @@
     "PipeStream writingTo:'sort'"
 ! !
 
-!PipeStream class methodsFor:'Signal constants'!
+!PipeStream  class methodsFor:'Signal constants'!
 
 brokenPipeSignal
     "return the signal used to handle SIGPIPE unix-signals"
@@ -230,30 +239,38 @@
     "return the command string"
 
     ^ commandString
+!
+
+exitStatus
+    "return exitStatus"
+
+    ^ exitStatus
+
+    "Created: 28.12.1995 / 14:54:41 / stefan"
+!
+
+pid
+    "return pid"
+
+    ^ pid
+
+    "Created: 28.12.1995 / 14:54:30 / stefan"
 ! !
 
 !PipeStream methodsFor:'instance release'!
 
 closeFile
-    "low level close - redefined since we close a pipe here.
+    "low level close
      This waits for the command to finish. 
      Use shutDown for a fast (nonBlocking) close."
 
-%{  /* UNLIMITEDSTACK */
-#if !defined(transputer) && !defined(MSDOS_LIKE)
-    OBJ fp;
-
-    if ((fp = __INST(filePointer)) != nil) {
-	__INST(filePointer) = nil;
-	/*
-	 * allow interrupt even when blocking here ...
-	 */
-	__BEGIN_INTERRUPTABLE__
-	pclose(__FILEVal(fp));
-	__END_INTERRUPTABLE__
-    }
-#endif /* not transputer && not MSDOS_LIKE */
-%}
+    filePointer notNil ifTrue:[
+        super closeFile.
+        filePointer := nil.
+        pid notNil ifTrue:[
+            exitSema wait.
+        ].
+    ].
 !
 
 closeFileDescriptor
@@ -270,8 +287,8 @@
 
     if ((fp = __INST(filePointer)) != nil) {
 	__INST(filePointer) = nil;
+	f = __FILEVal(fp);
 	__BEGIN_INTERRUPTABLE__
-	f = __FILEVal(fp);
 	close(fileno(f));
 	__END_INTERRUPTABLE__
     }
@@ -286,12 +303,27 @@
 !
 
 shutDown
-    "close the Stream, ignoring any broken-pipe errors"
+    "close the Stream, ignoring any broken-pipe errors.
+     Terminate the command"
 
     BrokenPipeSignal catch:[
-	Lobby unregister:self.
-	self closeFileDescriptor
+        |tpid|
+
+        Lobby unregister:self.
+        self closeFileDescriptor.
+        tpid := pid.                    "copy pid to avoid race"
+        tpid notNil ifTrue:[
+            "/
+            "/ Terminate both the process and group, just in case the
+            "/ operating system does not support process groups.
+            "/
+            OperatingSystem terminateProcess:tpid.
+            OperatingSystem terminateProcessGroup:tpid.
+            pid := nil.
+        ].
     ]
+
+    "Modified: 23.5.1996 / 09:15:41 / stefan"
 ! !
 
 !PipeStream methodsFor:'private'!
@@ -323,68 +355,79 @@
 !
 
 openPipeFor:aCommandString withMode:mode
-    "open a pipe to the unix command in aCcommandString; 
+    "open a pipe to the unix command in commandString; 
      mode may be 'r' or 'w'"
 
-    |retVal|
+    |blocked pipeFdArray execFdArray execFd myFd|
 
     filePointer notNil ifTrue:[
-	"the pipe was already open ...
-	 this should (can) not happen."
-	^ self errorOpen
+        "the pipe was already open ...
+         this should (can) not happen."
+        ^ self errorOpen
+    ].
+    lastErrorNumber := nil.
+    exitStatus := nil.
+    exitSema := Semaphore new.
+
+    pipeFdArray := OperatingSystem makePipe.
+    pipeFdArray isNil ifTrue:[
+        lastErrorNumber := OperatingSystem currentErrorNumber.
+        ^ self openError
+    ].
+
+    mode = 'r' ifTrue:[
+        execFd := pipeFdArray at:2.
+        execFdArray := Array with:0 with:execFd with:2.
+        myFd := pipeFdArray at:1.
+    ] ifFalse:[
+        execFd := pipeFdArray at:1.
+        execFdArray := Array with:execFd with:1 with:2.
+        myFd := pipeFdArray at:2.
     ].
 
-%{  /* STACK: 32000 */
-#if !defined(transputer) && !defined(MSDOS_LIKE)
-    FILE *f;
-    OBJ fp;
-
-    __INST(lastErrorNumber) = nil;
-
-    if (__isString(aCommandString) && __isString(mode)) {
-	__BEGIN_INTERRUPTABLE__
-	do {
-# ifdef LINUX
-	    /* LINUX returns a non-NULL f even when interrupted */
-	    errno = 0;
-	    f = (FILE *)popen((char *) __stringVal(aCommandString),
-			      (char *) __stringVal(mode));
-	    if (errno == EINTR)
-		f = NULL;
-# else
-	    f = (FILE *)popen((char *) __stringVal(aCommandString),
-			      (char *) __stringVal(mode));
-# endif /* LINUX */
-	} while ((f == NULL) && (errno == EINTR));
-	__END_INTERRUPTABLE__
+    blocked := OperatingSystem blockInterrupts.
+    pid := OperatingSystem exec:'/bin/sh'
+                           withArguments:(Array with:'sh' with:'-c' with:aCommandString)
+                           fileDescriptors:execFdArray
+                           closeDescriptors:(Array with:myFd)
+                           fork:true
+                           newPgrp:true.
 
-	if (f == NULL) {
-	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
-	} else {
-	    clearerr(f);
-	    __INST(filePointer) = fp = __MKOBJ(f); __STORE(self, fp);
-	    retVal = self;
-	}
-    }
-#endif /* not transputer && not MSDOS_LIKE */
-%}.
-    retVal notNil ifTrue:[
-	commandString := aCommandString.
-	buffered := true.
-	hitEOF := false.
-	binary := false.
-	Lobby register:self
+    OperatingSystem closeFd:execFd.
+    pid > 0 ifTrue:[
+        self fileDescriptor:myFd withMode:mode.
+        Processor monitorPid:pid action:[ :stat |
+            exitStatus := stat.
+            pid := nil.
+            exitSema signal.
+        ].
+    ] ifFalse:[
+        pid := nil.
+        lastErrorNumber := OperatingSystem currentErrorNumber.
+        OperatingSystem closeFd:myFd.
+    ].
+    blocked ifFalse:[
+        OperatingSystem unblockInterrupts
     ].
-    lastErrorNumber notNil ifTrue:[
-	"
-	 the pipe open failed for some reason ...
-	 ... this may be either due to an invalid command string,
-	 or due to the system running out of memory (when forking
-	 the unix process)
-	"
-	^ self openError
+
+    lastErrorNumber isNil ifTrue:[
+        commandString := aCommandString.
+        buffered := true.
+        hitEOF := false.
+        binary := false.
+        Lobby register:self
+    ] ifFalse:[
+        "
+         the pipe open failed for some reason ...
+         ... this may be either due to an invalid command string,
+         or due to the system running out of memory (when forking
+         the unix process)
+        "
+        ^ self openError
     ].
-    ^ retVal
+    ^ self
+
+    "Modified: 23.4.1996 / 17:05:59 / stefan"
 !
 
 readingFrom:command
@@ -401,9 +444,9 @@
     ^ self openPipeFor:command withMode:'w'
 ! !
 
-!PipeStream class methodsFor:'documentation'!
+!PipeStream  class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/Attic/PipeStr.st,v 1.43 1996-04-25 17:01:50 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/Attic/PipeStr.st,v 1.44 1996-09-09 10:30:27 stefan Exp $'
 ! !
 PipeStream initialize!
--- a/PipeStream.st	Sat Sep 07 14:11:39 1996 +0200
+++ b/PipeStream.st	Mon Sep 09 12:30:27 1996 +0200
@@ -11,7 +11,7 @@
 "
 
 NonPositionableExternalStream subclass:#PipeStream
-	instanceVariableNames:'commandString'
+	instanceVariableNames:'commandString pid exitStatus exitSema'
 	classVariableNames:'BrokenPipeSignal'
 	poolDictionaries:''
 	category:'Streams-External'
@@ -130,7 +130,7 @@
 %}
 ! !
 
-!PipeStream class methodsFor:'documentation'!
+!PipeStream  class methodsFor:'documentation'!
 
 copyright
 "
@@ -184,7 +184,7 @@
 "
 ! !
 
-!PipeStream class methodsFor:'initialization'!
+!PipeStream  class methodsFor:'initialization'!
 
 initialize
     "setup the signal"
@@ -196,7 +196,7 @@
     ]
 ! !
 
-!PipeStream class methodsFor:'instance creation'!
+!PipeStream  class methodsFor:'instance creation'!
 
 readingFrom:commandString
     "create and return a new pipeStream which can read from the unix command
@@ -204,7 +204,16 @@
 
     ^ (self basicNew) readingFrom:commandString
 
-    "PipeStream readingFrom:'ls -l'"
+    "
+        PipeStream readingFrom:'ls -l'
+
+        |s|
+        s := PipeStream readingFrom:'sh -c sleep\ 600'.
+        (Delay forSeconds:2) wait.
+        s shutDown
+    "
+
+    "Modified: 24.4.1996 / 09:09:25 / stefan"
 !
 
 writingTo:commandString
@@ -216,7 +225,7 @@
     "PipeStream writingTo:'sort'"
 ! !
 
-!PipeStream class methodsFor:'Signal constants'!
+!PipeStream  class methodsFor:'Signal constants'!
 
 brokenPipeSignal
     "return the signal used to handle SIGPIPE unix-signals"
@@ -230,30 +239,38 @@
     "return the command string"
 
     ^ commandString
+!
+
+exitStatus
+    "return exitStatus"
+
+    ^ exitStatus
+
+    "Created: 28.12.1995 / 14:54:41 / stefan"
+!
+
+pid
+    "return pid"
+
+    ^ pid
+
+    "Created: 28.12.1995 / 14:54:30 / stefan"
 ! !
 
 !PipeStream methodsFor:'instance release'!
 
 closeFile
-    "low level close - redefined since we close a pipe here.
+    "low level close
      This waits for the command to finish. 
      Use shutDown for a fast (nonBlocking) close."
 
-%{  /* UNLIMITEDSTACK */
-#if !defined(transputer) && !defined(MSDOS_LIKE)
-    OBJ fp;
-
-    if ((fp = __INST(filePointer)) != nil) {
-	__INST(filePointer) = nil;
-	/*
-	 * allow interrupt even when blocking here ...
-	 */
-	__BEGIN_INTERRUPTABLE__
-	pclose(__FILEVal(fp));
-	__END_INTERRUPTABLE__
-    }
-#endif /* not transputer && not MSDOS_LIKE */
-%}
+    filePointer notNil ifTrue:[
+        super closeFile.
+        filePointer := nil.
+        pid notNil ifTrue:[
+            exitSema wait.
+        ].
+    ].
 !
 
 closeFileDescriptor
@@ -270,8 +287,8 @@
 
     if ((fp = __INST(filePointer)) != nil) {
 	__INST(filePointer) = nil;
+	f = __FILEVal(fp);
 	__BEGIN_INTERRUPTABLE__
-	f = __FILEVal(fp);
 	close(fileno(f));
 	__END_INTERRUPTABLE__
     }
@@ -286,12 +303,27 @@
 !
 
 shutDown
-    "close the Stream, ignoring any broken-pipe errors"
+    "close the Stream, ignoring any broken-pipe errors.
+     Terminate the command"
 
     BrokenPipeSignal catch:[
-	Lobby unregister:self.
-	self closeFileDescriptor
+        |tpid|
+
+        Lobby unregister:self.
+        self closeFileDescriptor.
+        tpid := pid.                    "copy pid to avoid race"
+        tpid notNil ifTrue:[
+            "/
+            "/ Terminate both the process and group, just in case the
+            "/ operating system does not support process groups.
+            "/
+            OperatingSystem terminateProcess:tpid.
+            OperatingSystem terminateProcessGroup:tpid.
+            pid := nil.
+        ].
     ]
+
+    "Modified: 23.5.1996 / 09:15:41 / stefan"
 ! !
 
 !PipeStream methodsFor:'private'!
@@ -323,68 +355,79 @@
 !
 
 openPipeFor:aCommandString withMode:mode
-    "open a pipe to the unix command in aCcommandString; 
+    "open a pipe to the unix command in commandString; 
      mode may be 'r' or 'w'"
 
-    |retVal|
+    |blocked pipeFdArray execFdArray execFd myFd|
 
     filePointer notNil ifTrue:[
-	"the pipe was already open ...
-	 this should (can) not happen."
-	^ self errorOpen
+        "the pipe was already open ...
+         this should (can) not happen."
+        ^ self errorOpen
+    ].
+    lastErrorNumber := nil.
+    exitStatus := nil.
+    exitSema := Semaphore new.
+
+    pipeFdArray := OperatingSystem makePipe.
+    pipeFdArray isNil ifTrue:[
+        lastErrorNumber := OperatingSystem currentErrorNumber.
+        ^ self openError
+    ].
+
+    mode = 'r' ifTrue:[
+        execFd := pipeFdArray at:2.
+        execFdArray := Array with:0 with:execFd with:2.
+        myFd := pipeFdArray at:1.
+    ] ifFalse:[
+        execFd := pipeFdArray at:1.
+        execFdArray := Array with:execFd with:1 with:2.
+        myFd := pipeFdArray at:2.
     ].
 
-%{  /* STACK: 32000 */
-#if !defined(transputer) && !defined(MSDOS_LIKE)
-    FILE *f;
-    OBJ fp;
-
-    __INST(lastErrorNumber) = nil;
-
-    if (__isString(aCommandString) && __isString(mode)) {
-	__BEGIN_INTERRUPTABLE__
-	do {
-# ifdef LINUX
-	    /* LINUX returns a non-NULL f even when interrupted */
-	    errno = 0;
-	    f = (FILE *)popen((char *) __stringVal(aCommandString),
-			      (char *) __stringVal(mode));
-	    if (errno == EINTR)
-		f = NULL;
-# else
-	    f = (FILE *)popen((char *) __stringVal(aCommandString),
-			      (char *) __stringVal(mode));
-# endif /* LINUX */
-	} while ((f == NULL) && (errno == EINTR));
-	__END_INTERRUPTABLE__
+    blocked := OperatingSystem blockInterrupts.
+    pid := OperatingSystem exec:'/bin/sh'
+                           withArguments:(Array with:'sh' with:'-c' with:aCommandString)
+                           fileDescriptors:execFdArray
+                           closeDescriptors:(Array with:myFd)
+                           fork:true
+                           newPgrp:true.
 
-	if (f == NULL) {
-	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
-	} else {
-	    clearerr(f);
-	    __INST(filePointer) = fp = __MKOBJ(f); __STORE(self, fp);
-	    retVal = self;
-	}
-    }
-#endif /* not transputer && not MSDOS_LIKE */
-%}.
-    retVal notNil ifTrue:[
-	commandString := aCommandString.
-	buffered := true.
-	hitEOF := false.
-	binary := false.
-	Lobby register:self
+    OperatingSystem closeFd:execFd.
+    pid > 0 ifTrue:[
+        self fileDescriptor:myFd withMode:mode.
+        Processor monitorPid:pid action:[ :stat |
+            exitStatus := stat.
+            pid := nil.
+            exitSema signal.
+        ].
+    ] ifFalse:[
+        pid := nil.
+        lastErrorNumber := OperatingSystem currentErrorNumber.
+        OperatingSystem closeFd:myFd.
+    ].
+    blocked ifFalse:[
+        OperatingSystem unblockInterrupts
     ].
-    lastErrorNumber notNil ifTrue:[
-	"
-	 the pipe open failed for some reason ...
-	 ... this may be either due to an invalid command string,
-	 or due to the system running out of memory (when forking
-	 the unix process)
-	"
-	^ self openError
+
+    lastErrorNumber isNil ifTrue:[
+        commandString := aCommandString.
+        buffered := true.
+        hitEOF := false.
+        binary := false.
+        Lobby register:self
+    ] ifFalse:[
+        "
+         the pipe open failed for some reason ...
+         ... this may be either due to an invalid command string,
+         or due to the system running out of memory (when forking
+         the unix process)
+        "
+        ^ self openError
     ].
-    ^ retVal
+    ^ self
+
+    "Modified: 23.4.1996 / 17:05:59 / stefan"
 !
 
 readingFrom:command
@@ -401,9 +444,9 @@
     ^ self openPipeFor:command withMode:'w'
 ! !
 
-!PipeStream class methodsFor:'documentation'!
+!PipeStream  class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/PipeStream.st,v 1.43 1996-04-25 17:01:50 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/PipeStream.st,v 1.44 1996-09-09 10:30:27 stefan Exp $'
 ! !
 PipeStream initialize!