From 0da5273c97aa0fa1255d0be37095a1fa6d4c34e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Tue, 5 Sep 2023 12:13:06 +0200 Subject: [PATCH] Server mode for Gui&Console --- .../Configuration/MainConfiguration.cs | 8 +- .../FileTime.ConsoleUI.csproj | 1 + src/ConsoleApp/FileTime.ConsoleUI/Program.cs | 6 + .../FileTime.GuiApp/FileTime.GuiApp.csproj | 1 + .../Avalonia/FileTime.GuiApp/Program.cs | 7 ++ .../AdminElevationConfiguration.cs | 2 +- .../AdminElevationManager.cs | 77 ++++++++---- src/Server/FileTime.Server/Program.cs | 118 +++++++++--------- 8 files changed, 134 insertions(+), 86 deletions(-) diff --git a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs index 6ff16d9..759fd31 100644 --- a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs +++ b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs @@ -13,13 +13,7 @@ public class MainConfiguration static MainConfiguration() { - var serverFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? "FileTime.Server.exe" - : "FileTime.Server"; - Configuration = new() - { - {AdminElevationConfiguration.SectionName + ":" + nameof(AdminElevationConfiguration.ServerExecutablePath), serverFileName}, - }; + Configuration = new(); PopulateDefaultEditorPrograms(Configuration); PopulateDefaultKeyBindings(Configuration, DefaultKeybindings.Value, diff --git a/src/ConsoleApp/FileTime.ConsoleUI/FileTime.ConsoleUI.csproj b/src/ConsoleApp/FileTime.ConsoleUI/FileTime.ConsoleUI.csproj index 85b9c05..307f292 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI/FileTime.ConsoleUI.csproj +++ b/src/ConsoleApp/FileTime.ConsoleUI/FileTime.ConsoleUI.csproj @@ -18,6 +18,7 @@ + diff --git a/src/ConsoleApp/FileTime.ConsoleUI/Program.cs b/src/ConsoleApp/FileTime.ConsoleUI/Program.cs index a936bba..bd1337f 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI/Program.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI/Program.cs @@ -15,6 +15,12 @@ using TerminalUI.ConsoleDrivers; using ITheme = FileTime.ConsoleUI.App.Styling.ITheme; using Version = FileTime.ConsoleUI.InfoProviders.Version; +if(args.Length > 0 && args[0] == "--server") +{ + FileTime.Server.Program.Main(args.Skip(1).ToArray()); + return; +} + Console.OutputEncoding = System.Text.Encoding.UTF8; IConsoleDriver? driver = null; diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/FileTime.GuiApp.csproj b/src/GuiApp/Avalonia/FileTime.GuiApp/FileTime.GuiApp.csproj index d38c35d..c2afc2a 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/FileTime.GuiApp.csproj +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/FileTime.GuiApp.csproj @@ -47,6 +47,7 @@ + diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs index 2e523fa..2e57bb4 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading.Tasks; @@ -56,6 +57,12 @@ public static class Program [STAThread] public static async Task Main(string[] args) { + if(args.Length > 0 && args[0] == "--server") + { + Server.Program.Main(args.Skip(1).ToArray()); + return; + } + #if DEBUG (AppDataRoot, EnvironmentName) = Init.InitDevelopment(); #endif diff --git a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs index b30a069..5063d20 100644 --- a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs +++ b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs @@ -3,7 +3,7 @@ public class AdminElevationConfiguration { public const string SectionName = "AdminElevation"; - public string ServerExecutablePath { get; set; } + public string? ServerExecutablePath { get; set; } public string LinuxElevationTool { get; set; } public int? ServerPort { get; set; } public bool? StartProcess { get; set; } diff --git a/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs b/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs index 8a03fad..4321ae8 100644 --- a/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs +++ b/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs @@ -16,6 +16,7 @@ namespace FileTime.Providers.LocalAdmin; public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChanged, IExitHandler { private const string AdminContentProviderName = "localAdminRemote"; + private class ConnectionInfo { public string? SignalRBaseUrl { get; init; } @@ -60,8 +61,6 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan public async Task CreateAdminInstanceIfNecessaryAsync(string? confirmationMessage = null) { - ArgumentNullException.ThrowIfNull(_configuration.CurrentValue.ServerExecutablePath, "ServerExecutablePath"); - await _lock.WaitAsync(); try { @@ -74,11 +73,11 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan var port = _configuration.CurrentValue.ServerPort; _logger.LogTrace("Admin server port is {Port}", port is null ? "" : $"{port}"); if (StartProcess || port is null) - { + { var portFileName = Path.GetTempFileName(); File.Delete(portFileName); - - var process = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + + var process = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? CreateWindowsAdminProcess(portFileName) : CreateLinuxAdminProcess(portFileName); process.Exited += ProcessExitHandler; @@ -131,40 +130,74 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan } } - private Process CreateWindowsAdminProcess(string portFileName) - => new() + private Process CreateWindowsAdminProcess(string portFileName) + { + var (fileName, arguments) = GetServerPathAndArgs(portFileName); + var process = new Process { StartInfo = new() { - FileName = _configuration.CurrentValue.ServerExecutablePath, - ArgumentList = - { - "--PortWriter:FileName", - portFileName - }, + FileName = fileName, UseShellExecute = true, Verb = "runas" }, EnableRaisingEvents = true }; + foreach (var argument in arguments) + { + process.StartInfo.ArgumentList.Add(argument); + } + + return process; + } + private Process CreateLinuxAdminProcess(string portFileName) { - return new Process + var (fileName, arguments) = GetServerPathAndArgs(portFileName); + + arguments = arguments.Prepend(fileName); + + var process = new Process { StartInfo = new() { FileName = _configuration.CurrentValue.LinuxElevationTool, - ArgumentList = - { - _configuration.CurrentValue.ServerExecutablePath, - "--PortWriter:FileName", - portFileName - }, CreateNoWindow = true }, EnableRaisingEvents = true }; + + foreach (var argument in arguments) + { + process.StartInfo.ArgumentList.Add(argument); + } + + return process; + } + + private (string fileName, IEnumerable arguments) GetServerPathAndArgs(string portFileName) + { + var selfStart = _configuration.CurrentValue.ServerExecutablePath is null; + + var fileName = selfStart + ? Process.GetCurrentProcess().MainModule?.FileName + : _configuration.CurrentValue.ServerExecutablePath; + + if(fileName is null) throw new Exception("Could not get server executable path"); + + IEnumerable arguments = new[] + { + "--PortWriter:FileName", + portFileName + }; + + if (selfStart) + { + arguments = arguments.Prepend("--server"); + } + + return (fileName, arguments); } //Note: this does not have to return a task @@ -172,7 +205,7 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan { try { - if (_remoteContentProvider != null) return Task.FromResult((IRemoteContentProvider)_remoteContentProvider); + if (_remoteContentProvider != null) return Task.FromResult((IRemoteContentProvider) _remoteContentProvider); ArgumentNullException.ThrowIfNull(_connectionInfo); //TODO: use other connections too (if there will be any) @@ -186,7 +219,7 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan AdminContentProviderName ); - return Task.FromResult((IRemoteContentProvider)_remoteContentProvider); + return Task.FromResult((IRemoteContentProvider) _remoteContentProvider); } catch (Exception ex) { diff --git a/src/Server/FileTime.Server/Program.cs b/src/Server/FileTime.Server/Program.cs index c72c2f4..85f0bfa 100644 --- a/src/Server/FileTime.Server/Program.cs +++ b/src/Server/FileTime.Server/Program.cs @@ -2,83 +2,89 @@ using Autofac.Extensions.DependencyInjection; using FileTime.App.DependencyInjection; using FileTime.Providers.Local; -using FileTime.Server; using FileTime.Server.App; using FileTime.Server.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Serilog; +namespace FileTime.Server; -var applicationCancellation = new CancellationTokenSource(); -var configurationRoot = CreateConfiguration(); +public static class Program +{ + public static void Main(string[] args) + { + var applicationCancellation = new CancellationTokenSource(); + var configurationRoot = CreateConfiguration(); -Log.Logger = new LoggerConfiguration() + Log.Logger = new LoggerConfiguration() #if DEBUG - .MinimumLevel.Verbose() - .ReadFrom.Configuration(configurationRoot) + .MinimumLevel.Verbose() + .ReadFrom.Configuration(configurationRoot) #else .MinimumLevel.Information() #endif - .WriteTo.Console() - .CreateLogger(); + .WriteTo.Console() + .CreateLogger(); -var rootContainer = CreateRootDiContainer(configurationRoot); + var rootContainer = CreateRootDiContainer(configurationRoot); -var handlerParameters = new ConnectionHandlerParameters( - args, - rootContainer, - configurationRoot, - applicationCancellation.Token -); + var handlerParameters = new ConnectionHandlerParameters( + args, + rootContainer, + configurationRoot, + applicationCancellation.Token + ); -var webThread = CreateStartup(FileTime.Server.Web.Program.Start); -webThread.Start(); + var webThread = CreateStartup(FileTime.Server.Web.Program.Start); + webThread.Start(); -Thread CreateStartup(Func startup) -{ - var thread = new Thread(() => { HandleStartup(() => startup(handlerParameters).Wait()); }); - return thread; -} + Thread CreateStartup(Func startup) + { + var thread = new Thread(() => { HandleStartup(() => startup(handlerParameters).Wait()); }); + return thread; + } -void HandleStartup(Action action) -{ - try - { - action(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } -} + void HandleStartup(Action action) + { + try + { + action(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } -IConfigurationRoot CreateConfiguration() -{ - var configurationBuilder = new ConfigurationBuilder(); + IConfigurationRoot CreateConfiguration() + { + var configurationBuilder = new ConfigurationBuilder(); #if DEBUG - configurationBuilder.AddJsonFile("appsettings.Development.json", optional: true); - configurationBuilder.AddJsonFile("appsettings.Local.json", optional: true); + configurationBuilder.AddJsonFile("appsettings.Development.json", optional: true); + configurationBuilder.AddJsonFile("appsettings.Local.json", optional: true); #endif - configurationBuilder.AddCommandLine(args); - return configurationBuilder.Build(); -} + configurationBuilder.AddCommandLine(args); + return configurationBuilder.Build(); + } -IContainer CreateRootDiContainer(IConfigurationRoot configuration) -{ - var serviceCollection = DependencyInjection - .RegisterDefaultServices(configuration) - .AddLocalProviderServices() - .AddServerServices() - .AddServerCoreServices() - .AddLogging(loggingBuilder => loggingBuilder.AddSerilog()); + IContainer CreateRootDiContainer(IConfigurationRoot configuration) + { + var serviceCollection = DependencyInjection + .RegisterDefaultServices(configuration) + .AddLocalProviderServices() + .AddServerServices() + .AddServerCoreServices() + .AddLogging(loggingBuilder => loggingBuilder.AddSerilog()); - serviceCollection.AddSingleton( - new ApplicationStopper(() => applicationCancellation.Cancel()) - ); + serviceCollection.AddSingleton( + new ApplicationStopper(() => applicationCancellation.Cancel()) + ); - var containerBuilder = new ContainerBuilder(); - containerBuilder.Populate(serviceCollection); - return containerBuilder.Build(); + var containerBuilder = new ContainerBuilder(); + containerBuilder.Populate(serviceCollection); + return containerBuilder.Build(); + } + } } \ No newline at end of file