<<>> <> <> <> <> <> <> DIRECTORY CedarProcess, Convert, IO, Rope, UnixSpawnTCP, UnixSysCalls, UnixTypes, UXStrings; UnixSpawnTCPImpl: CEDAR MONITOR IMPORTS CedarProcess, Convert, IO, Rope, UnixSysCalls, UXStrings EXPORTS UnixSpawnTCP ~ BEGIN ROPE: TYPE ~ Rope.ROPE; SpawnData: TYPE ~ UnixSpawnTCP.SpawnData; FD: TYPE ~ UnixTypes.FileDescriptor; CHARPtr: TYPE ~ UnixTypes.CHARPtr; <> pidArrived: CONDITION; Spawn: PUBLIC PROC [ command: ROPE, out, err: IO.STREAM ¬ NIL, finish: UnixSpawnTCP.FinishProc ¬ NIL, readOut: CedarProcess.ForkableProc ¬ NIL, clientData: REF ANY ¬ NIL] RETURNS [s: REF SpawnData] ~ { FDPair: TYPE ~ RECORD [fd1, fd2: FD]; POpen: PROC [cmd: ROPE] RETURNS [fdp: FDPair] ~ { <> <> pppopen: PROC [p: POINTER TO FDPair, separate: BOOL, cmd: CHARPtr] RETURNS [INT] ~ TRUSTED MACHINE CODE {"XR_PPPOpen"}; TRUSTED {IF pppopen[@fdp, TRUE, UXStrings.Create[cmd]] # 0 THEN fdp ¬ [error,error]}; }; WaitForPID: ENTRY PROC [s: REF SpawnData] ~ { WHILE s.pid= -1 DO WAIT pidArrived ENDLOOP; }; <> <> fdp: FDPair ¬ POpen[Rope.Concat["echo $$; exec ", command]]; s ¬ NEW[SpawnData]; IF (s.fd1 ¬ fdp.fd1) = error OR (s.fd2 ¬ fdp.fd2) = error THEN RETURN[NIL] ELSE { s.pid ¬ -1; s.finish ¬ finish; s.clientData ¬ clientData; s.out ¬ out; s.err ¬ err; s.readOut ¬ IF readOut # NIL THEN readOut ELSE ReadOut; s.watch1 ¬ CedarProcess.Fork[WatchOut, s, [, TRUE]]; s.watch2 ¬ CedarProcess.Fork[WatchErr, s, [, TRUE]]; WaitForPID[s]; }; }; WatchOut: CedarProcess.ForkableProc ~ TRUSTED { s: REF SpawnData ¬ NARROW[data]; fd: UnixTypes.FileDescriptor ¬ s.fd1; buf: PACKED ARRAY[0..3] OF CHAR; nBytes: INT; rope: ROPE ¬ "0"; NotifyPID: ENTRY PROC ~ CHECKED { BROADCAST pidArrived; }; WHILE (nBytes ¬ UnixSysCalls.Read[fd, LOOPHOLE [@buf], 1]) > 0 DO char: CHAR ¬ buf[0]; IF char IN ['0..'9] THEN rope ¬ rope.Concat[Rope.FromChar[char]] ELSE EXIT; ENDLOOP; s.pid ¬ Convert.IntFromRope[rope ! Convert.Error => CONTINUE]; IF s.pid = -1 THEN s.pid ¬ 0; s.fdOut ¬ fd; s.readOutProcess ¬ CedarProcess.Fork[WrapReadOut, s, [, TRUE]]; NotifyPID[]; RETURN; }; outReaderDone: CONDITION; WatchErr: CedarProcess.ForkableProc ~ TRUSTED { s: REF SpawnData ¬ NARROW[data]; fd: UnixTypes.FileDescriptor ¬ s.fd2; nBytes: INT; stat: {bad, unknown} ¬ unknown; buf: UnixTypes.CHARPtr ¬ UXStrings.ObtainScratch[1024]; WaitForOutReaderDone: ENTRY PROC ~ CHECKED { WHILE s.readOut#NIL DO WAIT outReaderDone ENDLOOP; }; WHILE (nBytes ¬ UnixSysCalls.Read[fd, buf, 1023]) > 0 DO rope: ROPE ¬ UXStrings.ToRope[buf, nBytes]; IF Rope.Find[rope, "not found",, FALSE] # -1 THEN stat ¬ bad; IF s.err # NIL THEN IO.PutRope[s.err, rope]; IF CedarProcess.GetStatus[] = aborted THEN EXIT; ENDLOOP; UXStrings.ReleaseScratch[buf]; IF s.finish # NIL THEN { WaitForOutReaderDone[]; s.finish[IF stat = bad THEN $NotFound ELSE $Completed, s.clientData]; }; }; Kill: PUBLIC PROC [s: REF SpawnData] ~ { Close[s]; IF s # NIL AND s.pid # -1 THEN [] ¬ UnixSysCalls.Kill[s.pid, SIGTERM]; <> }; Write: PUBLIC PROC [s: REF SpawnData, rope: ROPE] ~ { string: UXStrings.CString ¬ UXStrings.CreateScratch[rope]; [] ¬ UnixSysCalls.Write[s.fdOut, string, Rope.Length[rope]]; UXStrings.ReleaseScratch[string]; }; Close: PUBLIC PROC [s: REF SpawnData] ~ { IF s # NIL THEN { CedarProcess.Abort[s.watch1]; CedarProcess.Abort[s.watch2]; CedarProcess.Abort[s.readOutProcess]; [] ¬ UnixSysCalls.Shutdown[s.fd1, noSend]; [] ¬ UnixSysCalls.Shutdown[s.fd2, noSend]; }; }; WrapReadOut: CedarProcess.ForkableProc ~ { s: REF SpawnData ¬ NARROW[data]; NotifyOutReaderDone: ENTRY PROC [] ~ { s.readOut ¬ NIL; BROADCAST outReaderDone; }; { ENABLE UNWIND => NotifyOutReaderDone[]; results ¬ s.readOut[data]; NotifyOutReaderDone[]; }; }; ReadOut: CedarProcess.ForkableProc ~ TRUSTED { nBytes: INT; s: REF SpawnData ¬ NARROW[data]; buf: CHARPtr ¬ UXStrings.ObtainScratch[1024]; WHILE (nBytes ¬ UnixSysCalls.Read[s.fdOut, buf, 1023]) > 0 DO IF s.out # NIL THEN IO.PutRope[s.out, UXStrings.ToRope[buf, nBytes]]; CedarProcess.CheckAbort[]; ENDLOOP; UXStrings.ReleaseScratch[buf]; CedarProcess.CheckAbort[]; Close[s]; }; END. .. <> <> <> <> <> <> <> < msg _ IO.PutFR1["no %g\n", IO.rope[args[1]]]];>> <> <> <> <<[] _ Buttons.Create[[name: IO.PutFR1["STOP (pid=%g)", IO.int[s.pid]], wx: 10, wy: 10, ww: 575, wh: 18, parent: v], Kill, s];>> <> <> < EXIT], "\n"]];>> <> <<};>> <<>> <> <<>> <> <<>> <> <> <> <> <<};>> <<>> <> <\ncat the file with optional kill"];>> <<>> <> <> <> <> <<};>> <> /* a.c - named pipe writer */ #include #include main() { int fd = open("/tmp/myfifo", O_WRONLY); char *output = "This is some output to a named pipe\n"; write(fd, output, strlen(output)); } /* b.c - named pipe reader */ /* advantage: can transmit EOF to the input of the invoked process, but how get back anything printed to stdout or stderr by the invoked process? also, the named pipes are not transient, and should be killed (unlink) after use */ #include #include #include #include main(ac, av) int ac; char **av; { char *name = ac == 2 ? av[1] : "/tmp/myfifo"; char buf[1024]; int fd; mkfifo(name, 0666); fd = open(name, O_RDONLY); (void) read(fd, buf, 1024); printf("result of read = %s\n", buf); } /* c.c */ #include char output[1024]; /* the advantage here is that the transmission medium is transient, also the Unix process stdout and stderr is fed back to the invoking XR environment. disadvantsges: no EOF can be sent to the input to the invoked process and no method for killing the invoked process */ /* see /pseudo/xrhome/INSTALLED/INCLUDE/xr/*.h */ int TestPPOpen(cmd) char *cmd; { XR_Fildes fd; int readAns; fd = XR_PPOpen(cmd); if( fd < 0 ) { XR_ConsoleMsg("PPOpen failed %d err %d\n", fd, XR_GetErrno() ); return (-1); } (void)bzero(output, 1024); readAns = XR_Read(fd, output, 1023); XR_Close(fd); return readAns; } /* pid.c */ #include #include main() { fprintf(stderr, "(pid %d)\n", getppid()); exit(0); } /* doit.c */ #include main(argc, argv) int argc; char **argv; { int childid; childid = fork(); if( childid == 0 ) /* this is child */ { sleep(5); printf("hello world %s\n", argv[1]); exit(0); } else /* parent */ { printf("%6d", childid); fflush(stdout); exit(0); } }