rpc calls!!!

main
eater 3 years ago
parent 58eba9a315
commit 7411bb5972
Signed by: eater
GPG Key ID: AD2560A0F84F0759

236
Cargo.lock generated

@ -1,5 +1,23 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.40"
@ -12,6 +30,17 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
@ -61,6 +90,35 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
"yaml-rust",
]
[[package]]
name = "config"
version = "0.11.0"
@ -79,6 +137,19 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "erased-serde"
version = "0.3.13"
@ -88,6 +159,24 @@ dependencies = [
"serde",
]
[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "itertools"
version = "0.9.0"
@ -175,6 +264,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
@ -184,6 +283,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "paste"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
[[package]]
name = "pkg-config"
version = "0.3.19"
@ -203,6 +308,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "pretty_env_logger"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074"
dependencies = [
"chrono",
"env_logger",
"log",
]
[[package]]
name = "proc-macro2"
version = "1.0.26"
@ -266,6 +382,12 @@ dependencies = [
"serde_json",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.9"
@ -275,6 +397,23 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]]
name = "ryu"
version = "1.0.5"
@ -312,12 +451,37 @@ dependencies = [
"serde",
]
[[package]]
name = "signal-hook"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.70"
@ -329,6 +493,35 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "toml"
version = "0.5.8"
@ -338,12 +531,24 @@ dependencies = [
"serde",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
@ -355,6 +560,9 @@ name = "vore"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"log",
"pretty_env_logger",
"vore-core",
]
@ -368,7 +576,10 @@ dependencies = [
"kiam",
"lazy_static",
"libc",
"log",
"mlua",
"paste",
"pretty_env_logger",
"qapi",
"qapi-qmp",
"serde",
@ -381,10 +592,20 @@ name = "vored"
version = "0.1.0"
dependencies = [
"anyhow",
"libc",
"log",
"polling",
"pretty_env_logger",
"signal-hook",
"vore-core",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wepoll-sys"
version = "3.0.1"
@ -410,8 +631,23 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"

@ -18,4 +18,7 @@ beau_collector = "0.2.1"
qapi-qmp = "0.7.0"
qapi = { version = "0.7.0", features = ["qapi-qmp"] }
libc = "0.2.94"
lazy_static = "1.4.0"
lazy_static = "1.4.0"
paste = "1.0"
log = "0.4.14"
pretty_env_logger = "0.3"

@ -2,6 +2,8 @@ use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub const GLOBAL_CONFIG_LOCATION: &str = "/home/eater/projects/vored/config/global.toml";
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GlobalConfig {
pub qemu: GlobalQemuConfig,

@ -9,4 +9,13 @@ pub use global_config::*;
pub use instance_config::*;
pub use qemu::QemuCommandBuilder;
pub use virtual_machine::*;
use log::LevelFilter;
pub fn init_logging() {
let mut builder = pretty_env_logger::formatted_timed_builder();
#[cfg(debug_assertions)] {
builder.filter_level(LevelFilter::Debug);
}
builder.parse_filters(&std::env::var("RUST_LOG").unwrap_or("".to_string()));
builder.init();
}

@ -1,4 +1,4 @@
use crate::{GlobalConfig, InstanceConfig};
use crate::{GlobalConfig, InstanceConfig, GLOBAL_CONFIG_LOCATION};
use anyhow::Context;
use mlua::prelude::LuaError;
use mlua::{
@ -8,8 +8,9 @@ use mlua::{
use serde::ser::Error;
use serde::Deserialize;
use std::collections::HashMap;
use std::path::PathBuf;
use std::path::{PathBuf, Path};
use std::sync::{Arc, Mutex, Weak};
use std::fs;
#[derive(Debug, Default, Deserialize, Clone)]
struct VM {
@ -66,7 +67,7 @@ impl UserData for VM {
.and_modify(|x| *x += 1)
.or_insert(0)
)
.to_lua(lua)
.to_lua(lua)
});
methods.add_method_mut("get_counter", |lua, this, args: (String, usize)| {
@ -213,6 +214,7 @@ impl VoreLuaStorage {
pub struct QemuCommandBuilder {
lua: Lua,
script: String,
storage: VoreLuaStorage,
}
@ -221,8 +223,11 @@ impl QemuCommandBuilder {
global: &GlobalConfig,
working_dir: PathBuf,
) -> Result<QemuCommandBuilder, anyhow::Error> {
let lua = Path::new(GLOBAL_CONFIG_LOCATION).join(&global.qemu.script);
let builder = QemuCommandBuilder {
lua: Lua::new(),
script: fs::read_to_string(lua)?,
storage: VoreLuaStorage::new(working_dir),
};
@ -250,9 +255,8 @@ impl QemuCommandBuilder {
}
pub fn build(self, config: &InstanceConfig) -> Result<Vec<String>, anyhow::Error> {
// TODO: load correct script
self.lua
.load(include_str!("../../config/qemu.lua"))
.load(&self.script)
.eval::<()>()
.context("Failed to run the configured qemu lua script")?;

@ -0,0 +1,101 @@
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use paste::paste;
use crate::rpc::{Request, Response};
use crate::VirtualMachineInfo;
macro_rules! define_requests {
($($name:ident($req:tt, $resp:tt))+) => {
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "query", rename_all = "snake_case")]
pub enum AllRequests {
$($name(paste! { [<$name Request >] })),+
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged, rename_all = "snake_case")]
pub enum AllResponses {
$($name(paste! { [<$name Response >] })),+
}
$(
paste! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct [<$name Request>] $req
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct [<$name Response>] $resp
impl Request for [<$name Request>] {
type Response = [<$name Response>];
fn into_enum(self) -> AllRequests {
AllRequests::$name(self)
}
}
impl Response for [<$name Response>] {
fn into_enum(self) -> AllResponses {
AllResponses::$name(self)
}
}
}
)+
};
}
impl Request for AllRequests {
type Response = AllResponses;
fn into_enum(self) -> AllRequests {
self
}
}
impl Response for AllResponses {
fn into_enum(self) -> AllResponses {
self
}
}
define_requests! {
Info({}, {
pub name: String,
pub version: String
})
List({}, {
pub items: Vec<VirtualMachineInfo>
})
Load({
pub toml: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cdroms: Vec<String>,
#[serde(default)]
pub save: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directory: Option<String>,
}, {
pub info: VirtualMachineInfo,
})
Prepare({
pub name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cdroms: Vec<String>,
}, {})
Start({
pub name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cdroms: Vec<String>,
}, {})
Stop({
pub name: String,
}, {})
Unload({
pub name: String,
}, {})
}

@ -0,0 +1,7 @@
mod calls;
mod serde;
mod traits;
pub use calls::*;
pub use crate::rpc::serde::*;
pub use traits::*;

@ -0,0 +1,72 @@
use crate::rpc::{Command, Request, Answer, AnswerResult, AnswerError, Response};
use std::fmt::{Display, Formatter};
use std::fmt;
use std::error::Error;
#[derive(Debug, Default)]
pub struct CommandCenter {
id: u64,
}
impl CommandCenter {
pub fn write_command<R: Request>(&mut self, request: R) -> Result<(u64, String), anyhow::Error> {
let command = Command {
id: self.id,
data: request.into_enum(),
};
self.id += 1;
let mut str = serde_json::to_string(&command)?;
str.push('\n');
Ok((command.id, str))
}
pub fn write_answer<R: Response>(request: &Command, answer: Result<R, anyhow::Error>) -> Result<String, anyhow::Error> {
let answer = Answer {
id: request.id,
data: match answer {
Ok(data) => AnswerResult::Ok(data),
Err(err) => AnswerResult::Error(AnswerError {
error: format!("{:?}", err)
})
},
};
let mut str = serde_json::to_string(&answer)?;
str.push('\n');
return Ok(str);
}
pub fn read_command(request: &str) -> Result<Command, anyhow::Error> {
serde_json::from_str(request).map_err(From::from)
}
pub fn read_answer<R: Request>(answer: &str) -> Result<(u64, R::Response), CommandError> {
let answer_obj: Answer<R::Response> = serde_json::from_str(answer).map_err(|err| CommandError::InternalError(err.into()))?;
match answer_obj.data {
AnswerResult::Error(err) => Err(CommandError::AnswerError(answer_obj.id, err)),
AnswerResult::Ok(data) => Ok((answer_obj.id, data))
}
}
}
#[derive(Debug)]
pub enum CommandError {
AnswerError(u64, AnswerError),
InternalError(anyhow::Error),
}
impl Display for CommandError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
CommandError::AnswerError(idx, err) => {
write!(f, "{}\n(rpc call {})", err.error, idx)
}
CommandError::InternalError(err) => err.fmt(f)
}
}
}
impl Error for CommandError {}

@ -0,0 +1,42 @@
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use crate::rpc::{AllRequests, AllResponses};
use serde::de::DeserializeOwned;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Command {
pub id: u64,
#[serde(flatten)]
pub data: AllRequests,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Answer<R: Response> {
pub(crate) id: u64,
#[serde(flatten, bound = "R: Response")]
pub(crate) data: AnswerResult<R>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AnswerResult<R: Response> {
Error(AnswerError),
#[serde(bound = "R: Response")]
Ok(R),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AnswerError {
pub(crate) error: String,
}
pub trait Request: Serialize + DeserializeOwned + Clone + Debug {
type Response: Response;
fn into_enum(self) -> AllRequests;
}
pub trait Response: Serialize + DeserializeOwned + Clone + Debug + Sized {
fn into_enum(self) -> AllResponses;
}

@ -4,13 +4,13 @@ use beau_collector::BeauCollector;
use qapi::qmp::{QMP, Event};
use qapi::{Qmp, ExecuteError};
use std::{fmt, mem};
use std::fmt::{Debug, Formatter};
use std::fmt::{Debug, Formatter, Display};
use std::fs::{read_link, OpenOptions, read_dir};
use std::io;
use std::io::{BufReader, ErrorKind, Read, Write};
use std::option::Option::Some;
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::path::{PathBuf, Path};
use std::process::{Child, Command};
use std::result::Result::Ok;
use std::sync::{Arc, Mutex, MutexGuard};
@ -19,14 +19,39 @@ use qapi_qmp::QmpCommand;
use std::str::FromStr;
use libc::{cpu_set_t, CPU_SET, sched_setaffinity};
use crate::cpu_list::CpuList;
use std::os::unix::prelude::AsRawFd;
use serde::{Deserialize, Serialize};
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
#[derive(Eq, PartialEq, Copy, Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum VirtualMachineState {
Loaded,
Prepared,
Stopped,
Paused,
Running,
}
impl Display for VirtualMachineState {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
VirtualMachineState::Loaded => write!(f, "loaded"),
VirtualMachineState::Prepared => write!(f, "prepared"),
VirtualMachineState::Stopped => write!(f, "stopped"),
VirtualMachineState::Paused => write!(f, "paused"),
VirtualMachineState::Running => write!(f, "running")
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct VirtualMachineInfo {
pub name: String,
pub working_dir: PathBuf,
pub config: InstanceConfig,
pub state: VirtualMachineState,
}
#[derive(Debug)]
pub struct VirtualMachine {
working_dir: PathBuf,
@ -54,14 +79,14 @@ impl Debug for ControlSocket {
const AUTO_UNBIND_BLACKLIST: &[&str] = &["nvidia"];
impl VirtualMachine {
pub fn new(
pub fn new<P: AsRef<Path>>(
config: InstanceConfig,
global_config: &GlobalConfig,
working_dir: PathBuf,
working_dir: P,
) -> VirtualMachine {
VirtualMachine {
working_dir,
state: VirtualMachineState::Stopped,
working_dir: working_dir.as_ref().to_path_buf(),
state: VirtualMachineState::Loaded,
config,
global_config: global_config.clone(),
process: None,
@ -69,6 +94,19 @@ impl VirtualMachine {
}
}
pub fn name(&self) -> &str {
&self.config.name
}
pub fn info(&self) -> VirtualMachineInfo {
VirtualMachineInfo {
name: self.name().to_string(),
working_dir: self.working_dir.clone(),
config: self.config.clone(),
state: self.state,
}
}
pub fn prepare(&mut self, execute_fixes: bool, force: bool) -> Result<(), anyhow::Error> {
let mut results = vec![];
results.extend(self.prepare_disks());
@ -77,6 +115,10 @@ impl VirtualMachine {
.into_iter()
.bcollect::<()>()
.with_context(|| format!("Failed to prepare VM {}", self.config.name))?;
if self.state == VirtualMachineState::Loaded {
self.state = VirtualMachineState::Prepared;
}
Ok(())
}
@ -424,8 +466,7 @@ impl VirtualMachine {
time -= 1;
}
let unix_stream = unix_stream.unwrap();
let unix_stream = CloneableUnixStream(Arc::new(Mutex::new(unix_stream)));
let unix_stream = CloneableUnixStream::new(unix_stream.unwrap());
let mut qmp = Qmp::from_stream(unix_stream.clone());
let handshake = qmp.handshake()?;
@ -461,12 +502,20 @@ impl VirtualMachine {
result_
}
pub fn control_stream(&self) -> Option<&CloneableUnixStream> {
self.control_socket.as_ref().map(|x| &x.unix_stream)
}
}
#[derive(Clone, Debug)]
struct CloneableUnixStream(Arc<Mutex<UnixStream>>);
pub struct CloneableUnixStream(Arc<Mutex<UnixStream>>);
impl CloneableUnixStream {
pub fn new(unix_stream: UnixStream) -> CloneableUnixStream {
CloneableUnixStream(Arc::new(Mutex::new(unix_stream)))
}
pub fn lock(&self) -> Result<MutexGuard<'_, UnixStream>, std::io::Error> {
self.0.lock().map_err(|_| {
io::Error::new(
@ -477,12 +526,15 @@ impl CloneableUnixStream {
}
}
impl AsRawFd for CloneableUnixStream {
fn as_raw_fd(&self) -> i32 {
self.lock().unwrap().as_raw_fd()
}
}
impl Read for CloneableUnixStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let res = self.lock()?.read(buf);
if let Ok(size) = res {
println!("READ: {}", String::from_utf8_lossy(&buf[..size]));
}
res
}
}

@ -8,4 +8,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0.40"
vore-core = { path = "../vore-core" }
vore-core = { path = "../vore-core" }
log = "0.4.14"
pretty_env_logger = "0.3"
clap = { version = "2.33.3", features = ["yaml"] }

@ -0,0 +1,453 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"structures": {
"arrayStrUnique": {
"type": "array",
"items": {
"type": "string",
"uniqueItems": true
}
},
"optionalStr": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"tuple2StrStr": {
"type": "array",
"maxLength": 2,
"minLength": 2,
"items": [
{
"type": "string"
},
{
"type": "string"
}
]
},
"tuple3StrOptStrStr": {
"type": "array",
"maxLength": 3,
"minLength": 3,
"items": [
{
"type": "string"
},
{
"$ref": "#/structures/optionalStr"
},
{
"type": "string"
}
]
}
},
"definitions": {
"namedArg": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"maxProperties": 1,
"patternProperties": {
".+": {
"$ref": "#/definitions/arg"
}
}
},
"arg": {
"type": "object",
"properties": {
"alias": {
"type": "string"
},
"aliases": {
"$ref": "#/structures/arrayStrUnique"
},
"allow_hyphen_values": {
"type": "boolean"
},
"case_insensitive": {
"type": "boolean"
},
"conflicts_with": {
"anyOf": [
{
"type": "string"
},
{
"$ref": "#/structures/arrayStrUnique"
}
]
},
"default_value": {
"type": "string"
},
"default_values": {
"$ref": "#/structures/arrayStrUnique"
},
"default_value_if": {
"$ref": "#/structures/tuple3StrOptStrStr"
},
"default_value_ifs": {
"type": "array",
"items": {
"$ref": "#/structures/tuple3StrOptStrStr"
}
},
"display_order": {
"type": "integer"
},
"env": {
"type": "string"
},
"exclusive": {
"type": "boolean"
},
"global": {
"type": "boolean"
},
"group": {
"type": "string"
},
"groups": {
"$ref": "#/structures/arrayStrUnique"
},
"help": {
"type": "string"
},
"help_heading": {
"$ref": "#/structures/optionalStr"
},
"hidden": {
"type": "boolean"
},
"hidden_long_help": {
"type": "boolean"
},
"hidden_short_help": {
"type": "boolean"
},
"hide_default_value": {
"type": "boolean"
},
"hide_env_value": {
"type": "boolean"
},
"hide_possible_value": {
"type": "boolean"
},
"index": {
"type": "integer"
},
"last": {
"type": "boolean"
},
"long": {
"type": "string"
},
"long_about": {
"type": "string"
},
"long_help": {
"type": "string"
},
"max_values": {
"type": "integer"
},
"min_values": {
"type": "integer"
},
"multiple": {
"type": "boolean"
},
"multiple_occurences": {
"type": "boolean"
},
"multiple_values": {
"type": "boolean"
},
"next_line_help": {
"type": "boolean"
},
"number_of_values": {
"type": "integer"
},
"overrides_with": {
"type": "string"
},
"overrides_with_all": {
"$ref": "#/structures/arrayStrUnique"
},
"possible_value": {
"type": "string"
},
"possible_values": {
"$ref": "#/structures/arrayStrUnique"
},
"raw": {
"type": "boolean"
},
"require_delimiter": {
"type": "boolean"
},
"require_equals": {
"type": "boolean"
},
"required": {
"type": "boolean"
},
"required_if": {
"$ref": "#/structures/tuple2StrStr"
},
"required_ifs": {
"type": "array",
"items": {
"$ref": "#/structures/tuple2StrStr"
}
},
"required_unless": {
"type": "string"
},
"required_unless_all": {
"$ref": "#/structures/arrayStrUnique"
},
"required_unless_one": {
"$ref": "#/structures/arrayStrUnique"
},
"requires": {
"type": "string"
},
"requires_all": {
"$ref": "#/structures/arrayStrUnique"
},
"requires_if": {
"$ref": "#/structures/tuple2StrStr"
},
"requires_ifs": {
"type": "array",
"items": {
"$ref": "#/structures/tuple2StrStr"
}
},
"setting": {
"type": "string"
},
"settings": {
"$ref": "#/structures/arrayStrUnique"
},
"short": {
"$ref": "#/definitions/shortArg"
},
"short_alias": {
"$ref": "#/definitions/shortArg"
},
"short_aliases": {
"type": "array",
"items": {
"$ref": "#/definitions/shortArg"
},
"uniqueItems": true
},
"takes_value": {
"type": "boolean"
},
"use_delimiter": {
"type": "boolean"
},
"value_name": {
"type": "string"
},
"value_names": {
"$ref": "#/structures/arrayStrUnique"
},
"value_terminator": {
"type": "string"
},
"value_delimiter": {
"type": "string"
},
"visible_alias": {
"type": "string"
},
"visible_aliases": {
"$ref": "#/structures/arrayStrUnique"
},
"visible_short_alias": {
"type": "string"
},
"visible_short_aliases": {
"$ref": "#/structures/arrayStrUnique"
}
}
},
"argGroup": {
"properties": {
"arg": {
"type": "string"
},
"args": {
"$ref": "#/structures/arrayStrUnique"
},
"conflicts_with": {
"type": "string"
},
"conflicts_with_all": {
"$ref": "#/structures/arrayStrUnique"
},
"multiple": {
"type": "boolean"
},
"required": {
"type": "boolean"
},
"requires": {
"type": "string"
},
"requires_all": {
"$ref": "#/structures/arrayStrUnique"
}
},
"type": "object"
},
"shortArg": {
"maxLength": 1,
"minLength": 1,
"pattern": "^[^-]$",
"type": "string"
}
},
"type": "object",
"TODO:": "set 'name' as required",
"properties": {
"about": {
"type": "string"
},
"after_help": {
"type": "string"
},
"alias": {
"type": "string"
},
"aliases": {
"$ref": "#/structures/arrayStrUnique"
},
"arg": {
"$ref": "#/definitions/arg"
},
"args": {
"type": "array",
"items": {
"$ref": "#/definitions/namedArg"
}
},
"author": {
"type": "string"
},
"before_help": {
"type": "string"
},
"bin_name": {
"type": "string"
},
"display_order": {
"type": "integer"
},
"global_setting": {
"type": "string"
},
"global_settings": {
"$ref": "#/structures/arrayStrUnique"
},
"group": {
"$ref": "#/definitions/argGroup"
},
"groups": {
"type": "array",
"items": {
"type": "object",
"minProperties": 1,
"maxProperties": 1,
"patternProperties": {
".+": {
"$ref": "#/definitions/argGroup"
}
}
}
},
"help_heading": {
"type": "string"
},
"help_template": {
"type": "string"
},
"long_about": {
"type": "string"
},
"long_version": {
"type": "string"
},
"max_term_width": {
"type": "integer"
},
"name": {
"type": "string"
},
"override_help": {
"type": "string"
},
"override_usage": {
"type": "string"
},
"replace": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"setting": {
"type": "string"
},
"settings": {
"$ref": "#/structures/arrayStrUnique"
},
"subcommand": {
"$ref": "#"
},
"subcommands": {
"type": "array",
"items": {
"type": "object",
"minProperties": 1,
"maxProperties": 1,
"patternProperties": {
".+": {
"$ref": "#"
}
}
}
},
"term_width": {
"type": "string"
},
"version": {
"type": "string"
},
"visible_alias": {
"type": "string"
},
"visible_aliases": {
"$ref": "#/structures/arrayStrUnique"
}
}
}

@ -0,0 +1,122 @@
name: "vore"
about: "vored management interface"
author: "eater <=eater.me>"
args:
- vored-socket:
global: true
help: "Connect to the specified socket"
required: false
takes_value: true
default_value: "/run/vore.sock"
long: conn
short: c
settings:
- SubcommandRequiredElseHelp
- GlobalVersion
subcommands:
- daemon:
visible_alias: d
setting: SubcommandRequiredElseHelp
about: "Daemon related commands"
subcommands:
- version:
about: "Get the version of the daemon"
- load:
about: "Load a new VM"
args: &loadvm
- vm-config:
required: true
takes_value: true
- cdrom:
help: "Attach a cdrom to this configuration"
long: cdrom
multiple: true
takes_value: true
- save:
help: "Save this VM configuration"
long: save
short: s
- prepare:
about: "Prepare a VM"
args:
- vm-name:
help: "VM to prepare, if not given the ONLY loaded instance will be used"
required: false
takes_value: true
- cdrom:
help: "Attach a cdrom to this vm when starting"
long: cdrom
multiple: true
takes_value: true
- boot:
about: "Load and boot a new VM"
args: *loadvm
- start:
about: "Start a VM"
args:
- vm-name:
help: "VM to start, if not given the ONLY loaded instance will be used"
takes_value: true
- cdrom:
help: "Attach a cdrom to this vm when starting"
long: cdrom
multiple: true
takes_value: true
- stop:
about: "Stop a VM"
args:
- vm-name:
help: "VM to stop, if not given the ONLY running instance will be used"
required: false
takes_value: true
- list:
about: "List loaded VMs"
- scream:
setting: SubcommandRequiredElseHelp
about: "Scream related actions"
subcommands:
- stop:
about: "Stop a running scream instance"
args:
- vm-name:
help: "VM to start looking glass instance for, if not given the ONLY running instance will be used"
required: false
takes_value: true
- start:
about: "Start a scream instance"
args:
- vm-name:
help: "VM to start looking glass instance for, if not given the ONLY running instance will be used"
required: false
takes_value: true
- looking-glass:
about: "Start a looking glass instance for a VM"
visible_alias: lg
args:
- vm-name:
help: "VM to start looking glass instance for, if not given the ONLY running instance will be used"
required: false
takes_value: true
- looking-glass-args:
help: "Arguments to pass to looking glass"
last: true
takes_value: true
require_delimiter: true
multiple: true
- x:
about: "Weird hidden actions"
setting: SubcommandRequiredElseHelp
subcommands:
- qemucmd:
about: "Print the qemu command that would run this vm"
args: *loadvm

@ -0,0 +1,57 @@
use std::os::unix::net::UnixStream;
use std::path::Path;
use vore_core::rpc::{CommandCenter, Request};
use std::io::{BufReader, Write, BufRead};
use vore_core::{CloneableUnixStream, VirtualMachineInfo};
use vore_core::rpc::*;
pub struct Client {
stream: CloneableUnixStream,
buf_reader: BufReader<CloneableUnixStream>,
center: CommandCenter,
}
impl Client {
pub fn connect<P: AsRef<Path>>(path: P) -> anyhow::Result<Client> {
let path = path.as_ref();
let stream = CloneableUnixStream::new(UnixStream::connect(path)?);
log::debug!("Connected to vore socket at {}", path.to_str().unwrap());
Ok(Client {
buf_reader: BufReader::new(stream.clone()),
stream,
center: Default::default(),
})
}
fn send<R: Request>(&mut self, request: R) -> anyhow::Result<R::Response> {
let (_, json) = self.center.write_command(request)?;
self.stream.write_all(json.as_bytes())?;
let mut response = String::new();
self.buf_reader.read_line(&mut response)?;
let (_, info) = CommandCenter::read_answer::<R>(&response)?;
Ok(info)
}
pub fn load_vm(&mut self, toml: &str, save: bool, cdroms: Vec<String>) -> anyhow::Result<VirtualMachineInfo> {
Ok(self.send(LoadRequest {
cdroms,
save,
toml: toml.to_string(),
working_directory: None,
})?.info)
}
pub fn list_vms(&mut self) -> anyhow::Result<Vec<VirtualMachineInfo>> {
Ok(self.send(ListRequest {})?.items)
}
pub fn host_version(&mut self) -> anyhow::Result<InfoResponse> {
self.send(InfoRequest {})
}
pub fn prepare(&mut self, vm: String, cdroms: Vec<String>) -> anyhow::Result<()> {
self.send(PrepareRequest { name: vm, cdroms })?;
Ok(())
}
}

@ -1,5 +1,126 @@
use vore_core::InstanceConfig;
mod client;
use vore_core::{init_logging};
use crate::client::Client;
use clap::{App, ArgMatches};
use std::fs;
use anyhow::Context;
use std::option::Option::Some;
fn main() {
let cfg = InstanceConfig::from_toml(include_str!("../../config/example.toml")).unwrap();
init_logging();
if let Err(err) = main_res() {
println!("{:?}", err)
}
}
fn main_res() -> anyhow::Result<()> {
let yaml = clap::load_yaml!("../clap.yml");
let app: App = App::from(yaml);
let matches = app.get_matches();
let client = Client::connect(matches.value_of("vored-socket").unwrap())?;
let mut vore = VoreApp {
client
};
match matches.subcommand() {
("load", Some(args)) => {
vore.load(args)?;
}
("list", Some(args)) => {
vore.list(args)?;
}
("prepare", Some(args)) => {
vore.prepare(args)?;
}
("daemon", Some(args)) => {
match args.subcommand() {
("version", _) => {
vore.daemon_version()?;
}
(s, _) => {
log::error!("Subcommand daemon.{} not implemented", s);
}
}
}
(s, _) => {
log::error!("Subcommand {} not implemented", s);
}
}
Ok(())
}
struct LoadVMOptions {
config: String,
cdroms: Vec<String>,
save: bool,
}
fn get_load_vm_options(args: &ArgMatches) -> anyhow::Result<LoadVMOptions> {
let vm_config_path = args.value_of("vm-config").unwrap();
let config = fs::read_to_string(vm_config_path)
.with_context(|| format!("Failed to read vm config at {}", vm_config_path))?;
Ok(LoadVMOptions {
config,
cdroms: args.values_of("cdrom").map_or(vec![], |x| x.map(|x| x.to_string()).collect::<Vec<_>>()),
save: args.is_present("save"),
})
}
struct VoreApp {
client: Client,
}
impl VoreApp {
fn get_vm_name(&mut self, args: &ArgMatches) -> anyhow::Result<String> {
if let Some(vm_name) = args.value_of("vm-name") {
Ok(vm_name.to_string())
} else {
let mut items = self.client.list_vms()?;
match (items.len(), items.pop()) {
(amount, Some(x)) if amount == 1 => return Ok(x.name),
(0, None) => anyhow::bail!("There are no VM's loaded"),
_ => anyhow::bail!("Multiple VM's are loaded, please specify one"),
}
}
}
fn daemon_version(&mut self) -> anyhow::Result<()> {
let info = self.client.host_version()?;
println!("{} ({})", info.version, info.name);
Ok(())
}
fn load(&mut self, args: &ArgMatches) -> anyhow::Result<()> {
let vm_options = get_load_vm_options(args)?;
let vm_info = self.client.load_vm(&vm_options.config, vm_options.save, vm_options.cdroms)?;
log::info!("Loaded VM {}", vm_info.name);
Ok(())
}
fn list(&mut self, _: &ArgMatches) -> anyhow::Result<()> {
let items = self.client.list_vms()?;
for info in items {
println!("{}\t{}", info.name, info.state)
}
Ok(())
}
fn prepare(&mut self, args: &ArgMatches) -> anyhow::Result<()> {
let name = self.get_vm_name(args)?;
self.client.prepare(name, args.values_of("cdrom").map_or(vec![], |x| x.map(|x| x.to_string()).collect::<Vec<_>>()))?;
Ok(())
}
}

@ -9,4 +9,8 @@ edition = "2018"
[dependencies]
anyhow = "1.0.40"
vore-core = { path = "../vore-core" }
polling = "2.0.3"
polling = "2.0.3"
log = "0.4.14"
pretty_env_logger = "0.3"
signal-hook = { version = "0.3.8", features = ["iterator"] }
libc = "0.2.94"

@ -1,17 +1,386 @@
use std::collections::HashMap;
use vore_core::VirtualMachine;
use std::os::unix::net::UnixListener;
use std::collections::{HashMap};
use vore_core::{VirtualMachine, InstanceConfig, GlobalConfig, GLOBAL_CONFIG_LOCATION};
use std::os::unix::net::{UnixListener, SocketAddr, UnixStream};
use polling::{Poller, Event};
use std::time::Duration;
use anyhow::Context;
use std::{mem, io};
use std::io::{Read, Write};
use vore_core::rpc::{CommandCenter, Response, Command, AllRequests, AllResponses};
use vore_core::rpc;
use signal_hook::low_level::{signal_name};
use signal_hook::consts::{SIGINT, SIGTERM, SIGHUP};
use std::path::PathBuf;
use std::str::FromStr;
use signal_hook::iterator::{SignalsInfo, Signals, Handle};
use std::os::unix::io::AsRawFd;
use std::ffi::CStr;
use std::mem::size_of;
struct RPCConnection {}
#[derive(Debug)]
struct RPCConnection {
stream: UnixStream,
address: SocketAddr,
buffer: Vec<u8>,
uid: u32,
user: Option<String>,
pid: i32,
}
impl Write for RPCConnection {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.stream.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.stream.flush()
}
}
impl Read for RPCConnection {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.stream.read(buf)
}
}
const NEWLINE: u8 = '\n' as u8;
impl RPCConnection {
pub fn handle_input(&mut self, own_id: usize) -> Result<(bool, Vec<(usize, Command)>), anyhow::Error> {
let mut still_open = true;
loop {
let mut buffer = vec![0u8; 4096];
match self.stream.read(&mut buffer) {
Ok(amount) if amount == 0 => {
still_open = false;
break;
}
Ok(amount) => self.buffer.extend_from_slice(&buffer[..amount]),
Err(err) if err.kind() == io::ErrorKind::WouldBlock => break,
Err(err) => return Err(err.into())
};
}
let mut buffer = mem::take(&mut self.buffer);
if still_open {
self.buffer = buffer.split_off(buffer.iter().enumerate().rev().find(|(_, x)| **x == NEWLINE).map(|(idx, _)| idx + 1).unwrap_or(buffer.len()));
}
let mut commands = vec![];
for part in buffer.split(|x| *x == NEWLINE) {
if part.is_empty() {
continue;
}
let lossy = String::from_utf8_lossy(part);
match CommandCenter::read_command(&lossy) {
Ok(cmd) => {
log::debug!("Got command: {:?}", cmd);
commands.push((own_id, cmd));
}
struct DaemonState {
Err(err) => {
log::info!("RPC Connection produced error: {}", err)
}
}
}
Ok((still_open, commands))
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
enum EventTarget {
RPCListener,
_Machine(String),
RPCConnection(usize),
None,
}
#[derive(Debug)]
pub struct Daemon {
event_key_storage: Vec<EventTarget>,
global_config: GlobalConfig,
machines: HashMap<String, VirtualMachine>,
connections: Vec<RPCConnection>,
connections: Vec<Option<RPCConnection>>,
rpc_listener: UnixListener,
socket_path: PathBuf,
poller: Poller,
signals: SignalsInfo,
signals_handle: Handle,
queue: Vec<Event>,
command_queue: Vec<(usize, Command)>,
}
impl DaemonState {
pub fn wait() {
impl Daemon {
pub fn new() -> Result<Daemon, anyhow::Error> {
log::debug!("Loading global config ({})", GLOBAL_CONFIG_LOCATION);
let toml = std::fs::read_to_string(GLOBAL_CONFIG_LOCATION)?;
let global_config = GlobalConfig::load(&toml)?;
log::debug!("Creating vore daemon");
let signals = Signals::new(&[SIGINT, SIGHUP])?;
let handle = signals.handle();
log::debug!("Bound signal handlers");
let poller = Poller::new().context("Failed to make poller")?;
let socket_path = PathBuf::from_str("/run/vore.sock")?;
let rpc_listener = UnixListener::bind(&socket_path).context("Failed to bind vore socket")?;
rpc_listener.set_nonblocking(true)?;
log::debug!("Bound to /run/vore.sock");
let mut daemon = Daemon {
event_key_storage: vec![],
global_config,
machines: Default::default(),
connections: vec![],
rpc_listener,
poller,
signals,
signals_handle: handle,
queue: vec![],
command_queue: vec![],
socket_path,
};
daemon.init()?;
Ok(daemon)
}
pub fn init(&mut self) -> Result<(), anyhow::Error> {
let new_key = self.add_target(EventTarget::RPCListener);
self.poller.add(&self.rpc_listener, Event::readable(new_key))?;
Ok(())
}
pub fn run(&mut self) -> Result<(), anyhow::Error> {
loop {
let res = self.wait().context("Got error while waiting for new notifications");
match res {
// Interrupted is uh "always" when we get a signal
Err(err) if err.downcast_ref::<io::Error>().map(|x| x.kind() == io::ErrorKind::Interrupted).unwrap_or(false) => {
if !self.handle_exit_code()? {
break;
}
}
err => err?
}
if !self.handle_event_queue()? {
break;
}
self.handle_command_queue()?;
}
// TODO: clean up
log::info!("vore daemon has ended");
std::fs::remove_file(&self.socket_path).context("Failed cleaning up socket")?;
Ok(())
}
pub fn handle_command_queue(&mut self) -> Result<(), anyhow::Error> {
while let Some((id, command)) = self.command_queue.pop() {
let resp = self.handle_command(&command);
if let Err(err) = &resp {
log::warn!("Command {:?} failed with error: {:?}", command, err)
}
if let Some(conn) = self.connections[id].as_mut() {
conn.write_all(CommandCenter::write_answer(&command, resp)?.as_bytes())?;
}
}
Ok(())
}
pub fn handle_command(&mut self, command: &Command) -> Result<AllResponses, anyhow::Error> {
let resp = match &command.data {
AllRequests::Info(_) => {
rpc::InfoResponse {
name: "vore".to_string(),
version: format!("{}.{}.{}{}",
env!("CARGO_PKG_VERSION_MAJOR"),
env!("CARGO_PKG_VERSION_MINOR"),
env!("CARGO_PKG_VERSION_PATCH"),
option_env!("CARGO_PKG_VERSION_PRE").unwrap_or("")),
}
.into_enum()
}
AllRequests::List(_) => {
rpc::ListResponse {
items: self.machines.values().map(|x| x.info()).collect()
}
.into_enum()
}
AllRequests::Load(val) => {
let config = InstanceConfig::from_toml(&val.toml)?;
let working_dir = val.working_directory.as_ref().cloned().unwrap_or_else(|| format!("/var/lib/vore/{}", config.name));
let vm = VirtualMachine::new(config, &self.global_config, working_dir);
let info = vm.info();
self.mount_machine(vm);
rpc::LoadResponse {
info,
}
.into_enum()
}
AllRequests::Prepare(val) => {
if let Some(machine) = self.machines.get_mut(&val.name) {
machine.prepare(true, false)?;
} else {
anyhow::bail!("No machine with the name {} exists", val.name);
}
rpc::PrepareResponse {}.into_enum()
}
AllRequests::Start(_) => {
anyhow::bail!("Unimplemented");
}
AllRequests::Stop(_) => {
anyhow::bail!("Unimplemented");
}
AllRequests::Unload(_) => {
anyhow::bail!("Unimplemented");
}
};
Ok(resp)
}
pub fn handle_exit_code(&mut self) -> Result<bool, anyhow::Error> {
for signal in self.signals.pending() {
log::info!("Received signal {} ({})", signal_name(signal).unwrap_or("<unknown>"), signal);
match signal {
SIGINT | SIGTERM => return Ok(false),
_ => {}
}
}
Ok(true)
}
pub fn handle_event_queue(&mut self) -> Result<bool, anyhow::Error> {
let queue = mem::take(&mut self.queue);
for event in queue {
let target = self.event_key_storage.get(event.key).cloned();
if let Some(item) = target {
log::debug!("Handling {:?} from target {:?}", event, item);
match item {
EventTarget::RPCListener => {
self.poller.modify(&self.rpc_listener, Event::readable(event.key))?;
self.accept_rpc_connections()?;
}
EventTarget::_Machine(name) if self.machines.contains_key(&name) => {
if let Some(control_socket) = self.machines[&name].control_stream() {
self.poller.modify(control_socket, Event::readable(event.key))?;
}
}
EventTarget::RPCConnection(rpc_connection_id) if self.connections.get(rpc_connection_id).map(Option::is_some).unwrap_or(false) => {
let (still_open, mut commands) = if let Some(rpc_connection) = &mut self.connections[rpc_connection_id] {
let input_res = rpc_connection.handle_input(rpc_connection_id)?;
if input_res.0 {
self.poller.modify(&rpc_connection.stream, Event::readable(event.key))?;
}
input_res
} else {
(false, vec![])
};
if !still_open {
log::info!("RPC connection {} closed", rpc_connection_id);
self.connections[rpc_connection_id] = None;
}
self.command_queue.append(&mut commands)
}
_ => continue,
}
}
}
Ok(true)
}
fn accept_rpc_connections(&mut self) -> Result<(), anyhow::Error> {
loop {
let (stream, address) = match self.rpc_listener.accept() {
Ok(value) => value,
Err(err) if err.kind() == io::ErrorKind::WouldBlock => return Ok(()),
Err(err) => return Err(err)?
};
stream.set_nonblocking(true)?;
let mut user: Option<String> = None;
let ucred = unsafe {
let mut ucred: libc::ucred = mem::zeroed();
let mut length = size_of::<libc::ucred>() as u32;
libc::getsockopt(stream.as_raw_fd(), libc::SOL_SOCKET, libc::SO_PEERCRED, (&mut ucred) as *mut _ as _, &mut length);
let passwd = libc::getpwuid(ucred.uid);
if !passwd.is_null() {
user = CStr::from_ptr((*passwd).pw_name).to_str().ok().map(|x| x.to_string())
}
ucred
};
let conn = RPCConnection {
stream,
address,
buffer: vec![],
uid: ucred.uid,
user,
pid: ucred.pid,
};
log::info!(
"Got new RPC connection from {} (pid: {}, socket: {:?})",
conn.user.as_ref().map_or_else(|| format!("uid:{}", conn.uid), |x| format!("{} ({})", x, conn.uid)),
conn.pid,
conn.address,
);
let id = self.add_rpc_connection(conn);
let event_target = self.add_target(EventTarget::RPCConnection(id));
self.poller.add(&self.connections[id].as_ref().unwrap().stream, Event::readable(event_target))?;
}
}
pub fn wait(&mut self) -> Result<(), anyhow::Error> {
self.poller.wait(&mut self.queue, Some(Duration::from_secs(5)))?;
Ok(())
}
fn add_target(&mut self, event_target: EventTarget) -> usize {
let id = self.event_key_storage.iter().enumerate().find(|(_, target)| target.eq(&&EventTarget::None));
if let Some((id, _)) = id {
self.event_key_storage[id] = event_target;
return id;
}
let new_id = self.event_key_storage.len();
self.event_key_storage.push(event_target);
return new_id;
}
fn add_rpc_connection(&mut self, rpc_connection: RPCConnection) -> usize {
let id = self.connections.iter().enumerate().find(|(_, target)| target.is_none());
if let Some((id, _)) = id {
self.connections[id] = Some(rpc_connection);
return id;
}
let new_id = self.connections.len();
self.connections.push(Some(rpc_connection));
return new_id;
}
fn mount_machine(&mut self, vm: VirtualMachine) {
let name = vm.name().to_string();
self.machines.insert(name.clone(), vm);
}
}

@ -1,14 +1,11 @@
mod daemon;
use crate::daemon::Daemon;
use vore_core::init_logging;
use std::path::PathBuf;
use vore_core::{GlobalConfig, InstanceConfig, VirtualMachine};
mod daemon;
fn main() {
let cfg = InstanceConfig::from_toml(include_str!("../../config/example.toml")).unwrap();
let global = GlobalConfig::load(include_str!("../../config/global.toml")).unwrap();
let mut vm = VirtualMachine::new(cfg, &global, PathBuf::from("/home/eater/.local/vore/win10"));
vm.prepare(true, false).unwrap();
vm.start().unwrap();
vm.wait_till_stopped().unwrap();
vm.stop_now().unwrap()
init_logging();
let mut daemon = Daemon::new().unwrap();
daemon.run().unwrap();
}

Loading…
Cancel
Save