DIRECTORY BasicTime USING [FromPupTime], GVBasics, Commander USING [CommandProc, Register], GVLocate USING [FindRegServer, FoundServerInfo], GVNames, GVProtocol, IO, Process USING [Detach], Pup USING [Address, nullSocket], PupName USING [NameLookup], Rope USING [Cat, Find, ROPE, Substr], ViewerIO USING [CreateViewerStreams]; DBCompare: MONITOR IMPORTS BasicTime, GVLocate, GVNames, GVProtocol, IO, Process, PupName, Rope, Commander, ViewerIO = BEGIN OPEN GVBasics, IO, GVNames, GVProtocol; targetRope: Rope.ROPE; -- server under investigation -- targetAddr: Pup.Address; -- target's address -- targetStr: Handle _ NIL; -- stream to target server -- serverAddr: Pup.Address; -- other server's address -- addrKnown: BOOLEAN _ FALSE; -- validity of serverAddr -- str: Handle _ NIL; -- stream to other server -- ReadElsewhere: SAFE PROC [out: IO.STREAM, name: GVBasics.RName] RETURNS [rc: GVProtocol.ReturnCode] = CHECKED BEGIN OPEN GVProtocol; TryUpdate: SAFE PROC[str: GVProtocol.Handle] = CHECKED BEGIN SendRSOperation[str, ReadEntry]; SendRName[str, name]; SendTimestamp[str, GVBasics.oldestTime]; SendNow[str]; rc _ ReceiveRC[str]; END; oldBad: BOOLEAN _ FALSE; Create: SAFE PROC = CHECKED BEGIN out.PutF["%b#%b#", [cardinal[serverAddr.net]], [cardinal[serverAddr.host]] ]; str _ GVProtocol.CreateStream[serverAddr, RSEnquiry]; END; Destroy: SAFE PROC = CHECKED BEGIN IF str # NIL THEN str.Close[]; str _ NIL; END; Accept: SAFE PROC [addr: Pup.Address] RETURNS [BOOL] = CHECKED BEGIN IF addr.net = targetAddr.net AND addr.host = targetAddr.host THEN RETURN[FALSE]; IF str # NIL AND serverAddr # addr THEN Destroy[]; IF str = NIL THEN BEGIN serverAddr _ addr; Create[ ! Failed => GOTO failed]; addrKnown _ TRUE; END; RETURN[TRUE]; EXITS failed => RETURN[FALSE] END; BEGIN IF str # NIL THEN BEGIN TryUpdate[ str ! Failed => GOTO streamGone ]; EXITS streamGone => Destroy[]; END; IF str = NIL THEN BEGIN IF addrKnown THEN Create[ ! Failed => GOTO notThere] ELSE BEGIN [] _ GVLocate.FindRegServer["x.GV", Accept]; IF str = NIL THEN GOTO notThere; END; TryUpdate[str ! Failed => GOTO notThere]; END; IF rc.code = WrongServer THEN oldBad _ TRUE ELSE oldBad _ FALSE; EXITS notThere => { Destroy[]; oldBad _ TRUE }; END; IF oldBad THEN BEGIN -- need to find the correct R-Server -- foundInfo: GVLocate.FoundServerInfo = GVLocate.FindRegServer[name, Accept]; WITH foundInfo SELECT FROM notFound => rc _ [BadRName, notFound]; allDown => rc _ [AllDown,notFound]; found => BEGIN TryUpdate[ str ! Failed => GOTO down ]; EXITS down => { Destroy[]; rc _ [AllDown,notFound] }; END; ENDCASE => ERROR; END; END; WriteStamp: PROC[out: IO.STREAM, stamp: Timestamp] = BEGIN out.PutF["[%b#%b,%t]", [cardinal[stamp.net]], [cardinal[stamp.host]], [time[BasicTime.FromPupTime[stamp.time]]] ]; END; WriteType: PROC[out: IO.STREAM, type: RNameType] = BEGIN out.PutRope[SELECT type FROM group => "group", individual => "individual", notFound => "not found", dead => "dead", ENDCASE => ERROR]; END; WriteRC: PROC[out: IO.STREAM, rc: ReturnCode] = BEGIN out.PutRope[SELECT rc.code FROM done => "ok", noChange => "no change", outOfDate => "out of date", NotAllowed => "not allowed", BadOperation => "bad operation", BadProtocol => "bad protocol", BadRName => "bad R-Name: ", BadPassword => "bad password", WrongServer => "wrong server", AllDown => "all suitable R-Servers down", ENDCASE => ERROR]; IF rc.code = BadRName THEN WriteType[out, rc.type]; END; WriteComponent: PROC[out: IO.STREAM, type: RNameType, i: CARDINAL] = BEGIN out.PutF["component=%g. ", [rope[ SELECT type FROM group => SELECT i FROM 0 => "prefix", 1 => "remark", 2 => "members", 3 => "memberStamps", 4 => "delMembers", 5 => "delMemberStamps", 6 => "owners", 7 => "ownerStamps", 8 => "delOwners", 9 => "delOwnerStamps", 10 => "friends", 11 => "friendStamps", 12 => "delFriends", 13 => "delFriendStamps", ENDCASE => ERROR, individual => SELECT i FROM 0 => "prefix", 1 => "password", 2 => "connect", 3 => "forward", 4 => "forwardStamps", 5 => "delForward", 6 => "delForwardStamps", 7 => "inboxes", 8 => "inboxStamps", 9 => "delInboxes", 10 => "delInboxStamps", ENDCASE => ERROR, dead => SELECT i FROM 0 => "prefix", ENDCASE => ERROR, ENDCASE => ERROR]] ]; END; ComponentType: TYPE = {rnames, stamps, other}; GetComponentType: PROC [type: RNameType, i: CARDINAL] RETURNS [ComponentType] = BEGIN SELECT type FROM group => SELECT i FROM 2, 4, 6, 8, 10, 12 => RETURN [rnames]; 3, 5, 7, 9, 11, 13 => RETURN [stamps]; ENDCASE; individual => SELECT i FROM 3, 5, 7, 9 => RETURN [rnames]; 4, 6, 8, 10 => RETURN [stamps]; ENDCASE; ENDCASE; RETURN[other]; END; Complaint: PROC[out: IO.STREAM, server: Rope.ROPE, name: RName, rc: ReturnCode] = BEGIN out.PutF["\n%g (%g) ", [rope[name]], [rope[server]] ]; WriteRC[out, rc]; END; Differ: PROC[out: IO.STREAM, name: RName, difference: Rope.ROPE] = BEGIN out.PutF["\n%g: %g different. ", [rope[name]], [rope[difference]] ]; END; ReceiveString: PROC [from: Handle, to: STRING] = BEGIN length: CARDINAL = ReceiveCount[from]; maxlength: CARDINAL = ReceiveCount[from]; ReceiveBytes[from, [base: LOOPHOLE[LONG[@to.text]], count: ((length+1)/2)*2]]; to.length _ length; END; CompareComponent: PROC[out: IO.STREAM, type: RNameType, count: CARDINAL, name: RName] = BEGIN differ: BOOLEAN _ FALSE; bLength: CARDINAL = 64; buffer1: PACKED ARRAY [0..bLength) OF CHARACTER; buffer2: PACKED ARRAY [0..bLength) OF CHARACTER; string1: STRING = [64]; string2: STRING = [64]; length1: CARDINAL _ ReceiveCount[targetStr]; length2: CARDINAL _ ReceiveCount[str]; stamp1: Timestamp; stamp2: Timestamp; IF length1 # length2 THEN BEGIN Differ[out, name, "component length"]; WriteComponent[out, type, count]; END; SELECT GetComponentType[type, count] FROM rnames => WHILE length1 > 0 OR length2 > 0 DO string1.length _ 0; string2.length _ 0; IF length1 > 0 THEN BEGIN ReceiveString[targetStr, string1]; length1 _ length1 - (string1.length + 1) / 2 - 2; END; IF length2 > 0 THEN BEGIN ReceiveString[str, string2]; length2 _ length2 - (string2.length + 1) / 2 - 2; END; IF string1.length # string2.length THEN differ _ TRUE ELSE FOR i: CARDINAL IN [0..string1.length) DO IF string1[i] # string2[i] THEN differ _ TRUE; ENDLOOP; ENDLOOP; stamps => WHILE length1 > 0 OR length2 > 0 DO IF length1 > 0 THEN BEGIN stamp1 _ ReceiveTimestamp[targetStr]; length1 _ length1 - SIZE[Timestamp]; END; IF length2 > 0 THEN BEGIN stamp2 _ ReceiveTimestamp[str]; length2 _ length2 - SIZE[Timestamp]; END; IF stamp1 # stamp2 THEN differ _ TRUE; ENDLOOP; ENDCASE => WHILE length1 > 0 OR length2 > 0 DO wanted1: CARDINAL = 2*MIN[bLength/2, length1] --bytes--; wanted2: CARDINAL = 2*MIN[bLength/2, length2]; IF wanted1 > 0 THEN ReceiveBytes[targetStr, [base: LOOPHOLE[LONG[@buffer1]], count: wanted1]]; IF wanted2 > 0 THEN ReceiveBytes[str, [base: LOOPHOLE[LONG[@buffer2]], count: wanted2]]; length1 _ length1 - wanted1/2; length2 _ length2 - wanted2/2; IF count # 0 THEN FOR i: CARDINAL IN [0..MIN[wanted1,wanted2]) DO IF buffer1[i] # buffer2[i] THEN differ _ TRUE; ENDLOOP; ENDLOOP; IF differ AND count # 0 THEN {Differ[out, name, "component contents"]; WriteComponent[out, type, count]}; END; SkipComponent: PROC[thisStr: Handle] = BEGIN bLength: CARDINAL = 64; buffer1: PACKED ARRAY [0..bLength) OF CHARACTER; length1: CARDINAL _ ReceiveCount[targetStr]; WHILE length1 > 0 DO wanted1: CARDINAL = 2*MIN[bLength/2, length1] --bytes--; ReceiveBytes[thisStr, [base: LOOPHOLE[LONG[@buffer1]], count: wanted1]]; length1 _ length1 - wanted1/2; ENDLOOP; END; Look: PROC[out: IO.STREAM, name: GVBasics.RName]RETURNS[done: BOOLEAN] = BEGIN ok: BOOLEAN _ TRUE; stamp1, stamp2: Timestamp; rc1, rc2: ReturnCode; [rc1,stamp1] _ Enquire[targetStr, ReadEntry, name, oldestTime]; rc2 _ ReadElsewhere[out: out, name: name]; IF rc2.code = done THEN stamp2 _ ReceiveTimestamp[str]; out.PutChar['!]; IF rc1.code # done THEN { Complaint[out, "target server", name, rc1]; ok_FALSE }; IF rc2.code # done THEN { Complaint[out, "other server", name, rc2]; ok_FALSE }; IF rc1.type # rc2.type THEN { Differ[out, name, "types"]; ok_FALSE }; BEGIN count1: CARDINAL _ IF rc1.code # done THEN 0 ELSE ReceiveCount[targetStr]; count2: CARDINAL _ IF rc2.code # done THEN 0 ELSE ReceiveCount[str]; IF ok AND count1 # count2 THEN ERROR; IF ok AND stamp1 # stamp2 THEN Differ[out, name, "global stamps"]; IF ok THEN FOR c: CARDINAL IN [0..MIN[count1,count2]) DO count1 _ count1-1; count2 _ count2-1; CompareComponent[out, rc1.type, c, name]; ENDLOOP; THROUGH [1..count1] DO SkipComponent[targetStr] ENDLOOP; THROUGH [1..count2] DO SkipComponent[str] ENDLOOP; END; done _ (rc1.code=WrongServer OR rc2.code=WrongServer) OR (rc1.code=AllDown OR rc2.code=AllDown); END; LookEnum: PROC[out: IO.STREAM, enumName: GVBasics.RName, work: PROC[IO.STREAM, GVBasics.RName]RETURNS[done: BOOLEAN] ] = BEGIN out.PutChar['\n]; out.Put[[rope[enumName]]]; BEGIN memberInfo: GVNames.MemberInfo = GVNames.GetMembers[enumName]; IF targetStr = NIL THEN targetStr _ CreateStream[targetAddr, RSEnquiry]; WITH memberInfo SELECT FROM group => BEGIN out.PutChar['\n]; FOR m: LIST OF GVBasics.RName _ members, m.rest UNTIL m = NIL DO IF work[out, m.first] THEN EXIT ENDLOOP; END; allDown => out.PutRope[": all R-Servers down!"]; notFound => out.PutRope[": not found!"]; ENDCASE => ERROR; IF targetStr # NIL THEN { targetStr.Close[]; targetStr_NIL }; END; END; LookAtRegistry: PROC[out: IO.STREAM, regGroup: GVBasics.RName]RETURNS[done: BOOLEAN] = BEGIN dotPos: INT = regGroup.Find["."]; reg: Rope.ROPE = regGroup.Substr[0,dotPos]; IF targetStr # NIL THEN { targetStr.Close[]; targetStr_NIL }; SELECT GVNames.IsMemberDirect[regGroup, targetRope] FROM yes => NULL; no => RETURN[FALSE]; -- target isn't in this registry -- notGroup => ERROR; allDown => NULL; ENDCASE => ERROR; out.PutF["\n\n%t Registry %g\n", IO.time[], [rope[reg]]]; LookEnum[out, Rope.Cat["Groups.", reg], Look]; LookEnum[out, Rope.Cat["Individuals.", reg], Look]; LookEnum[out, Rope.Cat["Dead.", reg], Look]; done _ FALSE; END; LookAtAll: ENTRY PROC = BEGIN out: IO.STREAM = ViewerIO.CreateViewerStreams["GV database comparison"].out; out.PutF["%t Comparing database on %g with rest of world.\n", IO.time[], [rope[targetRope]]]; LookEnum[out, "Groups.gv", LookAtRegistry ! GVProtocol.Failed => CHECKED{ out.PutRope["GVProtocol.Failed"]; CONTINUE }]; out.PutF["\n\n%t Finished!\n", IO.time[]]; END; GetStream: PROC[out, in: IO.STREAM] RETURNS[ok: BOOLEAN] = BEGIN info: GVNames.ConnectInfo; targetConnect: GVBasics.Connect; targetRope _ in.GetTokenRope[].token; IF targetRope.Find["."] >= 0 THEN [info, targetConnect] _ GVNames.GetConnect[targetRope] ELSE { info _ individual; targetConnect _ targetRope }; SELECT info FROM individual => NULL; ENDCASE => { out.PutRope[" bad name"]; RETURN[FALSE] }; targetAddr _ PupName.NameLookup[targetConnect, Pup.nullSocket]; RETURN[TRUE]; END; DoIt: ENTRY Commander.CommandProc = TRUSTED BEGIN ENABLE UNWIND => NULL; IF GetStream[cmd.out, IO.RIS[cmd.commandLine]] THEN Process.Detach[FORK LookAtAll[]]; IF targetStr # NIL THEN targetStr.Close[]; IF str # NIL THEN str.Close[]; END; Commander.Register["DBCompare", DoIt, "Compare GV database on given server"]; END. :DBCompare.mesa, Grapevine: database comparison tool Hal Murray May 1, 1985 8:05:57 pm PDT Hal Murray, March 13, 1986 5:32:38 pm PST HGM May 31, 1984 3:19:54 am PDT Andrew Birrell February 8, 1984 4:42 pm Mike Schroeder March 19, 1983 2:12 pm argument is "groups.reg", "individuals.reg", or "dead.reg" -- ΚO˜šœ3™3Icode™%K™)—˜Jšœ!™!Jšœ(™(Jšœ&™&—J˜šΟk ˜ Jšœ œ˜J˜ Jšœ œ˜(Jšœ œ!˜0J˜J˜ Jšœ˜Jšœœ ˜Jšœœ˜ Jšœœ˜Jšœœ œ ˜%Jšœ œ˜%—J˜šœ ˜Jšœ+œ/˜c—J˜Jš˜J˜Jšœ œ˜'J˜JšœœΟc ˜7Jšœž˜/Jšœœž˜=Jšœž˜5Jšœ œœž˜Jš˜Jš œœœœœ˜PJšœœœœ ˜2Jšœ˜ šœ˜ J˜Jšœœ ˜!Jšœ œ˜Jšœ˜—Jšœœ˜ Jšœ œœ˜Jšœ˜—š˜Jšœ˜ šœ˜ Jšœœ˜-Jšœ˜Jšœ˜—Jšœ˜ šœ˜ Jšœ ˜ Jšœœ ˜'šœ˜ J˜,Jšœœœœ ˜ Jšœ˜—Jšœœ ˜)Jšœ˜—Jšœ˜Jšœ ˜Jšœ œ˜—Jšœ#œ˜/Jšœ˜Jšœ˜ šœœž'˜2J˜Kšœ œ˜J˜&J˜#˜Jš˜Jšœœ˜'šœ˜ J˜'—Jšœ˜——Jšœœ˜Jšœ˜—Jšœ˜J˜J˜J˜—šŸ œœœœ˜4Jš˜˜J˜[—Jšœ˜J˜—šŸ œœœœ˜2Jš˜šœ œ˜J˜J˜J˜J˜Jšœœ˜—Jšœ˜J˜—šŸœœœœ˜/Jš˜šœ œ ˜J˜ J˜J˜J˜J˜ J˜J˜J˜J˜J˜)Jšœœ˜—Jšœœ˜3Jšœ˜J˜—š Ÿœœœœœ˜DJš˜˜!šœ˜šœ œ˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœœ˜—šœœ˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœœ˜—šœœ˜J˜Jšœœ˜——Jšœœ˜—Jšœ˜J˜—Jšœœ˜.J˜šŸœœœœ˜OJš˜šœ˜šœ œ˜Jšœœ ˜&Jšœœ ˜'Jšœ˜—šœœ˜Jšœœ ˜Jšœœ ˜ Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜J˜—š Ÿ œœœœœ ˜QJš˜J˜6J˜Jšœ˜J˜—š Ÿœœœœ œ˜BJš˜J˜EJšœ˜J˜—šŸ œœœ˜0Jš˜Jšœœ˜&Jšœ œ˜)J˜NJ˜Jšœ˜J˜—š Ÿœœœœœ˜WJš˜Jšœœœ˜Jšœ œ˜Jš œ œœœ œ˜0Jš œ œœœ œ˜0Jšœ œ˜Jšœ œ˜Jšœ œ˜,Jšœ œ˜&J˜J˜Jšœ˜šœ˜ J˜&J˜!Jšœ˜—šœ˜)šœ œ œ ˜-J˜J˜šœ œ˜J˜"J˜1Jšœ˜—šœ œ˜J˜J˜1Jšœ˜—Jšœ ˜"Jšœ ˜šœœœœ˜+Jš œœœ œœ˜:—Jšœ˜—šœ œ œ ˜-šœ œ˜J˜%Jšœœ ˜$Jšœ˜—šœ œ˜J˜Jšœœ ˜$Jšœ˜—Jšœœ œ˜'Jšœ˜—šœœ œ ˜.Jšœ œœž œ˜8Jšœ œœ˜.Jšœ œ œœ˜^Jšœ œœœ˜XJ˜J˜š œ œœœœœ˜AJšœœ œ˜.Jšœ˜—Jšœ˜——Jšœœ ˜šœ*˜.J˜"—Jšœ˜J˜—šŸ œœ˜&Jš˜Jšœ œ˜Jš œ œœœ œ˜0Jšœ œ˜,Jšœ ˜šœ œœž œ˜;Jšœœœ˜HJ˜—Jšœ˜Jšœ˜J˜—š Ÿœœœœœœ˜HJš˜Jšœœœ˜J˜J˜J˜?J˜*Jšœœ ˜7J˜Jšœ˜Jšœ2œ˜>Jšœ˜Jšœ1œ˜=Jšœ˜Jšœ"œ˜.š˜šœœœœ˜,Jšœ˜—šœœœœ˜,Jšœ˜—Jšœœœœ˜%Jšœœœ$˜Bš œœœœœœ˜5šœ˜J˜J˜)—Jšœ˜—Jšœ œœ˜8Jšœ œœ˜2—Jšœ˜šœœ˜5Jšœœ˜*—Jšœ˜J˜—šŸœœœœ˜8Jš œœœœœœ˜?Jšœ=™=Jš˜J˜J˜š˜J˜>Jšœ œœ1˜Hšœ œ˜šœ ˜J˜Jš œœœ"œ˜=Jš œœœœœ˜+Jšœ˜—J˜0J˜(—Jšœœ˜Jšœ œœ œ˜=—Jšœ˜Jšœ˜J˜—š Ÿœœœœœœ˜VJš˜Jšœœ˜!Jšœ œ˜+Jšœ œœ œ˜=šœ.˜8Jšœœ˜ Jšœœœž#˜8Jšœ œ˜Jšœ œ˜—Jšœœ˜Jšœ!œ˜9J˜.J˜3J˜,Jšœœ˜ Jšœ˜J˜—šŸ œœœ˜Jš˜Jšœœœ>˜LJšœ>œ˜]˜@Jšœ$œ˜7—Jšœœ ˜*Jšœ˜J˜—š Ÿ œœ œœœœ˜:Jš˜J˜J˜ J˜%Jšœœ7˜XJšœ3˜7šœ˜Jšœœ˜Jšœ œœ˜7—J˜?Jšœœ˜ Jšœ˜J˜J˜—šœœ˜+Jšœœœœ˜Jšœœœ˜.Jšœœ˜&Jšœ œœ˜*Jšœœœ ˜Jšœ˜J˜—J˜MJ˜J˜Jšœ˜J˜J˜—…—+Ό