DIRECTORY Commander, Convert, EditedStream, HostAndTerminalOps, IO, List, NetAddressing, NetworkName, NetworkStream, Process, RefTab, RefText, Rope, SimpleFeedback, TerminalMultiServing, ViewerClasses, ViewerIO, ViewerOps; RemoteSimpleTerminalByViewer: CEDAR MONITOR LOCKS rm USING rm: RefLock IMPORTS Commander, EditedStream, HostAndTerminalOps, IO, List, NetAddressing, NetworkName, NetworkStream, Process, RefTab, RefText, SimpleFeedback, TerminalMultiServing, ViewerIO, ViewerOps = BEGIN OPEN HAT:HostAndTerminalOps, NA:NetAddressing, NN:NetworkName, Nws:NetworkStream, TMS:TerminalMultiServing; LORA: TYPE ~ LIST OF REF ANY; ROPE: TYPE ~ Rope.ROPE; RefLock: TYPE ~ REF MONITORLOCK; arpaSimpleTerminalSocket: ROPE ¬ "58810"; debug: BOOL ¬ TRUE; Service: TYPE ~ REF ServicePrivate; ServicePrivate: TYPE ~ RECORD [ remote: TMS.Host, toNet, fromNet: IO.STREAM, stop: REF BOOL, toClient: PROCESS ¬ NIL, closing: BOOL ¬ FALSE]; serviceLock: RefLock ~ NEW [MONITORLOCK ¬ []]; services: LORA ¬ NIL; clients: RefTab.Ref ~ RefTab.Create[equal: TMS.EqualHostReferents, hash: TMS.HashHostReferent]; interest: TMS.Interest ~ NEW [TMS.InterestPrivate ¬ [NoteHost]]; arpaListener: Nws.Listener ¬ NIL; NoteHost: PROC [interest: TMS.Interest, addr: TMS.Host, isHost: BOOL] ~ { ra: REF TMS.Host ~ NEW [TMS.Host ¬ addr]; WithLock: ENTRY PROC [rm: RefLock] ~ {ENABLE UNWIND => NULL; IF isHost THEN {[] ¬ clients.Store[ra, ra]; RETURN}; IF NOT clients.Delete[ra] THEN RETURN; FOR ss: LORA ¬ services, ss.rest WHILE ss#NIL DO service: Service ~ NARROW[ss.first]; IF TMS.EqualHosts[addr, service.remote] THEN { service.closing ¬ TRUE; TRUSTED {Process.Detach[FORK Closem[service.fromNet, service.toNet]]}}; ENDLOOP; RETURN}; WithLock[serviceLock]; RETURN}; Closem: PROC [in, out: IO.STREAM] ~ { IO.Close[in !IO.Error => CONTINUE]; IO.Close[out !IO.Error => CONTINUE]; RETURN}; RestartArpaService: Commander.CommandProc ~ { [] ¬ StopArpaService[cmd]; IF arpaListener=NIL THEN arpaListener ¬ Nws.CreateListener[ protocolFamily: $ARPA, transportClass: $basicStream, local: NN.AddressFromName[$ARPA, NIL, arpaSimpleTerminalSocket, port].addr, listenerWorkerProc: NwsWorkForHost ! Nws.Error => { fmtd: ROPE ~ NA.FormatError[codes, msg]; SimpleFeedback.PutF[$RemoteSimpleTerminalByViewer, oneLiner, $Error, "Got error %g trying to create RemoteSimpleTerminalByViewer listener.", [rope[fmtd]] ]; CONTINUE}]; RETURN}; StopArpaService: Commander.CommandProc ~ { l: Nws.Listener ~ arpaListener; IF l#NIL THEN {arpaListener ¬ NIL; Nws.DestroyListener[l]}; RETURN}; Filter: ENTRY PROC [rm: RefLock, remote: TMS.Host] RETURNS [reject: ROPE] ~ { ENABLE UNWIND => NULL; reject ¬ IF NOT clients.Fetch[NEW [TMS.Host ¬ remote]].found THEN "I'm not serving you" ELSE NIL; RETURN}; NwsWorkForHost: PROC [listener: Nws.Listener, in, out: IO.STREAM] ~ { pf, tc: ATOM; remote, invertErr, reject, sessPort: ROPE ¬ NIL; rna: NA.Address ¬ NA.nullAddress; close: BOOL ¬ TRUE; [protocolFamily: pf, remote: remote, transportClass: tc] ¬ Nws.GetStreamInfo[out]; rna ¬ NA.FromNnAddress[remote, pf !NA.Error => {invertErr ¬ NA.FormatError[codes, msg]; CONTINUE}]; IF invertErr=NIL THEN { sessPort ¬ NA.ExtractSocket[rna]; rna ¬ NA.SetSocket[rna, "58812"]; reject ¬ Filter[serviceLock, rna]; IF reject=NIL THEN { IF debug THEN SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Debug, "Starting SimpleTerminal service for %g (session %g)", LIST[[rope[NA.FormatAddress[rna, TRUE]]], [rope[sessPort]]] ]; close ¬ Work[in, out, rna, sessPort]; IF debug THEN SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Debug, "Ending SimpleTerminal service for %g (session %g)", LIST[[rope[NA.FormatAddress[rna, TRUE]]], [rope[sessPort]]] ]; } ELSE SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Warning, "Refused SimpleTerminal service to %g (session %g) because %g", LIST[[rope[NA.FormatAddress[rna, TRUE]]], [rope[sessPort]], [rope[reject]]] ]; } ELSE SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Warning, "Refused SimpleTerminal service to %g %g because of error (%g) inverting that NetworkName.address", LIST[[atom[pf]], [rope[remote]], [rope[invertErr]]] ]; IF close THEN { in.Close[!IO.Error => CONTINUE]; out.Close[!IO.Error => CONTINUE]; }; RETURN}; Work: PROC [in, out: IO.STREAM, remote: TMS.Host, sessPort: ROPE] RETURNS [close: BOOL ¬ TRUE] ~ { service: Service ~ NEW [ServicePrivate ¬ [ remote: remote, toNet: out, fromNet: in, stop: NEW [BOOL ¬ FALSE] ]]; Addit: ENTRY PROC [rm: RefLock] RETURNS [BOOL] ~ { ENABLE UNWIND => NULL; IF NOT clients.Fetch[NEW [TMS.Host ¬ remote]].found THEN RETURN [FALSE]; services ¬ CONS[service, services]; RETURN [TRUE]}; Remit: ENTRY PROC [rm: RefLock] RETURNS [BOOL] ~ { ENABLE UNWIND => NULL; services ¬ List.Remove[service, services]; IF service.closing THEN RETURN [FALSE]; RETURN [service.closing ¬ TRUE]}; serverIn, serverOut: IO.STREAM; viewer: ViewerClasses.Viewer ¬ NIL; {ENABLE IO.EndOfStream, IO.Error => { close ¬ Remit[serviceLock]; serverOut.PutF["Aborted service for %g (session %g) because of IO.EndOfStream or IO.Error.\n", [rope[NA.FormatAddress[remote, TRUE]]], [rope[sessPort]] ]; CONTINUE}; bannerLeft: ROPE ~ in.GetLineRope[]; bannerRight: ROPE ~ in.GetLineRope[]; IF Addit[serviceLock] THEN { [serverIn, serverOut] ¬ ViewerIO.CreateViewerStreams[name: "Remote Simple Terminal", editedStream: FALSE]; EditedStream.SetEcho[serverIn, NIL]; viewer ¬ ViewerIO.GetViewerFromStream[serverOut]; serverOut.PutF["%g\n%g\n", [rope[bannerLeft]], [rope[bannerRight]] ]; TRUSTED {Process.Detach[service.toClient ¬ FORK Copy[from: serverIn, to: out, net: to, stop: service.stop]]}; Copy[from: in, to: serverOut, net: from, stop: service.stop]; close ¬ Remit[serviceLock]; viewer.name ¬ "closed Remote Simple Terminal"; ViewerOps.CloseViewer[viewer]; }; }; RETURN}; Copy: PROC [from, to: IO.STREAM, net: {from, to}, stop: REF BOOL] ~ { scratchLen: NAT ~ 128; buffer: REF TEXT ~ RefText.ObtainScratch[scratchLen]; ask, nBytes: NAT ¬ 0; DO ENABLE { IO.Error => IF (stream=from OR stream=to) AND (ec=StreamClosed OR ec=Failure) THEN EXIT; Nws.Error => EXIT; }; ask ¬ MIN[from.CharsAvail[TRUE], scratchLen]; IF stop­ THEN EXIT; IF from.EndOf[] THEN EXIT; IF ask = 0 THEN { IF net=to THEN [] ¬ Nws.GetTimeout[to] --touch it to see if closed by remote end--; Process.Pause[Process.SecondsToTicks[1]]; } ELSE { nBytes ¬ IO.GetBlock[from, buffer, 0, ask]; IO.PutBlock[to, buffer, 0, nBytes]; IF net=to THEN Nws.SendSoon[to, 0] ELSE IO.Flush[to]; }; ENDLOOP; stop­ ¬ TRUE; RefText.ReleaseScratch[buffer]; RETURN}; HAT.SetProtocolVersionRangeForSide[Terminal, "SimpleTerminal", [1, 1]]; [] ¬ RestartArpaService[NIL]; interest.AddMultiInterest[]; TMS.SetSimpleTerminalWorker[Work]; Commander.Register["RemoteSimpleTerminalByViewer.RestartArpaListener", RestartArpaService, "--- (re)start RemoteSimpleTerminalByViewer's ARPA listener"]; Commander.Register["RemoteSimpleTerminalByViewer.StopArpaListener", StopArpaService, "--- stop RemoteSimpleTerminalByViewer's ARPA listener"]; END. $ RemoteSimpleTerminalByViewer.mesa Copyright Σ 1990, 1992 by Xerox Corporation. All rights reserved. Last tweaked by Mike Spreitzer on February 6, 1992 5:45 pm PST Willie-Sue, January 21, 1988 10:39:27 am PST Willie-s, April 22, 1992 11:13 am PDT Stuff Simple Terminal Server Finally Κ±–(cedarcode) style•NewlineDelimiter ™codešœ!™!Kšœ Οeœ7™BKšœ>™>K™,K™%—K˜KšΟk œ7žœœ˜ήK˜šΟnœžœž˜+Kšžœžœ ˜Kšžœ.žœ‡˜ΎK˜—K˜KšžœžœžœžœžœŸœžœ˜qhead™Kš žœžœžœžœžœžœ˜Kšžœžœžœ˜K˜Kšœ žœžœž œ˜ K˜Kšœžœ ˜)Kšœžœžœ˜—™Kšœ žœžœ˜#šœžœžœ˜Kšœžœ˜Kšœžœžœ˜Kšœžœžœ˜Kšœ žœžœ˜Kšœ žœžœ˜—K˜Kšœžœž œ˜.Kšœ žœžœ˜Kšœ+žœžœ˜_K˜Kšœ žœ žœžœ˜@Kšœžœ˜!K˜š Ÿœžœ žœžœžœ˜IKš œžœžœžœžœ˜)š Ÿœžœžœžœžœžœ˜˜mKšœ=˜=K˜K˜.Kšœ˜K˜—Kšœ˜Kšžœ˜—K˜š Ÿœžœ žœžœžœžœ˜EKšœ žœ˜Kšœžœžœ%˜5Kšœ žœ˜šž˜šžœ˜Kšžœ žœžœ žœžœžœžœ˜YKšœ žœ˜K˜—Kšœžœžœ˜-Kšžœžœžœ˜Kšžœžœžœ˜šžœ žœ˜KšžœžœΟc+œ˜SK˜)K˜—šžœ˜Kšœ žœ ˜+Kšžœ!˜#Kšžœžœžœžœ ˜5K˜—Kšžœ˜—Kšœžœ˜ Kšœ˜Kšžœ˜——™KšžœD˜GKšœžœ˜Kšœ˜Kšžœ˜"Kšœ™˜™KšœŽ˜Ž—Lšžœ˜—…—€%U