-- Copyright (C) 1982, 1983, 1984 by Xerox Corporation. All rights reserved.
-- Enquiry.mesa, Transport mechanism: Viticulturists' interface
-- HGM, 15-Dec-84 20:06:07
-- 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],
ImageDefs USING [StopMesa],
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],
Time USING [Append, Current, Packed, Unpack];
Enquiry: MONITOR
IMPORTS
EnquiryDefs, GlassDefs, ImageDefs, LogDefs, LogPrivateDefs, LogWatchDefs,
NameInfoDefs, PolicyDefs, Process, ProtocolDefs,
String, Time
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, pwd: 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[];
Ascii.SP => {
acc: STRING = [8]; [] ← ReadString[" (account): "L, acc, word]; };
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: STRING]
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: 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];
ImageDefs.StopMesa[];
END;
Archive: PROC [str: GlassDefs.Handle, user: 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: 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: STRING, content: STRING ← NIL] =
BEGIN
log: STRING = [128];
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];
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