rpc calls!!!
This commit is contained in:
parent
58eba9a315
commit
7411bb5972
19 changed files with 1698 additions and 44 deletions
236
Cargo.lock
generated
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")?;
|
||||
|
||||
|
|
101
vore-core/src/rpc/calls.rs
Normal file
101
vore-core/src/rpc/calls.rs
Normal file
|
@ -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,
|
||||
}, {})
|
||||
}
|
7
vore-core/src/rpc/mod.rs
Normal file
7
vore-core/src/rpc/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod calls;
|
||||
mod serde;
|
||||
mod traits;
|
||||
|
||||
pub use calls::*;
|
||||
pub use crate::rpc::serde::*;
|
||||
pub use traits::*;
|
72
vore-core/src/rpc/serde.rs
Normal file
72
vore-core/src/rpc/serde.rs
Normal file
|
@ -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 {}
|
42
vore-core/src/rpc/traits.rs
Normal file
42
vore-core/src/rpc/traits.rs
Normal file
|
@ -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"] }
|
453
vore/clap.schema.json
Normal file
453
vore/clap.schema.json
Normal file
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
122
vore/clap.yml
Normal file
122
vore/clap.yml
Normal file
|
@ -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
|
||||
|
57
vore/src/client.rs
Normal file
57
vore/src/client.rs
Normal file
|
@ -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(())
|
||||
}
|
||||
}
|
125
vore/src/main.rs
125
vore/src/main.rs
|
@ -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 {}
|
||||
|
||||
struct DaemonState {
|
||||
machines: HashMap<String, VirtualMachine>,
|
||||
connections: Vec<RPCConnection>,
|
||||
rpc_listener: UnixListener,
|
||||
#[derive(Debug)]
|
||||
struct RPCConnection {
|
||||
stream: UnixStream,
|
||||
address: SocketAddr,
|
||||
buffer: Vec<u8>,
|
||||
uid: u32,
|
||||
user: Option<String>,
|
||||
pid: i32,
|
||||
}
|
||||
|
||||
impl DaemonState {
|
||||
pub fn wait() {
|
||||
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));
|
||||
}
|
||||
|
||||
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<Option<RPCConnection>>,
|
||||
rpc_listener: UnixListener,
|
||||
socket_path: PathBuf,
|
||||
poller: Poller,
|
||||
signals: SignalsInfo,
|
||||
signals_handle: Handle,
|
||||
queue: Vec<Event>,
|
||||
command_queue: Vec<(usize, Command)>,
|
||||
}
|
||||
|
||||
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 @@
|
|||
use crate::daemon::Daemon;
|
||||
use vore_core::init_logging;
|
||||
|
||||
mod daemon;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use vore_core::{GlobalConfig, InstanceConfig, VirtualMachine};
|
||||
|
||||
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…
Reference in a new issue