simplified bootstrapper script and introduced --install parameter for agent

This commit is contained in:
Oliver Kieselbach
2026-02-13 17:29:48 +01:00
parent 731894bc04
commit 65a20febcb
3 changed files with 194 additions and 51 deletions
+184
View File
@@ -15,6 +15,13 @@ namespace AutopilotMonitor.Agent
{
static void Main(string[] args)
{
// Install mode: ensure folders, deploy payload, create/update Scheduled Task, and start it.
if (args.Contains("--install"))
{
RunInstallMode(args);
return;
}
// Check for mass rollout simulator mode
if (args.Contains("--simulator-mass-rollout"))
{
@@ -28,6 +35,86 @@ namespace AutopilotMonitor.Agent
RunAgent(args);
}
static void RunInstallMode(string[] args)
{
var logDir = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Logs");
var logger = new AgentLogger(logDir, enableDebug: true);
var consoleMode = args.Contains("--console") || Environment.UserInteractive;
try
{
logger.Info("======================= Agent install mode (--install) =======================");
EnsureAgentDirectories(logger);
var sourceExePath = Assembly.GetExecutingAssembly().Location;
var sourceDir = Path.GetDirectoryName(sourceExePath) ?? string.Empty;
var targetAgentDir = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor\Agent");
var targetExePath = Path.Combine(targetAgentDir, "AutopilotMonitor.Agent.exe");
if (!string.Equals(
Path.GetFullPath(sourceDir).TrimEnd('\\'),
Path.GetFullPath(targetAgentDir).TrimEnd('\\'),
StringComparison.OrdinalIgnoreCase))
{
logger.Info($"Install mode called from '{sourceDir}'. Deploying payload to '{targetAgentDir}'.");
CopyDirectory(sourceDir, targetAgentDir, logger);
}
else
{
logger.Info("Agent already running from target install directory; payload copy not required.");
}
var taskName = "AutopilotMonitor-Agent";
var taskCommand = $"\"{targetExePath}\"";
logger.Info($"Registering Scheduled Task '{taskName}' for executable: {targetExePath}");
var createExitCode = RunProcess(
"schtasks.exe",
$"/Create /TN \"{taskName}\" /TR \"{taskCommand}\" /SC ONSTART /RU SYSTEM /RL HIGHEST /F",
logger);
if (createExitCode != 0)
{
throw new InvalidOperationException($"Failed to create/update Scheduled Task '{taskName}' (exit code {createExitCode})");
}
logger.Info($"Scheduled Task '{taskName}' created/updated successfully");
var startExitCode = RunProcess(
"schtasks.exe",
$"/Run /TN \"{taskName}\"",
logger);
if (startExitCode != 0)
{
throw new InvalidOperationException($"Failed to start Scheduled Task '{taskName}' (exit code {startExitCode})");
}
logger.Info($"Scheduled Task '{taskName}' started successfully");
if (consoleMode)
{
Console.WriteLine("Installation completed successfully.");
Console.WriteLine($"Task: {taskName}");
Console.WriteLine($"Executable: {targetExePath}");
Console.WriteLine($"Log: {logDir}");
}
Environment.Exit(0);
}
catch (Exception ex)
{
logger.Error("Registration mode failed", ex);
if (consoleMode)
{
Console.WriteLine($"ERROR: {ex.Message}");
}
Environment.Exit(1);
}
}
static void RunAgent(string[] args)
{
var consoleMode = args.Contains("--console") || Environment.UserInteractive;
@@ -416,6 +503,103 @@ namespace AutopilotMonitor.Agent
return null;
}
private static void EnsureAgentDirectories(AgentLogger logger)
{
var basePath = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor");
var paths = new[]
{
basePath,
Path.Combine(basePath, "Agent"),
Path.Combine(basePath, "Spool"),
Path.Combine(basePath, "Logs")
};
foreach (var path in paths)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
logger.Info($"Created directory: {path}");
}
else
{
logger.Debug($"Directory already exists: {path}");
}
}
}
private static int RunProcess(string fileName, string arguments, AgentLogger logger)
{
var psi = new ProcessStartInfo
{
FileName = fileName,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
logger.Info($"Executing: {fileName} {arguments}");
using (var process = Process.Start(psi))
{
if (process == null)
throw new InvalidOperationException($"Failed to start process: {fileName}");
var stdout = process.StandardOutput.ReadToEnd();
var stderr = process.StandardError.ReadToEnd();
process.WaitForExit();
if (!string.IsNullOrWhiteSpace(stdout))
logger.Info($"Process output: {stdout.Trim()}");
if (!string.IsNullOrWhiteSpace(stderr))
logger.Warning($"Process error output: {stderr.Trim()}");
logger.Info($"Process exit code: {process.ExitCode}");
return process.ExitCode;
}
}
private static void CopyDirectory(string sourceDir, string targetDir, AgentLogger logger)
{
if (!Directory.Exists(sourceDir))
throw new DirectoryNotFoundException($"Source directory not found: {sourceDir}");
Directory.CreateDirectory(targetDir);
foreach (var dir in Directory.GetDirectories(sourceDir, "*", SearchOption.AllDirectories))
{
var rel = dir.Substring(sourceDir.Length).TrimStart('\\');
Directory.CreateDirectory(Path.Combine(targetDir, rel));
}
foreach (var file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories))
{
var rel = file.Substring(sourceDir.Length).TrimStart('\\');
var dest = Path.Combine(targetDir, rel);
Directory.CreateDirectory(Path.GetDirectoryName(dest) ?? targetDir);
try
{
File.Copy(file, dest, true);
}
catch (Exception ex)
{
// If destination exists, keep going and let scheduled task use the latest available payload.
if (File.Exists(dest))
{
logger.Warning($"Could not overwrite '{dest}': {ex.Message}. Keeping existing file.");
continue;
}
throw;
}
}
logger.Info($"Payload deployment completed: '{sourceDir}' -> '{targetDir}'");
}
/// <summary>
/// Checks if another AutopilotMonitor.Agent.exe process is already running.
/// </summary>
@@ -1 +1 @@
9
12