You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iot/projects/IoTDameon/UpdateIoTNodeService.cs

176 lines
7.0 KiB

using CookComputing.XmlRpc;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace IoTDameon
{
public class UpdateIoTNodeService : BackgroundService
{
private readonly ILogger<UpdateIoTNodeService> _logger;
private readonly IWebHostEnvironment _env;
private readonly IConfiguration _cfg;
private readonly IHttpClientFactory _httpClientFactory;
public bool IsUpdating { get; set; }
public UpdateIoTNodeService(ILogger<UpdateIoTNodeService> logger, IWebHostEnvironment env, IConfiguration cfg, IHttpClientFactory httpClientFactory)
{
this._logger = logger;
this._env = env;
this._cfg = cfg;
this._httpClientFactory = httpClientFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"update service start");
stoppingToken.Register(() => _logger.LogDebug("update service stop"));
while (!stoppingToken.IsCancellationRequested)
{
this.Update();
await Task.Delay(1000 * 60, stoppingToken);
}
await Task.CompletedTask;
}
private void Update()
{
if (this.IsUpdating)
{
return;
}
else
{
this.IsUpdating = true;
}
try
{
UpdateInternal();
}
catch (Exception ex)
{
this._logger.LogError(ex, "update error");
}
this.IsUpdating = false;
}
private void UpdateInternal()
{
var processName = "iotnode";
var appFolder = "IoTNode";
var port = 8002;
var proxyUrl = "http://localhost:9001/RPC2";
var root = Directory.GetParent(_env.ContentRootPath).FullName;
var appPath = Path.Combine(root, appFolder);
var name = $"{appFolder}.zip";
var backupName = Path.Combine(root, $"{appFolder}.bk.zip");
var file = Path.Combine(root, name);
var currentCheckSum = string.Empty;
//检查是否有更新
this._logger.LogInformation("check version");
var currentVersion = this._httpClientFactory.CreateClient().GetAsync($"http://localhost:{port}/Home/GetVersion").Result.Content.ReadAsStringAsync().Result;
var server = this._httpClientFactory.CreateClient().GetAsync($"http://localhost:{port}/Home/GetServer").Result.Content.ReadAsStringAsync().Result;
var info = this._httpClientFactory.CreateClient().GetAsync($"{server}/{processName}.xml").Result.Content.ReadAsStringAsync().Result;
var doc = new XmlDocument();
doc.LoadXml(info);
var lastVersion = doc.GetElementsByTagName("version")[0].InnerText.Trim();
var lastCheckSum = doc.GetElementsByTagName("checksum")[0].InnerText.Trim();
if (currentVersion != lastVersion)
{
this._logger.LogInformation($"current dameon version({currentVersion}) is different from last dameon version {lastVersion}");
//查看是否已下载更新并删除旧的更新文件
if (File.Exists(file))
{
currentCheckSum = getCheckSum(file);
if (currentCheckSum != lastCheckSum)
{
this._logger.LogWarning("old file checksum not equals last checksum");
File.Delete(file);
}
}
//下载更新并校验
if (!File.Exists(file))
{
var bytes = this._httpClientFactory.CreateClient().GetAsync($"{this._cfg["notify:host"]}/{name}").Result.Content.ReadAsByteArrayAsync().Result;
using var fs = File.Create(file);
fs.Write(bytes);
currentCheckSum = getCheckSum(file);
if (currentCheckSum != lastCheckSum)
{
this._logger.LogWarning("download file checksum not equals last checksum");
File.Delete(file);
}
}
if (File.Exists(file))
{
//关闭要更新的程序
var proxy = XmlRpcProxyGen.Create<ISupervisorService>();
proxy.Url = proxyUrl;
proxy.Credentials = new NetworkCredential("usr", "pwd");
try
{
proxy.startProcess(processName);
}
catch (XmlRpcFaultException ex)
{
if (ex.FaultCode != 60)
{
throw ex;
}
}
//备份要更新的程序
try
{
ZipFile.CreateFromDirectory(appPath, backupName);
}
catch (Exception ex)
{
this._logger.LogError(ex, ex.Message);
throw new Exception("备份程序失败", ex);
}
Directory.Delete(appPath, true);
Directory.CreateDirectory(appPath);
//更新程序
try
{
ZipFile.ExtractToDirectory(file, appPath);
}
catch (Exception ex)
{
this._logger.LogError(ex, ex.Message);
ZipFile.ExtractToDirectory(backupName, appPath);
throw new Exception("更新程序失败,已还原", ex);
}
//设置权限
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var updateScript = Path.Combine(appPath, "update.sh");
Process.Start("/bin/bash", $"-c \"chmod 755 {file}\"");
Process.Start(updateScript);
}
//启动更新程序
proxy.startProcess(processName);
}
}
}
private string getCheckSum(string file)
{
using var sha = SHA512.Create();
return BitConverter.ToString(sha.ComputeHash(File.ReadAllBytes(file))).Replace("-", "").ToLower();
}
}
}