|
|
using CookComputing.XmlRpc;
|
|
|
using Microsoft.AspNetCore.Hosting;
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
using System;
|
|
|
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 backupPath = Path.Combine(root, $"{appFolder}_bk");
|
|
|
var file = Path.Combine(root, name);
|
|
|
var currentCheckSum = string.Empty;
|
|
|
//检查是否有更新
|
|
|
this._logger.LogInformation("check last 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 serverUrl = $"{server}/{processName}.xml";
|
|
|
this._logger.LogInformation($"request url:{serverUrl}");
|
|
|
var info = this._httpClientFactory.CreateClient().GetAsync(serverUrl).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 version {currentVersion} does not equals {lastVersion}");
|
|
|
//查看是否已下载更新并删除旧的更新文件
|
|
|
if (File.Exists(file))
|
|
|
{
|
|
|
currentCheckSum = getCheckSum(file);
|
|
|
if (currentCheckSum != lastCheckSum)
|
|
|
{
|
|
|
this._logger.LogWarning($"current file hash {currentCheckSum} does not equals last file hash {lastCheckSum}");
|
|
|
File.Delete(file);
|
|
|
this._logger.LogWarning($"delete old file {file}");
|
|
|
}
|
|
|
}
|
|
|
//下载更新并校验
|
|
|
if (!File.Exists(file))
|
|
|
{
|
|
|
var bytes = this._httpClientFactory.CreateClient().GetAsync($"{server}/{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 hash {currentCheckSum} does not math hash in xml {lastCheckSum}");
|
|
|
File.Delete(file);
|
|
|
this._logger.LogWarning($"delete old file {file}");
|
|
|
}
|
|
|
}
|
|
|
if (File.Exists(file))
|
|
|
{
|
|
|
this._logger.LogWarning($"begin update");
|
|
|
//关闭要更新的程序
|
|
|
var proxy = XmlRpcProxyGen.Create<ISupervisorService>();
|
|
|
proxy.Url = proxyUrl;
|
|
|
proxy.Credentials = new NetworkCredential("usr", "pwd");
|
|
|
try
|
|
|
{
|
|
|
proxy.stopProcess(processName);
|
|
|
this._logger.LogWarning($"close process {processName}");
|
|
|
}
|
|
|
catch (XmlRpcFaultException ex)
|
|
|
{
|
|
|
if (ex.FaultCode != 60)
|
|
|
{
|
|
|
throw ex;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogWarning($"close error,{processName} is alreday stoped");
|
|
|
}
|
|
|
}
|
|
|
//备份要更新的程序
|
|
|
try
|
|
|
{
|
|
|
if (Directory.Exists(backupPath))
|
|
|
{
|
|
|
Directory.Delete(backupPath, true);
|
|
|
}
|
|
|
Directory.Move(appPath, backupPath);
|
|
|
this._logger.LogInformation($"back up {appPath}");
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
this._logger.LogError(ex, ex.Message);
|
|
|
throw new Exception("backup error", ex);
|
|
|
}
|
|
|
Directory.CreateDirectory(appPath);
|
|
|
this._logger.LogInformation($"mkdir {appPath}");
|
|
|
foreach (var item in Directory.GetFiles(backupPath))
|
|
|
{
|
|
|
if (item.EndsWith(".db") || item == "appsettings.json")
|
|
|
{
|
|
|
File.Copy(item, Path.Combine(appPath, Path.GetFileName(item)));
|
|
|
}
|
|
|
}
|
|
|
this._logger.LogInformation($"copy db files to {appPath}");
|
|
|
//更新程序
|
|
|
try
|
|
|
{
|
|
|
ZipFile.ExtractToDirectory(file, root, true);
|
|
|
this._logger.LogInformation($"unzip {file} to {appPath}");
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
this._logger.LogError(ex, ex.Message);
|
|
|
Directory.Delete(appPath, true);
|
|
|
Directory.Move(backupPath, appPath);
|
|
|
throw new Exception("upzip error,restore old files", ex);
|
|
|
}
|
|
|
//设置权限
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
{
|
|
|
var updateScript = Path.Combine(appPath, "update.sh");
|
|
|
var command = $"chmod 755 {updateScript}";
|
|
|
Console.WriteLine(command.Bash());
|
|
|
Console.WriteLine(updateScript.Bash());
|
|
|
this._logger.LogInformation($"chmod and run {updateScript}");
|
|
|
}
|
|
|
//启动更新程序
|
|
|
proxy.startProcess(processName);
|
|
|
this._logger.LogInformation($"{appPath} start");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private string getCheckSum(string file)
|
|
|
{
|
|
|
using (var sha = SHA512.Create())
|
|
|
{
|
|
|
using (var fs = File.OpenRead(file))
|
|
|
{
|
|
|
var checksum = BitConverter.ToString(sha.ComputeHash(fs)).Replace("-", "").ToLower();
|
|
|
return checksum;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} |