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 _logger; private readonly IWebHostEnvironment _env; private readonly IConfiguration _cfg; private readonly IHttpClientFactory _httpClientFactory; public bool IsUpdating { get; set; } public UpdateIoTNodeService(ILogger 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(); 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(); } } }