170 lines
5.2 KiB
C#
170 lines
5.2 KiB
C#
using System.Diagnostics;
|
|
using System.IO;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
using Livia.Properties;
|
|
using Livia.Utility;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Livia.Models;
|
|
|
|
public interface IProcessManager
|
|
{
|
|
bool Start(ServerFileLocation[] serverFileLocations);
|
|
}
|
|
|
|
public readonly struct ServerFileLocation
|
|
{
|
|
public ServerFileLocation(string fileName, string workingDirectory)
|
|
{
|
|
FileName = fileName;
|
|
FileNameNoExtension = Path.GetFileNameWithoutExtension(fileName);
|
|
WorkingDirectory = workingDirectory;
|
|
FullPath = Path.Combine(WorkingDirectory, FileName);
|
|
}
|
|
|
|
public readonly string FileName;
|
|
public readonly string FileNameNoExtension;
|
|
public readonly string WorkingDirectory;
|
|
public readonly string FullPath;
|
|
}
|
|
|
|
|
|
public class ProcessManager : ObservableRecipient, IProcessManager, IRecipient<ShutdownMessage>
|
|
{
|
|
|
|
private readonly ILogger _logger;
|
|
private ServerFileLocation[] _serverFileLocations = Array.Empty<ServerFileLocation>();
|
|
private Process[] _servers = Array.Empty<Process>();
|
|
private const int MaxRestartCount = 10;
|
|
private int _restartCount;
|
|
|
|
public ProcessManager(ILogger logger)
|
|
{
|
|
_logger = logger;
|
|
WeakReferenceMessenger.Default.RegisterAll(this);
|
|
}
|
|
|
|
private Process StartProcess(ServerFileLocation fileLocation)
|
|
{
|
|
_logger.LogInformation("Starting service");
|
|
Process process = new();
|
|
process.StartInfo.FileName = fileLocation.FileName;
|
|
process.StartInfo.WorkingDirectory = fileLocation.WorkingDirectory;
|
|
process.StartInfo.UseShellExecute = true;
|
|
process.StartInfo.CreateNoWindow = true;
|
|
process.EnableRaisingEvents = true;
|
|
process.StartInfo.WindowStyle = Settings.Default.DeveloperMode ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden;
|
|
process.Exited += ProcessOnExited;
|
|
if (!process.Start())
|
|
{
|
|
throw new Exception("Failed to start process");
|
|
}
|
|
return process;
|
|
}
|
|
|
|
private void ProcessOnExited(object? sender, EventArgs e)
|
|
{
|
|
if (_restartCount >= MaxRestartCount)
|
|
{
|
|
_logger.LogCritical("ProcessManager MaxRestartCount reached");
|
|
return;
|
|
}
|
|
_logger.LogCritical("Server process has exited! Attempting to restart it");
|
|
|
|
for (int i = 0; i < _servers.Length; i++)
|
|
{
|
|
if (sender != _servers[i])
|
|
continue;
|
|
|
|
_restartCount++;
|
|
_servers[i] = StartProcess(_serverFileLocations[i]);
|
|
return;
|
|
}
|
|
|
|
_logger.LogCritical("Cannot determine which process has exited!");
|
|
}
|
|
|
|
private void CleanUp()
|
|
{
|
|
foreach (Process process in _servers)
|
|
{
|
|
//disable this so that we are not going to attempt to restart it
|
|
process.EnableRaisingEvents = false;
|
|
}
|
|
|
|
KillExisting();
|
|
|
|
//remove everything from temp
|
|
string[] folders = Directory.GetDirectories(Path.GetTempPath());
|
|
foreach (string folderPath in folders)
|
|
{
|
|
DirectoryInfo info = new(folderPath);
|
|
string folderName = info.Name;
|
|
if (!folderName.StartsWith("_MEI"))
|
|
continue;
|
|
|
|
//pretty new, wait and see if it will be auto deleted
|
|
TimeSpan folderAge = DateTime.Now - info.LastWriteTime;
|
|
if (folderAge < TimeSpan.FromDays(1))
|
|
continue;
|
|
|
|
try
|
|
{
|
|
_logger.LogInformation("{folder} is {age} old, deleting it", folderPath, folderAge);
|
|
Directory.Delete(folderPath, true);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogInformation(e, "Error Deleting {folder}", folderPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void KillExisting()
|
|
{
|
|
//find all and kill them
|
|
foreach (ServerFileLocation fileLocation in _serverFileLocations)
|
|
{
|
|
try
|
|
{
|
|
Process[] processes = Process.GetProcessesByName(fileLocation.FileNameNoExtension);
|
|
Process? lastCreatedProcess = processes.Where(process => process.MainModule?.FileName == fileLocation.FullPath).MaxBy(process => process.StartTime);
|
|
|
|
lastCreatedProcess?.Kill();
|
|
lastCreatedProcess?.WaitForExit();
|
|
lastCreatedProcess?.Dispose();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError(e, "Error killing process{process}", fileLocation.FileNameNoExtension);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Start(ServerFileLocation[] serverFileLocations)
|
|
{
|
|
KillExisting();
|
|
_serverFileLocations = serverFileLocations;
|
|
_servers = new Process[_serverFileLocations.Length];
|
|
try
|
|
{
|
|
for (int i = 0; i < _serverFileLocations.Length; i++)
|
|
{
|
|
_servers[i] = StartProcess(_serverFileLocations[i]);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogCritical(ex, "Cannot start server");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Receive(ShutdownMessage message)
|
|
{
|
|
CleanUp();
|
|
}
|
|
} |