towerops-agent/build.rs
Graham McIntire 55d001d9f8
Add fork()-based process isolation for SNMP operations
Each SNMP GET/WALK now runs in a forked child process. If libnetsnmp
triggers a SIGSEGV or other fatal signal, only the child dies - the
parent agent logs the crash and continues operating for all other devices.

Key changes:
- C helper: snmp_get_isolated() and snmp_walk_isolated() using
  fork+pipe pattern with 60s alarm watchdog and mutex-serialized forks
- Rust: IsolationMode enum (Fork/Direct) controlled by
  TOWEROPS_SNMP_ISOLATION env var, defaults to Fork
- New CrashRecovered error variant with signal info and logging
- Device poller logs crash recovery events at error level
- Startup logs active isolation mode

Set TOWEROPS_SNMP_ISOLATION=direct to disable isolation for debugging.
2026-02-10 16:12:41 -06:00

42 lines
1.4 KiB
Rust

fn main() {
// Compile protobuf definitions
prost_build::compile_protos(&["proto/agent.proto"], &["proto/"]).unwrap();
// Compile C helper for SNMP
cc::Build::new()
.file("native/snmp_helper.c")
.include("native")
.define("SNMP_HELPER_TEST", None)
.compile("snmp_helper");
// Link against netsnmp library
println!("cargo:rustc-link-lib=netsnmp");
// On macOS with Homebrew, net-snmp depends on OpenSSL which is keg-only
// (not linked into /usr/local/lib). Add the OpenSSL library path so the
// linker can find libcrypto.
#[cfg(target_os = "macos")]
{
if let Ok(output) = std::process::Command::new("brew")
.args(["--prefix", "openssl@3"])
.output()
{
if output.status.success() {
let prefix = String::from_utf8_lossy(&output.stdout).trim().to_string();
println!("cargo:rustc-link-search={}/lib", prefix);
}
}
}
// Inject compile timestamp as version
// This allows tracking when a specific agent binary was built
let version = get_version();
println!("cargo:rustc-env=BUILD_VERSION={}", version);
}
fn get_version() -> String {
// Generate RFC 3339 timestamp at compile time
// Format: YYYY-MM-DDTHH:MM:SSZ
let now = chrono::Utc::now();
now.format("%Y-%m-%dT%H:%M:%SZ").to_string()
}