public override IEnumerator Run() { return(Cmds.Get <CmdAhornRunJuliaTask>().Run(@" env = ENV[""AHORN_ENV""] logfilePath = joinpath(dirname(env), ""log-install-ahorn.txt"") println(""Logging to "" * logfilePath) logfile = open(logfilePath, ""w"") flush(stdout) flush(stderr) stdoutReal = stdout (rd, wr) = redirect_stdout() redirect_stderr(stdout) @async while !eof(rd) data = String(readavailable(rd)) print(stdoutReal, data) flush(stdoutReal) print(logfile, data) flush(logfile) end if VERSION < v""1.3"" println(""Outdated version of Julia - $VERSION installed, 1.3+ needed."") exit(1) end using Logging logger = SimpleLogger(stdout, Logging.Debug) loggerPrev = Logging.global_logger(logger) using Pkg Pkg.activate(ENV[""AHORN_ENV""]) install_or_update(url::String, pkg::String) = if ""Ahorn"" ∈ keys(Pkg.Types.Context().env.project.deps) println(""Updating $pkg..."") Pkg.update(pkg) else println(""Adding $pkg..."") Pkg.add(PackageSpec(url = url)) end try println(""#OLYMPUS# TIMEOUT START"") Pkg.instantiate() install_or_update(""https://github.com/CelestialCartographers/Maple.git"", ""Maple"") install_or_update(""https://github.com/CelestialCartographers/Ahorn.git"", ""Ahorn"") Pkg.instantiate() Pkg.API.precompile() import Ahorn println(""#OLYMPUS# TIMEOUT END"") catch e println(""FATAL ERROR"") println(sprint(showerror, e, catch_backtrace())) exit(1) end exit(0) ", null)); }
public static void Main(string[] args) { bool debug = false; bool verbose = false; for (int i = 1; i < args.Length; i++) { string arg = args[i]; if (arg == "--debug") { debug = true; } else if (arg == "--verbose") { verbose = true; } else if (arg == "--console") { AllocConsole(); } } CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; SelfPath = Assembly.GetExecutingAssembly().Location; RootDirectory = Path.GetDirectoryName(Environment.CurrentDirectory); ConfigDirectory = Environment.GetEnvironmentVariable("OLYMPUS_CONFIG"); if (string.IsNullOrEmpty(ConfigDirectory) || !Directory.Exists(ConfigDirectory)) { ConfigDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Olympus"); } Console.Error.WriteLine(RootDirectory); if (Type.GetType("Mono.Runtime") != null) { // Mono hates HTTPS. ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { return(true); }; } // Enable TLS 1.2 to fix connecting to GitHub. ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; if (args.Length >= 1 && args[0] == "--uninstall" && PlatformHelper.Is(Platform.Windows)) { new CmdWin32AppUninstall().Run(args.Length >= 2 && args[1] == "--quiet"); return; } Process parentProc = null; int parentProcID = 0; TcpListener listener = new TcpListener(IPAddress.Loopback, 0); List <Thread> threads = new List <Thread>(); listener.Start(); Console.WriteLine($"{((IPEndPoint) listener.LocalEndpoint).Port}"); try { parentProc = Process.GetProcessById(parentProcID = int.Parse(args.Last())); } catch { Console.Error.WriteLine("[sharp] Invalid parent process ID"); } if (debug) { Debugger.Launch(); Console.WriteLine(@"""debug"""); } else { Console.WriteLine(@"""ok"""); } Console.WriteLine(@"null"); Console.Out.Flush(); if (parentProc != null) { Thread killswitch = new Thread(() => { try { while (!parentProc.HasExited && parentProc.Id == parentProcID) { Thread.Yield(); Thread.Sleep(1000); } Environment.Exit(0); } catch { Environment.Exit(-1); } }) { Name = "Killswitch", IsBackground = true }; killswitch.Start(); } Cmds.Init(); try { while ((parentProc != null && !parentProc.HasExited && parentProc.Id == parentProcID) || parentProc == null) { TcpClient client = listener.AcceptTcpClient(); try { string ep = client.Client.RemoteEndPoint.ToString(); Console.Error.WriteLine($"[sharp] New TCP connection: {ep}"); client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000); Stream stream = client.GetStream(); MessageContext ctx = new MessageContext(); lock (Cache) foreach (Message msg in Cache.Values) { ctx.Reply(msg); } Thread threadW = new Thread(() => { try { using (StreamWriter writer = new StreamWriter(stream, UTF8NoBOM)) WriteLoop(parentProc, ctx, writer, verbose); } catch (Exception e) { if (e is ObjectDisposedException) { Console.Error.WriteLine($"[sharp] Failed writing to {ep}: {e.GetType()}: {e.Message}"); } else { Console.Error.WriteLine($"[sharp] Failed writing to {ep}: {e}"); } client.Close(); } finally { ctx.Dispose(); } }) { Name = $"Write Thread for Connection {ep}", IsBackground = true }; Thread threadR = new Thread(() => { try { using (StreamReader reader = new StreamReader(stream, UTF8NoBOM)) ReadLoop(parentProc, ctx, reader, verbose); } catch (Exception e) { if (e is ObjectDisposedException) { Console.Error.WriteLine($"[sharp] Failed reading from {ep}: {e.GetType()}: {e.Message}"); } else { Console.Error.WriteLine($"[sharp] Failed reading from {ep}: {e}"); } client.Close(); } finally { ctx.Dispose(); } }) { Name = $"Read Thread for Connection {ep}", IsBackground = true }; threads.Add(threadW); threads.Add(threadR); threadW.Start(); threadR.Start(); } catch (ThreadAbortException) { } catch (Exception e) { Console.Error.WriteLine($"[sharp] Failed listening for TCP connection:\n{e}"); client.Close(); } } } catch (ThreadAbortException) { } catch (Exception e) { Console.Error.WriteLine($"[sharp] Failed listening for TCP connection:\n{e}"); } Console.Error.WriteLine("[sharp] Goodbye"); }
public override IEnumerator Run(string root, string artifactBase) { // MiniInstaller reads orig/Celeste.exe and copies Celeste.exe into it but only if missing. // Olympus can help out and delete the orig folder if the existing Celeste.exe isn't modded. string installedVersion = Cmds.Get <CmdGetVersionString>().Run(root); if (installedVersion.StartsWith("Celeste ") && !installedVersion.Contains("Everest")) { string orig = Path.Combine(root, "orig"); if (Directory.Exists(orig)) { yield return(Status("Deleting previous backup", false, "", false)); Directory.Delete(orig, true); } } if (artifactBase.StartsWith("file://")) { artifactBase = artifactBase.Substring("file://".Length); yield return(Status($"Unzipping {Path.GetFileName(artifactBase)}", false, "download", false)); using (FileStream wrapStream = File.Open(artifactBase, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) using (ZipArchive wrap = new ZipArchive(wrapStream, ZipArchiveMode.Read)) { ZipArchiveEntry zipEntry = wrap.GetEntry("olympus-build/build.zip"); if (zipEntry == null) { yield return(Unpack(wrap, root, "main/")); } else { using (Stream zipStream = zipEntry.Open()) using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Read)) yield return(Unpack(zip, root)); } } } else { // Only new builds offer olympus-meta and olympus-build artifacts. yield return(Status("Downloading metadata", false, "", false)); int size; try { byte[] zipData; using (WebClient wc = new WebClient()) zipData = wc.DownloadData(artifactBase + "olympus-meta"); using (MemoryStream zipStream = new MemoryStream(zipData)) using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Read)) { using (Stream sizeStream = zip.GetEntry("olympus-meta/size.txt").Open()) using (StreamReader sizeReader = new StreamReader(sizeStream)) size = int.Parse(sizeReader.ReadToEnd().Trim()); } } catch (Exception) { size = 0; } if (size > 0) { yield return(Status("Downloading olympus-build.zip", false, "download", false)); using (MemoryStream wrapStream = new MemoryStream()) { yield return(Download(artifactBase + "olympus-build", size, wrapStream)); yield return(Status("Unzipping olympus-build.zip", false, "download", false)); wrapStream.Seek(0, SeekOrigin.Begin); using (ZipArchive wrap = new ZipArchive(wrapStream, ZipArchiveMode.Read)) { using (Stream zipStream = wrap.GetEntry("olympus-build/build.zip").Open()) using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Read)) { yield return(Unpack(zip, root)); } } } } else { yield return(Status("Downloading main.zip", false, "download", false)); using (MemoryStream zipStream = new MemoryStream()) { yield return(Download(artifactBase + "main", size, zipStream)); yield return(Status("Unzipping main.zip", false, "download", false)); zipStream.Seek(0, SeekOrigin.Begin); using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Read)) { yield return(Unpack(zip, root, "main/")); } } } } yield return(Status("Starting MiniInstaller", false, "monomod", false)); yield return(Install(root)); }
public static void ReadLoop(Process parentProc, MessageContext ctx, StreamReader reader, bool verbose, char delimiter = '\0') { // JsonTextReader would be neat here but Newtonsoft.Json is unaware of NetworkStreams and tries to READ PAST STRINGS while (!(parentProc?.HasExited ?? false)) { // Commands from Olympus come in pairs of two objects: if (verbose) { Console.Error.WriteLine("[sharp] Awaiting next command"); } // Unique ID string uid = JsonConvert.DeserializeObject <string>(reader.ReadTerminatedString(delimiter)); if (verbose) { Console.Error.WriteLine($"[sharp] Receiving command {uid}"); } // Command ID string cid = JsonConvert.DeserializeObject <string>(reader.ReadTerminatedString(delimiter)).ToLowerInvariant(); if (cid == "_ack") { reader.ReadTerminatedString(delimiter); if (verbose) { Console.Error.WriteLine($"[sharp] Ack'd command {uid}"); } lock (Cache) if (Cache.ContainsKey(uid)) { Cache.Remove(uid); } else { Console.Error.WriteLine($"[sharp] Ack'd command that was already ack'd {uid}"); } continue; } if (cid == "_stop") { // Let's hope that everyone knows how to handle this. Environment.Exit(0); continue; } Message msg = new Message() { UID = uid, CID = cid }; Cmd cmd = Cmds.Get(cid); if (cmd == null) { reader.ReadTerminatedString(delimiter); Console.Error.WriteLine($"[sharp] Unknown command {cid}"); msg.Error = "cmd failed running: not found: " + cid; ctx.Reply(msg); continue; } if (verbose) { Console.Error.WriteLine($"[sharp] Parsing args for {cid}"); } // Payload object input = JsonConvert.DeserializeObject(reader.ReadTerminatedString(delimiter), cmd.InputType); object output; try { if (verbose || cmd.LogRun) { Console.Error.WriteLine($"[sharp] Executing {cid}"); } if (cmd.Taskable) { output = Task.Run(() => cmd.Run(input)); } else { output = cmd.Run(input); } } catch (Exception e) { Console.Error.WriteLine($"[sharp] Failed running {cid}: {e}"); msg.Error = "cmd failed running: " + e; ctx.Reply(msg); continue; } if (output is Task <object> task) { task.ContinueWith(t => { if (task.Exception != null) { Exception e = task.Exception; Console.Error.WriteLine($"[sharp] Failed running task {cid}: {e}"); msg.Error = "cmd task failed running: " + e; ctx.Reply(msg); return; } msg.Value = t.Result; lock (Cache) Cache[uid] = msg; ctx.Reply(msg); }); } else { msg.Value = output; lock (Cache) Cache[uid] = msg; ctx.Reply(msg); } } }
public override IEnumerator Run() { yield return(Status("Preparing installation of Ahorn-VHD", false, "", false)); if (!PlatformHelper.Is(Platform.Windows | Platform.Bits64)) { yield return(Status("Unsupported platform.", 1f, "error", false)); throw new PlatformNotSupportedException($"Platform not supported: {PlatformHelper.Current}"); } string vhd = AhornHelper.VHDPath; if (File.Exists(vhd)) { yield return(Status("Ahorn-VHD already exists - please delete it before reinstalling.", 1f, "error", false)); throw new Exception("Ahorn-VHD already exists."); } string archive = vhd + ".7z"; bool tmpPerm = File.Exists(archive); try { if (tmpPerm) { yield return(Status($"{Path.GetFileName(archive)} already exists - reusing and preserving", false, "", false)); } else { const string url = "https://0x0ade.ga/ahornvhd/files/ahornvhd.7z"; yield return(Status($"Downloading {url} to {archive}", false, "download", false)); string tmp = archive + ".part"; if (File.Exists(tmp)) { File.Delete(tmp); } using (FileStream stream = File.Open(tmp, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) yield return(Download(url, 0, stream)); File.Move(tmp, archive); } string sevenzip = Path.Combine(Program.RootDirectory, "7zr.exe"); if (!File.Exists(sevenzip)) { const string url = "https://raw.githubusercontent.com/EverestAPI/Olympus/main/lib-windows/7zr.exe"; yield return(Status($"Couldn't find 7zr.exe, downloading from {url}", false, "download", false)); using (FileStream stream = File.Open(sevenzip, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) yield return(Download(url, 0, stream)); } yield return(Status("Extracting ahornvhd.7z", false, "download", false)); using (Process process = ProcessHelper.Wrap(sevenzip, $"x \"{archive}\" \"-o{Path.GetDirectoryName(vhd)}\" {Path.GetFileName(vhd)} -bb3")) { process.Start(); for (string line; (line = process.StandardOutput.ReadLine()) != null;) { yield return(Status(line, false, "download", false)); } process.WaitForExit(); if (process.ExitCode != 0) { throw new Exception("7zr encountered a fatal error:\n" + process.StandardError.ReadToEnd()); } if (!File.Exists(vhd)) { throw new Exception($"File not extracted: {vhd}"); } yield return(Status("ahornvhd.7z extracted", false, "download", false)); } } finally { if (!tmpPerm && File.Exists(archive)) { File.Delete(archive); } } yield return(Cmds.Get <CmdAhornMountAhornVHD>().Run()); yield return(Cmds.Get <CmdAhornInstallAhorn>().Run()); }