<<>> <> <> <> <> DIRECTORY BasicTime, Commander, Convert, IO, NetAddressing, Process, PupStream, PupStreamCellarWindow, RemoteViewersHostBackdoor, Rope, TypeScript, ViewerClasses, ViewerOps; RemoteViewersHostSpy: CEDAR PROGRAM IMPORTS BasicTime, Commander, Convert, IO, NetAddressing, Process, PupStream, PupStreamCellarWindow, RemoteViewersHostBackdoor, Rope, TypeScript, ViewerOps = BEGIN Viewer: TYPE ~ ViewerClasses.Viewer; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; Stats: TYPE ~ PupStreamCellarWindow.Stats; StatsLabels: TYPE ~ PupStreamCellarWindow.StatsLabels; StatsInfoList: TYPE = LIST OF StatsInfoObject; StatsInfoObject: TYPE = RECORD [inLabel, outLabel: ROPE, index: INT ¬ -1]; updateInterval: INT ¬ 15; -- seconds between updates script: Viewer ¬ NIL; statsInfoList: StatsInfoList ¬ LIST [ ["packetsPushed", "snd"], ["inOrderPackets", "rcv"], ["packetsRetransmitted", "rtr"], ["oldPackets", "dup"], ["resourceLimitsErrors", "res"] <<["funnyPacketType", "funnyPacketType"],>> <<["acksDefered", "acksDefered"],>> <<["oldAcks", "oldAcks"],>> <<["futurePackets", "futurePackets"],>> <<["clumpsRetransmitted", "clumpsRetransmitted"],>> ]; DoViewer: PROC [stream: STREAM, address: NetAddressing.Address] ~ { PutRope[IO.PutFR["Connection Established from: %g", [rope[NetAddressing.FormatAddress[address, FALSE]]]]]; IF address.kind # pup THEN { PutRope["Sorry, no Stats available for this transport.\n"]; RETURN; }; {ENABLE IO.Error, PupStream.StreamClosing => CONTINUE; prevStats: Stats ¬ PupStreamCellarWindow.GetStreamStats[stream]; DO Process.PauseMsec[updateInterval*1000]; PupStream.CheckConnection[stream]; {stats: Stats ¬ PupStreamCellarWindow.GetStreamStats[stream]; PutRope[CreateStatsRope[stream, stats, prevStats]]; prevStats ¬ stats; }ENDLOOP; }; PutRope["Connection Closed since last sampling.\n"]; RETURN}; CreateStatsRope: PROC [stream: STREAM, stats, prevStats: Stats] RETURNS [rope: ROPE] ~ { FOR info: StatsInfoList ¬ statsInfoList, info.rest WHILE info # NIL DO IF info.first.index < 0 THEN LOOP; rope ¬ Rope.Cat[rope, IO.PutFR["%g: %g/%g", [rope[info.first.outLabel]], [cardinal[stats[info.first.index]-prevStats[info.first.index]]], [cardinal[stats[info.first.index]]]]]; IF info.rest # NIL THEN rope ¬ Rope.Cat[rope, ", "]; ENDLOOP; RETURN}; PutRope: PROC [rope: ROPE, stampIt: BOOL ¬ TRUE] ~ { IF script = NIL OR script.destroyed THEN RETURN; IF stampIt THEN rope ¬ Rope.Cat[IO.PutFR["%g: ", [rope[Convert.RopeFromTime[from: BasicTime.Now[], start: hours, end: seconds, includeDayOfWeek: FALSE, useAMPM: TRUE, includeZone: FALSE]]]], rope]; script.name ¬ rope; TypeScript.PutRope[script, rope]; TypeScript.PutChar[script, '\n]; ViewerOps.PaintViewer[script, caption]; }; HostStatsProc: Commander.CommandProc = { GetViewer[]; RETURN}; GetViewer: PROC ~ { IF script # NIL AND script.destroyed THEN { script ¬ NIL; }; IF script = NIL THEN { s: Viewer ~ TypeScript.Create[info: [name: "Host Spy", label: "Host Spy", column: right, iconic: TRUE]]; ViewerOps.SetOpenHeight[s, -12]; ViewerOps.OpenIcon[icon: s, bottom: FALSE]; script ¬ s; }; }; SetUpStatsInfoList: PROC ~ { statsLabels: StatsLabels ¬ PupStreamCellarWindow.GetStreamStatsLabels[]; GetIndex: PROC [label: ROPE] RETURNS [index: INT ¬ -1] ~ { FOR i: CARDINAL IN [0..statsLabels.length) DO IF Rope.Equal[s1: label, s2: statsLabels[i], case: FALSE] THEN RETURN[i]; ENDLOOP; }; FOR info: StatsInfoList ¬ statsInfoList, info.rest WHILE info # NIL DO info.first.index ¬ GetIndex[info.first.inLabel]; ENDLOOP; }; Start: PROC ~ { SetUpStatsInfoList[]; RemoteViewersHostBackdoor.SetSpy[DoViewer]; Commander.Register[key: "HostSpy", proc: HostStatsProc, doc: "Create a viewer for spying on the host side of a RemoteTerminal session."]; RETURN}; Start[]; END.