diff --git a/GRILO.Bootloader/BootApps/BootLoadContext.cs b/GRILO.Bootloader/BootApps/BootLoadContext.cs new file mode 100644 index 0000000..20232a8 --- /dev/null +++ b/GRILO.Bootloader/BootApps/BootLoadContext.cs @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2022 Aptivi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if NETCOREAPP +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace GRILO.Bootloader.BootApps +{ + public class BootLoadContext : AssemblyLoadContext + { + internal AssemblyDependencyResolver resolver; + internal string bootPath = ""; + + protected override Assembly Load(AssemblyName assemblyName) + { + // Check to see if we have this assembly in the default context + var finalAsm = Default.Assemblies.FirstOrDefault((asm) => asm.FullName == assemblyName.FullName); + if (finalAsm is not null) + return finalAsm; + + // Now, try to resolve + string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath != null) + return LoadFromAssemblyPath(assemblyPath); + return null; + } + + protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + { + string libraryPath = resolver.ResolveUnmanagedDllToPath(unmanagedDllName); + if (libraryPath != null) + return LoadUnmanagedDllFromPath(libraryPath); + return IntPtr.Zero; + } + } +} +#endif diff --git a/GRILO.Bootloader/BootApps/BootLoader.cs b/GRILO.Bootloader/BootApps/BootLoader.cs index fe13ec6..56a2cee 100644 --- a/GRILO.Bootloader/BootApps/BootLoader.cs +++ b/GRILO.Bootloader/BootApps/BootLoader.cs @@ -33,9 +33,9 @@ namespace GRILO.Bootloader.BootApps { public class BootLoader : MarshalByRefObject { - internal byte[] bytes = Array.Empty(); - internal string lookupPath = ""; - internal bool shutting = false; + public byte[] bytes = Array.Empty(); + public string lookupPath = ""; + public bool shutting = false; public void ProxyExecuteBootable(params string[] args) { diff --git a/GRILO.Bootloader/BootApps/BootManager.cs b/GRILO.Bootloader/BootApps/BootManager.cs index 0482d23..b144c73 100644 --- a/GRILO.Bootloader/BootApps/BootManager.cs +++ b/GRILO.Bootloader/BootApps/BootManager.cs @@ -32,6 +32,7 @@ using GRILO.Bootloader.Diagnostics; using GRILO.Bootloader.Configuration; using Terminaux.Writer.ConsoleWriters; +using System.Reflection; #if NET6_0_OR_GREATER using System.Runtime.Loader; @@ -48,11 +49,6 @@ public static class BootManager { { "Shutdown the system", new BootAppInfo("", "", Array.Empty(), new Shutdown()) } }; -#if NET6_0_OR_GREATER - private static readonly List loads = new(); -#else - private static readonly List loads = new(); -#endif /// /// Adds all bootable applications to the bootloader @@ -68,20 +64,49 @@ public static void PopulateBootApps() { // Get the boot ID string bootId = Path.GetFileName(bootDir); + + // Create boot context #if NET6_0_OR_GREATER - var bootContext = new AssemblyLoadContext($"Boot context for {bootId}", true); + var bootContext = new BootLoadContext + { + bootPath = bootDir + }; #else AppDomain ad2 = AppDomain.CreateDomain($"Boot context for {bootId}", null, bootId, bootId, false); var assemblyLoader = (BootLoader)ad2.CreateInstanceFromAndUnwrap(typeof(BootLoader).Assembly.CodeBase, typeof(BootLoader).FullName); #endif + + // Now, check the metadata to get a bootable file path + string metadataFile = Path.Combine(GRILOPaths.GRILOBootablesPath, bootId, "BootMetadata.json"); + List paths = new(); + DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Info, "Trying to find metadata file {0} to get bootable file path...", metadataFile); + + // Now, check for metadata existence + if (File.Exists(metadataFile)) + { + // Metadata file exists! Now, parse it. + DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Info, "Metadata found! Parsing JSON..."); + var metadataToken = JArray.Parse(File.ReadAllText(metadataFile)); + + // Enumerate through metadata array + foreach (var metadata in metadataToken) + { + string path = metadata["BootFilePath"]?.ToString() is not null ? Path.Combine(GRILOPaths.GRILOBootablesPath, bootId, metadata["BootFilePath"]?.ToString()) : ""; + if (!paths.Contains(path) && !string.IsNullOrEmpty(path)) + paths.Add(path); + DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Info, "Boot path {0}.", path); + } + } + + // Process boot files DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Info, "Boot ID: {0}", bootId); try { // Using the boot ID, check for executable files #if NETCOREAPP - var bootFiles = Directory.EnumerateFiles(bootDir, "*.dll"); + var bootFiles = paths.Count > 0 ? paths : Directory.EnumerateFiles(bootDir, "*.dll"); #else - var bootFiles = Directory.EnumerateFiles(bootDir, "*.exe"); + var bootFiles = paths.Count > 0 ? paths : Directory.EnumerateFiles(bootDir, "*.exe"); #endif foreach (var bootFile in bootFiles) { @@ -97,6 +122,7 @@ public static void PopulateBootApps() BootAppInfo bootApp; DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Info, "Loading assembly..."); #if NET6_0_OR_GREATER + bootContext.resolver = new AssemblyDependencyResolver(bootFile); var asm = bootContext.LoadFromAssemblyPath(bootFile); IBootable bootable = null; @@ -117,7 +143,6 @@ public static void PopulateBootApps() if (bootable != null) { // We found it! Now, populate info from the metadata file - string metadataFile = Path.Combine(GRILOPaths.GRILOBootablesPath, bootId, "BootMetadata.json"); DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Info, "Trying to find metadata file {0}...", metadataFile); // Let's put some variables here @@ -141,7 +166,6 @@ public static void PopulateBootApps() bootArgs = metadata["Arguments"]?.ToObject() ?? Array.Empty(); bootApp = new(bootFile, bootOverrideTitle, bootArgs, bootable); bootApps.Add(bootOverrideTitle, bootApp); - loads.Add(bootContext); } } else @@ -163,7 +187,6 @@ public static void PopulateBootApps() if (assemblyLoader.VerifyBoot(File.ReadAllBytes(bootFile))) { // We found it! Now, populate info from the metadata file - string metadataFile = Path.Combine(GRILOPaths.GRILOBootablesPath, bootId, "BootMetadata.json"); DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Info, "Trying to find metadata file {0}...", metadataFile); // Let's put some variables here @@ -189,7 +212,6 @@ public static void PopulateBootApps() bootArgs = metadata["Arguments"]?.ToObject() ?? Array.Empty(); bootApp = new(bootFile, bootOverrideTitle, bootArgs, proxy); bootApps.Add(bootOverrideTitle, bootApp); - loads.Add(assemblyLoader); } } else @@ -218,16 +240,14 @@ public static void PopulateBootApps() DiagnosticsWriter.WriteDiag(DiagnosticsLevel.Error, "Stack trace:\n{0}", ex.StackTrace); TextWriterColor.Write($"Unknown error when parsing boot ID {bootId}: {ex.Message}"); } -#if NET6_0_OR_GREATER - loads.Add(bootContext); -#endif } } /// /// Gets boot applications /// - public static Dictionary GetBootApps() => new(bootApps); + public static Dictionary GetBootApps() => + new(bootApps); /// /// Gets boot application by name diff --git a/GRILO.Bootloader/GRILOPaths.cs b/GRILO.Bootloader/GRILOPaths.cs index 8fd3865..ef85a8c 100644 --- a/GRILO.Bootloader/GRILOPaths.cs +++ b/GRILO.Bootloader/GRILOPaths.cs @@ -77,10 +77,17 @@ public static string GRILOStylesPath { public static string GRILOBootablesPath { get { +#if NETCOREAPP if (GRILOPlatform.IsOnWindows()) return Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA"), "GRILO/Bootables"); else return Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".config/GRILO/Bootables"); +#else + if (GRILOPlatform.IsOnWindows()) + return Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA"), "GRILO/Bootables_DotNetFx"); + else + return Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".config/GRILO/Bootables_DotNetFx"); +#endif } }