-- Copyright (C) 1982, 1983, 1984, 1985 by Xerox Corporation. All rights reserved. -- Enquiry.mesa, Transport mechanism: Viticulturists' interface -- HGM, 15-Sep-85 8:20:24 -- Andrew Birrell 27-Oct-82 16:07:51 -- Michael Schroeder, 25-Jan-83 13:03:03 -- Hankins: 23-Aug-84 10:30:40 DIRECTORY Ascii USING [CR, DEL, SP], BodyDefs USING [maxRNameLength, RName], EnquiryDefs USING [ Archive, AddRegistry, AddSelfToRegistry, DisplayStats, DriverStats, Histograms, ImmediatePurge, InaccessibleArchive, LoginMSMail, LoginRSMail, MailboxCount, PolicyControls, Poll, PupEcho, PupRoutingTable, RemoteServers, SetArchiveDays, SLQueueCount], GlassDefs USING [Handle, Listen, TimeOut], Heap USING [systemZone], LogDefs USING [WriteLogEntry], LogPrivateDefs USING [AppendElapsedTime, startUpTime, uptimeHouse], LogWatchDefs USING [GetLogPosition, LogPosition, PutLogChars], NameInfoDefs USING [Authenticate, IsMemberClosure, Membership], PolicyDefs USING [ Activate, Operation, PeriodicProcess, ReadOperationControl, ReadOperationCurrent, SetOperationAllowed, SetTelnetAllowed, Wait], Process USING [Detach, SecondsToTicks], ProtocolDefs USING [AppendTimestamp], RestartDefs USING [], String USING [ AppendChar, AppendString, EquivalentString, InvalidNumber, StringToDecimal], TemporaryBooting USING [BootFromVolume], Time USING [Append, Current, Packed, Unpack], Volume USING [systemID]; Enquiry: MONITOR IMPORTS EnquiryDefs, GlassDefs, Heap, LogDefs, LogPrivateDefs, LogWatchDefs, NameInfoDefs, PolicyDefs, Process, ProtocolDefs, String, TemporaryBooting, Time, Volume EXPORTS RestartDefs --PROGRAM-- = BEGIN Command: TYPE = { addRegistry, histograms, inbox, servers, displayPolicy, queues, statistics, time, enable, archive, background, msMail, purge, rsMail, login, observeLog, pupEcho, pupRoute, pupStats, quit, restart, archiveDays, setPolicy, wait}; Del: ERROR = CODE; LoginState: TYPE = {none, ok, enabled}; Login: PROC [str: GlassDefs.Handle, user: BodyDefs.RName, pwd: LONG STRING] RETURNS [ok: BOOLEAN] = BEGIN OPEN str; default: STRING = ".pa"L; WriteChar[Ascii.CR]; IF ReadString["Your name please: "L, user, word] = Ascii.DEL THEN ERROR Del[]; IF String.EquivalentString[user, "ogin"L] -- ugh! -- THEN BEGIN WriteString["← "L]; user.length ← 0; IF ReadString[""L, user, word] = Ascii.DEL THEN ERROR Del[]; END; FOR i: CARDINAL DECREASING IN [0..user.length) DO IF user[i] = '. THEN EXIT; REPEAT FINISHED => IF user.length + default.length <= user.maxlength THEN { String.AppendString[user, default]; WriteString[default]; }; ENDLOOP; WriteChar[Ascii.CR]; SELECT ReadString["Your password: "L, pwd, pwd] FROM Ascii.DEL => ERROR Del[]; ENDCASE => NULL; WriteString[" ... "L]; SendNow[]; ok ← FALSE; SELECT NameInfoDefs.Authenticate[user, pwd] FROM individual => {WriteString["ok"L]; ok ← TRUE}; group => WriteString["Can't login as a group"L]; notFound => WriteString["Not a valid user name"L]; badPwd => WriteString["Incorrect password"L]; allDown => WriteString["Can't contact authentication server"L]; ENDCASE => ERROR; IF ok THEN LogAction[user, "Vc login"L]; END; Enable: PROC [str: GlassDefs.Handle, user: BodyDefs.RName] RETURNS [privileged: BOOLEAN] = BEGIN OPEN str; privileged ← FALSE; SELECT (IF String.EquivalentString[user, "Wizard.GV"L --side door!-- ] THEN NameInfoDefs.Membership[yes] ELSE NameInfoDefs.IsMemberClosure["Transport↑.ms"L, user]) FROM yes => {WriteString["ok"L]; privileged ← TRUE}; allDown => WriteString["can't contact access control server"L]; no, notGroup => WriteString["not privileged"L]; ENDCASE; IF privileged THEN LogAction[user, "Vc enabled"L]; END; Confirm: PROC [str: GlassDefs.Handle] RETURNS [ok: BOOLEAN] = BEGIN OPEN str; WriteString[" [Confirm] "L]; DO SELECT ReadChar[] FROM 'y, 'Y, Ascii.CR => {ok ← TRUE; EXIT}; 'n, 'N => {ok ← FALSE; EXIT}; Ascii.DEL => ERROR Del[]; ENDCASE => WriteChar['?]; ENDLOOP; WriteString[IF ok THEN "yes ... "L ELSE "no"L]; SendNow[]; END; DoAddRegistry: PROC [str: GlassDefs.Handle] = BEGIN OPEN str; name: BodyDefs.RName = [BodyDefs.maxRNameLength]; ok: BOOLEAN ← TRUE; c, m, b: BOOLEAN; IF ReadString[" registry R-Name: "L, name, word] = Ascii.DEL THEN ERROR Del[]; IF NOT Confirm[str] THEN RETURN; WriteString["turning off policy controls ... "L]; c ← PolicyDefs.ReadOperationControl[connection].allowed; m ← PolicyDefs.ReadOperationControl[mainLine].allowed; b ← PolicyDefs.ReadOperationControl[background].allowed; PolicyDefs.SetOperationAllowed[connection, FALSE]; PolicyDefs.SetOperationAllowed[mainLine, FALSE]; PolicyDefs.SetOperationAllowed[background, FALSE]; WriteString["wait until idle ... "L]; WaitUntilIdle[str ! Del => {WriteString["(not idle) "L]; CONTINUE}]; WriteString["adding entry ... "L]; ok ← EnquiryDefs.AddSelfToRegistry[name]; IF ok THEN BEGIN WriteString["now use Maintain (not on this machine) to add to registry."L]; DO WriteString["Confirm when update has propagated "L]; IF Confirm[str] THEN EXIT; ENDLOOP; WriteString["fetching registry ... "L]; ok ← EnquiryDefs.AddRegistry[name]; END; IF ok THEN BEGIN WriteString["ok, restoring policy controls ... "L]; PolicyDefs.SetOperationAllowed[connection, c]; PolicyDefs.SetOperationAllowed[mainLine, m]; PolicyDefs.SetOperationAllowed[background, b]; END ELSE WriteString["failed (controls still off)"L]; END; ControlOperation: PROC [str: GlassDefs.Handle] = BEGIN OPEN str; opNames: ARRAY PolicyDefs.Operation OF STRING = [ work: "work"L, connection: "connection"L, clientInput: "clientInput"L, serverInput: "serverInput"L, readMail: "readMail"L, regExpand: "regExpand"L, FTP: "FTP"L, telnet: "telnet"L, mainLine: "mainLine"L, readExpress: "readExpress", readInput: "readInput"L, readPending: "readPending"L, readForward: "readForward"L, readMailbox: "readMailbox"L, remailing: "remailing"L, background: "background"L, RSReadMail: "RSReadMail"L, MSReadMail: "MSReadMail"L, archiver: "archiver"L, regPurger: "regPurger"L]; opName: STRING = [20]; IF ReadString[" operation: "L, opName, word] = Ascii.DEL THEN ERROR Del[]; IF String.EquivalentString[opName, "?"L] THEN BEGIN WriteString[" one of:"L]; FOR op: PolicyDefs.Operation IN PolicyDefs.Operation DO WriteString[" "L]; WriteString[opNames[op]]; ENDLOOP; END ELSE FOR op: PolicyDefs.Operation IN PolicyDefs.Operation DO IF String.EquivalentString[opName, opNames[op]] THEN BEGIN allowed: BOOLEAN = NOT PolicyDefs.ReadOperationControl[op].allowed; WriteString[IF allowed THEN " ← allowed"L ELSE " ← not allowed"L]; IF Confirm[str] THEN BEGIN PolicyDefs.SetOperationAllowed[op, allowed]; WriteString["ok"L]; END; EXIT END; REPEAT FINISHED => WriteString[" ... unknown operation (type ? for help)"L]; ENDLOOP; END; ForceActivation: PROC [str: GlassDefs.Handle] = BEGIN OPEN str; processes: ARRAY PolicyDefs.PeriodicProcess OF STRING = [ "ReadPending"L, "ProdServers"L, "Archiver"L, "RegPurger"L]; name: STRING = [20]; IF ReadString[" process: "L, name, word] = Ascii.DEL THEN ERROR Del[]; IF String.EquivalentString[name, "?"L] THEN BEGIN WriteString[" one of:"L]; FOR pr: PolicyDefs.PeriodicProcess IN PolicyDefs.PeriodicProcess DO WriteString[" "L]; WriteString[processes[pr]]; ENDLOOP; END ELSE FOR pr: PolicyDefs.PeriodicProcess IN PolicyDefs.PeriodicProcess DO IF String.EquivalentString[name, processes[pr]] THEN BEGIN IF Confirm[str] THEN {PolicyDefs.Activate[pr]; WriteString["ok"L]; }; EXIT END; REPEAT FINISHED => WriteString[" ... unknown process (type ? for help)"L]; ENDLOOP; END; ImmediatePurge: PROC [str: GlassDefs.Handle] = BEGIN OPEN str; name: BodyDefs.RName = [BodyDefs.maxRNameLength]; IF ReadString[" of R-Name: "L, name, word] = Ascii.DEL THEN ERROR Del[]; IF Confirm[str] THEN BEGIN done: BOOLEAN = EnquiryDefs.ImmediatePurge[name]; WriteString[IF done THEN "ok"L ELSE "no change"]; END; END; Stop: ENTRY PROC [str: GlassDefs.Handle, user: LONG STRING] RETURNS [stopping: BOOLEAN] = BEGIN OPEN str; ENABLE UNWIND => NULL; reason: STRING = [64]; stopping ← FALSE; WriteChar[Ascii.CR]; IF ReadString["Restart reason: "L, reason, line] = Ascii.DEL THEN ERROR Del[]; WriteChar[Ascii.CR]; WriteString["Do you really want me to stop the server?"L]; IF Confirm[str] THEN BEGIN p: PROCESS; LogAction[user, "Restart"L, reason]; p ← FORK ReallyStop[]; stopping ← TRUE; END; END; ReallyStop: PROC = BEGIN PolicyDefs.SetOperationAllowed[work, FALSE]; PolicyDefs.Wait[secs: 5]; TemporaryBooting.BootFromVolume[Volume.systemID, ]; END; Archive: PROC [str: GlassDefs.Handle, user: LONG STRING] = BEGIN OPEN str; target: BodyDefs.RName = [BodyDefs.maxRNameLength]; IF ReadString["mailbox: "L, target, word] = Ascii.DEL THEN ERROR Del[]; IF Confirm[str] THEN BEGIN IF NOT EnquiryDefs.Poll[target] THEN GOTO none; EnquiryDefs.Archive[ target, 0 ! EnquiryDefs.InaccessibleArchive => GOTO failed]; LogAction[user, "Mailbox archived"L]; WriteString["ok"L]; EXITS failed => WriteString["failed"L]; none => WriteString["no mail"L]; END; END; ArchiveDays: PROC [str: GlassDefs.Handle] = BEGIN OPEN str; days: STRING = [2]; count: INTEGER; IF ReadString["to be: "L, days, word] = Ascii.DEL THEN ERROR Del[]; count ← String.StringToDecimal[days ! String.InvalidNumber => GOTO bad]; IF count < 0 THEN WriteString[" Silly!"L] ELSE EnquiryDefs.SetArchiveDays[count]; EXITS bad => WriteString[" invalid number"L]; END; DisplayTime: PROC [str: GlassDefs.Handle] = BEGIN OPEN str; now: Time.Packed = Time.Current[]; nowText: STRING = [22]; -- 23-Jun-63 12:12:12 PDT or 0#0@1234512345 -- Time.Append[nowText, Time.Unpack[now], TRUE]; WriteString[nowText]; WriteString[" = "]; nowText.length ← 0; ProtocolDefs.AppendTimestamp[nowText, [0, 0, now]]; WriteString[nowText]; END; WaitUntilIdle: ENTRY PROC [str: GlassDefs.Handle] = BEGIN OPEN str; ENABLE UNWIND => NULL; cond: CONDITION ← [timeout: Process.SecondsToTicks[1]]; DO IF DelTyped[] THEN ERROR Del[]; IF PolicyDefs.ReadOperationCurrent[work] = PolicyDefs.ReadOperationCurrent[ telnet] THEN EXIT; WAIT cond; WriteChar['!]; SendNow[]; ENDLOOP; END; ObserveLog: PROCEDURE [str: GlassDefs.Handle, user: LONG STRING] = BEGIN OPEN str; p: LogWatchDefs.LogPosition; WriteChar[Ascii.CR]; WriteChar[Ascii.CR]; WriteString["*** Type DEL to stop log observation. ***"L]; WriteChar[Ascii.CR]; WriteChar[Ascii.CR]; SendNow[]; p ← LogWatchDefs.GetLogPosition[]; LogAction[user, "Obs. started"L]; [] ← LogWatchDefs.PutLogChars[ p, WriteChar, SendNow, DelTyped ! UNWIND => LogAction[user, "Obs. abandonded"L]]; LogAction[user, "Obs. stopped"L]; WriteChar[Ascii.CR]; ERROR Del[]; END; -- ObserveLog -- LogAction: PROC [user, action: LONG STRING, content: LONG STRING ← NIL] = BEGIN log: LONG STRING ← Heap.systemZone.NEW[StringBody[200]]; String.AppendString[log, action]; String.AppendString[log, " by "L]; String.AppendString[log, user]; IF content # NIL THEN BEGIN String.AppendString[log, ": "L]; String.AppendString[log, content]; END; LogDefs.WriteLogEntry[log]; Heap.systemZone.FREE[@log]; END; LowerCase: PROC [c: CHARACTER] RETURNS [CHARACTER] = INLINE { RETURN[IF c IN ['A..'Z] THEN 'a + (c - 'A) ELSE c]; }; BeginsWith: PROC [a, b: STRING] RETURNS [BOOLEAN] = BEGIN FOR i: CARDINAL IN [0..b.length) DO IF i >= a.length OR LowerCase[a[i]] # LowerCase[b[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE] END; ChooseCommand: PROC [str: GlassDefs.Handle, state: LoginState] RETURNS [Command] = BEGIN OPEN str; names: ARRAY Command OF STRING = [ addRegistry: "Add-registry"L, histograms: "Display Histograms"L, inbox: "Display Inbox"L, servers: "Display Other-servers"L, displayPolicy: "Display Policy-controls"L, queues: "Display Queues"L, statistics: "Display Statistics"L, time: "Display Time"L, enable: "Enable"L, archive: "Force Archive"L, background: "Force Background-process"L, msMail: "Force MSMail-login"L, purge: "Force Purge"L, rsMail: "Force RSMail-login"L, login: "Login"L, observeLog: "Observe-log"L, pupEcho: "Pup Echo"L, pupRoute: "Pup Routing-Table"L, pupStats: "Pup Statistics"L, quit: "Quit"L, restart: "Restart"L, archiveDays: "Set Archive-days"L, setPolicy: "Set Policy-control"L, wait: "Wait-until-idle"L]; allowed: PACKED ARRAY Command OF {yes, no} = SELECT state FROM none => [ addRegistry: no, histograms: yes, inbox: no, servers: yes, displayPolicy: yes, queues: no, statistics: yes, time: yes, enable: no, archive: no, background: no, msMail: no, purge: no, rsMail: no, login: yes, observeLog: no, pupEcho: no, pupRoute: no, pupStats: no, quit: yes, restart: no, archiveDays: no, setPolicy: no, wait: no], ok => [ addRegistry: no, histograms: yes, inbox: yes, servers: yes, displayPolicy: yes, queues: yes, statistics: yes, time: yes, enable: yes, archive: no, background: no, msMail: no, purge: no, rsMail: no, login: yes, observeLog: no, pupEcho: yes, pupRoute: yes, pupStats: yes, quit: yes, restart: no, archiveDays: no, setPolicy: no, wait: no], enabled => [ addRegistry: yes, histograms: yes, inbox: yes, servers: yes, displayPolicy: yes, queues: yes, statistics: yes, time: yes, enable: no, archive: yes, background: yes, msMail: yes, purge: yes, rsMail: yes, login: yes, observeLog: yes, pupEcho: yes, pupRoute: yes, pupStats: yes, quit: yes, restart: yes, archiveDays: yes, setPolicy: yes, wait: yes], ENDCASE => ERROR; buff: STRING = [64]; prompt: STRING = IF state = enabled THEN "V! "L ELSE "V: "L; WriteString[prompt]; DO c: CHARACTER = LowerCase[ReadChar[]]; FOR i: Command IN Command DO IF allowed[i] = yes AND BeginsWith[names[i], buff] AND c = LowerCase[names[i][buff.length]] THEN BEGIN FOR j: CARDINAL IN [buff.length..names[i].length) DO nChar: CHARACTER = names[i][j]; String.AppendChar[buff, nChar]; WriteChar[nChar]; IF nChar = Ascii.SP THEN EXIT; REPEAT FINISHED => RETURN[i] ENDLOOP; EXIT END REPEAT FINISHED => BEGIN SELECT c FROM '? => BEGIN first: BOOLEAN ← TRUE; WriteString["? One of: "L]; FOR i: Command IN Command DO IF allowed[i] = yes AND BeginsWith[names[i], buff] THEN BEGIN IF first THEN first ← FALSE ELSE WriteString[", "L]; FOR j: CARDINAL IN [buff.length..names[i].length) DO WriteChar[names[i][j]] ENDLOOP; END; ENDLOOP; END; Ascii.DEL => ERROR Del[]; ENDCASE => {WriteChar[c]; WriteChar['?]; }; WriteChar[Ascii.CR]; WriteString[prompt]; WriteString[buff]; END; ENDLOOP; ENDLOOP; END; Receive: PROC [str: GlassDefs.Handle] = BEGIN OPEN str; state: LoginState ← none; user: BodyDefs.RName = [BodyDefs.maxRNameLength]; pwd: STRING = [16]; target: STRING = [20]; stopping: BOOLEAN ← FALSE; Cleanup: PROC = {IF NOT stopping THEN PolicyDefs.SetTelnetAllowed[]; }; WriteChar[Ascii.CR]; -- put in msg with name and uptime: BEGIN s: STRING = [20]; LogPrivateDefs.AppendElapsedTime[ s, Time.Current[] - LogPrivateDefs.startUpTime]; WriteString[LogPrivateDefs.uptimeHouse.caption]; WriteString[s]; WriteChar[Ascii.CR]; END; WriteString["Grapevine Server Viticulturists' Entrance"L]; WriteChar[Ascii.CR]; DO ENABLE UNWIND => Cleanup[]; WriteChar[Ascii.CR]; BEGIN ENABLE Del => GOTO del; comm: Command ← ChooseCommand[ str, state ! GlassDefs.TimeOut => GOTO timeOut]; WriteString[" ... "L]; SendNow[]; SELECT comm FROM addRegistry => DoAddRegistry[str]; -- "Display" sub-commands -- histograms => EnquiryDefs.Histograms[str]; inbox => BEGIN name: BodyDefs.RName = [BodyDefs.maxRNameLength]; IF ReadString["Inbox Name (* or CR for all): "L, name, word] = Ascii.DEL THEN ERROR Del[]; IF name.length = 0 THEN String.AppendString[name, "*"L]; SendNow[]; EnquiryDefs.MailboxCount[str, name]; END; servers => EnquiryDefs.RemoteServers[str]; displayPolicy => EnquiryDefs.PolicyControls[str]; queues => EnquiryDefs.SLQueueCount[str]; statistics => EnquiryDefs.DisplayStats[str]; time => DisplayTime[str]; -- misc commands -- enable => IF Confirm[str] THEN {IF Enable[str, user] THEN state ← enabled}; -- "Force" sub-commands -- archive => Archive[str, user]; background => ForceActivation[str]; msMail => {EnquiryDefs.LoginMSMail[]; WriteString["ok"L]; }; purge => ImmediatePurge[str]; rsMail => {EnquiryDefs.LoginRSMail[]; WriteString["ok"L]; }; login => { state ← none; -- because Login changes "user" IF Login[str, user, pwd] THEN state ← ok}; -- misc commands -- observeLog => IF Confirm[str] THEN ObserveLog[str, user]; pupEcho => { IF ReadString["Target: "L, target, word] = Ascii.DEL THEN ERROR Del[]; EnquiryDefs.PupEcho[str, target]; }; pupRoute => EnquiryDefs.PupRoutingTable[str]; pupStats => EnquiryDefs.DriverStats[str]; quit => IF Confirm[str] THEN EXIT; restart => IF Confirm[str] AND Stop[str, user] THEN EXIT; -- "Set" sub-commands -- archiveDays => ArchiveDays[str]; setPolicy => ControlOperation[str]; -- misc commands -- wait => WaitUntilIdle[str]; ENDCASE => WriteString["Unimplemented command"L]; EXITS timeOut => BEGIN WriteString["Type any character to continue ... "L]; [] ← ReadChar[ ! GlassDefs.TimeOut => GOTO end]; EXITS end => {WriteString["good-bye"L]; EXIT} END; del => {Flush[]; WriteString[" XXX"L]; }; END; ENDLOOP; Cleanup[]; END; Work: PROC = {DO GlassDefs.Listen[Receive] ENDLOOP}; Process.Detach[FORK Work[]]; END. Aug: Klamath conv (IsBound takes a LONG) BLH 10-Aug-84 16:24:49 change DisplayInboxes to take indiv mailbox name. BLH