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. .. /* 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); } } Ί UnixSpawnTCPImpl.mesa Copyright Σ 1991 by Xerox Corporation. All rights reserved. Jules Bloomenthal November 28, 1992 9:09 am PST Thanks to Alan Demers, Jim Foote, and Michael Plass Chauser, September 22, 1992 3:29 pm PDT Michael Plass, October 15, 1992 11:01 am PDT Unix Spawn Procedures using TCP Fork Unix process "cmd" and connect fd to stdin/stdout and fdErr to stderr. XR_PPPOpen found in URExec.o; see xr/UIO.h. PPPOpen runs a shell that substitutes its process id for $$ and runs echo in a subprocess; then the exec runs command using the same process as the shell's. SIGKILL more reliable but less polite than SIGTERM Test Application Cat: Commander.CommandProc ~ { in: IO.STREAM; v: ViewerClasses.Viewer; s: REF UnixSpawnTCP.SpawnData; args: CommanderOps.ArgumentVector _ CommanderOps.Parse[cmd]; IF args.argc # 2 THEN RETURN[$Failure, "need filename"]; in _ FS.StreamOpen[args[1] ! FS.Error => msg _ IO.PutFR1["no %g\n", IO.rope[args[1]]]]; IF msg # NIL THEN RETURN[$Failure, msg]; IF (s _ UnixSpawnTCP.Spawn["cat", cmd.out,, Fin]) = NIL THEN RETURN[$Failure, "failed"]; v _ ViewerOps.CreateViewer[$Container, [name: "Test", openHeight: 52, scrollable: FALSE]]; [] _ Buttons.Create[[name: IO.PutFR1["STOP (pid=%g)", IO.int[s.pid]], wx: 10, wy: 10, ww: 575, wh: 18, parent: v], Kill, s]; ViewerOps.OpenIcon[v]; DO UnixSpawnTCP.Write[s, Rope.Concat[IO.GetLineRope[in ! IO.EndOfStream => EXIT], "\n"]]; ENDLOOP; }; Fin: UnixSpawnTCP.FinishProc ~ {MessageWindow.Append["DONE!", TRUE]}; Kill: ViewerClasses.ClickProc ~ {UnixSpawnTCP.Kill[NARROW[clientData]]}; PID: Commander.CommandProc ~ { s: REF UnixSpawnTCP.SpawnData _ UnixSpawnTCP.Spawn["ps -x", cmd.out]; IF s = NIL THEN RETURN[$Failure, "can't spawn"]; IO.PutF1[cmd.out, "s.pid = %g\n", IO.int[s.pid]]; }; Commander.Register["SpawnPID", PID, "print pid and spawn ps"]; Commander.Register["SpawnCat", Cat, "SpawnCat \ncat the file with optional kill"]; POpen: PROC [cmd: ROPE] RETURNS [FD] ~ { Fork a Unix process running cmd, connecting fd to stdin/(stdout, stderr); see xr/UIO.h. popen: PROC [cmd: CHARPtr] RETURNS [FD] ~ TRUSTED MACHINE CODE {"XR_PPOpen"}; RETURN[popen[UXStrings.Create[cmd]]]; }; Miscellaneous C Fragments (for acquiring pid) Κ–(cedarcode) style•NewlineDelimiter ™šœ™Jšœ Οeœ1™Jšžœ žœ ˜J˜ Jš œ£œ£œ£œ£œžœ˜?J˜ Jšžœ˜J˜J˜—Jšœž œ˜J˜š‘œžœ˜/Jšœžœ žœ˜!J˜%Jšœžœ˜ J˜J˜7š‘œžœžœžœ˜,Kš žœ žœžœžœžœ˜2K˜—šžœ1ž˜8Jšœžœ!˜+Jšžœžœžœ ˜=Jšžœ žœžœžœ˜,Jšžœ$žœžœ˜0Jšžœ˜—J˜šžœ žœ˜J˜Jš œ žœ£œ£œ£žœ žœ˜EJ˜—J˜J˜—š‘œžœžœžœ˜(J˜ š žœžœžœ žœžœ˜FJ™2—J˜J˜—š ‘œžœžœžœžœ˜5Jšœ:˜:J˜J™]J™š ‘œžœžœžœžœ™(JšœW™WJš’œ£ž£œ£œ£ž£œžœ£œ£žœžœžœ™MJšžœ™%Jšœ™——š -™-J˜ΨJ˜ΎJ˜ΐJ˜sJ˜J˜γJ˜——…—r(@