diff --git a/Agent/Agent.csproj b/Agent/Agent.csproj
index 9bf238e52..57945a43f 100644
--- a/Agent/Agent.csproj
+++ b/Agent/Agent.csproj
@@ -23,16 +23,19 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Agent/Program.cs b/Agent/Program.cs
index 6c6297302..c57e10da2 100644
--- a/Agent/Program.cs
+++ b/Agent/Program.cs
@@ -14,131 +14,115 @@
using Remotely.Agent.Services.Linux;
using Remotely.Agent.Services.MacOS;
using Remotely.Agent.Services.Windows;
+using Microsoft.Extensions.Hosting;
+using System.Linq;
-namespace Remotely.Agent
-{
- public class Program
- {
+namespace Remotely.Agent;
- public static IServiceProvider Services { get; set; }
+public class Program
+{
+ [Obsolete("Remove this when all services are in DI behind interfaces.")]
+ public static IServiceProvider Services { get; set; }
- public static async Task Main(string[] args)
+ public static async Task Main(string[] args)
+ {
+ try
{
- try
- {
- // TODO: Convert to generic host.
- BuildServices();
+ var host = Host
+ .CreateDefaultBuilder(args)
+ .UseWindowsService()
+ .UseSystemd()
+ .ConfigureServices(RegisterServices)
+ .Build();
- await Init();
+ await host.StartAsync();
- await Task.Delay(-1);
+ await Init(host.Services);
- }
- catch (Exception ex)
- {
- Logger.Write(ex);
- throw;
- }
+ await host.WaitForShutdownAsync();
}
-
- private static void BuildServices()
+ catch (Exception ex)
{
- var serviceCollection = new ServiceCollection();
- serviceCollection.AddHttpClient();
- serviceCollection.AddLogging(builder =>
- {
- builder.AddConsole().AddDebug();
- var version = typeof(Program).Assembly.GetName().Version?.ToString() ?? "0.0.0";
- builder.AddProvider(new FileLoggerProvider("Remotely_Agent", version));
- });
-
- // TODO: All these should be registered as interfaces.
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
- serviceCollection.AddHostedService(services => services.GetRequiredService());
- serviceCollection.AddScoped();
- serviceCollection.AddTransient();
- serviceCollection.AddTransient();
- serviceCollection.AddScoped();
- serviceCollection.AddScoped();
- serviceCollection.AddScoped();
- serviceCollection.AddScoped();
- serviceCollection.AddScoped();
-
- if (OperatingSystem.IsWindows())
- {
- serviceCollection.AddScoped();
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
- }
- else if (OperatingSystem.IsLinux())
- {
- serviceCollection.AddScoped();
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
- }
- else if (OperatingSystem.IsMacOS())
- {
- serviceCollection.AddScoped();
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
- }
- else
- {
- throw new NotSupportedException("Operating system not supported.");
- }
-
- Services = serviceCollection.BuildServiceProvider();
+ var version = typeof(Program).Assembly.GetName().Version?.ToString() ?? "0.0.0";
+ var logger = new FileLogger("Remotely_Agent", version, "Main");
+ logger.LogError(ex, "Error during agent startup.");
+ throw;
}
+ }
- private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ private static void RegisterServices(IServiceCollection services)
+ {
+ services.AddHttpClient();
+ services.AddLogging(builder =>
{
- Logger.Write(e.ExceptionObject as Exception);
+ builder.AddConsole().AddDebug();
+ var version = typeof(Program).Assembly.GetName().Version?.ToString() ?? "0.0.0";
+ builder.AddProvider(new FileLoggerProvider("Remotely_Agent", version));
+ });
+
+ // TODO: All these should be registered as interfaces.
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddHostedService(services => services.GetRequiredService());
+ services.AddScoped();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ if (OperatingSystem.IsWindows())
+ {
+ services.AddScoped();
+ services.AddSingleton();
+ services.AddSingleton();
}
-
- private static async Task Init()
+ else if (OperatingSystem.IsLinux())
{
- try
- {
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
-
- SetWorkingDirectory();
-
-
- if (OperatingSystem.IsWindows() &&
- Process.GetCurrentProcess().SessionId == 0)
- {
- _ = Task.Run(StartService);
- }
-
- await Services.GetRequiredService().BeginChecking();
-
- await Services.GetRequiredService().Connect();
- }
- catch (Exception ex)
- {
- Logger.Write(ex);
- }
+ services.AddScoped();
+ services.AddSingleton();
+ services.AddSingleton();
}
-
- private static void SetWorkingDirectory()
+ else if (OperatingSystem.IsMacOS())
{
- var assemblyPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
- var assemblyDir = Path.GetDirectoryName(assemblyPath);
- Directory.SetCurrentDirectory(assemblyDir);
+ services.AddScoped();
+ services.AddSingleton();
+ services.AddSingleton();
}
+ else
+ {
+ throw new NotSupportedException("Operating system not supported.");
+ }
+ }
- [SupportedOSPlatform("windows")]
- private static void StartService()
+ private static async Task Init(IServiceProvider services)
+ {
+ AppDomain.CurrentDomain.UnhandledException += (sender, ex) =>
{
- try
+ var logger = services.GetRequiredService>();
+ if (ex.ExceptionObject is Exception exception)
{
- ServiceBase.Run(new WindowsService());
+ logger.LogError(exception, "Unhandled exception in AppDomain.");
}
- catch (Exception ex)
+ else
{
- Logger.Write(ex, "Failed to start service.", EventType.Warning);
+ logger.LogError("Unhandled exception in AppDomain.");
}
- }
+ };
+
+ SetWorkingDirectory();
+
+ await services.GetRequiredService().BeginChecking();
+
+ await services.GetRequiredService().Connect();
+ }
+
+ private static void SetWorkingDirectory()
+ {
+ var exePath = Environment.ProcessPath ?? Environment.GetCommandLineArgs().First();
+ var exeDir = Path.GetDirectoryName(exePath);
+ Directory.SetCurrentDirectory(exeDir);
}
}
diff --git a/Agent/Services/AgentHubConnection.cs b/Agent/Services/AgentHubConnection.cs
index 4d25bdc62..ff0f1432c 100644
--- a/Agent/Services/AgentHubConnection.cs
+++ b/Agent/Services/AgentHubConnection.cs
@@ -95,7 +95,7 @@ public async Task Connect()
_hubConnection = new HubConnectionBuilder()
.WithUrl(_connectionInfo.Host + "/hubs/service")
- .WithAutomaticReconnect(new RetryPolicy())
+ .WithAutomaticReconnect(new RetryPolicy(_logger))
.AddMessagePackProtocol()
.Build();
@@ -521,6 +521,13 @@ private async Task VerifyServer()
private class RetryPolicy : IRetryPolicy
{
+ private readonly ILogger _logger;
+
+ public RetryPolicy(ILogger logger)
+ {
+ _logger = logger;
+ }
+
public TimeSpan? NextRetryDelay(RetryContext retryContext)
{
if (retryContext.PreviousRetryCount == 0)
@@ -528,7 +535,8 @@ private class RetryPolicy : IRetryPolicy
return TimeSpan.FromSeconds(3);
}
- var waitSeconds = Math.Min(30, retryContext.PreviousRetryCount * 5);
+ var waitSeconds = Random.Shared.Next(3, 10);
+ _logger.LogDebug("Attempting to reconnect in {seconds} seconds.", waitSeconds);
return TimeSpan.FromSeconds(waitSeconds);
}
}
diff --git a/Agent/Services/CpuUtilizationSampler.cs b/Agent/Services/CpuUtilizationSampler.cs
index bace9bb7f..4f2b488c8 100644
--- a/Agent/Services/CpuUtilizationSampler.cs
+++ b/Agent/Services/CpuUtilizationSampler.cs
@@ -29,6 +29,9 @@ public CpuUtilizationSampler(ILogger logger)
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
+ // Allow host startup to continue immediately.
+ await Task.Yield();
+
while (!stoppingToken.IsCancellationRequested)
{
try
diff --git a/Agent/Services/Windows/DeviceInfoGeneratorWin.cs b/Agent/Services/Windows/DeviceInfoGeneratorWin.cs
index 762b68aaf..ff2cb180e 100644
--- a/Agent/Services/Windows/DeviceInfoGeneratorWin.cs
+++ b/Agent/Services/Windows/DeviceInfoGeneratorWin.cs
@@ -29,7 +29,6 @@ public Task CreateDevice(string deviceId, string orgId)
try
{
-
var (usedStorage, totalStorage) = GetSystemDriveInfo();
var (usedMemory, totalMemory) = GetMemoryInGB();
diff --git a/Desktop.Linux/Program.cs b/Desktop.Linux/Program.cs
index 84b67e77c..17e5139aa 100644
--- a/Desktop.Linux/Program.cs
+++ b/Desktop.Linux/Program.cs
@@ -12,10 +12,11 @@
using Immense.RemoteControl.Desktop.Startup;
using Remotely.Shared.Utilities;
using Immense.RemoteControl.Desktop.Shared.Startup;
+using System.Linq;
var version = typeof(Program).Assembly.GetName().Version?.ToString() ?? "0.0.0";
var logger = new FileLogger("Remotely_Desktop", version, "Program.cs");
-var filePath = Process.GetCurrentProcess()?.MainModule?.FileName;
+var filePath = Environment.ProcessPath ?? Environment.GetCommandLineArgs().First();
var serverUrl = Debugger.IsAttached ? "http://localhost:5000" : string.Empty;
var getEmbeddedResult = await EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath);
if (getEmbeddedResult.IsSuccess)
diff --git a/Desktop.Win/Program.cs b/Desktop.Win/Program.cs
index db92b98fc..1fd67c4b5 100644
--- a/Desktop.Win/Program.cs
+++ b/Desktop.Win/Program.cs
@@ -13,10 +13,11 @@
using Remotely.Shared.Utilities;
using Immense.RemoteControl.Desktop.Windows.Startup;
using Immense.RemoteControl.Desktop.Shared.Startup;
+using System.Linq;
var version = typeof(Program).Assembly.GetName().Version?.ToString() ?? "0.0.0";
var logger = new FileLogger("Remotely_Desktop", version, "Program.cs");
-var filePath = Process.GetCurrentProcess()?.MainModule?.FileName;
+var filePath = Environment.ProcessPath ?? Environment.GetCommandLineArgs().First();
var serverUrl = Debugger.IsAttached ? "https://localhost:5001" : string.Empty;
var getEmbeddedResult = await EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath);
if (getEmbeddedResult.IsSuccess)
diff --git a/Remotely.sln.startup.json b/Remotely.sln.startup.json
index 25b0894c6..6c33f5d7e 100644
--- a/Remotely.sln.startup.json
+++ b/Remotely.sln.startup.json
@@ -64,6 +64,16 @@
"ProfileName": "Desktop.Win"
}
}
+ },
+ "Server+Agent": {
+ "Projects": {
+ "Server\\Server.csproj": {
+ "ProfileName": "Server"
+ },
+ "Agent": {
+ "ProfileName": "Agent"
+ }
+ }
}
}
}
diff --git a/Server/API/RemoteControlController.cs b/Server/API/RemoteControlController.cs
index ac17ad025..a93dcb04a 100644
--- a/Server/API/RemoteControlController.cs
+++ b/Server/API/RemoteControlController.cs
@@ -128,7 +128,7 @@ private async Task InitiateRemoteControl(string deviceID, string
{
UnattendedSessionId = sessionId,
UserConnectionId = HttpContext.Connection.Id,
- ServiceConnectionId = serviceConnectionId,
+ AgentConnectionId = serviceConnectionId,
DeviceId = deviceID,
OrganizationId = orgID
};
@@ -137,7 +137,7 @@ private async Task InitiateRemoteControl(string deviceID, string
{
if (v is RemoteControlSessionEx ex)
{
- ex.ServiceConnectionId = HttpContext.Connection.Id;
+ ex.AgentConnectionId = HttpContext.Connection.Id;
return ex;
}
v.Dispose();
diff --git a/Server/Hubs/CircuitConnection.cs b/Server/Hubs/CircuitConnection.cs
index e79202bb4..4e0ff3dfe 100644
--- a/Server/Hubs/CircuitConnection.cs
+++ b/Server/Hubs/CircuitConnection.cs
@@ -256,7 +256,7 @@ public async Task> RemoteControl(string deviceId,
{
UnattendedSessionId = sessionId,
UserConnectionId = ConnectionId,
- ServiceConnectionId = serviceConnectionId,
+ AgentConnectionId = serviceConnectionId,
DeviceId = deviceId,
ViewOnly = viewOnly,
OrganizationId = User.OrganizationID
diff --git a/Server/Models/RemoteControlSessionEx.cs b/Server/Models/RemoteControlSessionEx.cs
index 18188391e..689fd7193 100644
--- a/Server/Models/RemoteControlSessionEx.cs
+++ b/Server/Models/RemoteControlSessionEx.cs
@@ -5,10 +5,7 @@ namespace Remotely.Server.Models
{
public class RemoteControlSessionEx : RemoteControlSession
{
- public string ServiceConnectionId { get; set; } = string.Empty;
- public string UserConnectionId { get; set; } = string.Empty;
public string DeviceId { get; set; } = string.Empty;
- public bool ViewOnly { get; set; }
public string OrganizationId { get; set; } = string.Empty;
}
}
diff --git a/Server/Services/RcImplementations/HubEventHandler.cs b/Server/Services/RcImplementations/HubEventHandler.cs
index bc30ce985..c99141275 100644
--- a/Server/Services/RcImplementations/HubEventHandler.cs
+++ b/Server/Services/RcImplementations/HubEventHandler.cs
@@ -39,7 +39,7 @@ public Task ChangeWindowsSession(RemoteControlSession session, string viewerConn
}
return _serviceHub.Clients
- .Client(ex.ServiceConnectionId)
+ .Client(ex.AgentConnectionId)
.SendAsync("ChangeWindowsSession",
viewerConnectionId,
ex.UnattendedSessionId,
@@ -59,7 +59,7 @@ public Task InvokeCtrlAltDel(RemoteControlSession session, string viewerConnecti
return Task.CompletedTask;
}
- return _serviceHub.Clients.Client(ex.ServiceConnectionId).SendAsync("CtrlAltDel");
+ return _serviceHub.Clients.Client(ex.AgentConnectionId).SendAsync("CtrlAltDel");
}
public Task NotifySessionChanged(RemoteControlSession session, SessionSwitchReasonEx reason, int currentSessionId)
@@ -79,7 +79,7 @@ public Task NotifySessionChanged(RemoteControlSession session, SessionSwitchReas
case SessionSwitchReasonEx.SessionLock:
case SessionSwitchReasonEx.SessionRemoteControl:
return _serviceHub.Clients
- .Client(ex.ServiceConnectionId)
+ .Client(ex.AgentConnectionId)
.SendAsync("RestartScreenCaster",
ex.ViewerList,
ex.UnattendedSessionId,
@@ -108,13 +108,13 @@ public Task RestartScreenCaster(RemoteControlSession session, HashSet vi
}
return _serviceHub.Clients
- .Client(ex.ServiceConnectionId)
+ .Client(ex.AgentConnectionId)
.SendAsync("RestartScreenCaster",
viewerList,
ex.UnattendedSessionId,
ex.AccessKey,
ex.UserConnectionId,
- ex.RequesterUserName,
+ ex.RequesterName,
ex.OrganizationName,
ex.OrganizationId);
}
diff --git a/Shared/Services/FileLogger.cs b/Shared/Services/FileLogger.cs
index c207221d0..01d5542d4 100644
--- a/Shared/Services/FileLogger.cs
+++ b/Shared/Services/FileLogger.cs
@@ -59,7 +59,8 @@ private static string LogsFolderPath
}
private string LogPath => Path.Combine(LogsFolderPath, _componentName, $"LogFile_{DateTime.Now:yyyy-MM-dd}.log");
- public IDisposable BeginScope(TState state)
+ public IDisposable? BeginScope(TState state)
+ where TState : notnull
{
_scopeStack.Push($"{state}");
return new NoopDisposable();
diff --git a/submodules/Immense.RemoteControl b/submodules/Immense.RemoteControl
index 39d4f853e..aaa2b1585 160000
--- a/submodules/Immense.RemoteControl
+++ b/submodules/Immense.RemoteControl
@@ -1 +1 @@
-Subproject commit 39d4f853eaa2a87590fb3a56e35db244f6eae08e
+Subproject commit aaa2b1585d62ab87911c8396060e1a445687c74e