<> <> <<>> <> <<>> <> <> DIRECTORY AlpineControl USING [UnexportInterfaces], Commander USING [CommandProc, Register], CommandTool USING [ArgumentVector, Parse], Convert USING [CardFromRope], CoordinatorMap USING [Count], IO USING [Put, int, rope], Process USING [Pause, SecondsToTicks], TransactionMap USING [AbortUnilaterally, Handle, UnlockedEnumerate] ; AlpineControlImpl: CEDAR PROGRAM IMPORTS AlpineControl, Commander, CommandTool, Convert, CoordinatorMap, IO, Process, TransactionMap EXPORTS AlpineControl = BEGIN Halt: PUBLIC PROC [waitMinutes: CARDINAL _ 1] RETURNS [nCoordinators: NAT] ~ { <> <<(If there are still coordinators, use SkiPatrol to figure out who they are. Committed or aborted coordinators should go away quickly, and the remaining coordinators are harmless: their workers will eventually time out and abort themselves.)>> <> KillTranses: PROC [h: TransactionMap.Handle] RETURNS [stop: BOOL] ~ { <> TRUSTED {TransactionMap.AbortUnilaterally[self: h, why: clientRequest]}; RETURN [FALSE]; }; -- KillTranses TRUSTED {AlpineControl.UnexportInterfaces[]}; Process.Pause[ticks: Process.SecondsToTicks[60 * waitMinutes]]; TRUSTED {TransactionMap.UnlockedEnumerate[proc: KillTranses]}; TRUSTED {RETURN [CoordinatorMap.Count[]]} }; -- Halt Shutdown: Commander.CommandProc ~ { <> args: CommandTool.ArgumentVector _ CommandTool.Parse[cmd]; nCoords: NAT; -- number of coordinators left running SELECT args.argc FROM >2 => { IO.Put[cmd.err, IO.rope["usage: shutdown [ nMinutes]\n"]]; nCoords _ 0; }; =2 => nCoords _ Halt[Convert.CardFromRope[args[1]]]; =1 => nCoords _ Halt[]; ENDCASE => ERROR; IF nCoords > 0 THEN IO.Put[cmd.err, IO.rope["WARNING: "], IO.int[nCoords], IO.rope[" coordinator(s) left running\n"]] }; Commander.Register[key: "Shutdown", proc: Shutdown, doc: "Shuts down Alpine: shutdown [ nMinutes ]"]; END.