发布2020.0601
大石头 编写于 2020-05-31 10:19:29
X
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using NewLife.Log;

namespace NewLife.Agent
{
    /// <summary>Linux版进程守护</summary>
    public class Systemd : Host
    {
        private String _path;
        private IHostedService _service;

        /// <summary>实例化</summary>
        public Systemd()
        {
            var ps = new[] {
                "/etc/systemd/system",
                "/run/systemd/system",
                "/usr/lib/systemd/system",
                "/lib/systemd/system" };
            foreach (var p in ps)
            {
                if (Directory.Exists(p))
                {
                    _path = p;
                    break;
                }
            }
        }

        /// <summary>启动服务</summary>
        /// <param name="service"></param>
        public override void Run(IHostedService service)
        {
            if (service == null) throw new ArgumentNullException(nameof(service));

            _service = service;

            var source = new CancellationTokenSource();
            service.StartAsync(source.Token);

            // 阻塞
            Thread.Sleep(-1);
        }

        /// <summary>服务是否已安装</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean IsInstalled(String serviceName)
        {
            var file = _path.CombinePath($"{serviceName}.service");

            return File.Exists(file);
        }

        /// <summary>服务是否已启动</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean IsRunning(String serviceName)
        {
            var file = _path.CombinePath($"{serviceName}.service");
            if (!File.Exists(file)) return false;

            var str = Execute("systemctl", $"status {serviceName}", false);
            if (!str.IsNullOrEmpty() && str.Contains("running")) return true;

            return false;
        }

        /// <summary>安装服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <param name="displayName">显示名</param>
        /// <param name="binPath">文件路径</param>
        /// <param name="description">描述信息</param>
        /// <returns></returns>
        public override Boolean Install(String serviceName, String displayName, String binPath, String description)
        {
            XTrace.WriteLine("{0}.Install {1}, {2}, {3}, {4}", GetType().Name, serviceName, displayName, binPath, description);

            var file = _path.CombinePath($"{serviceName}.service");
            XTrace.WriteLine(file);

            var asm = Assembly.GetEntryAssembly();
            var des = !displayName.IsNullOrEmpty() ? displayName : description;

            var sb = new StringBuilder();
            sb.AppendLine("[Unit]");
            sb.AppendLine($"Description={des}");

            sb.AppendLine();
            sb.AppendLine("[Service]");
            sb.AppendLine("Type=simple");
            //sb.AppendLine($"ExecStart=/usr/bin/dotnet {asm.Location}");
            sb.AppendLine($"ExecStart={binPath}");
            sb.AppendLine($"WorkingDirectory={".".GetFullPath()}");
            sb.AppendLine("Restart=on-failure");

            sb.AppendLine();
            sb.AppendLine("[Install]");
            sb.AppendLine("WantedBy=multi-user.target");

            File.WriteAllText(file, sb.ToString());

            // sudo systemctl daemon-reload
            // sudo systemctl enable StarAgent
            // sudo systemctl start StarAgent

            Execute("systemctl", "daemon-reload");
            Execute("systemctl", $"enable {serviceName}");
            //Execute("systemctl", $"start {serviceName}");

            return true;
        }

        /// <summary>卸载服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean Remove(String serviceName)
        {
            XTrace.WriteLine("{0}.Remove {1}", GetType().Name, serviceName);

            var file = _path.CombinePath($"{serviceName}.service");
            if (File.Exists(file)) File.Delete(file);

            return true;
        }

        /// <summary>启动服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean Start(String serviceName) => Execute("systemctl", $"start {serviceName}") != null;

        /// <summary>停止服务</summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public override Boolean Stop(String serviceName) => Execute("systemctl", $"stop {serviceName}") != null;

        private static String Execute(String cmd, String arguments, Boolean writeLog = true)
        {
            if (writeLog) XTrace.WriteLine("{0} {1}", cmd, arguments);

            var psi = new ProcessStartInfo(cmd, arguments) { RedirectStandardOutput = true };
            var process = Process.Start(psi);
            if (!process.WaitForExit(3_000))
            {
                process.Kill();
                return null;
            }

            return process.StandardOutput.ReadToEnd();
        }
    }
}