Refactor to align with changes in submodule.

This commit is contained in:
Jared Goodwin 2023-04-18 14:40:32 -07:00
parent 19805a81b1
commit c0b10ef93d
34 changed files with 336 additions and 342 deletions

View File

@ -24,9 +24,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="6.0.9" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.9" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.6" />

View File

@ -12,6 +12,9 @@ using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Versioning;
using Remotely.Agent.Services.Linux;
using Remotely.Agent.Services.MacOS;
using Remotely.Agent.Services.Windows;
namespace Remotely.Agent
{
@ -24,6 +27,7 @@ namespace Remotely.Agent
{
try
{
// TODO: Convert to generic host.
BuildServices();
await Init();
@ -45,7 +49,8 @@ namespace Remotely.Agent
serviceCollection.AddLogging(builder =>
{
builder.AddConsole().AddDebug();
builder.AddProvider(new FileLoggerProvider("Remotely_Agent"));
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.
@ -59,23 +64,23 @@ namespace Remotely.Agent
serviceCollection.AddScoped<IProcessInvoker, ProcessInvoker>();
serviceCollection.AddScoped<IUpdateDownloader, UpdateDownloader>();
if (EnvironmentHelper.IsWindows)
if (OperatingSystem.IsWindows())
{
serviceCollection.AddScoped<IAppLauncher, AppLauncherWin>();
serviceCollection.AddSingleton<IUpdater, UpdaterWin>();
serviceCollection.AddSingleton<IDeviceInformationService, DeviceInformationServiceWin>();
serviceCollection.AddSingleton<IDeviceInformationService, DeviceInfoGeneratorWin>();
}
else if (EnvironmentHelper.IsLinux)
else if (OperatingSystem.IsLinux())
{
serviceCollection.AddScoped<IAppLauncher, AppLauncherLinux>();
serviceCollection.AddSingleton<IUpdater, UpdaterLinux>();
serviceCollection.AddSingleton<IDeviceInformationService, DeviceInformationServiceLinux>();
serviceCollection.AddSingleton<IDeviceInformationService, DeviceInfoGeneratorLinux>();
}
else if (EnvironmentHelper.IsMac)
else if (OperatingSystem.IsMacOS())
{
serviceCollection.AddScoped<IAppLauncher, AppLauncherMac>();
serviceCollection.AddSingleton<IUpdater, UpdaterMac>();
serviceCollection.AddSingleton<IDeviceInformationService, DeviceInformationServiceMac>();
serviceCollection.AddSingleton<IDeviceInformationService, DeviceInfoGeneratorMac>();
}
else
{

View File

@ -107,6 +107,7 @@ namespace Remotely.Agent.Services
_logger.LogInformation("Connected to server.");
// TODO: Move CPU sampler to background service.
var device = await _deviceInfoService.CreateDevice(_connectionInfo.DeviceID, _connectionInfo.OrganizationID);
var result = await _hubConnection.InvokeAsync<bool>("DeviceCameOnline", device);
@ -132,6 +133,7 @@ namespace Remotely.Agent.Services
continue;
}
// TODO: Move to background service.
_heartbeatTimer?.Dispose();
_heartbeatTimer = new Timer(TimeSpan.FromMinutes(5).TotalMilliseconds);
_heartbeatTimer.Elapsed += HeartbeatTimer_Elapsed;

View File

@ -0,0 +1,88 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Remotely.Agent.Services;
internal interface ICpuUtilizationSampler : IHostedService
{
double CurrentUtilization { get; }
}
internal class CpuUtilizationSampler : BackgroundService, ICpuUtilizationSampler
{
private double _currentUtilization;
public double CurrentUtilization => _currentUtilization;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var currentUtil = await GetCpuUtilization(stoppingToken);
Interlocked.Exchange(ref _currentUtilization, currentUtil);
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
private static async Task<double> GetCpuUtilization(CancellationToken cancelToken)
{
double totalUtilization = 0;
var utilizations = new Dictionary<int, Tuple<DateTimeOffset, TimeSpan>>();
var processes = Process.GetProcesses();
foreach (var proc in processes)
{
if (cancelToken.IsCancellationRequested)
{
return 0;
}
try
{
var startTime = DateTimeOffset.Now;
var startCpuUsage = proc.TotalProcessorTime;
utilizations.Add(proc.Id, new Tuple<DateTimeOffset, TimeSpan>(startTime, startCpuUsage));
}
catch
{
continue;
}
}
await Task.Delay(1_000, cancelToken);
foreach (var kvp in utilizations)
{
if (cancelToken.IsCancellationRequested)
{
return 0;
}
var endTime = DateTimeOffset.Now;
try
{
var proc = Process.GetProcessById(kvp.Key);
var startTime = kvp.Value.Item1;
var startCpuUsage = kvp.Value.Item2;
var endCpuUsage = proc.TotalProcessorTime;
var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds;
var totalMsPassed = (endTime - startTime).TotalMilliseconds;
var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed);
totalUtilization += cpuUsageTotal;
}
catch
{
continue;
}
}
return totalUtilization;
}
}

View File

@ -12,7 +12,7 @@ using System.Threading.Tasks;
namespace Remotely.Agent.Services
{
public class DeviceInformationServiceBase
public class DeviceInfoGeneratorBase
{
public Device GetDeviceBase(string deviceID, string orgID)
{

View File

@ -13,7 +13,7 @@ using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Web.Services.Description;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.Linux
{
public class AppLauncherLinux : IAppLauncher
@ -24,7 +24,7 @@ namespace Remotely.Agent.Services
private readonly ILogger<AppLauncherLinux> _logger;
public AppLauncherLinux(
ConfigService configService,
ConfigService configService,
IProcessInvoker processInvoker,
ILogger<AppLauncherLinux> logger)
{
@ -157,7 +157,7 @@ namespace Remotely.Agent.Services
// Start Desktop app.
await hubConnection.SendAsync("DisplayMessage", "Starting remote control.", "Starting remote control.", "bg-success", userConnectionId);
var args =
var args =
_rcBinaryPath +
$" --mode Unattended" +
$" --host {_connectionInfo.Host}" +

View File

@ -8,13 +8,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.Linux
{
public class DeviceInformationServiceLinux : DeviceInformationServiceBase, IDeviceInformationService
public class DeviceInfoGeneratorLinux : DeviceInfoGeneratorBase, IDeviceInformationService
{
private readonly IProcessInvoker _processInvoker;
public DeviceInformationServiceLinux(IProcessInvoker processInvoker)
public DeviceInfoGeneratorLinux(IProcessInvoker processInvoker)
{
_processInvoker = processInvoker;
}
@ -75,8 +75,8 @@ namespace Remotely.Agent.Services
.Split(' ')
.First(); // 16637468
var freeGB = Math.Round((double.Parse(freeKB) / 1024 / 1024), 2);
var totalGB = Math.Round((double.Parse(totalKB) / 1024 / 1024), 2);
var freeGB = Math.Round(double.Parse(freeKB) / 1024 / 1024, 2);
var totalGB = Math.Round(double.Parse(totalKB) / 1024 / 1024, 2);
return (totalGB - freeGB, totalGB);
}

View File

@ -14,9 +14,9 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.Linux
{
public class UpdaterLinux : IUpdater
{
private readonly SemaphoreSlim _checkForUpdatesLock = new(1, 1);
@ -27,10 +27,10 @@ namespace Remotely.Agent.Services
private readonly SemaphoreSlim _installLatestVersionLock = new(1, 1);
private readonly System.Timers.Timer _updateTimer = new(TimeSpan.FromHours(6).TotalMilliseconds);
private DateTimeOffset _lastUpdateFailure;
public UpdaterLinux(
ConfigService configService,
IUpdateDownloader updateDownloader,
ConfigService configService,
IUpdateDownloader updateDownloader,
IHttpClientFactory httpClientFactory,
ILogger<UpdaterLinux> logger)
{

View File

@ -3,7 +3,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using Remotely.Agent.Interfaces;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.MacOS
{
public class AppLauncherMac : IAppLauncher
{

View File

@ -9,13 +9,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.MacOS
{
public class DeviceInformationServiceMac : DeviceInformationServiceBase, IDeviceInformationService
public class DeviceInfoGeneratorMac : DeviceInfoGeneratorBase, IDeviceInformationService
{
private readonly IProcessInvoker _processInvoker;
public DeviceInformationServiceMac(IProcessInvoker processInvoker)
public DeviceInfoGeneratorMac(IProcessInvoker processInvoker)
{
_processInvoker = processInvoker;
}

View File

@ -15,7 +15,7 @@ using System.Threading;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.MacOS
{
public class UpdaterMac : IUpdater
{
@ -30,8 +30,8 @@ namespace Remotely.Agent.Services
private readonly System.Timers.Timer _updateTimer = new(TimeSpan.FromHours(6).TotalMilliseconds);
public UpdaterMac(
ConfigService configService,
IUpdateDownloader updateDownloader,
ConfigService configService,
IUpdateDownloader updateDownloader,
IHttpClientFactory httpClientFactory,
ILogger<UpdaterMac> logger)
{

View File

@ -13,7 +13,7 @@ using System.Security.Cryptography;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.Windows
{
[SupportedOSPlatform("windows")]
public class AppLauncherWin : IAppLauncher
@ -57,9 +57,9 @@ namespace Remotely.Agent.Services
out var procInfo);
if (!result)
{
await hubConnection.SendAsync("DisplayMessage",
"Chat service failed to start on target device.",
"Failed to start chat service.",
await hubConnection.SendAsync("DisplayMessage",
"Chat service failed to start on target device.",
"Failed to start chat service.",
"bg-danger",
userConnectionId);
}
@ -82,7 +82,7 @@ namespace Remotely.Agent.Services
catch (Exception ex)
{
_logger.LogError(ex, "Error while launching chat.");
await hubConnection.SendAsync("DisplayMessage",
await hubConnection.SendAsync("DisplayMessage",
"Chat service failed to start on target device.",
"Failed to start chat service.",
"bg-danger",
@ -97,9 +97,9 @@ namespace Remotely.Agent.Services
{
if (!File.Exists(_rcBinaryPath))
{
await hubConnection.SendAsync("DisplayMessage",
"Remote control executable not found on target device.",
"Executable not found on device.",
await hubConnection.SendAsync("DisplayMessage",
"Remote control executable not found on target device.",
"Executable not found on device.",
"bg-danger",
userConnectionId);
return;
@ -107,7 +107,7 @@ namespace Remotely.Agent.Services
// Start Desktop app.
await hubConnection.SendAsync("DisplayMessage",
await hubConnection.SendAsync("DisplayMessage",
"Starting remote control.",
"Starting remote control.",
"bg-success",
@ -130,7 +130,7 @@ namespace Remotely.Agent.Services
out _);
if (!result)
{
await hubConnection.SendAsync("DisplayMessage",
await hubConnection.SendAsync("DisplayMessage",
"Remote control failed to start on target device.",
"Failed to start remote control.",
"bg-danger",
@ -152,8 +152,8 @@ namespace Remotely.Agent.Services
catch (Exception ex)
{
_logger.LogError(ex, "Error while launching remote control.");
await hubConnection.SendAsync("DisplayMessage",
"Remote control failed to start on target device.",
await hubConnection.SendAsync("DisplayMessage",
"Remote control failed to start on target device.",
"Failed to start remote control.",
"bg-danger",
userConnectionId);
@ -170,7 +170,7 @@ namespace Remotely.Agent.Services
// Give a little time for session changing, etc.
await Task.Delay(1000);
var result = Win32Interop.OpenInteractiveProcess(_rcBinaryPath +
var result = Win32Interop.OpenInteractiveProcess(_rcBinaryPath +
$" --mode Unattended" +
$" --relaunch true" +
$" --host {_connectionInfo.Host}" +
@ -191,7 +191,7 @@ namespace Remotely.Agent.Services
{
_logger.LogWarning("Failed to relaunch screen caster.");
await hubConnection.SendAsync("SendConnectionFailedToViewers", viewerIDs);
await hubConnection.SendAsync("DisplayMessage",
await hubConnection.SendAsync("DisplayMessage",
"Remote control failed to start on target device.",
"Failed to start remote control.",
"bg-danger",

View File

@ -8,9 +8,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.Windows
{
public class DeviceInformationServiceWin : DeviceInformationServiceBase, IDeviceInformationService
public class DeviceInfoGeneratorWin : DeviceInfoGeneratorBase, IDeviceInformationService
{
public async Task<Device> CreateDevice(string deviceId, string orgId)
{
@ -49,8 +49,8 @@ namespace Remotely.Agent.Services
if (Kernel32.GlobalMemoryStatusEx(memoryStatus))
{
freeGB = Math.Round(((double)memoryStatus.ullAvailPhys / 1024 / 1024 / 1024), 2);
totalGB = Math.Round(((double)memoryStatus.ullTotalPhys / 1024 / 1024 / 1024), 2);
freeGB = Math.Round((double)memoryStatus.ullAvailPhys / 1024 / 1024 / 1024, 2);
totalGB = Math.Round((double)memoryStatus.ullTotalPhys / 1024 / 1024 / 1024, 2);
}
return (totalGB - freeGB, totalGB);

View File

@ -10,7 +10,7 @@ using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
namespace Remotely.Agent.Services.Windows
{
public class UpdaterWin : IUpdater
{
@ -25,8 +25,8 @@ namespace Remotely.Agent.Services
public UpdaterWin(
ConfigService configService,
IUpdateDownloader updateDownloader,
ConfigService configService,
IUpdateDownloader updateDownloader,
IHttpClientFactory httpClientFactory,
ILogger<UpdaterWin> logger)
{

View File

@ -2,19 +2,18 @@
using System.Threading.Tasks;
using System.Threading;
using System;
using Immense.RemoteControl.Desktop.Linux;
using Remotely.Desktop.Shared.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Remotely.Shared.Services;
using Immense.RemoteControl.Desktop.Shared.Services;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Immense.RemoteControl.Desktop.Shared.Enums;
using Immense.RemoteControl.Desktop.UI.Services;
using Remotely.Shared;
using System.Diagnostics;
using Immense.RemoteControl.Desktop.Linux.Startup;
using Remotely.Shared.Utilities;
var logger = new FileLogger("Remotely_Desktop", "Program.cs");
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 serverUrl = Debugger.IsAttached ? "http://localhost:5000" : string.Empty;
var getEmbeddedResult = await EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath);
@ -27,37 +26,41 @@ else
logger.LogWarning(getEmbeddedResult.Exception, "Failed to extract embedded server data.");
}
var provider = await Startup.UseRemoteControlClient(
args,
var services = new ServiceCollection();
services.AddSingleton<IOrganizationIdProvider, OrganizationIdProvider>();
services.AddSingleton<IEmbeddedServerDataSearcher, EmbeddedServerDataSearcher>();
services.AddRemoteControlLinux(
config =>
{
config.AddBrandingProvider<BrandingProvider>();
},
services =>
{
services.AddLogging(builder =>
{
#if DEBUG
builder.SetMinimumLevel(LogLevel.Debug);
#endif
builder.AddProvider(new FileLoggerProvider("Remotely_Desktop"));
});
});
services.AddSingleton<IOrganizationIdProvider, OrganizationIdProvider>();
services.AddSingleton<IEmbeddedServerDataSearcher, EmbeddedServerDataSearcher>();
},
services =>
services.AddLogging(builder =>
{
if (EnvironmentHelper.IsDebug)
{
var appState = services.GetRequiredService<IAppState>();
if (appState.ArgDict.TryGetValue("org-id", out var orgId))
{
var orgIdProvider = services.GetRequiredService<IOrganizationIdProvider>();
orgIdProvider.OrganizationId = orgId;
}
return Task.CompletedTask;
},
serverUrl);
builder.SetMinimumLevel(LogLevel.Debug);
}
builder.AddProvider(new FileLoggerProvider("Remotely_Desktop", version));
});
var provider = services.BuildServiceProvider();
var appState = provider.GetRequiredService<IAppState>();
var orgIdProvider = provider.GetRequiredService<IOrganizationIdProvider>();
if (getEmbeddedResult.IsSuccess)
{
orgIdProvider.OrganizationId = getEmbeddedResult.Value.OrganizationId;
appState.Host = getEmbeddedResult.Value.ServerUrl.AbsoluteUri;
}
if (appState.ArgDict.TryGetValue("org-id", out var orgId))
{
orgIdProvider.OrganizationId = orgId;
}
Console.WriteLine("Press Ctrl + C to exit.");

View File

@ -1,5 +1,6 @@
using Immense.RemoteControl.Desktop.Shared.Abstractions;
using Immense.RemoteControl.Desktop.Shared.Services;
using Immense.RemoteControl.Shared;
using Immense.RemoteControl.Shared.Models;
using Microsoft.Extensions.Logging;
using Remotely.Shared;
@ -89,7 +90,9 @@ namespace Remotely.Desktop.Shared.Services
if (!result.IsSuccess)
{
return Result.Fail<BrandingInfo>(result.Exception);
return result.HadException ?
Result.Fail<BrandingInfo>(result.Exception) :
Result.Fail<BrandingInfo>(result.Reason);
}
if (!string.IsNullOrWhiteSpace(result.Value.OrganizationId))

View File

@ -10,8 +10,11 @@ using Microsoft.Extensions.Logging;
using Remotely.Shared.Services;
using Immense.RemoteControl.Desktop.Shared.Services;
using System.Diagnostics;
using Remotely.Shared.Utilities;
using Immense.RemoteControl.Desktop.Windows.Startup;
var logger = new FileLogger("Remotely_Desktop", "Program.cs");
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 serverUrl = Debugger.IsAttached ? "https://localhost:5001" : string.Empty;
var getEmbeddedResult = await EmbeddedServerDataSearcher.Instance.TryGetEmbeddedData(filePath);
@ -24,45 +27,49 @@ else
logger.LogWarning(getEmbeddedResult.Exception, "Failed to extract embedded server data.");
}
var provider = await Startup.UseRemoteControlClient(
args,
var services = new ServiceCollection();
services.AddSingleton<IOrganizationIdProvider, OrganizationIdProvider>();
services.AddSingleton<IEmbeddedServerDataSearcher>(EmbeddedServerDataSearcher.Instance);
services.AddRemoteControlWindows(
config =>
{
config.AddBrandingProvider<BrandingProvider>();
},
services =>
});
services.AddLogging(builder =>
{
if (EnvironmentHelper.IsDebug)
{
services.AddLogging(builder =>
{
#if DEBUG
builder.SetMinimumLevel(LogLevel.Debug);
#endif
builder.AddProvider(new FileLoggerProvider("Remotely_Desktop"));
});
builder.SetMinimumLevel(LogLevel.Debug);
}
builder.AddProvider(new FileLoggerProvider("Remotely_Desktop", version));
});
services.AddSingleton<IOrganizationIdProvider, OrganizationIdProvider>();
services.AddSingleton<IEmbeddedServerDataSearcher>(EmbeddedServerDataSearcher.Instance);
},
services =>
{
var appState = services.GetRequiredService<IAppState>();
var orgIdProvider = services.GetRequiredService<IOrganizationIdProvider>();
var provider = services.BuildServiceProvider();
if (getEmbeddedResult.IsSuccess)
{
orgIdProvider.OrganizationId = getEmbeddedResult.Value.OrganizationId;
appState.Host = getEmbeddedResult.Value.ServerUrl.AbsoluteUri;
}
var appState = provider.GetRequiredService<IAppState>();
var orgIdProvider = provider.GetRequiredService<IOrganizationIdProvider>();
if (appState.ArgDict.TryGetValue("org-id", out var orgId))
{
orgIdProvider.OrganizationId = orgId;
}
if (getEmbeddedResult.IsSuccess)
{
orgIdProvider.OrganizationId = getEmbeddedResult.Value.OrganizationId;
appState.Host = getEmbeddedResult.Value.ServerUrl.AbsoluteUri;
}
return Task.CompletedTask;
},
serverUrl);
if (appState.ArgDict.TryGetValue("org-id", out var orgId))
{
orgIdProvider.OrganizationId = orgId;
}
var result = await provider.UseRemoteControlClientWindows(args, serverUrl);
if (!result.IsSuccess)
{
logger.LogError(result.Exception, "Failed to remote control client.");
Environment.Exit(1);
}
var dispatcher = provider.GetRequiredService<IWindowsUiDispatcher>();

View File

@ -26,7 +26,7 @@ namespace Remotely.Server.API
{ ExpirationScanFrequency = TimeSpan.FromSeconds(10) });
private readonly IHubContext<ServiceHub> _agentHubContext;
private readonly IHubContext<AgentHub> _agentHubContext;
private readonly IApplicationConfig _appConfig;
@ -40,7 +40,7 @@ namespace Remotely.Server.API
IDataService dataService,
IApplicationConfig appConfig,
IServiceHubSessionCache serviceSessionCache,
IHubContext<ServiceHub> agentHubContext)
IHubContext<AgentHub> agentHubContext)
{
_hostEnv = hostingEnv;
_dataService = dataService;

View File

@ -24,7 +24,7 @@ namespace Remotely.Server.API
[ApiController]
public class RemoteControlController : ControllerBase
{
private readonly IHubContext<ServiceHub> _serviceHub;
private readonly IHubContext<AgentHub> _serviceHub;
private readonly IDesktopHubSessionCache _desktopSessionCache;
private readonly IServiceHubSessionCache _serviceSessionCache;
private readonly IApplicationConfig _appConfig;
@ -37,7 +37,7 @@ namespace Remotely.Server.API
SignInManager<RemotelyUser> signInManager,
IDataService dataService,
IDesktopHubSessionCache desktopSessionCache,
IHubContext<ServiceHub> serviceHub,
IHubContext<AgentHub> serviceHub,
IServiceHubSessionCache serviceSessionCache,
IOtpProvider otpProvider,
IHubEventHandlerEx hubEvents,

View File

@ -22,7 +22,7 @@ namespace Remotely.Server.API
[Route("api/[controller]")]
public class ScriptingController : ControllerBase
{
private readonly IHubContext<ServiceHub> _agentHubContext;
private readonly IHubContext<AgentHub> _agentHubContext;
private readonly IDataService _dataService;
private readonly IServiceHubSessionCache _serviceSessionCache;
@ -34,7 +34,7 @@ namespace Remotely.Server.API
IDataService dataService,
IServiceHubSessionCache serviceSessionCache,
IExpiringTokenService expiringTokenService,
IHubContext<ServiceHub> agentHub)
IHubContext<AgentHub> agentHub)
{
_dataService = dataService;
_serviceSessionCache = serviceSessionCache;
@ -91,15 +91,16 @@ namespace Remotely.Server.API
var requestID = Guid.NewGuid().ToString();
var authToken = _expiringTokenService.GetToken(Time.Now.AddMinutes(AppConstants.ScriptRunExpirationMinutes));
// TODO: Replace with new invoke capability in .NET 7.
await _agentHubContext.Clients.Client(connectionId).SendAsync("ExecuteCommandFromApi", shell, authToken, requestID, command, User?.Identity?.Name);
var success = await WaitHelper.WaitForAsync(() => ServiceHub.ApiScriptResults.TryGetValue(requestID, out _), TimeSpan.FromSeconds(30));
var success = await WaitHelper.WaitForAsync(() => AgentHub.ApiScriptResults.TryGetValue(requestID, out _), TimeSpan.FromSeconds(30));
if (!success)
{
return NotFound();
}
ServiceHub.ApiScriptResults.TryGetValue(requestID, out var commandID);
ServiceHub.ApiScriptResults.Remove(requestID);
AgentHub.ApiScriptResults.TryGetValue(requestID, out var commandID);
AgentHub.ApiScriptResults.Remove(requestID);
var result = _dataService.GetScriptResult(commandID.ToString(), orgID);
return result;
}

View File

@ -17,16 +17,16 @@ using System.Threading.Tasks;
namespace Remotely.Server.Hubs
{
public class ServiceHub : Hub
public class AgentHub : Hub
{
private readonly IApplicationConfig _appConfig;
private readonly ICircuitManager _circuitManager;
private readonly IExpiringTokenService _expiringTokenService;
private readonly IDataService _dataService;
private readonly IExpiringTokenService _expiringTokenService;
private readonly IServiceHubSessionCache _serviceSessionCache;
private readonly IHubContext<ViewerHub> _viewerHubContext;
public ServiceHub(IDataService dataService,
public AgentHub(IDataService dataService,
IApplicationConfig appConfig,
IServiceHubSessionCache serviceSessionCache,
IHubContext<ViewerHub> viewerHubContext,
@ -41,7 +41,7 @@ namespace Remotely.Server.Hubs
_expiringTokenService = expiringTokenService;
}
// TODO: Move to service behind interface.
// TODO: Replace with new invoke capability in .NET 7 in ScriptingController.
public static IMemoryCache ApiScriptResults { get; } = new MemoryCache(new MemoryCacheOptions());
private Device Device
@ -93,17 +93,6 @@ namespace Remotely.Server.Hubs
return Task.FromResult(false);
}
//if (_serviceSessionCache.Sessions.Any(x => x.Value == device.ID))
//{
// _dataService.WriteEvent(new EventLog()
// {
// EventType = EventType.Info,
// OrganizationID = device.OrganizationID,
// Message = $"Device connection for {device?.DeviceName} was denied because it is already connected."
// });
// return Task.FromResult(false);
//}
var ip = Context.GetHttpContext()?.Connection?.RemoteIpAddress;
if (ip != null && ip.IsIPv4MappedToIPv6)
{
@ -209,6 +198,16 @@ namespace Remotely.Server.Hubs
return _circuitManager.InvokeOnConnection(requesterID, CircuitEventName.DownloadFileProgress, progressPercent);
}
public string GetServerUrl()
{
return _appConfig.ServerUrl;
}
public string GetServerVerificationToken()
{
return Device.ServerVerificationToken;
}
public override Task OnDisconnectedAsync(Exception exception)
{
try
@ -266,21 +265,11 @@ namespace Remotely.Server.Hubs
{
return _circuitManager.InvokeOnConnection(requesterConnectionId, CircuitEventName.RemoteLogsReceived, logChunk);
}
public string GetServerVerificationToken()
{
return Device.ServerVerificationToken;
}
public void SetServerVerificationToken(string verificationToken)
{
Device.ServerVerificationToken = verificationToken;
_dataService.SetServerVerificationToken(Device.ID, verificationToken);
}
public string GetServerUrl()
{
return _appConfig.ServerUrl;
}
public Task TransferCompleted(string transferID, string requesterID)
{
return _circuitManager.InvokeOnConnection(requesterID, CircuitEventName.TransferCompleted, transferID);

View File

@ -58,7 +58,7 @@ namespace Remotely.Server.Hubs
public class CircuitConnection : CircuitHandler, ICircuitConnection
{
private readonly IHubContext<ServiceHub> _agentHubContext;
private readonly IHubContext<AgentHub> _agentHubContext;
private readonly IApplicationConfig _appConfig;
private readonly IClientAppState _appState;
private readonly IAuthService _authService;
@ -74,7 +74,7 @@ namespace Remotely.Server.Hubs
IAuthService authService,
IDataService dataService,
IClientAppState appState,
IHubContext<ServiceHub> agentHubContext,
IHubContext<AgentHub> agentHubContext,
IApplicationConfig appConfig,
ICircuitManager circuitManager,
IToastService toastService,

View File

@ -142,7 +142,7 @@ namespace Remotely.Server.Pages
[Inject]
private IHubContext<ServiceHub> AgentHubContext { get; set; }
private IHubContext<AgentHub> AgentHubContext { get; set; }
[Inject]
private IConfiguration Configuration { get; set; }

View File

@ -257,7 +257,7 @@ app.UseRemoteControlServer();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ServiceHub>("/hubs/service");
endpoints.MapHub<AgentHub>("/hubs/service");
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");

View File

@ -27,12 +27,12 @@ namespace Remotely.Server.Services.RcImplementations
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _sessionWaitHandlers = new();
private readonly ICircuitManager _circuitManager;
private readonly IHubContext<ServiceHub> _serviceHub;
private readonly IHubContext<AgentHub> _serviceHub;
private readonly ILogger<HubEventHandlerEx> _logger;
public HubEventHandlerEx(
ICircuitManager circuitManager,
IHubContext<ServiceHub> serviceHub,
IHubContext<AgentHub> serviceHub,
ILogger<HubEventHandlerEx> logger)
{
_circuitManager = circuitManager;

View File

@ -37,10 +37,10 @@ namespace Remotely.Server.Services
return _currentVersion;
}
var fileVersion = System.Diagnostics.FileVersionInfo.GetVersionInfo("Remotely_Server.dll").FileVersion;
if (Version.TryParse(fileVersion, out var result))
var asmVersion = typeof(UpgradeService).Assembly.GetName().Version;
if (asmVersion is not null)
{
_currentVersion = result;
_currentVersion = asmVersion;
return _currentVersion;
}
}

View File

@ -1,93 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Remotely.Shared
{
public class Result
{
public static Result<T> Empty<T>()
{
return new Result<T>(true, default);
}
public static Result Fail(string error)
{
return new Result(false, error);
}
public static Result Fail(Exception ex)
{
return new Result(false, null, ex);
}
public static Result<T> Fail<T>(string error)
{
return new Result<T>(false, default, error);
}
public static Result<T> Fail<T>(Exception ex)
{
return new Result<T>(false, default, exception: ex);
}
public static Result Ok()
{
return new Result(true);
}
public static Result<T> Ok<T>(T value)
{
return new Result<T>(true, value, null);
}
public Result(bool isSuccess, string error = null, Exception exception = null)
{
IsSuccess = isSuccess;
Error = error;
Exception = exception;
if (string.IsNullOrWhiteSpace(Error) && !string.IsNullOrWhiteSpace(Exception?.Message))
{
Error = Exception.Message;
}
Exception ??= new Exception(Error ?? string.Empty);
}
public bool IsSuccess { get; init; }
public string Error { get; init; } = string.Empty;
public Exception Exception { get; init; }
}
public class Result<T>
{
public Result(bool isSuccess, T value, string error = null, Exception exception = null)
{
IsSuccess = isSuccess;
Value = value;
Error = error;
Exception = exception;
if (string.IsNullOrWhiteSpace(Error) && !string.IsNullOrWhiteSpace(Exception?.Message))
{
Error = Exception.Message;
}
Exception ??= new Exception(Error ?? string.Empty);
}
public bool IsSuccess { get; init; }
public string Error { get; init; } = string.Empty;
public Exception Exception { get; init; }
public T Value { get; init; }
}
}

View File

@ -1,5 +1,6 @@
#nullable enable
using Immense.RemoteControl.Shared;
using Microsoft.Extensions.Logging;
using Remotely.Shared.Models;
using Remotely.Shared.Utilities;

View File

@ -1,12 +1,11 @@
using Microsoft.Extensions.Logging;
using Remotely.Shared.Extensions;
#nullable enable
using Microsoft.Extensions.Logging;
using Remotely.Shared.Utilities;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@ -14,42 +13,51 @@ namespace Remotely.Shared.Services
{
public class FileLogger : ILogger
{
private static readonly ConcurrentQueue<string> _logQueue = new();
private static readonly ConcurrentStack<string> _scopeStack = new();
private static readonly SemaphoreSlim _writeLock = new(1, 1);
private static string _logDir;
private readonly string _applicationName;
private readonly string _categoryName;
private readonly System.Timers.Timer _sinkTimer = new(5000) { AutoReset = false };
private readonly string _componentName;
private readonly string _componentVersion;
private DateTimeOffset _lastLogCleanup;
public FileLogger(string applicationName, string categoryName)
public FileLogger(string componentName, string componentVersion, string categoryName)
{
_applicationName = applicationName?.SanitizeFileName() ?? string.Empty;
_componentName = componentName;
_componentVersion = componentVersion;
_categoryName = categoryName;
_sinkTimer.Elapsed += SinkTimer_Elapsed;
}
private string LogDir
private static string LogsFolderPath
{
get
{
if (!string.IsNullOrWhiteSpace(_logDir))
{
return _logDir;
}
if (OperatingSystem.IsWindows())
{
_logDir = Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Remotely", "Logs")).FullName;
var logsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Remotely",
"Logs");
if (EnvironmentHelper.IsDebug)
{
logsPath += "_Debug";
}
return logsPath;
}
else
if (OperatingSystem.IsLinux())
{
_logDir = Directory.CreateDirectory("/var/log/remotely").FullName;
if (EnvironmentHelper.IsDebug)
{
return "/var/log/remotely_debug";
}
return "/var/log/remotely";
}
return _logDir;
throw new PlatformNotSupportedException();
}
}
private string LogPath => Path.Combine(LogDir, $"LogFile_{_applicationName}_{DateTime.Now:yyyy-MM-dd}.log");
private string LogPath => Path.Combine(LogsFolderPath, _componentName, $"LogFile_{DateTime.Now:yyyy-MM-dd}.log");
public IDisposable BeginScope<TState>(TState state)
{
@ -85,21 +93,25 @@ namespace Remotely.Shared.Services
};
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
_writeLock.Wait();
try
{
var scopeStack = _scopeStack.Any() ?
new string[] { _scopeStack.First(), _scopeStack.Last() } :
Array.Empty<string>();
var message = FormatLogEntry(logLevel, _categoryName, $"{state}", exception, scopeStack);
_logQueue.Enqueue(message);
_sinkTimer.Start();
var message = FormatLogEntry(logLevel, _categoryName, $"{state}", exception, _scopeStack.ToArray());
CheckLogFileExists();
File.AppendAllText(LogPath, message);
CleanupLogs();
}
catch (Exception ex)
{
Debug.WriteLine($"Error queueing log entry: {ex.Message}");
Console.WriteLine($"Error writing log entry: {ex.Message}");
}
finally
{
_writeLock.Release();
}
}
@ -126,42 +138,38 @@ namespace Remotely.Shared.Services
private void CheckLogFileExists()
{
_ = Directory.CreateDirectory(Path.GetDirectoryName(LogPath)!);
Directory.CreateDirectory(Path.GetDirectoryName(LogPath)!);
if (!File.Exists(LogPath))
{
File.Create(LogPath).Close();
}
}
private void CleanupLogs()
{
if (DateTimeOffset.Now - _lastLogCleanup < TimeSpan.FromDays(1))
{
return;
}
_lastLogCleanup = DateTimeOffset.Now;
var logFiles = Directory.GetFiles(Path.GetDirectoryName(LogPath)!)
.Select(x => new FileInfo(x))
.Where(x => DateTime.Now - x.CreationTime > TimeSpan.FromDays(7));
foreach (var file in logFiles)
{
try
{
if (OperatingSystem.IsWindows())
{
Process.Start("cmd", $"/c icacls \"{LogPath}\" /grant Users:M").WaitForExit(1_000);
}
else if (OperatingSystem.IsLinux())
{
Process.Start("sudo", $"chmod 775 {LogPath}").WaitForExit(1_000);
}
file.Delete();
}
catch (Exception ex)
{
Debug.WriteLine($"Error modifying log file permissions: {ex.Message}");
}
}
if (File.Exists(LogPath))
{
var fi = new FileInfo(LogPath);
while (fi.Length > 1_000_000)
{
var content = File.ReadAllLines(LogPath);
File.WriteAllLines(LogPath, content.Skip(10));
fi = new FileInfo(LogPath);
Console.WriteLine($"Error while trying to delete log file {file.FullName}. Message: {ex.Message}");
}
}
}
private string FormatLogEntry(LogLevel logLevel, string categoryName, string state, Exception exception, string[] scopeStack)
private string FormatLogEntry(LogLevel logLevel, string categoryName, string state, Exception? exception, string[] scopeStack)
{
var ex = exception;
var exMessage = exception?.Message;
@ -174,13 +182,16 @@ namespace Remotely.Shared.Services
var entry =
$"[{logLevel}]\t" +
$"{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss.fff}\t";
$"[v{_componentVersion}]\t" +
$"[Process ID: {Environment.ProcessId}]\t" +
$"[Thread ID: {Environment.CurrentManagedThreadId}]\t" +
$"[{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss.fff}]\t";
entry += scopeStack.Any() ?
$"[{string.Join(" - ", scopeStack)} - {categoryName}]\t" :
$"[{categoryName} => {string.Join(" => ", scopeStack)}]\t" :
$"[{categoryName}]\t";
entry += $"Message: {state}\t";
entry += $"{state}\t";
if (!string.IsNullOrWhiteSpace(exMessage))
{
@ -197,32 +208,6 @@ namespace Remotely.Shared.Services
return entry;
}
private async void SinkTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
await _writeLock.WaitAsync();
CheckLogFileExists();
var message = string.Empty;
while (_logQueue.TryDequeue(out var entry))
{
message += entry;
}
File.AppendAllText(LogPath, message);
}
catch (Exception ex)
{
Debug.WriteLine($"Error writing log entry: {ex.Message}");
}
finally
{
_writeLock.Release();
}
}
private class NoopDisposable : IDisposable
{
public void Dispose()

View File

@ -5,16 +5,18 @@ namespace Remotely.Shared.Services
{
public class FileLoggerProvider : ILoggerProvider
{
private readonly string _applicationName;
private readonly string _componentName;
private readonly string _componentVersion;
public FileLoggerProvider(string applicationName)
public FileLoggerProvider(string componentName, string componentVersion)
{
_applicationName = applicationName;
_componentName = componentName;
_componentVersion = componentVersion;
}
public ILogger CreateLogger(string categoryName)
{
return new FileLogger(_applicationName, categoryName);
return new FileLogger(_componentName, _componentVersion, categoryName);
}
public void Dispose()

View File

@ -4,6 +4,7 @@ using System.Diagnostics;
namespace Remotely.Shared.Utilities
{
// TODO: Make instanced and put behind interface.
public static class EnvironmentHelper
{
public static string AgentExecutableFileName

View File

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.SignalR.Client;
using Remotely.Agent.Services;
using Remotely.Agent.Services.Windows;
using System;
using System.Collections.Generic;
using System.Threading;
@ -14,11 +15,11 @@ namespace Remotely.Tests.LoadTester
private static int _agentCount;
private static string _organizationId;
private static string _serverurl;
private static DeviceInformationServiceWin _deviceInfo;
private static DeviceInfoGeneratorWin _deviceInfo;
private static void Main(string[] args)
{
_deviceInfo = new DeviceInformationServiceWin();
_deviceInfo = new DeviceInfoGeneratorWin();
ConnectAgents();
Console.Write("Press Enter to exit...");

View File

@ -42,7 +42,7 @@ namespace Remotely.Tests
appConfig.Setup(x => x.BannedDevices).Returns(new string[] { _testData.Device1.DeviceName });
var hub = new ServiceHub(DataService, appConfig.Object, serviceSessionCache.Object, viewerHub.Object, circuitManager.Object, expiringTokenService.Object);
var hub = new AgentHub(DataService, appConfig.Object, serviceSessionCache.Object, viewerHub.Object, circuitManager.Object, expiringTokenService.Object);
var hubClients = new Mock<IHubCallerClients>();
var caller = new Mock<IClientProxy>();
@ -69,7 +69,7 @@ namespace Remotely.Tests
appConfig.Setup(x => x.BannedDevices).Returns(new string[] { _testData.Device1.ID });
var hub = new ServiceHub(DataService, appConfig.Object, serviceSessionCache.Object, viewerHub.Object, circuitManager.Object, expiringTokenService.Object);
var hub = new AgentHub(DataService, appConfig.Object, serviceSessionCache.Object, viewerHub.Object, circuitManager.Object, expiringTokenService.Object);
var hubClients = new Mock<IHubCallerClients>();
var caller = new Mock<IClientProxy>();
@ -100,7 +100,7 @@ namespace Remotely.Tests
appConfig.Setup(x => x.BannedDevices).Returns(Array.Empty<string>());
var hub = new ServiceHub(DataService, appConfig.Object, serviceSessionCache.Object, viewerHub.Object, circuitManager.Object, expiringTokenService.Object)
var hub = new AgentHub(DataService, appConfig.Object, serviceSessionCache.Object, viewerHub.Object, circuitManager.Object, expiringTokenService.Object)
{
Context = new CallerContext()
};

@ -1 +1 @@
Subproject commit 345c43e03dc8e32cb0d3519a9767dd654ad420ba
Subproject commit 283f66562d90b84132b40116b31694c691a97cc9