Skip to main content

lab_ops_natmap/
install.rs

1//! Systemd installation support for the natmap daemon.
2
3use std::process::Command;
4
5use color_eyre::Result;
6use color_eyre::eyre::bail;
7
8/// Installs the natmap daemon as a systemd service.
9///
10/// Performs the following steps:
11///
12/// 1. Copies the current binary to `binary` (unless already at that path).
13/// 2. Creates the `group` system group if it does not exist.
14/// 3. Adds the current user to the group (requires re-login to take effect).
15/// 4. Writes a systemd service unit to `/etc/systemd/system/natmap.service`.
16/// 5. Runs `systemctl daemon-reload`, then `systemctl enable --now natmap`.
17pub fn install_systemd(binary: &str, group: &str) -> Result<()> {
18    install_binary(binary)?;
19    create_group(group)?;
20    add_user_to_grp(group);
21    write_service(binary, group)?;
22
23    // Reload systemd
24    println!("Reloading systemd...");
25    Command::new("systemctl").arg("daemon-reload").status()?;
26
27    enable_service()?;
28
29    println!("natmap installed and running.");
30    println!("Use `systemctl status natmap` to check.");
31    println!("Use `lab-ops natmap ls` to see mappings (after re-login for group membership).");
32
33    Ok(())
34}
35
36/// Copy current binary to the target path (unless already there)
37fn install_binary(binary: &str) -> Result<()> {
38    let current_exe = std::env::current_exe()?;
39    let target = std::path::Path::new(binary);
40
41    if std::fs::canonicalize(&current_exe).ok().as_ref()
42        != std::fs::canonicalize(target).ok().as_ref()
43    {
44        println!(
45            "Installing binary: {} -> {}",
46            current_exe.display(),
47            target.display()
48        );
49        std::fs::copy(&current_exe, target)?;
50        let _ = Command::new("chmod").args(["755", binary]).status();
51    } else {
52        println!("Binary already at {}, skipping copy.", target.display());
53    }
54
55    Ok(())
56}
57
58/// Create group if not exists
59fn create_group(group: &str) -> Result<()> {
60    let group_exists = Command::new("getent")
61        .args(["group", group])
62        .output()
63        .map(|o| o.status.success())
64        .unwrap_or(false);
65
66    if !group_exists {
67        println!("Creating group '{group}'...");
68        let status = Command::new("groupadd")
69            .args(["--system", group])
70            .status()?;
71        if !status.success() {
72            bail!("Failed to create group '{group}'");
73        }
74        println!("Group '{group}' created.");
75    }
76
77    Ok(())
78}
79
80/// Add current user to group
81fn add_user_to_grp(group: &str) {
82    if let Ok(user) = std::env::var("USER")
83        && !user.is_empty()
84        && user != "root"
85    {
86        println!("Adding user '{user}' to group '{group}'...");
87        let _ = Command::new("usermod")
88            .args(["-a", "-G", group, &user])
89            .status();
90        println!(
91            "User '{user}' added to group '{group}'. You may need to re-login for this to take effect."
92        );
93    }
94}
95
96/// Write service file with placeholder substitution
97fn write_service(binary: &str, group: &str) -> Result<()> {
98    let state_dir = "/var/lib/natmap/state.json";
99
100    let rendered = include_str!("../assets/natmap.service")
101        .replace("{binary}", binary)
102        .replace("{state_dir}", state_dir)
103        .replace("{group}", group);
104    let path = std::path::Path::new("/etc/systemd/system/natmap.service");
105    println!("Writing systemd service to {}", path.display());
106    if path.exists() {
107        println!("Service file already exists, overwriting.");
108    }
109    std::fs::write(path, rendered)?;
110
111    Ok(())
112}
113
114/// Enable and start
115fn enable_service() -> Result<()> {
116    println!("Enabling natmap service...");
117    let status = Command::new("systemctl")
118        .args(["enable", "--now", "natmap"])
119        .status()?;
120    if !status.success() {
121        bail!("Failed to enable natmap service");
122    }
123
124    Ok(())
125}