server-re/decomp/Agent_Release_x64/porting.md
2026-10-15 09:08:48 -04:00

55 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Agent_Release_x64.exe — porting spec (Win32 → Linux)
This was kept from the removed Linux reconstruction `src/agent_release/`
(removed 2026-10-15). It records the facts about the original binary that a
re-implementation needs, and the nearest Linux equivalent for each Win32 call.
Sources: `evidence.md`, `cpp/AgentProcessControl.cpp`. Names are inferred;
the behaviour is static-analysis evidence only.
## Behaviour to reproduce
The servers being managed are read from config (`option\*.xml`, mapping not
yet recovered).
| Item | Behaviour |
|---|---|
| Single instance (`0x1400100b0`) | Named mutex `Global\RustyHearts_Agent` |
| Launch (`0x1400094e0`) | `CreateProcessW(NULL, "<exe> <arg>", …, CREATE_NEW_CONSOLE, …, workDir)`, console 700×110, fill attribute 0x17. Command line is limited to 260 characters (`MAX_PATH`). Returns the pid, or 0 on failure (logs `=======Server Fail=======`) |
| After each launch attempt | `Sleep(1000)` |
| Command line (`0x140008ea0`) | `<exe for server type> <code>`. An alternative argument builder `FUN_140027540` exists, but its output is unknown |
| Watcher (`0x140008c90`) | Polls `GetExitCodeProcess` every 10 ms. On exit it sends two RMIs to AgentManager, then relaunches if the per-server revive flag (+0xd5) or the global flag (+0x171) is set |
| Kill (`0x140009780`) | Toolhelp32 snapshot, case-insensitive image-name match, terminates the **first** match only (`TerminateProcess(h,0)`) |
| Main loop (`0x1400037a0`) | 100 ms tick, 1 s FPS counter. Optionally auto-starts servers after a configured delay (+0x178). At startup it kills a stale process of type 0x18 (identity unknown) |
## Win32 → Linux substitutions
| Original (address) | Win32 | Linux replacement | Behaviour difference |
|---|---|---|---|
| Single instance (`0x1400100b0`) | `CreateMutexW("Global\\RustyHearts_Agent")` | `flock(LOCK_EX\|LOCK_NB)` on `/tmp/RustyHearts_Agent.lock` | Lock file instead of a kernel object; still released on exit or crash |
| Launch (`0x1400094e0`) | `CreateProcessW(…, CREATE_NEW_CONSOLE, …, workDir)` + `STARTUPINFOW` console geometry | `posix_spawnp` + `POSIX_SPAWN_SETSID` + `posix_spawn_file_actions_addchdir_np` (glibc ≥ 2.29) | No console window; the command line is split on whitespace (no Windows quoting rules) |
| Watcher (`0x140008c90`) | `GetExitCodeProcess` polled every 10 ms | blocking `waitpid` on a detached thread | No polling. Reaping fixes the original handle leak; passing the pid by value fixes the original stack-pointer race |
| Kill (`0x140009780`) | Toolhelp32 + `OpenProcess` + `TerminateProcess(h,0)` | scan `/proc/*/cmdline` for the argv[0] basename with `strcasecmp`, then `kill(SIGKILL)` | Same rule: first match only, case-insensitive |
| Main-loop timer | `QueryPerformanceCounter` (`TimeSourceHRWin32`) | `std::chrono::steady_clock` | — |
| Thread start | `_beginthreadex` | `std::thread` | — |
| `Sleep` | `Sleep` | `std::this_thread::sleep_for` | — |
| Shutdown | console menu (`FUN_140001dc0`, not analysed) | `SIGINT` / `SIGTERM` | Different trigger |
## Original bugs (O)
- **Process handle leak:** the watcher never closes the process handle.
- **Stack-pointer race:** `LaunchProcess` hands the watcher a pointer to a
stack `PROCESS_INFORMATION`. It is only safe because of the
`Sleep(1000)` that follows.
- **Snapshot handle leak:** `KillProcessByName` leaks the snapshot handle on
the success path.
## Unknown / not recovered
- **ProudNet RMI to AgentManager and the game servers:** proprietary
transport. Wire-incompatible with ProudNet 1.8 (see the ProudNet section of
`third-party/README.md`). The proxy vtable slots used are `+0x40` and `+0x50`.
- **XML config field mapping** for the Agent.
- **`FUN_140027540` argument builder; the `type == 1` exit follow-up
(`FUN_140009f20`); the type 0x18 process killed at startup.**
- **Locking around the server table:** not visible in the analysed code.