-- File: WebServerTool.mesa - last edit: -- AOF 25-Apr-96 10:43:16 -- DLion 2-Mar-96 23:10:57 -- WebServerTool.mesa -- Create by FormSWLayoutTool on 22-Oct-95 22:20 -- Copyright (C) 1995, 1996 by Freier's Garage. All rights reserved. DIRECTORY Exec USING [ AddCommand, EndOfCommandLine, ExecProc, FreeTokenString, GetToken, Handle, Outcome, OutputProc, RemoveCommand], Format USING[LongDecimal, StringProc], FormSW USING[ AllocateItemDescriptor, ClientItemsProcType, CommandItem, Enumerated, EnumeratedItem, EnumeratedNotifyProcType, line0, line1, line3, line4, line6, line7, LongNumberItem, ProcType, StringItem, TagOnlyItem], Heap USING[Create, GetAttributes, Delete], Process USING[Detach, priorityNormal, SetPriority], Put USING[Text], String USING [AppendChar, LowerCase], Tool USING[ Create, Destroy, MakeFileSW, MakeFormSW, MakeMsgSW, MakeSWsProc, UnusedLogName], ToolWindow USING[TransitionProcType], WebCache USING[serverCache], WebLog USING[MailLogFile], WebOps USING[StartServer, StopServer], WebTool USING[Data, DataHandle, FormItems, LevelTypes, ServerisTypes], Window USING[Handle]; WebServerTool: MONITOR IMPORTS Exec, Format, FormSW, Heap, Process, Put, String, Tool, WebCache, WebLog, WebOps EXPORTS WebTool = BEGIN wh: Window.Handle ¬ NIL; data: PUBLIC --WebTool-- WebTool.DataHandle ¬ NIL; zone: PUBLIC --WebTool-- UNCOUNTED ZONE ¬ Heap.Create[initial: 4]; busyBit: BOOLEAN ¬ FALSE; Busy: ENTRY PROCEDURE RETURNS [isBusy: BOOLEAN] = BEGIN ENABLE UNWIND => NULL; isBusy ¬ busyBit; busyBit ¬ TRUE; END; Done: ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; busyBit ¬ FALSE; END; Msg: PUBLIC --WebTool-- Format.StringProc = {Put.Text[data.msgSW, s]}; Write: PUBLIC --WebTool-- Format.StringProc = {Put.Text[data.fileSW, s]}; PruneServerCache: FormSW.ProcType = BEGIN ENABLE ABORTED => {Done[]; CONTINUE}; IF Busy[] THEN {Msg["Tool is busy.\n"L]; RETURN}; Process.Detach[FORK PruneServerCacheInternal[]]; END; PruneServerCacheInternal: PROCEDURE = BEGIN ENABLE ABORTED => {Done[]; CONTINUE}; Process.SetPriority[Process.priorityNormal]; Msg["\nPruning server cache ... "L]; WebCache.serverCache.prune[WebCache.serverCache]; Msg["done\n"L]; Done[]; END; EnumerateServerCache: FormSW.ProcType = BEGIN ENABLE ABORTED => {Done[]; CONTINUE}; IF Busy[] THEN {Msg["Tool is busy.\n"L]; RETURN}; Process.Detach[FORK EnumerateServerCacheInternal[]]; END; EnumerateServerCacheInternal: PROCEDURE = BEGIN ENABLE ABORTED => {Done[]; CONTINUE}; Process.SetPriority[Process.priorityNormal]; Write["EnumerateServerCache called\n"L]; Done[]; END; Sendlogto: FormSW.ProcType = BEGIN ENABLE ABORTED => {Done[]; CONTINUE}; IF Busy[] THEN {Msg["Tool is busy.\n"L]; RETURN}; Process.Detach[FORK SendlogtoInternal[]]; END; SendlogtoInternal: PROCEDURE = BEGIN ENABLE ABORTED => {Done[]; CONTINUE}; Process.SetPriority[Process.priorityNormal]; WebLog.MailLogFile[]; Done[]; END; ServerTransition: FormSW.EnumeratedNotifyProcType = BEGIN ENABLE ABORTED => CONTINUE; SELECT oldValue FROM WebTool.ServerisTypes[inactive].ORD => WebOps.StartServer[]; WebTool.ServerisTypes[active].ORD => WebOps.StopServer[]; ENDCASE; END; ClientTransition: ToolWindow.TransitionProcType = { SELECT TRUE FROM old = inactive => IF data = NIL THEN BEGIN data ¬ zone.NEW[WebTool.Data ¬ []]; WebOps.StartServer[]; END; new = inactive => BEGIN IF data # NIL THEN BEGIN IF (data.serveris = active) THEN WebOps.StopServer[]; zone.FREE[@data]; END; END; ENDCASE; }; Init: PROCEDURE = BEGIN Exec.AddCommand["WebServer.~"L, ExecCommand, Help, Unload]; wh ¬ Tool.Create[ makeSWsProc: MakeSWs, initialState: default, clientTransition: ClientTransition, name: "WebServer"L, tinyName1: "Web"L, tinyName2: "Server"L, cmSection: "WebServer"L]; END; --Init-- Help: Exec.ExecProc = BEGIN out: Format.StringProc ¬ Exec.OutputProc[h]; out["WebServer.~ <command>[/switch[:value]]\n"L]; out[" <command> ::= <server> | <cache> | <logging>\n"L]; out[" <server> ::= ""Server""\n"L]; out[" shows start time and connections served\n"L]; out[" <cache> ::= ""Cache"" (<empty> | '/ <cache-switch>)\n"L]; out[" with no switches shows current cache value\n"L]; out[" <cache-switch> ::= ""prune"" | ""enum"" | <maximum>\n"L]; out[" ""prune"" causes the cache to be purged of older entries\n"L]; out[" ""enum"" will list the names and sizes of the items in cache\n"L]; out[" <maximum> ::= ""max"" ': <kilobytes>\n"L]; out[" <kilobytes> ::= {decimal number}\n"L]; out[" sets the maximum kilobytes of information to be cached\n"L]; END; --Help-- ExecCommand: Exec.ExecProc = BEGIN ENABLE ABORTED => {Done[]; CONTINUE}; cmd, switch: LONG STRING ¬ NIL; out: Format.StringProc ¬ Exec.OutputProc[h]; IF Busy[] THEN BEGIN out["Busy - try later\n"L]; outcome ¬ abort; END ELSE BEGIN ENABLE UNWIND => BEGIN IF (cmd # NIL) THEN cmd ¬ Exec.FreeTokenString[cmd]; IF (switch # NIL) THEN switch ¬ Exec.FreeTokenString[switch]; END; WHILE (~Exec.EndOfCommandLine[h]) DO [cmd, switch] ¬ Exec.GetToken[h]; SELECT TRUE FROM (cmd = NIL) => NULL; Match[cmd, "server"L] => outcome ¬ ServerCommand[switch, out]; Match[cmd, "cache"L] => outcome ¬ CacheCommand[switch, out]; Match[cmd, "logging"L] => outcome ¬ LoggingCommand[switch, out]; ENDCASE => BEGIN out["Command '"L]; out[cmd]; out["' was not recognized\n"L]; outcome ¬ abort; END; IF (cmd # NIL) THEN cmd ¬ Exec.FreeTokenString[cmd]; IF (switch # NIL) THEN switch ¬ Exec.FreeTokenString[switch]; ENDLOOP; END; Done[]; END; --ExecCommand-- ServerCommand: PROC[switch: LONG STRING, out: Format.StringProc] RETURNS[outcome: Exec.Outcome ¬ normal] = BEGIN out["Server started at "L]; out[data.serverstarted]; out["\n\tConnections served: "L]; Format.LongDecimal[out, data.connectionsserved]; out["\n\tCurrent heap size(pages): "L]; Format.LongDecimal[out, Heap.GetAttributes[zone].heapPages]; out["\n"G]; SELECT TRUE FROM (switch = NIL) => NULL; Match[switch, "recycle"L] => BEGIN out["Stopping server ... '"L]; WebOps.StopServer[]; out["restarting ..."L]; WebOps.StartServer[]; out["done\n"L]; END; ENDCASE => BEGIN out["'"L]; out[switch]; out["' is undefined for 'Server' command\n"L]; outcome ¬ abort; END; END; --ServerCommand-- CacheCommand: PROC[switch: LONG STRING, out: Format.StringProc] RETURNS[outcome: Exec.Outcome ¬ normal] = BEGIN out["Currently there are approximately "L]; Format.LongDecimal[out, data.cacheCurrent]; out[" kilobytes of cache\n"L]; SELECT TRUE FROM (switch = NIL) => NULL; Match[switch, "prune"L] => BEGIN out["Pruning server cache ... "L]; WebCache.serverCache.prune[WebCache.serverCache]; out["done\n"L]; END; Match[switch, "enum"L] => BEGIN out["I can't do that!"L]; outcome ¬ abort; END; Match[switch, "max"L] => BEGIN out["I can't do that!"L]; outcome ¬ abort; END; ENDCASE => BEGIN out["Switch '"L]; out[switch]; out["' was not recognized\n"L]; outcome ¬ abort; END; END; --CacheCommand-- LoggingCommand: PROC[switch: LONG STRING, out: Format.StringProc] RETURNS[outcome: Exec.Outcome ¬ normal] = BEGIN IF (switch = NIL) THEN BEGIN out["Expected a switch with 'Logging' command\n"L]; outcome ¬ abort; END ELSE BEGIN value: LONG STRING ¬ Value[switch]; IF (value = NIL) THEN BEGIN out["Expected a 'value' to follow '"L]; out[switch]; out["' switch\n"L]; outcome ¬ abort; END ELSE BEGIN ENABLE UNWIND => zone.FREE[@value]; SELECT TRUE FROM Match[switch, "level"L] => BEGIN SELECT TRUE FROM Match[value, "silent"L] => data.level ¬ silent; Match[value, "basic"L] => data.level ¬ basic; Match[value, "http"L] => data.level ¬ http; Match[value, "verbose"L] => data.level ¬ verbose; ENDCASE => BEGIN out["'"]; out[value]; out["' is not a valid logging level\n"L]; outcome ¬ abort; END; END; Match[switch, "mailto"L] => BEGIN saved: LONG STRING ¬ data.emailaddress; data.emailaddress ¬ value; out["Mailing log file to '"L]; out[value]; out[" ... "L]; WebLog.MailLogFile[! UNWIND => data.emailaddress ¬ saved]; data.emailaddress ¬ saved; out["done\n"L]; END; ENDCASE => BEGIN out["'"L]; out[switch]; out["' is undefined for 'Logging' command\n"L]; outcome ¬ abort; END; zone.FREE[@value]; END; END; END; --LoggingCommand-- Match: PROC[string, literal: LONG STRING] RETURNS[match: BOOLEAN ¬ FALSE] = BEGIN --has to match for at least as long as the literal FOR index: NATURAL IN[0..literal.length) DO IF (String.LowerCase[string[index]] # literal[index]) THEN EXIT; REPEAT FINISHED => match ¬ TRUE; ENDLOOP; END; --Match-- Value: PROC[string: LONG STRING] RETURNS[value: LONG STRING ¬ NIL] = BEGIN --looking for the string past the first ': FOR index: NATURAL IN[0..string.length) DO IF (string[index] = ':) THEN BEGIN value ¬ zone.NEW[StringBody[string.length - index]]; FOR copy: NATURAL IN(index..string.length) DO String.AppendChar[value, string[copy]]; ENDLOOP; EXIT; END; ENDLOOP; END; --Value-- Unload: Exec.ExecProc = { IF Busy[] THEN { Exec.OutputProc[h]["Tool is busy. Sorry.\n"L]; RETURN[error] }; Tool.Destroy[wh]; Exec.RemoveCommand[h, "WebServer.~"L]; Heap.Delete[zone]; Done[]; }; MakeSWs: Tool.MakeSWsProc = BEGIN logName: LONG STRING ¬ [20]; Tool.UnusedLogName[unused: logName, root: "WebServer.log"L]; data.msgSW ¬ Tool.MakeMsgSW[window: window, lines: 3]; data.formSW ¬ Tool.MakeFormSW[ window: window, formProc: MakeForm, zone: zone]; data.fileSW ¬ Tool.MakeFileSW[window: window, name: logName]; END; --MakeSWs-- MakeForm: FormSW.ClientItemsProcType = { nItems: CARDINAL = WebTool.FormItems.LAST.ORD + 1; serveris: ARRAY[0..2) OF FormSW.Enumerated ¬ [ ["inactive"L, WebTool.ServerisTypes.inactive.ORD], ["active"L, WebTool.ServerisTypes.active.ORD]]; level: ARRAY[0..4) OF FormSW.Enumerated ¬ [ ["silent"L, WebTool.LevelTypes.silent.ORD], ["basic"L, WebTool.LevelTypes.basic.ORD], ["http"L, WebTool.LevelTypes.http.ORD], ["verbose"L, WebTool.LevelTypes.verbose.ORD]]; items ¬ FormSW.AllocateItemDescriptor[nItems, zone]; items[WebTool.FormItems.serveris.ORD] ¬ FormSW.EnumeratedItem[ tag: "Server is"L, place: [6, FormSW.line0], feedback: all, proc: ServerTransition, choices: DESCRIPTOR[serveris], value: @data.serveris, z: zone]; items[WebTool.FormItems.serverstarted.ORD] ¬ FormSW.StringItem[ tag: "Server started"L, place: [24, FormSW.line1], readOnly: TRUE, inHeap: TRUE, string: @data.serverstarted, z: zone]; items[WebTool.FormItems.connectionsserved.ORD] ¬ FormSW.LongNumberItem[ tag: "Connections served"L, place: [282, FormSW.line1], readOnly: TRUE, signed: FALSE, default: 2147418112, value: @data.connectionsserved, z: zone]; items[WebTool.FormItems.cache.ORD] ¬ FormSW.TagOnlyItem[ tag: "Cache"L, place: [6, FormSW.line3], z: zone]; items[WebTool.FormItems.cachePrune.ORD] ¬ FormSW.CommandItem[ tag: "Prune"L, place: [42, FormSW.line4], proc: PruneServerCache, z: zone]; items[WebTool.FormItems.cacheEnumerate.ORD] ¬ FormSW.CommandItem[ tag: "Enumerate"L, place: [96, FormSW.line4], proc: EnumerateServerCache, z: zone]; items[WebTool.FormItems.cacheSize.ORD] ¬ FormSW.TagOnlyItem[ tag: "Size(kb)-"L, place: [174, FormSW.line4], z: zone]; items[WebTool.FormItems.cacheLimit.ORD] ¬ FormSW.LongNumberItem[ tag: "maximum"L, place: [234, FormSW.line4], signed: FALSE, default: 0, value: @data.cacheLimit, z: zone]; items[WebTool.FormItems.cacheCurrent.ORD] ¬ FormSW.LongNumberItem[ tag: "current"L, place: [348, FormSW.line4], readOnly: TRUE, signed: FALSE, default: 0, value: @data.cacheCurrent, z: zone]; items[WebTool.FormItems.logging.ORD] ¬ FormSW.TagOnlyItem[ tag: "Logging"L, place: [6, FormSW.line6], z: zone]; items[WebTool.FormItems.level.ORD] ¬ FormSW.EnumeratedItem[ tag: "Level"L, place: [36, FormSW.line7], choices: DESCRIPTOR[level], value: @data.level, z: zone]; items[WebTool.FormItems.sendlogto.ORD] ¬ FormSW.CommandItem[ tag: "Send log to"L, place: [156, FormSW.line7], proc: Sendlogto, z: zone]; items[WebTool.FormItems.emailaddress.ORD] ¬ FormSW.StringItem[ tag: "e-mail address"L, place: [234, FormSW.line7], inHeap: TRUE, string: @data.emailaddress, z: zone]; RETURN[items: items, freeDesc: TRUE]; }; -- Mainline code Init[]; END...