Linux下,需要给予可执行权限
This commit is contained in:
parent
96a14d7b71
commit
2a2b0bc667
|
@ -1,7 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Xml.Serialization;
|
||||
using NewLife;
|
||||
using NewLife.Log;
|
||||
|
||||
|
@ -12,41 +9,41 @@ public class ZipDeploy
|
|||
{
|
||||
#region 属性
|
||||
/// <summary>应用名称</summary>
|
||||
public String Name { get; set; }
|
||||
public String Name { get; set; } = null!;
|
||||
|
||||
/// <summary>文件名</summary>
|
||||
public String FileName { get; set; }
|
||||
public String? FileName { get; set; }
|
||||
|
||||
/// <summary>启动参数</summary>
|
||||
public String Arguments { get; set; }
|
||||
public String? Arguments { get; set; }
|
||||
|
||||
/// <summary>工作目录</summary>
|
||||
public String WorkingDirectory { get; set; }
|
||||
public String? WorkingDirectory { get; set; }
|
||||
|
||||
/// <summary>影子目录。应用将在其中执行</summary>
|
||||
/// <remarks>默认使用上一级的shadow目录,无权时使用临时目录</remarks>
|
||||
public String Shadow { get; set; }
|
||||
public String? Shadow { get; set; }
|
||||
|
||||
/// <summary>可执行文件路径</summary>
|
||||
public String ExecuteFile { get; set; }
|
||||
public String? ExecuteFile { get; set; }
|
||||
|
||||
/// <summary>用户。以该用户执行应用</summary>
|
||||
public String UserName { get; set; }
|
||||
public String? UserName { get; set; }
|
||||
|
||||
/// <summary>覆盖文件。需要拷贝覆盖已存在的文件或子目录,支持*模糊匹配,多文件分号隔开。如果目标文件不存在,配置文件等自动拷贝</summary>
|
||||
public String Overwrite { get; set; }
|
||||
public String? Overwrite { get; set; }
|
||||
|
||||
/// <summary>进程</summary>
|
||||
public Process Process { get; private set; }
|
||||
public Process? Process { get; private set; }
|
||||
|
||||
/// <summary>是否调试模式。在调试模式下,重定向控制台输出到日志</summary>
|
||||
public Boolean Debug { get; set; }
|
||||
|
||||
/// <summary>最后的错误信息</summary>
|
||||
public String LastError { get; set; }
|
||||
public String? LastError { get; set; }
|
||||
|
||||
/// <summary>链路追踪</summary>
|
||||
public ITracer Tracer { get; set; }
|
||||
public ITracer? Tracer { get; set; }
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
|
@ -66,7 +63,7 @@ public class ZipDeploy
|
|||
// 在参数中找到zip文件
|
||||
var name = "";
|
||||
var shadow = "";
|
||||
var gs = new String[args.Length];
|
||||
var gs = new String?[args.Length];
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i].EndsWithIgnoreCase(".zip"))
|
||||
|
@ -238,6 +235,11 @@ public class ZipDeploy
|
|||
si.FileName = "java";
|
||||
si.Arguments = $"{runfile.FullName} {Arguments}";
|
||||
}
|
||||
else if (Runtime.Linux)
|
||||
{
|
||||
// Linux下,需要给予可执行权限
|
||||
Process.Start("chmod", $"+x {runfile.FullName}");
|
||||
}
|
||||
|
||||
// 指定用户时,以特定用户启动进程
|
||||
if (!UserName.IsNullOrEmpty())
|
||||
|
|
|
@ -123,8 +123,13 @@ public class LocalStarClient
|
|||
Init();
|
||||
|
||||
var p = Process.GetCurrentProcess();
|
||||
var fileName = p.MainModule.FileName;
|
||||
var args = Environment.CommandLine.TrimStart(Path.ChangeExtension(fileName, ".dll")).Trim();
|
||||
var fileName = p.MainModule?.FileName;
|
||||
var args = "";
|
||||
if (!fileName.IsNullOrEmpty())
|
||||
{
|
||||
var ext = Path.ChangeExtension(fileName, ".dll");
|
||||
args = Environment.CommandLine.TrimStart(ext).Trim();
|
||||
}
|
||||
|
||||
// 发起命令
|
||||
var rs = _client.Invoke<String>("KillAndStart", new
|
||||
|
@ -148,7 +153,7 @@ public class LocalStarClient
|
|||
/// <param name="url">zip包下载源</param>
|
||||
/// <param name="version">版本号</param>
|
||||
/// <param name="target">目标目录</param>
|
||||
public Boolean ProbeAndInstall(String url = null, String version = null, String target = null)
|
||||
public Boolean ProbeAndInstall(String? url = null, String? version = null, String? target = null)
|
||||
{
|
||||
//if (url.IsNullOrEmpty()) throw new ArgumentNullException(nameof(url));
|
||||
if (url.IsNullOrEmpty())
|
||||
|
@ -183,7 +188,7 @@ public class LocalStarClient
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog("没有探测到StarAgent,{0}", ex.GetTrue().Message);
|
||||
WriteLog("没有探测到StarAgent,{0}", ex.GetTrue()?.Message);
|
||||
}
|
||||
|
||||
if (target.IsNullOrEmpty())
|
||||
|
@ -194,12 +199,12 @@ public class LocalStarClient
|
|||
{
|
||||
try
|
||||
{
|
||||
target = Path.GetDirectoryName(p.MainModule.FileName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
target = Path.GetDirectoryName(p.MainWindowTitle);
|
||||
if (p.MainModule != null)
|
||||
target = Path.GetDirectoryName(p.MainModule.FileName);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (target.IsNullOrEmpty()) target = Path.GetDirectoryName(p.MainWindowTitle);
|
||||
|
||||
WriteLog("发现进程StarAgent,ProcessId={0},target={1}", p.Id, target);
|
||||
}
|
||||
|
@ -255,7 +260,7 @@ public class LocalStarClient
|
|||
}
|
||||
|
||||
var fileName = info?.FileName;
|
||||
if (!fileName.IsNullOrEmpty() && Path.GetFullPath(fileName).EqualIgnoreCase("dotnet.exe")) fileName = info.Arguments;
|
||||
if (!fileName.IsNullOrEmpty() && Path.GetFullPath(fileName).EqualIgnoreCase("dotnet.exe")) fileName = info?.Arguments;
|
||||
|
||||
var rs = false;
|
||||
if (Runtime.Windows)
|
||||
|
@ -269,7 +274,7 @@ public class LocalStarClient
|
|||
return true;
|
||||
}
|
||||
|
||||
private Boolean RunAgentOnWindows(String fileName, String target, Boolean inService)
|
||||
private Boolean RunAgentOnWindows(String? fileName, String target, Boolean inService)
|
||||
{
|
||||
if (!fileName.IsNullOrEmpty() && Path.GetExtension(fileName) == ".dll") return false;
|
||||
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent.exe").GetFullPath();
|
||||
|
@ -293,13 +298,13 @@ public class LocalStarClient
|
|||
};
|
||||
var p = Process.Start(si);
|
||||
|
||||
WriteLog("启动进程成功 pid={0}", p.Id);
|
||||
WriteLog("启动进程成功 pid={0}", p?.Id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Boolean RunAgentOnLinux(String fileName, String target, Boolean inService)
|
||||
private Boolean RunAgentOnLinux(String? fileName, String target, Boolean inService)
|
||||
{
|
||||
if (!fileName.IsNullOrEmpty() && Path.GetExtension(fileName) == ".dll") return false;
|
||||
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent").GetFullPath();
|
||||
|
@ -326,13 +331,13 @@ public class LocalStarClient
|
|||
};
|
||||
var p = Process.Start(si);
|
||||
|
||||
WriteLog("启动进程成功 pid={0}", p.Id);
|
||||
WriteLog("启动进程成功 pid={0}", p?.Id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Boolean RunAgentOnDotnet(String fileName, String target, Boolean inService)
|
||||
private Boolean RunAgentOnDotnet(String? fileName, String target, Boolean inService)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent.dll").GetFullPath();
|
||||
if (!File.Exists(fileName)) return false;
|
||||
|
@ -355,7 +360,7 @@ public class LocalStarClient
|
|||
};
|
||||
var p = Process.Start(si);
|
||||
|
||||
WriteLog("启动进程成功 pid={0}", p.Id);
|
||||
WriteLog("启动进程成功 pid={0}", p?.Id);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -423,7 +428,7 @@ public class LocalStarClient
|
|||
/// <param name="local">本地信息,用于告知对方我是谁</param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<AgentInfo> Scan(AgentInfo local = null, Int32 timeout = 15_000)
|
||||
public static IEnumerable<AgentInfo> Scan(AgentInfo? local = null, Int32 timeout = 15_000)
|
||||
{
|
||||
var encoder = new JsonEncoder { Log = XTrace.Log };
|
||||
// 构造请求消息
|
||||
|
@ -438,7 +443,8 @@ public class LocalStarClient
|
|||
//};
|
||||
//var buf = msg.ToPacket().ToArray();
|
||||
|
||||
var buf = encoder.CreateRequest("Info", null).ToPacket().ToArray();
|
||||
var buf = encoder.CreateRequest("Info", null).ToPacket()?.ToArray();
|
||||
if (buf == null) yield break;
|
||||
|
||||
// 在局域网中广播消息
|
||||
var udp = new UdpClient();
|
||||
|
@ -448,7 +454,7 @@ public class LocalStarClient
|
|||
while (DateTime.Now < end)
|
||||
{
|
||||
var rs = new DefaultMessage();
|
||||
IPEndPoint ep = null;
|
||||
IPEndPoint? ep = null;
|
||||
buf = udp.Receive(ref ep);
|
||||
if (buf != null && rs.Read(buf))
|
||||
{
|
||||
|
@ -467,7 +473,7 @@ public class LocalStarClient
|
|||
|
||||
#region 日志
|
||||
/// <summary>日志</summary>
|
||||
public ILog Log { get; set; }
|
||||
public ILog Log { get; set; } = Logger.Null;
|
||||
|
||||
/// <summary>写日志</summary>
|
||||
/// <param name="format"></param>
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
public class ProcessInfo
|
||||
{
|
||||
/// <summary>名称</summary>
|
||||
public String Name { get; set; }
|
||||
public String Name { get; set; } = null!;
|
||||
|
||||
/// <summary>进程Id</summary>
|
||||
public Int32 ProcessId { get; set; }
|
||||
|
||||
/// <summary>进程名</summary>
|
||||
public String ProcessName { get; set; }
|
||||
public String? ProcessName { get; set; }
|
||||
|
||||
/// <summary>创建时间</summary>
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
|
|
@ -23,22 +23,22 @@ internal class ServiceController : DisposeBase
|
|||
public Int32 Id => _id;
|
||||
|
||||
/// <summary>服务名</summary>
|
||||
public String Name { get; set; }
|
||||
public String Name { get; set; } = null!;
|
||||
|
||||
/// <summary>进程ID</summary>
|
||||
public Int32 ProcessId { get; set; }
|
||||
|
||||
/// <summary>进程名</summary>
|
||||
public String ProcessName { get; set; }
|
||||
public String? ProcessName { get; set; }
|
||||
|
||||
/// <summary>服务信息</summary>
|
||||
public ServiceInfo Info { get; private set; }
|
||||
public ServiceInfo? Info { get; private set; }
|
||||
|
||||
/// <summary>部署信息</summary>
|
||||
public DeployInfo DeployInfo { get; set; }
|
||||
public DeployInfo? DeployInfo { get; set; }
|
||||
|
||||
/// <summary>进程</summary>
|
||||
public Process Process { get; set; }
|
||||
public Process? Process { get; set; }
|
||||
|
||||
/// <summary>是否正在工作</summary>
|
||||
public Boolean Running { get; set; }
|
||||
|
@ -59,13 +59,13 @@ internal class ServiceController : DisposeBase
|
|||
public Int32 MaxFails { get; set; } = 20;
|
||||
|
||||
/// <summary>事件客户端</summary>
|
||||
public IEventProvider EventProvider { get; set; }
|
||||
public IEventProvider? EventProvider { get; set; }
|
||||
|
||||
private String _fileName;
|
||||
private String _workdir;
|
||||
private TimerX _timer;
|
||||
private String? _fileName;
|
||||
private String? _workdir;
|
||||
private TimerX? _timer;
|
||||
private Int32 _error;
|
||||
private AppInfo _appInfo;
|
||||
private AppInfo? _appInfo;
|
||||
#endregion
|
||||
|
||||
#region 构造
|
||||
|
@ -114,8 +114,14 @@ internal class ServiceController : DisposeBase
|
|||
if (file.Contains('/') || file.Contains('\\'))
|
||||
{
|
||||
file = file.GetFullPath();
|
||||
if (workDir.IsNullOrEmpty()) workDir = Path.GetDirectoryName(file);
|
||||
}
|
||||
if (workDir.IsNullOrEmpty()) workDir = Path.GetDirectoryName(file);
|
||||
if (workDir.IsNullOrEmpty())
|
||||
{
|
||||
WriteLog("应用[{0}]工作目录为空", Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
workDir = workDir.GetFullPath();
|
||||
_fileName = null;
|
||||
_workdir = workDir;
|
||||
|
@ -128,7 +134,7 @@ internal class ServiceController : DisposeBase
|
|||
using var span = Tracer?.NewSpan("StartService", service);
|
||||
try
|
||||
{
|
||||
Process p;
|
||||
Process? p;
|
||||
var isZip = src.EndsWithIgnoreCase(".zip");
|
||||
|
||||
// 在环境变量中设置BasePath,不用担心影响当前进程,因为PathHelper仅读取一次
|
||||
|
@ -212,7 +218,7 @@ internal class ServiceController : DisposeBase
|
|||
}
|
||||
}
|
||||
|
||||
public ZipDeploy Extract(String file, String args, String workDir, Boolean needRun)
|
||||
public ZipDeploy? Extract(String file, String? args, String workDir, Boolean needRun)
|
||||
{
|
||||
var isZip = file.EqualIgnoreCase("ZipDeploy") || file.EndsWithIgnoreCase(".zip");
|
||||
if (!isZip) return null;
|
||||
|
@ -246,7 +252,7 @@ internal class ServiceController : DisposeBase
|
|||
return deploy;
|
||||
}
|
||||
|
||||
private Process RunZip(String file, String args, String workDir, ServiceInfo service)
|
||||
private Process? RunZip(String file, String? args, String workDir, ServiceInfo service)
|
||||
{
|
||||
var deploy = new ZipDeploy
|
||||
{
|
||||
|
@ -279,7 +285,7 @@ internal class ServiceController : DisposeBase
|
|||
return deploy.Process;
|
||||
}
|
||||
|
||||
private Process RunExe(String file, String args, String workDir, ServiceInfo service)
|
||||
private Process? RunExe(String file, String? args, String workDir, ServiceInfo service)
|
||||
{
|
||||
//WriteLog("拉起进程:{0} {1}", file, args);
|
||||
|
||||
|
@ -300,6 +306,12 @@ internal class ServiceController : DisposeBase
|
|||
UseShellExecute = false,
|
||||
};
|
||||
|
||||
if (Runtime.Linux)
|
||||
{
|
||||
// Linux下,需要给予可执行权限
|
||||
Process.Start("chmod", $"+x {fileName}");
|
||||
}
|
||||
|
||||
// 指定用户时,以特定用户启动进程
|
||||
if (!service.UserName.IsNullOrEmpty())
|
||||
{
|
||||
|
@ -336,7 +348,7 @@ internal class ServiceController : DisposeBase
|
|||
WriteLog("启动用户:{0}", si.UserName);
|
||||
|
||||
var p = Process.Start(si);
|
||||
if (StartWait > 0 && p.WaitForExit(StartWait) && p.ExitCode != 0)
|
||||
if (StartWait > 0 && p != null && p.WaitForExit(StartWait) && p.ExitCode != 0)
|
||||
{
|
||||
WriteLog("启动失败!ExitCode={0}", p.ExitCode);
|
||||
|
||||
|
@ -369,7 +381,7 @@ internal class ServiceController : DisposeBase
|
|||
|
||||
WriteLog("停止应用 PID={0}/{1} 原因:{2}", p.Id, p.ProcessName, reason);
|
||||
|
||||
using var span = Tracer?.NewSpan("StopService", $"{Info.Name} reason={reason}");
|
||||
using var span = Tracer?.NewSpan("StopService", $"{Info?.Name} reason={reason}");
|
||||
_timer.TryDispose();
|
||||
_timer = null;
|
||||
|
||||
|
@ -447,7 +459,8 @@ internal class ServiceController : DisposeBase
|
|||
/// <returns>本次是否成功启动(或接管),原来已启动返回false</returns>
|
||||
public Boolean Check()
|
||||
{
|
||||
using var span = Tracer?.NewSpan("CheckService", Info.Name);
|
||||
var inf = Info ?? new ServiceInfo();
|
||||
using var span = Tracer?.NewSpan("CheckService", inf.Name);
|
||||
|
||||
// 获取当前进程Id
|
||||
var mypid = Process.GetCurrentProcess().Id;
|
||||
|
@ -464,12 +477,12 @@ internal class ServiceController : DisposeBase
|
|||
_error = 0;
|
||||
|
||||
// 检查内存限制
|
||||
if (Info.MaxMemory <= 0) return false;
|
||||
if (inf.MaxMemory <= 0) return false;
|
||||
|
||||
var mem = p.WorkingSet64 / 1024 / 1024;
|
||||
if (mem <= Info.MaxMemory) return true;
|
||||
if (mem <= inf.MaxMemory) return true;
|
||||
|
||||
WriteLog("内存超限!{0}>{1}", mem, Info.MaxMemory);
|
||||
WriteLog("内存超限!{0}>{1}", mem, inf.MaxMemory);
|
||||
|
||||
Stop("内存超限");
|
||||
}
|
||||
|
@ -525,14 +538,14 @@ internal class ServiceController : DisposeBase
|
|||
}
|
||||
|
||||
// 进程不存在,但名称存在
|
||||
if (p == null && !ProcessName.IsNullOrEmpty() && Info.Mode != ServiceModes.Multiple)
|
||||
if (p == null && !ProcessName.IsNullOrEmpty() && inf.Mode != ServiceModes.Multiple)
|
||||
{
|
||||
if (ProcessName.EqualIgnoreCase("dotnet", "java"))
|
||||
{
|
||||
var target = _fileName ?? Info.FileName;
|
||||
var target = _fileName ?? inf.FileName;
|
||||
if (target.EqualIgnoreCase("dotnet", "java"))
|
||||
{
|
||||
var ss = Info.Arguments.Split(' ');
|
||||
var ss = inf.Arguments?.Split(' ');
|
||||
if (ss != null) target = ss.FirstOrDefault(e => e.EndsWithIgnoreCase(".dll", ".jar"));
|
||||
}
|
||||
if (!target.IsNullOrEmpty())
|
||||
|
@ -575,7 +588,7 @@ internal class ServiceController : DisposeBase
|
|||
if (p != null && EventProvider is StarClient client)
|
||||
{
|
||||
if (_appInfo == null)
|
||||
_appInfo = new AppInfo(p) { AppName = Info.Name };
|
||||
_appInfo = new AppInfo(p) { AppName = inf.Name };
|
||||
else
|
||||
_appInfo.Refresh();
|
||||
|
||||
|
@ -601,7 +614,7 @@ internal class ServiceController : DisposeBase
|
|||
return true;
|
||||
}
|
||||
|
||||
public void SetProcess(Process process)
|
||||
public void SetProcess(Process? process)
|
||||
{
|
||||
Process = process;
|
||||
if (process != null)
|
||||
|
@ -643,7 +656,7 @@ internal class ServiceController : DisposeBase
|
|||
private Boolean _ready;
|
||||
private DateTime _readyTime;
|
||||
|
||||
private void MonitorFileChange(Object state)
|
||||
private void MonitorFileChange(Object? state)
|
||||
{
|
||||
var first = _files.Count == 0;
|
||||
var changed = "";
|
||||
|
@ -711,15 +724,15 @@ internal class ServiceController : DisposeBase
|
|||
|
||||
#region 日志
|
||||
/// <summary>性能追踪</summary>
|
||||
public ITracer Tracer { get; set; }
|
||||
public ITracer? Tracer { get; set; }
|
||||
|
||||
/// <summary>日志</summary>
|
||||
public ILog Log { get; set; }
|
||||
public ILog Log { get; set; } = Logger.Null;
|
||||
|
||||
/// <summary>写日志</summary>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="args"></param>
|
||||
public void WriteLog(String format, params Object[] args)
|
||||
public void WriteLog(String format, params Object?[] args)
|
||||
{
|
||||
Log?.Info($"[{Id}/{Name}]{format}", args);
|
||||
|
||||
|
|
|
@ -14,22 +14,22 @@ public class Upgrade
|
|||
{
|
||||
#region 属性
|
||||
/// <summary>名称</summary>
|
||||
public String Name { get; set; }
|
||||
public String Name { get; set; } = null!;
|
||||
|
||||
/// <summary>更新目录。默认./Update</summary>
|
||||
public String UpdatePath { get; set; } = "Update";
|
||||
public String? UpdatePath { get; set; } = "Update";
|
||||
|
||||
/// <summary>目标目录</summary>
|
||||
public String DestinationPath { get; set; } = ".";
|
||||
public String? DestinationPath { get; set; } = ".";
|
||||
|
||||
/// <summary>源文件下载地址</summary>
|
||||
public String Url { get; set; }
|
||||
public String? Url { get; set; }
|
||||
|
||||
/// <summary>更新源文件</summary>
|
||||
public String SourceFile { get; set; }
|
||||
public String? SourceFile { get; set; }
|
||||
|
||||
/// <summary>解压缩的临时目录</summary>
|
||||
public String TempPath { get; set; }
|
||||
public String? TempPath { get; set; }
|
||||
#endregion
|
||||
|
||||
#region 构造
|
||||
|
@ -303,7 +303,7 @@ public class Upgrade
|
|||
#endregion
|
||||
|
||||
#region 辅助
|
||||
private HttpClient _Client;
|
||||
private HttpClient? _Client;
|
||||
private HttpClient CreateClient()
|
||||
{
|
||||
if (_Client != null) return _Client;
|
||||
|
|
Loading…
Reference in New Issue