-- Transport Mechanism Mail Server - policy module --
-- [Indigo]<Grapevine>MS>Policy.mesa --
-- Randy Gobbel 20-May-81 12:58:17 --
-- Andrew Birrell 20-Sep-82 9:16:26 --
-- Mike Schroeder 25-Jan-83 13:41:07F --
DIRECTORY
Ascii USING[ CR ],
EnquiryDefs USING[ ],
GlassDefs USING[ Handle ],
LogDefs USING[ DisplayNumber, WriteLogEntry ],
PolicyDefs -- using everything --,
Process USING[ DisableTimeout, GetPriority, InitializeCondition,
MsecToTicks, Priority, SetPriority, SetTimeout,
Ticks ],
String USING[ AppendChar, AppendDecimal, AppendString ],
Time USING[ Current, Pack, Packed, Unpack, Unpacked ];
Policy: MONITOR
IMPORTS LogDefs, Process, String, Time
EXPORTS EnquiryDefs, PolicyDefs =
BEGIN
-- Egg-timer --
minsCond: CONDITION;
secsCond: CONDITION;
Wait: PUBLIC PROCEDURE[ days: CARDINAL ← 0,
hrs: [0..24) ← 0,
mins: [0..60) ← 0,
secs: [0..60) ← 0 ] =
BEGIN
limit: Time.Packed = LOOPHOLE[Time.Current[] +
days * (LONG[24] * 60 * 60) +
hrs * (LONG[60] * 60) +
mins * LONG[60] +
LONG[secs]];
WaitUntil[limit];
END;
WaitUntil: PUBLIC ENTRY PROC[time: Time.Packed] =
BEGIN
UNTIL Time.Current[] + 60 >= time DO WAIT minsCond ENDLOOP;
UNTIL Time.Current[] >= time DO WAIT secsCond ENDLOOP;
END;
-- Compactor scheduling strategy --
compactorEnabled: BOOLEAN; -- whether compactor should run at all --
compactorWanted: BOOLEAN; -- whether compactor should start another cycle --
compactorDelay: CARDINAL; -- max delay in milliseconds --
compactorStart: CONDITION;
gapsNotified: CARDINAL ← 0; -- number of calls on "GapExists" --
CompactorStart: PUBLIC ENTRY PROCEDURE =
BEGIN
UNTIL compactorEnabled AND compactorWanted DO WAIT compactorStart ENDLOOP;
compactorWanted ← FALSE;
END;
compactorPause: CONDITION;
CompactorPause: PUBLIC ENTRY PROCEDURE =
BEGIN
delay: Process.Ticks = Process.MsecToTicks[
(compactorDelay / (100-minFreeHeap) ) * --beware of overflow!--
(IF freeHeap<minFreeHeap THEN 0 ELSE freeHeap-minFreeHeap) ];
UNTIL compactorEnabled DO WAIT compactorPause ENDLOOP;
IF current[work] = 0 THEN RETURN;
IF gapsNotified > 0 THEN { gapsNotified ← gapsNotified - 1; RETURN };
IF delay = 0 THEN RETURN;
Process.SetTimeout[@compactorPause, delay];
WAIT compactorPause;
END;
freeHeap: [0..100];
minFreeHeap: [0..100]; -- min free heap for running compactor with pauses --
loggedHeap: [0..100] ← 100; -- free heap recorded in log --
AmountOfFreeHeap: PUBLIC ENTRY PROCEDURE[ given: [0..100] ] =
BEGIN
freeHeap ← given;
IF given # loggedHeap
AND ( given < minFreeHeap OR loggedHeap < minFreeHeap
OR given NOT IN (loggedHeap-5..loggedHeap+5) )
THEN LogFreeHeap[];
END;
LogFreeHeap: INTERNAL PROC =
BEGIN
s: STRING = [16]; -- 100% free heap --
String.AppendString[s, "Free heap: "L];
String.AppendDecimal[s, freeHeap];
String.AppendChar[s, '%];
LogDefs.WriteLogEntry[s];
loggedHeap ← freeHeap;
END;
GapExists: PUBLIC ENTRY PROCEDURE =
BEGIN
compactorWanted ← TRUE; NOTIFY compactorStart;
IF gapsNotified = 0 THEN NOTIFY compactorPause;
gapsNotified ← gapsNotified + 1;
END;
-- Other time delays --
periodicWantedNow: PACKED ARRAY PolicyDefs.PeriodicProcess
OF BOOLEAN ← ALL[FALSE];
readPendingDelay: CARDINAL ← 15; -- minutes --
prodServersDelay: CARDINAL ← 15; -- minutes --
archiverHour: [0..24) ← 23; -- time of day --
regPurgerHour: [0..24) ← 23; -- time of day --
PeriodicWait: PUBLIC ENTRY PROC[process: PolicyDefs.PeriodicProcess] =
BEGIN
limit: LONG CARDINAL = SELECT process FROM
readPending => Time.Current[] + readPendingDelay*60,
prodServers => Time.Current[] + prodServersDelay*60,
archiver => CalculateNextTime[archiverHour],
regPurger => CalculateNextTime[regPurgerHour],
ENDCASE => ERROR;
UNTIL Time.Current[] >= limit OR periodicWantedNow[process]
DO WAIT minsCond ENDLOOP;
periodicWantedNow[process] ← FALSE;
END;
Activate: PUBLIC ENTRY PROC[process: PolicyDefs.PeriodicProcess] =
BEGIN
periodicWantedNow[process] ← TRUE;
BROADCAST minsCond;
END;
CalculateNextTime: PROC[wantedHour: [0..24)] RETURNS[Time.Packed] =
BEGIN
unpacked: Time.Unpacked ←
Time.Unpack[Time.Current[]];
IF unpacked.hour >= wantedHour
THEN -- move to next day --
unpacked ← Time.Unpack[LOOPHOLE[Time.Current[]
+ 24*60*LONG[60]]];
unpacked.minute ← 0; unpacked.second ← 0;
unpacked.hour ← wantedHour;
RETURN[ Time.Pack[unpacked, FALSE] ]
END;
expressThreshold: CARDINAL ← 1;
ExpressAllowed: PUBLIC ENTRY PROC[inputLength: CARDINAL] RETURNS[BOOLEAN] =
BEGIN
RETURN[inputLength < expressThreshold]
END;
SetExpressThreshold: PUBLIC ENTRY PROC[thresh: CARDINAL] =
{ expressThreshold ← thresh };
-- Control on operations --
control: PACKED ARRAY PolicyDefs.Operation OF PolicyDefs.Control;
current: ARRAY PolicyDefs.Operation OF PolicyDefs.OpLimit;
high: ARRAY PolicyDefs.Operation OF PolicyDefs.OpLimit;
reject: ARRAY PolicyDefs.Operation OF LONG CARDINAL;
total: ARRAY PolicyDefs.Operation OF LONG CARDINAL;
opWait: CONDITION;
WaitOperation: PUBLIC ENTRY PROCEDURE[ op: PolicyDefs.Operation ] =
{ UNTIL CheckOp[op] DO WAIT opWait ENDLOOP };
CheckOperation: PUBLIC ENTRY PROCEDURE[ op: PolicyDefs.Operation ]
RETURNS[ BOOLEAN ] =
{ RETURN[ CheckOp[op] ] };
CheckOp: INTERNAL PROCEDURE[ op: PolicyDefs.Operation ]
RETURNS[ BOOLEAN ] =
BEGIN
IF current[op] < control[op].limit AND control[op].allowed
AND( SELECT op FROM
clientInput, serverInput, MTP =>
(freeHeap > minFreeHeap/2 AND CheckOp[connection]),
readMail, regExpand, lily, FTP => CheckOp[connection],
readExpress, readInput, readPending, readForward, readMailbox => CheckOp[mainLine],
remailing =>
(freeHeap > minFreeHeap/2 AND CheckOp[mainLine]),
RSReadMail, MSReadMail, archiver,
regPurger => CheckOp[background],
connection, telnet, mainLine, background => CheckOp[work],
work => TRUE,
ENDCASE => ERROR )
THEN BEGIN
current[op] ← current[op] + 1;
IF current[op] > high[op] THEN high[op] ← current[op];
total[op] ← total[op] + 1;
RETURN[ TRUE ]
END
ELSE BEGIN
reject[op] ← reject[op] + 1;
RETURN[ FALSE ]
END;
END;
EndOperation: PUBLIC ENTRY PROCEDURE[ op: PolicyDefs.Operation ] =
{ EndOp[op] };
EndOp: INTERNAL PROCEDURE[ op: PolicyDefs.Operation ] =
BEGIN
current[op] ← current[op] - 1;
SELECT op FROM
clientInput, serverInput, readMail,
regExpand, lily, MTP, FTP => EndOp[connection];
readExpress, readInput, readPending, readForward, readMailbox,
remailing => EndOp[mainLine];
RSReadMail, MSReadMail, archiver,
regPurger => EndOp[background];
connection, telnet, mainLine, background, lily => EndOp[work];
work => NULL;
ENDCASE => ERROR;
BROADCAST opWait;
END;
ReadOperationCurrent: PUBLIC ENTRY PROC[op: PolicyDefs.Operation ]
RETURNS[ PolicyDefs.OpLimit ] =
{ RETURN[ current[op] ] };
ReadOperationControl: PUBLIC ENTRY PROCEDURE[ op: PolicyDefs.Operation ]
RETURNS[ PolicyDefs.Control ] =
BEGIN
RETURN[ control[op] ]
END;
SetOperationLimit: PUBLIC ENTRY PROCEDURE[ op: PolicyDefs.Operation,
limit: PolicyDefs.OpLimit ] =
BEGIN
control[op].limit ← limit;
BROADCAST opWait;
END;
SetOperationAllowed: PUBLIC ENTRY PROCEDURE[ op: PolicyDefs.Operation,
allowed: BOOLEAN ] =
BEGIN
control[op].allowed ← allowed;
BROADCAST opWait;
END;
SetTelnetAllowed: PUBLIC ENTRY PROCEDURE =
BEGIN
control[work].allowed ← control[telnet].allowed ← TRUE;
END;
PolicyControls: PUBLIC PROC[str: GlassDefs.Handle] =
BEGIN
OPEN str;
WriteChar[Ascii.CR];
WriteString[
"Operation: Allowed Limit Current High Reject Accepted"L];
-- clientInput yes 127 127 127 65535 655355555 --
FOR op: PolicyDefs.Operation IN PolicyDefs.Operation
DO control: PolicyDefs.Control = ReadOperationControl[op];
gap: STRING = " "L;
WriteChar[Ascii.CR];
WriteString[SELECT op FROM
work => "work "L,
connection => " connection "L,
clientInput => " clientInput "L,
serverInput => " serverInput "L,
readMail => " readMail "L,
regExpand => " regExpand "L,
lily => " Lily "L,
MTP => " MTP "L,
FTP => " FTP "L,
telnet => " Telnet "L,
mainLine => " mainLine "L,
readExpress => " readExpress "L,
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,
ENDCASE => ERROR ];
WriteString[gap];
WriteString[IF control.allowed THEN "yes"L ELSE "no"L];
WriteString[gap];
WriteDecimal[control.limit];
WriteString[gap];
WriteDecimal[current[op]];
WriteString[gap];
WriteDecimal[high[op]];
WriteString[gap];
WriteLongDecimal[reject[op]];
WriteString[gap];
WriteLongDecimal[total[op]];
WriteString[gap];
ENDLOOP;
WriteChar[Ascii.CR];
WriteString["readPendingDelay="L]; WriteDecimal[readPendingDelay];
WriteString[" mins"L];
WriteChar[Ascii.CR];
WriteString["prodServersDelay="L]; WriteDecimal[prodServersDelay];
WriteString[" mins"L];
END;
-- misc procedures for use from the debugger: use with care! --
BroadcastCondition: ENTRY PROC[cond: POINTER TO CONDITION] =
{ BROADCAST cond↑ };
forever: CONDITION; -- time-out is disabled --
WaitOnCondition: ENTRY PROC[cond: POINTER TO CONDITION] =
{ WAIT cond↑ };
Ready: SIGNAL = CODE;
SignalAtPriority: PROC[new: Process.Priority] =
BEGIN
old: Process.Priority = Process.GetPriority[];
Process.SetPriority[new];
SIGNAL Ready[];
Process.SetPriority[old];
END;
-- Initialisation --
Init: ENTRY PROCEDURE =
BEGIN
OPEN Process;
-- Egg-timer --
InitializeCondition[@minsCond, MsecToTicks[60000]];
InitializeCondition[@secsCond, MsecToTicks[1000]];
-- Compactor scheduling --
compactorEnabled ← TRUE;
compactorWanted ← TRUE;
InitializeCondition[@compactorStart, 0];
DisableTimeout[@compactorStart];
compactorDelay ← 1000;
InitializeCondition[@compactorPause, MsecToTicks[compactorDelay]];
minFreeHeap ← 10;
freeHeap ← (minFreeHeap+100)/2;
-- Operation controls --
BEGIN
max: PolicyDefs.OpLimit = LAST[PolicyDefs.OpLimit];
control[ work ] ← [limit:max, allowed:TRUE];
control[ connection ] ← [limit:12, allowed:TRUE];
control[ clientInput ] ← [limit:5, allowed:TRUE];
control[ serverInput ] ← [limit:5, allowed:TRUE];
control[ readMail ] ← [limit:8, allowed:TRUE];
control[ regExpand ] ← [limit:9, allowed:TRUE];
control[ lily ] ← [limit:4, allowed:TRUE];
control[ MTP ] ← [limit:7, allowed:TRUE];
control[ FTP ] ← [limit:2, allowed:TRUE];
control[ telnet ] ← [limit:3, allowed:TRUE];
control[ mainLine ] ← [limit:max, allowed:TRUE];
control[ readExpress ] ← [limit:1, allowed:TRUE];
control[ readInput ] ← [limit:1, allowed:TRUE];
control[ readPending ] ← [limit:1, allowed:TRUE];
control[ readForward ] ← [limit:2, allowed:TRUE];
control[ readMailbox ] ← [limit:1, allowed:TRUE];
control[ background ] ← [limit:1, allowed:TRUE];
control[ RSReadMail ] ← [limit:1, allowed:TRUE];
control[ MSReadMail ] ← [limit:1, allowed:TRUE];
control[ remailing ] ← [limit:1, allowed:TRUE];
control[ archiver ] ← [limit:1, allowed:TRUE];
control[ regPurger ] ← [limit:1, allowed:TRUE];
END;
current ← high ← ALL[0]; reject ← total ← ALL[LONG[0]];
InitializeCondition[@opWait, 0];
DisableTimeout[@opWait];
DisableTimeout[@forever];
-- statistics --
LogDefs.DisplayNumber["Free heap"L, [percent[@freeHeap]] ];
LogDefs.DisplayNumber["Connections"L, [short[@(current[connection])]] ];
END;
Init[];
END.