use std::process::Command;
use color_eyre::Result;
use color_eyre::eyre::bail;
pub fn install_systemd(binary: &str, group: &str) -> Result<()> {
install_binary(binary)?;
create_group(group)?;
add_user_to_grp(group);
write_service(binary, group)?;
println!("Reloading systemd...");
Command::new("systemctl").arg("daemon-reload").status()?;
enable_service()?;
println!("natmap installed and running.");
println!("Use `systemctl status natmap` to check.");
println!("Use `lab-ops natmap ls` to see mappings (after re-login for group membership).");
Ok(())
}
fn install_binary(binary: &str) -> Result<()> {
let current_exe = std::env::current_exe()?;
let target = std::path::Path::new(binary);
if std::fs::canonicalize(¤t_exe).ok().as_ref()
!= std::fs::canonicalize(target).ok().as_ref()
{
println!(
"Installing binary: {} -> {}",
current_exe.display(),
target.display()
);
std::fs::copy(¤t_exe, target)?;
let _ = Command::new("chmod").args(["755", binary]).status();
} else {
println!("Binary already at {}, skipping copy.", target.display());
}
Ok(())
}
fn create_group(group: &str) -> Result<()> {
let group_exists = Command::new("getent")
.args(["group", group])
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if !group_exists {
println!("Creating group '{group}'...");
let status = Command::new("groupadd")
.args(["--system", group])
.status()?;
if !status.success() {
bail!("Failed to create group '{group}'");
}
println!("Group '{group}' created.");
}
Ok(())
}
fn add_user_to_grp(group: &str) {
if let Ok(user) = std::env::var("USER")
&& !user.is_empty()
&& user != "root"
{
println!("Adding user '{user}' to group '{group}'...");
let _ = Command::new("usermod")
.args(["-a", "-G", group, &user])
.status();
println!(
"User '{user}' added to group '{group}'. You may need to re-login for this to take effect."
);
}
}
fn write_service(binary: &str, group: &str) -> Result<()> {
let state_dir = "/var/lib/natmap/state.json";
let rendered = include_str!("../assets/natmap.service")
.replace("{binary}", binary)
.replace("{state_dir}", state_dir)
.replace("{group}", group);
let path = std::path::Path::new("/etc/systemd/system/natmap.service");
println!("Writing systemd service to {}", path.display());
if path.exists() {
println!("Service file already exists, overwriting.");
}
std::fs::write(path, rendered)?;
Ok(())
}
fn enable_service() -> Result<()> {
println!("Enabling natmap service...");
let status = Command::new("systemctl")
.args(["enable", "--now", "natmap"])
.status()?;
if !status.success() {
bail!("Failed to enable natmap service");
}
Ok(())
}