From 880180f16a5d62a97b2deef688bba4945761b4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Wed, 30 Aug 2023 01:04:05 +0200 Subject: [PATCH] Linux admin mode --- .vscode/settings.json | 3 + .../Configuration/MainConfiguration.cs | 6 +- .../MainConsoleConfiguration.cs | 11 ++- .../Configuration/MainGuiConfiguration.cs | 18 +++++ .../FileTime.GuiApp.App.csproj | 1 + .../Avalonia/FileTime.GuiApp/Startup.cs | 2 + .../AdminElevationConfiguration.cs | 1 + .../AdminElevationManager.cs | 67 ++++++++++++++----- .../FileTime.Server.Web/PortWriterService.cs | 17 ++++- 9 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/GuiApp/Avalonia/FileTime.GuiApp.App/Configuration/MainGuiConfiguration.cs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6e8912a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "src/FileTime.sln" +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs index 0359082..6ff16d9 100644 --- a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs +++ b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using FileTime.App.Core.UserCommand; using FileTime.Providers.LocalAdmin; using GeneralInputKey; @@ -12,9 +13,12 @@ public class MainConfiguration static MainConfiguration() { + var serverFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? "FileTime.Server.exe" + : "FileTime.Server"; Configuration = new() { - {AdminElevationConfiguration.SectionName + ":" + nameof(AdminElevationConfiguration.ServerExecutablePath), "FileTime.Server.exe"}, + {AdminElevationConfiguration.SectionName + ":" + nameof(AdminElevationConfiguration.ServerExecutablePath), serverFileName}, }; PopulateDefaultEditorPrograms(Configuration); diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App/MainConsoleConfiguration.cs b/src/ConsoleApp/FileTime.ConsoleUI.App/MainConsoleConfiguration.cs index 7aed740..ade5a0c 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App/MainConsoleConfiguration.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App/MainConsoleConfiguration.cs @@ -1,10 +1,17 @@ -namespace FileTime.ConsoleUI.App; +using FileTime.Providers.LocalAdmin; + +namespace FileTime.ConsoleUI.App; public class MainConsoleConfiguration { public static Dictionary Configuration { get; } static MainConsoleConfiguration() { - Configuration = new(); + Configuration = new() + { + { + AdminElevationConfiguration.SectionName + ":" + nameof(AdminElevationConfiguration.LinuxElevationTool), "sudo" + }, + }; } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Configuration/MainGuiConfiguration.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Configuration/MainGuiConfiguration.cs new file mode 100644 index 0000000..6531a9e --- /dev/null +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Configuration/MainGuiConfiguration.cs @@ -0,0 +1,18 @@ +using FileTime.Providers.LocalAdmin; + +namespace FileTime.GuiApp.App.Configuration; + +public class MainGuiConfiguration +{ + + public static Dictionary Configuration { get; } + static MainGuiConfiguration() + { + Configuration = new() + { + { + AdminElevationConfiguration.SectionName + ":" + nameof(AdminElevationConfiguration.LinuxElevationTool), "pkexec" + }, + }; + } +} \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj b/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj index 64b65ef..e676efb 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj @@ -55,6 +55,7 @@ + diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs index 38ed5b8..8000839 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs @@ -5,6 +5,7 @@ using FileTime.App.Core.Configuration; using FileTime.App.Core.Services; using FileTime.App.Core.ViewModels; using FileTime.Core.Interactions; +using FileTime.GuiApp.App.Configuration; using FileTime.GuiApp.CustomImpl.ViewModels; using FileTime.GuiApp.App.IconProviders; using FileTime.GuiApp.App.InstanceManagement; @@ -26,6 +27,7 @@ public static class Startup { var configurationBuilder = new ConfigurationBuilder() .AddInMemoryCollection(MainConfiguration.Configuration) + .AddInMemoryCollection(MainGuiConfiguration.Configuration) .AddJsonFile("appsettings.json", optional: true) .AddJsonFile($"appsettings.{Program.EnvironmentName}.json", true) .AddJsonFile("appsettings.Local.json", optional: true); diff --git a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs index 4844c29..b30a069 100644 --- a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs +++ b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/AdminElevationConfiguration.cs @@ -4,6 +4,7 @@ public class AdminElevationConfiguration { public const string SectionName = "AdminElevation"; public string ServerExecutablePath { get; set; } + public string LinuxElevationTool { get; set; } public int? ServerPort { get; set; } public bool? StartProcess { get; set; } } \ No newline at end of file diff --git a/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs b/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs index a3f83d0..8a03fad 100644 --- a/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs +++ b/src/Providers/FileTime.Providers.LocalAdmin/AdminElevationManager.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using FileTime.App.Core.Services; using FileTime.Core.Interactions; using FileTime.Core.Timeline; @@ -73,30 +74,28 @@ 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(); - var process = new Process - { - StartInfo = new() - { - FileName = _configuration.CurrentValue.ServerExecutablePath, - ArgumentList = - { - "--PortWriter:FileName", - portFileName - }, - UseShellExecute = true, - Verb = "runas" - }, - EnableRaisingEvents = true - }; + File.Delete(portFileName); + + var process = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? CreateWindowsAdminProcess(portFileName) + : CreateLinuxAdminProcess(portFileName); process.Exited += ProcessExitHandler; process.Start(); _adminProcess = process; //TODO: timeout while (!File.Exists(portFileName) || new FileInfo(portFileName).Length == 0) + { await Task.Delay(10); + if (process.HasExited) + { + throw new Exception( + $"Server process exited with code {process.ExitCode} without creating the port file" + ); + } + } var content = await File.ReadAllLinesAsync(portFileName); if (int.TryParse(content.FirstOrDefault(), out var parsedPort)) @@ -132,6 +131,42 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan } } + private Process CreateWindowsAdminProcess(string portFileName) + => new() + { + StartInfo = new() + { + FileName = _configuration.CurrentValue.ServerExecutablePath, + ArgumentList = + { + "--PortWriter:FileName", + portFileName + }, + UseShellExecute = true, + Verb = "runas" + }, + EnableRaisingEvents = true + }; + + private Process CreateLinuxAdminProcess(string portFileName) + { + return new Process + { + StartInfo = new() + { + FileName = _configuration.CurrentValue.LinuxElevationTool, + ArgumentList = + { + _configuration.CurrentValue.ServerExecutablePath, + "--PortWriter:FileName", + portFileName + }, + CreateNoWindow = true + }, + EnableRaisingEvents = true + }; + } + //Note: this does not have to return a task public Task GetRemoteContentProviderAsync() { diff --git a/src/Server/FileTime.Server.Web/PortWriterService.cs b/src/Server/FileTime.Server.Web/PortWriterService.cs index 609dd9e..bfdc88e 100644 --- a/src/Server/FileTime.Server.Web/PortWriterService.cs +++ b/src/Server/FileTime.Server.Web/PortWriterService.cs @@ -44,7 +44,6 @@ public class PortWriterService : IHostedService return; } - using var tempFileStream = File.CreateText(filename); var address = GetAddress(); if (address is null) { @@ -59,7 +58,23 @@ public class PortWriterService : IHostedService return; } + _logger.LogInformation("Writing port to {PortFile}", filename); + using var tempFileStream = File.CreateText(filename); tempFileStream.Write(port.ToString()); + + Task.Run(async () => + { + await Task.Delay(5000); + try + { + _logger.LogInformation("Deleting port file {PortFile}", filename); + File.Delete(filename); + } + catch (Exception e) + { + _logger.LogError(e, "Error while deleting port file {PortFile}", filename); + } + }); } catch (Exception ex) {