aboutsummaryrefslogtreecommitdiffstats
path: root/zone_nspawn/src/nspawn.rs
blob: d0683c8264a9fea0be9066c01e4e1d9522871308 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::{ffi::OsStr, path::PathBuf, process::Command};

use async_trait::async_trait;
use tokio::sync::mpsc::UnboundedReceiver;
use wspty::{PtyCommand, PtyMaster};
use zone_core::{Container, Runtime};

use crate::{machine::MachineList, Error, Result};

#[derive(Default, Debug)]
pub struct NSpawn;

impl NSpawn {
    async fn spawn<O, S, C, A>(opts: O, cmd: C, kill_rx: UnboundedReceiver<()>) -> Result<PtyMaster>
    where
        O: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
        C: IntoIterator<Item = A>,
        A: AsRef<OsStr>,
    {
        let base_opts = ["--quiet", "--wait", "--collect", "--service-type=exec"];

        let mut proc = tokio::process::Command::new("systemd-run");

        proc.args(base_opts)
            .args(opts)
            .args(cmd)
            .env("TERM", "xterm-256color");

        PtyCommand::from(proc)
            .run(kill_rx)
            .await
            .map_err(Error::from)
    }
}

#[async_trait]
impl Runtime for NSpawn {
    type Error = crate::Error;

    fn list(&self) -> Result<Vec<Container>> {
        Command::new("machinectl")
            .arg("list")
            .args(["-o", "json"])
            .output()?
            .try_into()
            .map(MachineList::into)
    }

    fn create(&self, volume: PathBuf, container: &Container) -> Result<()> {
        let opts = [
            "--settings=trusted",
            "--quiet",
            "--private-users=no",
            "--link-journal=no",
            "--resolv-conf=off",
            "--timezone=off",
            "--capability=all",
            "--boot",
            "--bind-ro=/sys/module",
            "--bind-ro=/lib/modules",
        ];

        Command::new("systemd-nspawn")
            .arg("--machine")
            .arg(container.to_string())
            .arg("--directory")
            .arg(volume)
            .args(opts)
            .status()
            .map_err(Error::from)?
            .success()
            .then(|| ())
            .ok_or_else(|| Error::Initialization(container.to_owned()))
            .map_err(Error::from)
    }

    async fn attach(
        &self,
        container: Container,
        kill_rx: UnboundedReceiver<()>,
    ) -> Result<PtyMaster> {
        let opts = [
            &format!("--machine={}", container),
            "--pty",
            "--send-sighup",
        ];
        let cmd = ["/usr/bin/login", "-H", "-f", "root"];
        Self::spawn(opts, cmd, kill_rx).await
    }

    async fn run<C, S>(
        &self,
        container: Container,
        cmd: C,
        kill_rx: UnboundedReceiver<()>,
    ) -> Result<PtyMaster>
    where
        C: IntoIterator<Item = S>,
        C: std::marker::Send,
        S: AsRef<OsStr>,
    {
        let opts = [
            &format!("--machine={}", container),
            "--pty",
            "--send-sighup",
        ];

        Self::spawn(opts, cmd, kill_rx).await
    }

    fn shutdown(container: Container) -> Result<()> {
        Command::new("machinectl")
            .arg("poweroff")
            .arg(container.to_string())
            .status()?
            .success()
            .then(|| ())
            .ok_or(Error::Shutdown(container))
    }
}