Last active
April 17, 2023 08:39
-
-
Save berichan/f932188501c1e31c864b488f1c175ee0 to your computer and use it in GitHub Desktop.
A very simple cross-platform C# / .NET Core program that restarts an application whever it dies or every x seconds/minutes/hours/days (whatever you need really) and optionally looks out for a line in the output or console and restarts whenever it sees it after the restart period.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a very simple program that takes in a config file (config.txt) | |
// in the same directory as the application and creates a process with | |
// (optional, can stay blank) arguments, and restarts it whenever it | |
// dies or (optionally) every x number of seconds/minutes/hours/days etc. | |
// You can also have it set up to look out for a certain line in the | |
// output and restart it whenever it sees that line (uses String.Contains) | |
// Can obviously be much cleaner. | |
// 2020 Berichan https://berichan.net/ | |
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Threading; | |
namespace ProcessRestarter | |
{ | |
class Program | |
{ | |
private static string Commands; // all values are comma ',' separated | |
//executable path,args,checkalive rate (seconds),(optional) restart rate (seconds),(optional) command to look for in output before restart is called (contains) | |
private static string ConfigPath = Path.Combine(Directory.GetCurrentDirectory(), "config.txt"); | |
private static Process currentRunningProcess; | |
private static string lastLine; | |
public static void Main(string[] args) | |
{ | |
if (!File.Exists(ConfigPath)) | |
{ | |
Console.WriteLine("Rstr: No config file in this directory"); | |
return; | |
} | |
Commands = File.ReadAllText(ConfigPath); | |
var split = Commands.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); | |
if (split.Length < 3) | |
{ | |
Console.WriteLine("Rstr: Invalid config format."); | |
return; | |
} | |
AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit); | |
var checkAliveRate = int.Parse(split[2]) * 1000; | |
var restartRate = -1; | |
var timer = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); | |
if (split.Length > 3) | |
restartRate = int.Parse(split[3]); | |
var commandToLookOutFor = string.Empty; | |
if (split.Length > 4) | |
commandToLookOutFor = split[4]; | |
currentRunningProcess = startProcessCommand(split[0], split[1]); | |
while (true) | |
{ | |
while (!currentRunningProcess.HasExited) | |
{ | |
Thread.Sleep(checkAliveRate); | |
if (restartRate != -1) | |
{ | |
if (DateTimeOffset.UtcNow.ToUnixTimeSeconds() - timer > restartRate) | |
{ | |
if (commandToLookOutFor != string.Empty) | |
{ | |
Console.WriteLine($"Rstr: Waiting for line: {commandToLookOutFor}"); | |
while (true) | |
{ | |
if (lastLine.Contains(commandToLookOutFor)) | |
{ | |
timer = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); | |
Console.WriteLine($"Rstr: Found {commandToLookOutFor}. Process needs to be restarted at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}. Restarting..."); | |
currentRunningProcess.Kill(); | |
currentRunningProcess = startProcessCommand(split[0], split[1]); | |
break; | |
} | |
} | |
} | |
else | |
{ | |
timer = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); | |
Console.WriteLine($"Rstr: Process needs to be restarted at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}. Restarting..."); | |
currentRunningProcess.Kill(); | |
currentRunningProcess = startProcessCommand(split[0], split[1]); | |
} | |
} | |
} | |
} | |
Console.WriteLine($"Rstr: Process dead at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}. Exit Code: ${currentRunningProcess.ExitCode} Restarting..."); | |
currentRunningProcess = startProcessCommand(split[0], split[1]); | |
} | |
} | |
static Process startProcessCommand(string proc, string args) | |
{ | |
var prc = new Process | |
{ | |
StartInfo = new ProcessStartInfo | |
{ | |
FileName = proc, | |
Arguments = args, | |
UseShellExecute = false, | |
RedirectStandardOutput = true, | |
CreateNoWindow = true, | |
RedirectStandardError = true | |
} | |
}; | |
prc.OutputDataReceived += (p, a) => | |
{ | |
if (a.Data != null) | |
{ | |
lastLine = a.Data; | |
Console.WriteLine(lastLine); | |
} | |
}; | |
prc.Start(); | |
prc.BeginOutputReadLine(); | |
Console.WriteLine("Rstr: Started process with pid: " + prc.Id.ToString()); | |
return prc; | |
} | |
static void OnProcessExit(object sender, EventArgs e) | |
{ | |
if (currentRunningProcess != null) | |
if (!currentRunningProcess.HasExited) | |
currentRunningProcess.Kill(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment