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
|
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is>
mod ch;
mod modprobe;
mod net;
use std::env::{args, current_dir};
use std::ffi::{CString, OsString};
use std::io::{self, ErrorKind};
use std::os::unix::prelude::*;
use std::path::PathBuf;
use std::process::{exit, Command};
use modprobe::modprobe;
use net::{format_mac, net_setup, NetConfig};
macro_rules! errx {
($code:expr, $fmt:expr $(,$args:expr)*) => ({
let argv0_option = args().next();
let argv0 = argv0_option.as_ref().map(String::as_str).unwrap_or("start-vm");
eprintln!(concat!("{}: ", $fmt), argv0 $(,$args)*);
exit($code);
})
}
macro_rules! err {
($code:expr, $fmt:expr $(,$args:expr)*) =>
(|e| errx!($code, concat!($fmt, ": {}") $(,$args)*, e))
}
fn main() {
modprobe(&["kvm-intel"]).unwrap_or_else(err!(1, "modprobe kvm-intel"));
let mut command = Command::new("s6-notifyoncheck");
command.args(&["-dc", "test -S env/cloud-hypervisor.sock"]);
command.arg("cloud-hypervisor");
command.args(&["--api-socket", "env/cloud-hypervisor.sock"]);
command.args(&["--cmdline", "console=ttyS0 root=/dev/vda"]);
command.args(&["--memory", "size=128M"]);
command.args(&["--console", "pty"]);
let dir = current_dir()
.unwrap_or_else(err!(1, "getting current directory"))
.into_os_string()
.into_vec();
let dir = PathBuf::from(OsString::from_vec(dir));
let vm_name = dir
.file_name()
.unwrap_or_else(|| errx!(1, "current directory has no name"));
let mut net_providers_dir = PathBuf::new();
net_providers_dir.push("/ext/svc/data");
net_providers_dir.push(vm_name);
net_providers_dir.push("providers/net");
match net_providers_dir.read_dir() {
Ok(entries) => {
for r in entries {
let entry = r
.unwrap_or_else(err!(1, "examining directory entry"))
.file_name();
// Safe because prov is the name of a directory entry, so
// con't contain a null byte.
let provider_name = unsafe { CString::from_vec_unchecked(entry.into_vec()) };
// Safe because we pass a valid pointer and check the result.
let NetConfig { fd, mac } = unsafe { net_setup(provider_name.as_ptr()) };
if fd == -1 {
let e = io::Error::last_os_error();
errx!(1, "setting up networking failed: {}", e);
}
command
.arg("--net")
.arg(format!("fd={},mac={}", fd, format_mac(&mac)));
// TODO: to support multiple net providers, we'll need
// a better naming scheme for tap and bridge devices.
break;
}
}
Err(e) if e.kind() == ErrorKind::NotFound => {}
Err(e) => errx!(1, "reading directory {:?}: {}", net_providers_dir, e),
}
command.arg("--kernel").arg({
let mut kernel = OsString::from("/ext/svc/data/");
kernel.push(&vm_name);
kernel.push("/vmlinux");
kernel
});
command.arg("--disk").arg({
let mut disk = OsString::from("path=/ext/svc/data/");
disk.push(&vm_name);
disk.push("/rootfs.ext4,readonly=on");
disk
});
command.arg("--serial").arg({
let mut serial = OsString::from("file=/run/");
serial.push(&vm_name);
serial.push(".log");
serial
});
errx!(1, "failed to exec: {}", command.exec());
}
|