From d82da3dea9ee316bbbea11e311561205754cdcd0 Mon Sep 17 00:00:00 2001
From: Jared Goodwin
Date: Thu, 23 Jan 2020 17:43:48 -0800
Subject: [PATCH] Agents auto-update via downloaded script. Pauses for random
seconds based on total device count before downloading (to even out the
bandwidth).
---
Agent/Program.cs | 5 -
Agent/Services/DeviceSocket.cs | 2 +-
Agent/Services/Updater.cs | 159 +++---------------
Server/API/AgentUpdateController.cs | 41 +++++
Server/API/ClientDownloadsController.cs | 25 ++-
Server/API/CoreVersionController.cs | 1 +
Server/Pages/Agents.cshtml | 2 +-
Server/Services/DataService.cs | 8 +
Server/wwwroot/Downloads/Install-Linux-x64.sh | 10 +-
Shared/Services/OSUtils.cs | 10 +-
10 files changed, 108 insertions(+), 155 deletions(-)
create mode 100644 Server/API/AgentUpdateController.cs
diff --git a/Agent/Program.cs b/Agent/Program.cs
index 9822334c..0f50f448 100644
--- a/Agent/Program.cs
+++ b/Agent/Program.cs
@@ -108,11 +108,6 @@ namespace Remotely.Agent
SetWorkingDirectory();
var argDict = ProcessArgs(args);
- if (argDict.ContainsKey("update"))
- {
- Services.GetRequiredService().CoreUpdate();
- }
-
if (!IsDebug && OSUtils.IsWindows)
{
_ = Task.Run(() =>
diff --git a/Agent/Services/DeviceSocket.cs b/Agent/Services/DeviceSocket.cs
index 5f4081c8..a4b1be26 100644
--- a/Agent/Services/DeviceSocket.cs
+++ b/Agent/Services/DeviceSocket.cs
@@ -181,7 +181,7 @@ namespace Remotely.Agent.Services
IsServerVerified = true;
if (!Program.IsDebug)
{
- Updater.CheckForCoreUpdates();
+ Task.Run(Updater.CheckForUpdates);
}
}
else
diff --git a/Agent/Services/Updater.cs b/Agent/Services/Updater.cs
index 44a1e3fa..27434999 100644
--- a/Agent/Services/Updater.cs
+++ b/Agent/Services/Updater.cs
@@ -22,52 +22,46 @@ namespace Remotely.Agent.Services
private ConfigService ConfigService { get; }
- public void CheckForCoreUpdates()
+ public async Task CheckForUpdates()
{
try
{
var wc = new WebClient();
- var response = new HttpClient().GetAsync(ConfigService.GetConnectionInfo().Host + $"/API/CoreVersion/").Result;
+ var connectionInfo = ConfigService.GetConnectionInfo();
+ var response = new HttpClient().GetAsync(connectionInfo.Host + $"/API/AgentUpdate/CurrentVersion").Result;
var latestVersion = response.Content.ReadAsStringAsync().Result;
var thisVersion = FileVersionInfo.GetVersionInfo("Remotely_Agent.dll").FileVersion.ToString().Trim();
if (thisVersion != latestVersion)
{
- Logger.Write($"Service Updater: Downloading update. Current Version: {thisVersion}. Latest Version: {latestVersion}.");
- var fileName = OSUtils.CoreZipFileName;
- var tempFile = Path.Combine(Path.GetTempPath(), fileName);
- var tempFolder = Path.Combine(Path.GetTempPath(), "Remotely_Update");
- if (File.Exists(tempFile))
- {
- File.Delete(tempFile);
- }
- if (Directory.Exists(tempFolder))
- {
- Directory.Delete(tempFolder, true);
- }
- wc.DownloadFile(new Uri(ConfigService.GetConnectionInfo().Host + $"/Downloads/{fileName}"), tempFile);
+ Logger.Write($"Service Updater: Update found. Current Version: {thisVersion}. Latest Version: {latestVersion}.");
- Logger.Write($"Service Updater: Extracting files.");
+ var updateWindow = int.Parse(wc.DownloadString(connectionInfo.Host + $"/API/AgentUpdate/UpdateWindow"));
+ var waitTime = new Random().Next(1, updateWindow);
+ Logger.Write($"Waiting {waitTime} seconds before updating.");
+ await Task.Delay(TimeSpan.FromSeconds(waitTime));
+ Logger.Write($"Service Updater: Downloading installer.");
if (OSUtils.IsWindows)
{
- ZipFile.ExtractToDirectory(tempFile, tempFolder, true);
- Logger.Write($"Service Updater: Launching extracted process to perform update.");
- var psi = new ProcessStartInfo()
- {
- FileName = Path.Combine(Path.GetTempPath(), "Remotely_Update", OSUtils.ClientExecutableFileName),
- Arguments = "-update true",
- Verb = "RunAs"
- };
- Process.Start(psi);
+ var filePath = Path.Combine(Path.GetTempPath(), "RemotelyUpdate.ps1");
+
+ wc.DownloadFile(
+ ConfigService.GetConnectionInfo().Host + $"/API/ClientDownloads/{connectionInfo.OrganizationID}/{OSUtils.PlatformString}",
+ filePath);
+
+ Process.Start("powershell.exe", $"-f \"{filePath}\"");
}
else if (OSUtils.IsLinux)
{
- Process.Start("sudo", "apt-get install unzip").WaitForExit();
- Process.Start("sudo", $"unzip -o {tempFile} -d /usr/local/bin/Remotely/").WaitForExit();
- Process.Start("sudo", "chmod +x /usr/local/bin/Remotely/Remotely_Agent").WaitForExit();
- Process.Start("sudo", "chmod +x /usr/local/bin/Remotely/ScreenCast/Remotely_ScreenCast.Linux").WaitForExit();
- Logger.Write($"Service Updater: Update complete. Restarting service.");
- Process.Start("sudo", "systemctl restart remotely-agent");
+ var filePath = Path.Combine(Path.GetTempPath(), "RemotelyUpdate.sh");
+
+ wc.DownloadFile(
+ ConfigService.GetConnectionInfo().Host + $"/API/ClientDownloads/{connectionInfo.OrganizationID}/{OSUtils.PlatformString}",
+ filePath);
+
+ Process.Start("sudo", $"chmod +x {filePath}").WaitForExit();
+
+ Process.Start("sudo", $"{filePath} & disown");
}
}
}
@@ -76,108 +70,5 @@ namespace Remotely.Agent.Services
Logger.Write(ex);
}
}
- public void CoreUpdate()
- {
- try
- {
- Logger.Write("Service Updater: Starting update.");
- var ps = PowerShell.Create();
- if (OSUtils.IsWindows)
- {
- ps.AddScript(@"Get-Service | Where-Object {$_.Name -like ""Remotely_Service""} | Stop-Service -Force");
- ps.Invoke();
- ps.Commands.Clear();
- }
- else if (OSUtils.IsLinux)
- {
- Process.Start("sudo", "systemctl stop remotely-agent");
- }
-
-
- foreach (var proc in Process.GetProcesses().Where(x =>
- (x.ProcessName.Contains("Remotely_Agent") ||
- x.ProcessName.Contains("Remotely_ScreenCast") ||
- x.ProcessName.Contains("Remotely_Desktop"))
- && x.Id != Process.GetCurrentProcess().Id))
- {
- proc.Kill();
- }
-
- string targetDir = "";
-
- if (OSUtils.IsWindows)
- {
- targetDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Remotely");
- }
- else if (OSUtils.IsLinux)
- {
- targetDir = "/usr/local/bin/Remotely";
- }
-
- Logger.Write("Service Updater: Copying new files.");
-
-
- var fileList = Directory.GetFiles(Path.Combine(Path.GetTempPath(), "Remotely_Update"));
- foreach (var file in fileList)
- {
- try
- {
- var targetPath = Path.Combine(targetDir, Path.GetFileName(file));
- File.Copy(file, targetPath, true);
- }
- catch (Exception ex)
- {
- Logger.Write(ex);
- continue;
- }
- }
-
-
- var subdirList = Directory.GetDirectories(Path.Combine(Path.GetTempPath(), "Remotely_Update"));
-
- foreach (var subdir in subdirList)
- {
- try
- {
- var targetPath = Path.Combine(targetDir, Path.GetFileName(subdir.TrimEnd(Path.DirectorySeparatorChar)));
- if (Directory.Exists(targetPath))
- {
- Directory.Delete(targetPath, true);
- while (Directory.Exists(targetPath))
- {
- Thread.Sleep(100);
- }
- }
- Directory.Move(subdir, targetPath);
- }
- catch (Exception ex)
- {
- Logger.Write(ex);
- continue;
- }
-
- }
- Logger.Write("Service Updater: Update completed.");
- }
- catch (Exception ex)
- {
- Logger.Write(ex);
- }
- finally
- {
- Logger.Write("Service Updater: Starting service.");
- if (OSUtils.IsWindows)
- {
- var ps = PowerShell.Create();
- ps.AddScript("Start-Service -Name \"Remotely_Service\"");
- ps.Invoke();
- }
- else if (OSUtils.IsLinux)
- {
- Process.Start("sudo", "systemctl restart remotely-agent").WaitForExit();
- }
- Environment.Exit(0);
- }
- }
}
}
diff --git a/Server/API/AgentUpdateController.cs b/Server/API/AgentUpdateController.cs
new file mode 100644
index 00000000..a2cd2cd7
--- /dev/null
+++ b/Server/API/AgentUpdateController.cs
@@ -0,0 +1,41 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Remotely.Server.Services;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+
+namespace Remotely.Server.API
+{
+ [Route("api/[controller]")]
+ public class AgentUpdateController : Controller
+ {
+ public AgentUpdateController(IWebHostEnvironment hostingEnv, DataService dataService)
+ {
+ this.HostingEnv = hostingEnv;
+ DataService = dataService;
+ }
+
+ private IWebHostEnvironment HostingEnv { get; }
+ private DataService DataService { get; }
+
+ // GET: api/
+ [HttpGet("[action]")]
+ public string CurrentVersion()
+ {
+ var filePath = Path.Combine(HostingEnv.ContentRootPath, "CurrentVersion.txt");
+ if (!System.IO.File.Exists(filePath))
+ {
+ return "0.0.0.0";
+ }
+ return System.IO.File.ReadAllText(filePath).Trim();
+ }
+
+ [HttpGet("[action]")]
+ public int UpdateWindow()
+ {
+ return DataService.GetDeviceCount() * 10;
+ }
+ }
+}
diff --git a/Server/API/ClientDownloadsController.cs b/Server/API/ClientDownloadsController.cs
index 2d5f1331..742e473c 100644
--- a/Server/API/ClientDownloadsController.cs
+++ b/Server/API/ClientDownloadsController.cs
@@ -28,9 +28,20 @@ namespace Remotely.Server.API
[Authorize]
[HttpGet("{platformID}")]
- public ActionResult Get(string platformID)
+ public async Task Get(string platformID)
{
var user = DataService.GetUserByName(User.Identity.Name);
+ return await GetInstallFile(user.OrganizationID, platformID);
+ }
+
+ [HttpGet("{organizationID}/{platformID}")]
+ public async Task Get(string organizationID, string platformID)
+ {
+ return await GetInstallFile(organizationID, platformID);
+ }
+
+ private async Task GetInstallFile(string organizationID, string platformID)
+ {
var fileContents = new List();
string fileName;
byte[] fileBytes;
@@ -41,32 +52,32 @@ namespace Remotely.Server.API
{
fileName = $"Install-{platformID}.ps1";
- fileContents.AddRange(System.IO.File.ReadAllLines(Path.Combine(HostEnv.WebRootPath, "Downloads", $"{fileName}")));
+ fileContents.AddRange(await System.IO.File.ReadAllLinesAsync(Path.Combine(HostEnv.WebRootPath, "Downloads", $"{fileName}")));
var hostIndex = fileContents.IndexOf("[string]$HostName = $null");
var orgIndex = fileContents.IndexOf("[string]$Organization = $null");
fileContents[hostIndex] = $"[string]$HostName = \"{Request.Scheme}://{Request.Host}\"";
- fileContents[orgIndex] = $"[string]$Organization = \"{user.Organization.ID}\"";
+ fileContents[orgIndex] = $"[string]$Organization = \"{organizationID}\"";
fileBytes = System.Text.Encoding.UTF8.GetBytes(string.Join(Environment.NewLine, fileContents));
break;
}
-
+
case "Linux-x64":
{
fileName = "Install-Linux-x64.sh";
- fileContents.AddRange(System.IO.File.ReadAllLines(Path.Combine(HostEnv.WebRootPath, "Downloads", $"{fileName}")));
+ fileContents.AddRange(await System.IO.File.ReadAllLinesAsync(Path.Combine(HostEnv.WebRootPath, "Downloads", $"{fileName}")));
var hostIndex = fileContents.IndexOf("HostName=");
var orgIndex = fileContents.IndexOf("Organization=");
fileContents[hostIndex] = $"HostName=\"{Request.Scheme}://{Request.Host}\"";
- fileContents[orgIndex] = $"Organization=\"{user.Organization.ID}\"";
+ fileContents[orgIndex] = $"Organization=\"{organizationID}\"";
fileBytes = System.Text.Encoding.UTF8.GetBytes(string.Join("\n", fileContents));
break;
}
-
+
default:
return BadRequest();
}
diff --git a/Server/API/CoreVersionController.cs b/Server/API/CoreVersionController.cs
index d49b921e..bab08ac4 100644
--- a/Server/API/CoreVersionController.cs
+++ b/Server/API/CoreVersionController.cs
@@ -7,6 +7,7 @@ using System.IO.Compression;
namespace Remotely.Server.API
{
+ // TODO: Delete after a few versions.
[Route("api/[controller]")]
public class CoreVersionController : Controller
{
diff --git a/Server/Pages/Agents.cshtml b/Server/Pages/Agents.cshtml
index 1683dd94..e004b7ad 100644
--- a/Server/Pages/Agents.cshtml
+++ b/Server/Pages/Agents.cshtml
@@ -114,7 +114,7 @@
Local Install:
- sudo [path]/Install-Linux-x64.sh --path [path]/Remotely-Win10-x86.zip
+ sudo [path]/Install-Linux-x64.sh --path [path]/Remotely-Linux.zip
Uninstall:
diff --git a/Server/Services/DataService.cs b/Server/Services/DataService.cs
index ebd22f00..aa0cc067 100644
--- a/Server/Services/DataService.cs
+++ b/Server/Services/DataService.cs
@@ -129,6 +129,7 @@ namespace Remotely.Server.Services
RemotelyContext.SaveChanges();
return true;
}
+
public string AddSharedFile(IFormFile file, string organizationID)
{
var expirationDate = DateTime.Now.AddDays(-AppConfig.DataRetentionInDays);
@@ -209,6 +210,7 @@ namespace Remotely.Server.Services
RemotelyContext.DeviceGroups.Remove(deviceGroup);
RemotelyContext.SaveChanges();
}
+
public void DeleteInvite(string requesterUserName, string inviteID)
{
var requester = RemotelyContext.Users
@@ -277,6 +279,7 @@ namespace Remotely.Server.Services
.Where(x => x.OrganizationID == orgID)
.OrderByDescending(x => x.TimeStamp);
}
+
public IEnumerable GetAllDevicesForUser(string userID)
{
var user = RemotelyContext.Users.FirstOrDefault(x => x.Id == userID);
@@ -294,6 +297,7 @@ namespace Remotely.Server.Services
.Where(x => x.OrganizationID == orgID)
.OrderByDescending(x => x.TimeStamp);
}
+
public ICollection GetAllInviteLinks(string userName)
{
return RemotelyContext.Users
@@ -336,6 +340,10 @@ namespace Remotely.Server.Services
return AppConfig.DefaultPrompt;
}
+ public int GetDeviceCount()
+ {
+ return RemotelyContext.Devices.Count();
+ }
public Device GetDeviceForUser(string userID, string deviceID)
{
var user = RemotelyContext.Users.FirstOrDefault(x => x.Id == userID);
diff --git a/Server/wwwroot/Downloads/Install-Linux-x64.sh b/Server/wwwroot/Downloads/Install-Linux-x64.sh
index 3802b706..0356e129 100644
--- a/Server/wwwroot/Downloads/Install-Linux-x64.sh
+++ b/Server/wwwroot/Downloads/Install-Linux-x64.sh
@@ -4,8 +4,6 @@ Organization=
GUID=$(cat /proc/sys/kernel/random/uuid)
systemctl stop remotely-agent
-rm -r -f /usr/local/bin/Remotely
-rm -f /etc/systemd/system/remotely-agent.service
systemctl daemon-reload
if [ "$1" = "--uninstall" ]; then
@@ -27,6 +25,14 @@ apt-get -y install libc6-dev
apt-get -y install libgdiplus
apt-get -y install libxtst-dev
apt-get -y install xclip
+apt-get -y install jq
+
+if [ -f "/usr/local/bin/Remotely/ConnectionInfo.json" ]; then
+ GUID=`cat "/usr/local/bin/Remotely/ConnectionInfo.json" | jq -r '.DeviceID'`
+fi
+
+rm -r -f /usr/local/bin/Remotely
+rm -f /etc/systemd/system/remotely-agent.service
mkdir -p /usr/local/bin/Remotely/
cd /usr/local/bin/Remotely/
diff --git a/Shared/Services/OSUtils.cs b/Shared/Services/OSUtils.cs
index c9f86097..3b522627 100644
--- a/Shared/Services/OSUtils.cs
+++ b/Shared/Services/OSUtils.cs
@@ -56,7 +56,7 @@ namespace Remotely.Shared.Services
}
}
- public static string CoreZipFileName
+ public static string PlatformString
{
get
{
@@ -64,17 +64,17 @@ namespace Remotely.Shared.Services
{
if (Environment.Is64BitOperatingSystem)
{
- return "Remotely-Win10-x64.zip";
+ return "Win10-x64";
}
else
{
- return "Remotely-Win10-x86.zip";
+ return "Win10-x86";
}
}
- else if (OSUtils.IsLinux)
+ else if (IsLinux)
{
- return "Remotely-Linux.zip";
+ return "Linux-x64";
}
else
{