Network-agnostic, air-gapped pod install for the two DOSBox titles (BT/RP 4.10), fitting the existing TeslaConsole/TeslaLauncher pod-bay architecture. emulator/DEPLOYMENT-PLAN.md Full design: bridge on the one NIC (pods form a source-proven P2P TCP mesh, so NAT/slirp is out); launcher keeps the bay IP, the DOSBox guest bridges at bayIP+100 (egg/mesh/game endpoint); two-edit +100 convention (postinstall + console DOSBox flag); static, air-gapped, <=32 pods. emulator/pod-launch/ (C# net8 supervising entry-point) Creates a Job Object (KILL_ON_JOB_CLOSE), launches DOSBox-X + the render bridge into it and blocks -- kill this process and both die (kernel-enforced, even on a hard TerminateProcess of a hung session; verified). Mode dispatch: bt/rp wired (also serve camera + live mission-review via egg hostType); review + diagnostics recognized but fail clean until their DOS launch args are known. emulator/deploy/ (install side) postinstall.bat (thin elevated wrapper) + configure.ps1 (NIC detect, realnic bind as a contiguous letter-leading GUID fragment, game IP = bayIP+100, stable MAC, render net_*.conf templates, stamp WATTCP.CFG my_ip) + tokenized conf templates + package.ps1 (assemble the zip). Render/bind/stamp + packaging verified against a scratch tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
54 lines
2.2 KiB
C#
54 lines
2.2 KiB
C#
// Launch a child process and put it in the job immediately, redirecting its
|
|
// stdout/stderr to log files (matches the pod_out/err.txt + bridge_out/err.txt
|
|
// the old launch_pod.ps1 produced).
|
|
//
|
|
// The assign happens right after Start(), before DOSBox/pythonw has a chance to
|
|
// spawn anything, so the whole tree is captured. (A CREATE_SUSPENDED launch
|
|
// would close even that microscopic gap; neither of these children forks at
|
|
// startup, so Start()+assign is sufficient here.)
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
|
|
namespace VwePod
|
|
{
|
|
internal static class ChildProcess
|
|
{
|
|
public static Process StartInJob(
|
|
JobObject job, string exe, string args,
|
|
IDictionary<string, string> extraEnv,
|
|
string outPath, string errPath, string workingDir)
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = exe,
|
|
Arguments = args,
|
|
UseShellExecute = false, // inherit our env; allow job assign
|
|
CreateNoWindow = false, // children make their own windows
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
};
|
|
if (!string.IsNullOrEmpty(workingDir)) psi.WorkingDirectory = workingDir;
|
|
if (extraEnv != null)
|
|
foreach (var kv in extraEnv) psi.Environment[kv.Key] = kv.Value;
|
|
|
|
var proc = new Process { StartInfo = psi };
|
|
|
|
var outWriter = new StreamWriter(outPath, false) { AutoFlush = true };
|
|
var errWriter = new StreamWriter(errPath, false) { AutoFlush = true };
|
|
proc.OutputDataReceived += (s, e) => { if (e.Data != null) lock (outWriter) outWriter.WriteLine(e.Data); };
|
|
proc.ErrorDataReceived += (s, e) => { if (e.Data != null) lock (errWriter) errWriter.WriteLine(e.Data); };
|
|
proc.Exited += (s, e) => { try { outWriter.Dispose(); errWriter.Dispose(); } catch { } };
|
|
proc.EnableRaisingEvents = true;
|
|
|
|
proc.Start();
|
|
job.Add(proc.Handle); // capture before it can spawn a child
|
|
proc.BeginOutputReadLine();
|
|
proc.BeginErrorReadLine();
|
|
return proc;
|
|
}
|
|
}
|
|
}
|