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))
}
}
|