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

211 lines
8.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 errorrestore 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;
}
}
}
}
}