<<>> <> <> <> <> <> <> <<>> DIRECTORY Commander, CommanderBackdoor, CommanderOps, IO, PFS, Process, Rope, RopeList, SymTab, InstallationComforts, InstallationSupportPrivate, MesaLoadState; CommanderInstallCommandsImpl: CEDAR MONITOR IMPORTS Commander, CommanderBackdoor, CommanderOps, IO, PFS, Process, Rope, RopeList, SymTab, InstallationComforts, MesaLoadState EXPORTS MesaLoadState -- Opaque type ~ BEGIN PATH: TYPE ~ PFS.PATH; ROPE: TYPE ~ Rope.ROPE; <> ConfigRep: PUBLIC TYPE ~ InstallationSupportPrivate.ConfigRep; IsExported: PROC [qualifiedName: ROPE] RETURNS [BOOL] ~ { global: REF ConfigRep = MesaLoadState.GlobalConfig[]; dot: INT ~ Rope.Find[qualifiedName, "."]; IF dot >= 0 THEN { interface: ROPE ~ Rope.Substr[qualifiedName, 0, dot]; item: ROPE ~ Rope.Substr[qualifiedName, dot+1]; a: PROCEDURE ANY RETURNS ANY ~ InstallationComforts.ProcFromNamedInterface[interface, item, global]; RETURN [a # NIL] } ELSE { RETURN [FALSE] -- might want to test for existence of the interface record }; }; <> InstallData: TYPE = REF InstallDataPrivate; InstallDataPrivate: TYPE = RECORD [ statuses: SymTab.Ref, change: CONDITION ]; InstallStatus: TYPE = REF InstallStatusPrivate; InstallStatusPrivate: TYPE = RECORD [ state: InstallState ¬ notDone, foundFile: BOOL ¬ FALSE, failed: BOOL ¬ TRUE ]; InstallState: TYPE = {notDone, inProgress, done}; installData: InstallData ¬ InitInstallData[]; InitInstallData: PROC RETURNS [installData: InstallData] ~ { installData ¬ NEW [InstallDataPrivate ¬ [ statuses: SymTab.Create[case: FALSE] ]]; TRUSTED { Process.InitializeCondition[@installData.change, Process.SecondsToTicks[60]]; Process.EnableAborts[@installData.change]; }; }; ForgetInstallationCommand: Commander.CommandProc = { from: IO.STREAM = IO.RIS[cmd.commandLine]; FOR i: INT ¬ from.SkipWhitespace[], from.SkipWhitespace[] WHILE NOT from.EndOf[] DO packageName: ROPE = from.GetTokenRope[IO.IDProc].token; ForgetInstallation[packageName]; ENDLOOP; }; ForgetInstallation: PUBLIC PROC [packageName: ROPE] = { SetInstallState[installData, AcquireInstallStatus[installData, packageName], notDone]; }; Installed: PUBLIC PROC [packageName: ROPE] = { status: InstallStatus ~ AcquireInstallStatus[installData, packageName]; status.failed ¬ FALSE; SetInstallState[installData, status, done]; }; GetInstalled: PUBLIC ENTRY PROC RETURNS [LIST OF ROPE] = { list: LIST OF ROPE ¬ NIL; EachPair: PROC [key: ROPE, val: REF] RETURNS [quit: BOOL ¬ FALSE] ~ { WITH val SELECT FROM status: InstallStatus => { IF status.state = done THEN list ¬ CONS[key, list]; }; ENDCASE => NULL; }; [] ¬ SymTab.Pairs[installData.statuses, EachPair]; RETURN [RopeList.Sort[list, RopeList.IgnoreCase]] }; IsInstalled: PUBLIC ENTRY PROC [key: ROPE] RETURNS [BOOL] = { ENABLE UNWIND => NULL; sought: InstallStatus ¬ NARROW[installData.statuses.Fetch[key].val]; RETURN [IF sought = NIL OR sought.state = notDone THEN FALSE ELSE TRUE]; }; IfInstalledCommand: Commander.CommandProc = { ris: IO.STREAM ~ IO.RIS[cmd.commandLine]; resource: ROPE ~ CommanderOps.GetCmdToken[ris].value; IF resource = NIL THEN CommanderOps.Failed[cmd.procData.doc]; IF (cmd.procData.clientData=$NOT) # IsInstalled[resource] THEN RETURN CommanderOps.ExecuteCommand[cmd, IO.GetRope[ris]]; }; IfProcExportedCommand: Commander.CommandProc = { ris: IO.STREAM ~ IO.RIS[cmd.commandLine]; resource: ROPE ~ CommanderOps.GetCmdToken[ris].value; IF resource = NIL THEN CommanderOps.Failed[cmd.procData.doc]; IF (cmd.procData.clientData=$NOT) # IsExported[resource] THEN RETURN CommanderOps.ExecuteCommand[cmd, IO.GetRope[ris]]; }; whenInstalled: SymTab.Ref ¬ SymTab.Create[case: FALSE]; WhenInstalled: PUBLIC ENTRY PROC [key: ROPE, commandLine: ROPE] RETURNS [doItNow: BOOL ¬ FALSE] = { <> ENABLE UNWIND => NULL; sought: InstallStatus ¬ NARROW[installData.statuses.Fetch[key].val]; doItNow ¬ NOT (sought = NIL OR sought.state = notDone); IF NOT doItNow THEN { Update: SymTab.UpdateAction ~ { <> actions: LIST OF ROPE ¬ NARROW[val]; IF actions = NIL THEN actions ¬ LIST[commandLine] ELSE FOR tail: LIST OF ROPE ¬ actions, tail.rest DO IF tail.rest = NIL THEN {tail.rest ¬ LIST[commandLine]; EXIT}; ENDLOOP; RETURN [op: store, new: actions]; }; SymTab.Update[whenInstalled, key, Update]; }; }; WhenInstalledCommand: Commander.CommandProc = { ris: IO.STREAM ~ IO.RIS[cmd.commandLine]; resource: ROPE ~ CommanderOps.GetCmdToken[ris].value; IF resource = NIL THEN { PrintEachPair: SymTab.EachPairAction ~ { <> FOR tail: LIST OF ROPE ¬ NARROW[val], tail.rest UNTIL tail = NIL DO IO.PutRope[cmd.out, "WhenInstalled "]; IO.PutRope[cmd.out, key]; IO.PutRope[cmd.out, " "]; IO.PutRope[cmd.out, tail.first]; ENDLOOP; }; [] ¬ SymTab.Pairs[whenInstalled, PrintEachPair]; } ELSE { commandLine: ROPE ~ Rope.Cat["From ", PFS.RopeFromPath[PFS.GetWDir[]], IO.GetRope[ris]]; doItNow: BOOL ~ WhenInstalled[resource, commandLine]; IF doItNow THEN RETURN CommanderOps.ExecuteCommand[cmd, commandLine]; }; }; RequireCommand: Commander.CommandProc = { ENABLE PFS.Error => { IF error.group=user THEN ERROR CommanderOps.Failed[error.explanation] }; world: ROPE ~ CommanderOps.NextArgument[cmd]; component: ROPE ~ CommanderOps.NextArgument[cmd]; resource: ROPE ~ CommanderOps.NextArgument[cmd]; found, failed: BOOL; IF CommanderOps.NextArgument[cmd] # NIL OR resource = NIL THEN CommanderOps.Failed[cmd.procData.doc]; [found, failed] ¬ Require[world: world, component: component, resource: resource, parent: cmd]; SELECT TRUE FROM NOT failed => { NULL }; NOT found => { result ¬ $Failure; msg ¬ msg.Cat["Failed to find ", resource, ".require file.\n"]; }; ENDCASE => { result ¬ $Failure; msg ¬ msg.Cat[resource, ".require file ", " failed.\n"]; }; }; InstallCommand: Commander.CommandProc = { ENABLE PFS.Error => { IF error.group=user THEN ERROR CommanderOps.Failed[error.explanation] }; again: BOOL ¬ FALSE; n: NAT ¬ 0; FOR resource: ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd] UNTIL resource = NIL DO n ¬ n + 1; SELECT TRUE FROM resource.Equal["-a", FALSE] => again ¬ TRUE; resource.Equal["-~a", FALSE] => again ¬ FALSE; ENDCASE => { found, failed: BOOL; IF again THEN ForgetInstallation[resource]; [found, failed] ¬ Install[resource, cmd]; SELECT TRUE FROM NOT failed => { NULL }; NOT found => { result ¬ $Failure; msg ¬ msg.Cat["Failed to find ", resource, ".require file.\n"]; }; ENDCASE => { result ¬ $Failure; msg ¬ msg.Cat[resource, ".require file ", " failed.\n"]; }; }; ENDLOOP; IF n = 0 THEN RETURN [$Failure, "Install (-a|-~a|packageName)* - Ensure the installation of one or more packages. -a => install again, even if already installed"]; }; InstalledCommand: Commander.CommandProc = { n: NAT ¬ 0; FOR arg: ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd] UNTIL arg = NIL DO Installed[arg]; n ¬ n + 1; ENDLOOP; IF n = 0 THEN { FOR tail: LIST OF ROPE ¬ GetInstalled[], tail.rest UNTIL tail = NIL DO IO.PutRope[cmd.out, tail.first]; IO.PutRope[cmd.out, IF tail.rest = NIL THEN "\n" ELSE " "]; ENDLOOP; }; }; SecondSource: PUBLIC PROC [fileName: ROPE, parent: Commander.Handle, stopOnFailure: BOOL ¬ FALSE] RETURNS [allOK: BOOL ¬ FALSE] = { fileStream: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[fileName]]; BEGIN ENABLE UNWIND => { IO.Close[fileStream, TRUE] }; child: Commander.Handle ~ CommanderOps.CreateFromStreams[in: fileStream, parentCommander: parent]; data: CommanderBackdoor.CommandToolData ~ CommanderBackdoor.GetCommandToolData[child]; data.stopOnFailure ¬ stopOnFailure; allOK ¬ NOT CommanderOps.ReadEvalPrintLoop[child].hadFailure; END; IO.Close[fileStream, TRUE]; }; Install: PUBLIC PROC [packageName: ROPE, parent: Commander.Handle] RETURNS [foundFile, failed: BOOL] = { status: InstallStatus = AcquireInstallStatus[installData, packageName]; SELECT status.state FROM notDone => ERROR; inProgress => { finalState: InstallState ¬ done; { ENABLE UNWIND => { SetInstallState[installData, status, notDone] }; installFileName: ROPE = packageName.Concat[".require"]; foundFile ¬ TRUE; [] ¬ PFS.FileInfo[PFS.PathFromRope[installFileName] ! PFS.Error => IF error.group = user THEN { foundFile ¬ FALSE; CONTINUE } ]; SELECT status.foundFile ¬ foundFile FROM FALSE => { finalState ¬ notDone; failed ¬ TRUE }; TRUE => { status.failed ¬ failed ¬ NOT SecondSource[installFileName, parent, stopOnFailureDuringInstall]; IF failed THEN finalState ¬ notDone; }; ENDCASE => ERROR; }; SetInstallState[installData, status, finalState]; }; done => { foundFile ¬ status.foundFile; failed ¬ status.failed }; ENDCASE => ERROR; IF NOT failed THEN { commandLines: LIST OF ROPE ¬ NIL; Update: SymTab.UpdateAction ~ { <> IF found THEN { commandLines ¬ NARROW[val]; RETURN [op: delete] }; }; SymTab.Update[whenInstalled, packageName, Update]; FOR tail: LIST OF ROPE ¬ commandLines, tail.rest UNTIL tail = NIL DO [] ¬ CommanderOps.DoCommand[tail.first, parent]; ENDLOOP; }; }; stopOnFailureDuringInstall: BOOL ¬ TRUE; Require: --PUBLIC-- PROC [world, component, resource: ROPE, parent: Commander.Handle] RETURNS [foundFile, failed: BOOL ¬ FALSE] = { RequireInner: PROC ~ { [foundFile, failed] ¬ Install[resource, parent]; }; path: PATH ~ PFS.PathFromRope[Rope.Cat["/", world, "/", component, "/"]]; PFS.DoInWDir[path, RequireInner]; }; AcquireInstallStatus: ENTRY PROC [d: InstallData, packageName: ROPE] RETURNS [sought: InstallStatus] = { ENABLE UNWIND => {}; sought ¬ NARROW[d.statuses.Fetch[packageName].val]; IF sought = NIL THEN { sought ¬ NEW [InstallStatusPrivate ¬ []]; IF NOT d.statuses.Insert[packageName, sought] THEN ERROR; }; WHILE sought.state = inProgress DO WAIT d.change ENDLOOP; IF sought.state = notDone THEN sought.state ¬ inProgress; }; SetInstallState: ENTRY PROC [d: InstallData, status: InstallStatus, state: InstallState] = { ENABLE UNWIND => {}; status.state ¬ state; BROADCAST d.change; }; <> Commander.Register[key: "ForgetInstallation", proc: ForgetInstallationCommand, doc: "ForgetInstallation packageName* - Forget that one or more packages are installed", clientData: NIL]; Commander.Register[key: "IfInstalled", proc: IfInstalledCommand, doc: "packageName commandLine\ndo the commandLine if the named package has been installed", clientData: NIL]; Commander.Register[key: "IfNotInstalled", proc: IfInstalledCommand, doc: "packageName commandLine\ndo the commandLine if the named package has not been installed", clientData: $NOT]; Commander.Register[key: "IfProcExported", proc: IfProcExportedCommand, doc: "interface.procedure commandLine\ndo the commandLine if the named procedure is exported", clientData: NIL]; Commander.Register[key: "IfProcNotExported", proc: IfProcExportedCommand, doc: "interface.procedure commandLine\ndo the commandLine if the named procedure is not exported", clientData: $NOT]; Commander.Register[key: "Install", proc: InstallCommand, doc: "Install (-a|-~a|packageName)* - Ensure the installation of one or more packages. -a => install again, even if already installed", clientData: NIL]; Commander.Register[key: "Installed", proc: InstalledCommand, doc: "packageName ... - Record the named packages as having been installed\n (no args tells you what has been installed)", clientData: NIL]; Commander.Register[key: "Require", proc: RequireCommand, doc: "Usage: Require - Ensure the installation of a resource", clientData: NIL]; Commander.Register[key: "WhenInstalled", proc: WhenInstalledCommand, doc: "packageName commandLine\ndo the commandLine as soon as the named package becomes installed\n(no arguments => print the pending commands)", clientData: $NOT]; <> Installed["BasicCedar"]; Installed["CedarCore"]; Installed["Commander"]; IF IsExported["BootTime.Get"] THEN Installed["DebugNubCedarParts"]; IF IsExported["Feedback.Append"] THEN Installed["Feedback"]; IF IsExported["LocalRegistryAgent.MaintainService"] THEN Installed["LocalRegistryAgent"]; IF IsExported["LocalRegistryClient.Register"] THEN Installed["LocalRegistryClient"]; IF IsExported["PFS.FileInfo"] THEN Installed["PFS"]; IF IsExported["SunPMapClient.CallWithMultipleReplies"] THEN Installed["SunPMapClient"]; IF IsExported["SunRPC.StartCall"] THEN Installed["SunRPCRuntime"]; IF IsExported["SystemNames.UserName"] THEN Installed["SystemNames"]; END.