Generate NetworkSettings with IPC

This generates and applies NetworkSettings object with unix socket IPC.

- domain socket, json-rpc based communication
- switches to anyhow for burrow crate
- adds support for starting daemons on macos
This commit is contained in:
Jett Chen 2023-10-01 11:49:00 +08:00 committed by Conrad Kramer
parent 6368ca7f74
commit c9f104e523
31 changed files with 909 additions and 117 deletions

View file

@ -10,18 +10,20 @@ crate-type = ["lib", "staticlib"]
[dependencies]
anyhow = "1.0"
tokio = { version = "1.21", features = ["rt", "macros", "sync", "io-util"] }
tun = { version = "0.1", path = "../tun", features = ["serde"] }
tokio = { version = "1.21", features = ["rt", "macros", "sync", "io-util", "rt-multi-thread"] }
tun = { version = "0.1", path = "../tun", features = ["serde", "tokio"] }
clap = { version = "4.3.2", features = ["derive"] }
tracing = "0.1"
tracing-log = "0.1"
tracing-journald = "0.3"
tracing-oslog = {git = "https://github.com/Stormshield-robinc/tracing-oslog"}
tracing-subscriber = "0.3"
tracing-subscriber = { version = "0.3" , features = ["std", "env-filter"]}
env_logger = "0.10"
log = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
async-channel = "1.9"
schemars = "0.8"
[target.'cfg(target_os = "linux")'.dependencies]
caps = "0.5.5"
@ -30,6 +32,9 @@ libsystemd = "0.6"
[target.'cfg(target_vendor = "apple")'.dependencies]
nix = { version = "0.26.2" }
[dev-dependencies]
insta = { version = "1.32.0", features = ["yaml"] }
[package.metadata.generate-rpm]
assets = [
{ source = "target/release/burrow", dest = "/usr/bin/burrow", mode = "755" },

15
burrow/src/apple.rs Normal file
View file

@ -0,0 +1,15 @@
use tracing::{debug, Subscriber};
use tracing::instrument::WithSubscriber;
use tracing_oslog::OsLogger;
use tracing_subscriber::FmtSubscriber;
use tracing_subscriber::layer::SubscriberExt;
pub use crate::daemon::start_srv;
#[no_mangle]
pub extern "C" fn initialize_oslog() {
let collector = tracing_subscriber::registry()
.with(OsLogger::new("com.hackclub.burrow", "backend"));
tracing::subscriber::set_global_default(collector).unwrap();
debug!("Initialized oslog tracing in libburrow rust FFI");
}

View file

@ -1,13 +1,32 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tun::TunOptions;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub enum DaemonCommand {
Start(DaemonStartOptions),
ServerInfo,
ServerConfig,
Stop,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct DaemonStartOptions {
pub(super) tun: TunOptions,
}
#[test]
fn test_daemoncommand_serialization() {
insta::assert_snapshot!(
serde_json::to_string(&DaemonCommand::Start(DaemonStartOptions::default())).unwrap()
);
insta::assert_snapshot!(
serde_json::to_string(&DaemonCommand::ServerInfo).unwrap()
);
insta::assert_snapshot!(
serde_json::to_string(&DaemonCommand::Stop).unwrap()
);
insta::assert_snapshot!(
serde_json::to_string(&DaemonCommand::ServerConfig).unwrap()
)
}

View file

@ -1,40 +1,70 @@
use tracing::{debug, info, warn};
use DaemonResponse;
use crate::daemon::response::{DaemonResponseData, ServerConfig, ServerInfo};
use super::*;
pub struct DaemonInstance {
rx: mpsc::Receiver<DaemonCommand>,
rx: async_channel::Receiver<DaemonCommand>,
sx: async_channel::Sender<DaemonResponse>,
tun_interface: Option<TunInterface>,
}
impl DaemonInstance {
pub fn new(rx: mpsc::Receiver<DaemonCommand>) -> Self {
pub fn new(rx: async_channel::Receiver<DaemonCommand>, sx: async_channel::Sender<DaemonResponse>) -> Self {
Self {
rx,
sx,
tun_interface: None,
}
}
pub async fn run(&mut self) -> Result<()> {
while let Some(command) = self.rx.recv().await {
match command {
DaemonCommand::Start(options) => {
if self.tun_interface.is_none() {
self.tun_interface = Some(options.tun.open()?);
eprintln!("Daemon starting tun interface.");
} else {
eprintln!("Got start, but tun interface already up.");
}
async fn proc_command(&mut self, command: DaemonCommand) -> Result<DaemonResponseData> {
info!("Daemon got command: {:?}", command);
match command {
DaemonCommand::Start(st) => {
if self.tun_interface.is_none() {
debug!("Daemon attempting start tun interface.");
self.tun_interface = Some(st.tun.open()?);
info!("Daemon started tun interface");
} else {
warn!("Got start, but tun interface already up.");
}
DaemonCommand::Stop => {
if self.tun_interface.is_some() {
self.tun_interface = None;
eprintln!("Daemon stopping tun interface.");
} else {
eprintln!("Got stop, but tun interface is not up.")
Ok(DaemonResponseData::None)
}
DaemonCommand::ServerInfo => {
match &self.tun_interface {
None => {Ok(DaemonResponseData::None)}
Some(ti) => {
info!("{:?}", ti);
Ok(
DaemonResponseData::ServerInfo(
ServerInfo::try_from(ti)?
)
)
}
}
}
DaemonCommand::Stop => {
if self.tun_interface.is_some() {
self.tun_interface = None;
info!("Daemon stopping tun interface.");
} else {
warn!("Got stop, but tun interface is not up.")
}
Ok(DaemonResponseData::None)
}
DaemonCommand::ServerConfig => {
Ok(DaemonResponseData::ServerConfig(ServerConfig::default()))
}
}
}
pub async fn run(&mut self) -> Result<()> {
while let Ok(command) = self.rx.recv().await {
let response = self.proc_command(command).await;
info!("Daemon response: {:?}", response);
self.sx.send(DaemonResponse::new(response)).await?;
}
Ok(())
}
}

View file

@ -4,6 +4,7 @@ use tokio::sync::mpsc;
mod command;
mod instance;
mod net;
mod response;
use instance::DaemonInstance;
use net::listen;
@ -11,9 +12,15 @@ use net::listen;
pub use command::{DaemonCommand, DaemonStartOptions};
pub use net::DaemonClient;
pub async fn daemon_main() -> Result<()> {
let (tx, rx) = mpsc::channel(2);
let mut inst = DaemonInstance::new(rx);
#[cfg(target_vendor = "apple")]
pub use net::start_srv;
tokio::try_join!(inst.run(), listen(tx)).map(|_| ())
pub use response::{DaemonResponseData, DaemonResponse, ServerInfo};
pub async fn daemon_main() -> Result<()> {
let (commands_tx, commands_rx) = async_channel::unbounded();
let (response_tx, response_rx) = async_channel::unbounded();
let mut inst = DaemonInstance::new(commands_rx, response_tx);
tokio::try_join!(inst.run(), listen(commands_tx, response_rx)).map(|_| ())
}

View file

@ -0,0 +1,24 @@
use std::thread;
use tokio::runtime::Runtime;
use tracing::error;
use crate::daemon::{daemon_main, DaemonClient};
#[no_mangle]
pub extern "C" fn start_srv(){
let _handle = thread::spawn(move || {
let rt = Runtime::new().unwrap();
rt.block_on(async {
if let Err(e) = daemon_main().await {
error!("Error when starting rpc server: {}", e);
}
});
});
let rt = Runtime::new().unwrap();
rt.block_on(async {
loop {
if let Ok(_) = DaemonClient::new().await{
break
}
}
});
}

View file

@ -13,17 +13,19 @@ pub use systemd::{listen, DaemonClient};
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::{listen, DaemonClient};
#[cfg(target_vendor = "apple")]
mod apple;
#[cfg(target_vendor = "apple")]
pub use apple::start_srv;
#[derive(Clone, Serialize, Deserialize)]
pub struct DaemonRequest {
pub id: u32,
pub command: DaemonCommand,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct DaemonResponse {
// Error types can't be serialized, so this is the second best option.
result: std::result::Result<(), String>,
}

View file

@ -1,16 +1,16 @@
use super::*;
use std::os::fd::IntoRawFd;
pub async fn listen(cmd_tx: mpsc::Sender<DaemonCommand>) -> Result<()> {
if !libsystemd::daemon::booted() || listen_with_systemd(cmd_tx.clone()).await.is_err() {
unix::listen(cmd_tx).await?;
pub async fn listen(cmd_tx: async_channel::Sender<DaemonCommand>, rsp_rx: async_channel::Receiver<DaemonResponse>) -> Result<()> {
if !libsystemd::daemon::booted() || listen_with_systemd(cmd_tx.clone(), rsp_rx.clone()).await.is_err() {
unix::listen(cmd_tx, rsp_rx).await?;
}
Ok(())
}
async fn listen_with_systemd(cmd_tx: mpsc::Sender<DaemonCommand>) -> Result<()> {
let fds = libsystemd::activation::receive_descriptors(false).unwrap();
super::unix::listen_with_optional_fd(cmd_tx, Some(fds[0].clone().into_raw_fd())).await
async fn listen_with_systemd(cmd_tx: async_channel::Sender<DaemonCommand>, rsp_rx: async_channel::Receiver<DaemonResponse>) -> Result<()> {
let fds = libsystemd::activation::receive_descriptors(false)?;
super::unix::listen_with_optional_fd(cmd_tx, rsp_rx,Some(fds[0].clone().into_raw_fd())).await
}
pub type DaemonClient = unix::DaemonClient;

View file

@ -1,22 +1,51 @@
use super::*;
use std::{
os::fd::{FromRawFd, RawFd},
os::unix::net::UnixListener as StdUnixListener,
path::Path,
};
use std::{ascii, io, os::fd::{FromRawFd, RawFd}, os::unix::net::UnixListener as StdUnixListener, path::Path};
use std::hash::Hash;
use std::path::PathBuf;
use anyhow::anyhow;
use log::log;
use tracing::info;
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
net::{UnixListener, UnixStream},
};
use tracing::debug;
#[cfg(not(target_vendor = "apple"))]
const UNIX_SOCKET_PATH: &str = "/run/burrow.sock";
pub async fn listen(cmd_tx: mpsc::Sender<DaemonCommand>) -> Result<()> {
listen_with_optional_fd(cmd_tx, None).await
#[cfg(target_vendor = "apple")]
const UNIX_SOCKET_PATH: &str = "burrow.sock";
#[cfg(target_os = "macos")]
fn fetch_socket_path() -> Option<PathBuf>{
let tries = vec![
"burrow.sock".to_string(),
format!("{}/Library/Containers/com.hackclub.burrow.network/Data/burrow.sock",
std::env::var("HOME").unwrap_or_default())
.to_string(),
];
for path in tries{
let path = PathBuf::from(path);
if path.exists(){
return Some(path);
}
}
None
}
#[cfg(not(target_os = "macos"))]
fn fetch_socket_path() -> Option<PathBuf>{
Some(Path::new(UNIX_SOCKET_PATH).to_path_buf())
}
pub async fn listen(cmd_tx: async_channel::Sender<DaemonCommand>, rsp_rx: async_channel::Receiver<DaemonResponse>) -> Result<()> {
listen_with_optional_fd(cmd_tx, rsp_rx, None).await
}
pub(crate) async fn listen_with_optional_fd(
cmd_tx: mpsc::Sender<DaemonCommand>,
cmd_tx: async_channel::Sender<DaemonCommand>,
rsp_rx: async_channel::Receiver<DaemonResponse>,
raw_fd: Option<RawFd>,
) -> Result<()> {
let path = Path::new(UNIX_SOCKET_PATH);
@ -32,7 +61,16 @@ pub(crate) async fn listen_with_optional_fd(
listener
} else {
// Won't help all that much, if we use the async version of fs.
std::fs::remove_file(path)?;
if let Some(par) = path.parent(){
std::fs::create_dir_all(
par
)?;
}
match std::fs::remove_file(path){
Err(e) if e.kind()==io::ErrorKind::NotFound => {Ok(())}
stuff => stuff
}?;
info!("Relative path: {}", path.to_string_lossy());
UnixListener::bind(path)?
};
loop {
@ -41,29 +79,35 @@ pub(crate) async fn listen_with_optional_fd(
// I'm pretty sure we won't need to manually join / shut this down,
// `lines` will return Err during dropping, and this task should exit gracefully.
tokio::task::spawn(async {
let rsp_rxc = rsp_rx.clone();
tokio::task::spawn(async move {
let cmd_tx = cmd_tx;
let mut stream = stream;
let (mut read_stream, mut write_stream) = stream.split();
let buf_reader = BufReader::new(&mut read_stream);
let mut lines = buf_reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
let mut res = DaemonResponse { result: Ok(()) };
let command = match serde_json::from_str::<DaemonRequest>(&line) {
Ok(req) => Some(req.command),
info!("Got line: {}", line);
debug!("Line raw data: {:?}", line.as_bytes());
let mut res : DaemonResponse = DaemonResponseData::None.into();
let req = match serde_json::from_str::<DaemonRequest>(&line) {
Ok(req) => Some(req),
Err(e) => {
res.result = Err(format!("{}", e));
res.result = Err(e.to_string());
None
}
};
let mut res = serde_json::to_string(&res).unwrap();
res.push('\n');
write_stream.write_all(res.as_bytes()).await.unwrap();
// I want this to come at the very end so that we always send a reponse back.
if let Some(command) = command {
cmd_tx.send(command).await.unwrap();
if let Some(req) = req {
cmd_tx.send(req.command).await.unwrap();
let res = rsp_rxc.recv().await.unwrap().with_id(req.id);
let mut retres = serde_json::to_string(&res).unwrap();
retres.push('\n');
info!("Sending response: {}", retres);
write_stream.write_all(retres.as_bytes()).await.unwrap();
}
}
});
@ -76,7 +120,12 @@ pub struct DaemonClient {
impl DaemonClient {
pub async fn new() -> Result<Self> {
Self::new_with_path(UNIX_SOCKET_PATH).await
let path = fetch_socket_path()
.ok_or(anyhow!("Failed to find socket path"))?;
// debug!("found path: {:?}", path);
let connection = UnixStream::connect(path).await?;
debug!("connected to socket");
Ok(Self { connection })
}
pub async fn new_with_path(path: &str) -> Result<Self> {
@ -86,17 +135,19 @@ impl DaemonClient {
Ok(Self { connection })
}
pub async fn send_command(&mut self, command: DaemonCommand) -> Result<()> {
pub async fn send_command(&mut self, command: DaemonCommand) -> Result<DaemonResponse> {
let mut command = serde_json::to_string(&DaemonRequest { id: 0, command })?;
command.push('\n');
self.connection.write_all(command.as_bytes()).await?;
let buf_reader = BufReader::new(&mut self.connection);
let mut lines = buf_reader.lines();
// This unwrap *should* never cause issues.
let response = lines.next_line().await?.unwrap();
let response = lines
.next_line()
.await?
.ok_or(anyhow!("Failed to read response"))?;
debug!("Got raw response: {}", response);
let res: DaemonResponse = serde_json::from_str(&response)?;
res.result.unwrap();
Ok(())
Ok(res)
}
}

View file

@ -1,6 +1,6 @@
use super::*;
pub async fn listen(_: mpsc::Sender<DaemonCommand>) -> Result<()> {
pub async fn listen(_cmd_tx: async_channel::Sender<DaemonCommand>, _rsp_rx: async_channel::Receiver<DaemonResponse>) -> Result<()> {
unimplemented!("This platform does not currently support daemon mode.")
}

View file

@ -0,0 +1,109 @@
use anyhow::anyhow;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tun::TunInterface;
#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
pub struct DaemonResponse {
// Error types can't be serialized, so this is the second best option.
pub result: Result<DaemonResponseData, String>,
pub id: u32
}
impl DaemonResponse{
pub fn new(result: Result<DaemonResponseData, impl ToString>) -> Self{
Self{
result: result.map_err(|e| e.to_string()),
id: 0
}
}
}
impl Into<DaemonResponse> for DaemonResponseData{
fn into(self) -> DaemonResponse{
DaemonResponse::new(Ok::<DaemonResponseData, String>(self))
}
}
impl DaemonResponse{
pub fn with_id(self, id: u32) -> Self{
Self {
id,
..self
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct ServerInfo {
pub name: Option<String>,
pub ip: Option<String>,
pub mtu: Option<i32>
}
impl TryFrom<&TunInterface> for ServerInfo{
type Error = anyhow::Error;
#[cfg(any(target_os="linux",target_vendor="apple"))]
fn try_from(server: &TunInterface) -> anyhow::Result<Self> {
Ok(
ServerInfo{
name: server.name().ok(),
ip: server.ipv4_addr().ok().map(|ip| ip.to_string()),
mtu: server.mtu().ok()
}
)
}
#[cfg(not(any(target_os="linux",target_vendor="apple")))]
fn try_from(server: &TunInterface) -> anyhow::Result<Self> {
Err(anyhow!("Not implemented in this platform"))
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct ServerConfig {
pub address: Option<String>,
pub name: Option<String>,
pub mtu: Option<i32>
}
impl Default for ServerConfig {
fn default() -> Self {
Self{
address: Some("10.0.0.1".to_string()), // Dummy remote address
name: None,
mtu: None
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub enum DaemonResponseData{
ServerInfo(ServerInfo),
ServerConfig(ServerConfig),
None
}
#[test]
fn test_response_serialization() -> anyhow::Result<()>{
insta::assert_snapshot!(
serde_json::to_string(&DaemonResponse::new(Ok::<DaemonResponseData, String>(DaemonResponseData::None)))?
);
insta::assert_snapshot!(
serde_json::to_string(&DaemonResponse::new(Ok::<DaemonResponseData, String>(DaemonResponseData::ServerInfo(ServerInfo{
name: Some("burrow".to_string()),
ip: None,
mtu: Some(1500)
}))))?
);
insta::assert_snapshot!(
serde_json::to_string(&DaemonResponse::new(Err::<DaemonResponseData, String>("error".to_string())))?
);
insta::assert_snapshot!(
serde_json::to_string(&DaemonResponse::new(Ok::<DaemonResponseData, String>(DaemonResponseData::ServerConfig(
ServerConfig::default()
))))?
);
Ok(())
}

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/command.rs
expression: "serde_json::to_string(&DaemonCommand::ServerInfo).unwrap()"
---
"ServerInfo"

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/command.rs
expression: "serde_json::to_string(&DaemonCommand::Stop).unwrap()"
---
"Stop"

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/command.rs
expression: "serde_json::to_string(&DaemonCommand::ServerConfig).unwrap()"
---
"ServerConfig"

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/command.rs
expression: "serde_json::to_string(&DaemonCommand::Start(DaemonStartOptions::default())).unwrap()"
---
{"Start":{"tun":{"name":null,"no_pi":null,"tun_excl":null}}}

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/response.rs
expression: "serde_json::to_string(&DaemonResponse::new(Ok::<DaemonResponseData,\n String>(DaemonResponseData::ServerInfo(ServerInfo {\n name: Some(\"burrow\".to_string()),\n ip: None,\n mtu: Some(1500),\n }))))?"
---
{"result":{"Ok":{"ServerInfo":{"name":"burrow","ip":null,"mtu":1500}}},"id":0}

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/response.rs
expression: "serde_json::to_string(&DaemonResponse::new(Err::<DaemonResponseData,\n String>(\"error\".to_string())))?"
---
{"result":{"Err":"error"},"id":0}

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/response.rs
expression: "serde_json::to_string(&DaemonResponse::new(Ok::<DaemonResponseData,\n String>(DaemonResponseData::ServerConfig(ServerConfig::default()))))?"
---
{"result":{"Ok":{"ServerConfig":{"address":"10.0.0.1","name":null,"mtu":null}}},"id":0}

View file

@ -0,0 +1,5 @@
---
source: burrow/src/daemon/response.rs
expression: "serde_json::to_string(&DaemonResponse::new(Ok::<DaemonResponseData,\n String>(DaemonResponseData::None)))?"
---
{"result":{"Ok":"None"},"id":0}

View file

@ -1,6 +1,8 @@
#![deny(missing_debug_implementations)]
pub mod ensureroot;
use anyhow::Result;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
use std::{
mem,
@ -11,6 +13,15 @@ use tun::TunInterface;
// TODO Separate start and retrieve functions
mod daemon;
pub use daemon::{DaemonCommand, DaemonResponseData, DaemonStartOptions, DaemonResponse, ServerInfo};
#[cfg(target_vendor = "apple")]
mod apple;
#[cfg(target_vendor = "apple")]
pub use apple::*;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[no_mangle]
pub extern "C" fn retrieve() -> i32 {

View file

@ -4,12 +4,12 @@ use std::mem;
use std::os::fd::FromRawFd;
use clap::{Args, Parser, Subcommand};
use tracing::instrument;
use tracing::{instrument, Level};
use tracing_log::LogTracer;
use tracing_oslog::OsLogger;
use tracing_subscriber::{prelude::*, FmtSubscriber};
use tokio::io::Result;
use tracing_subscriber::{prelude::*, FmtSubscriber, EnvFilter};
use anyhow::Result;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
use burrow::retrieve;
use tun::TunInterface;
@ -17,6 +17,7 @@ use tun::TunInterface;
mod daemon;
use daemon::{DaemonClient, DaemonCommand, DaemonStartOptions};
use crate::daemon::DaemonResponseData;
#[derive(Parser)]
#[command(name = "Burrow")]
@ -44,6 +45,10 @@ enum Commands {
Stop,
/// Start Burrow daemon
Daemon(DaemonArgs),
/// Server Info
ServerInfo,
/// Server config
ServerConfig,
}
#[derive(Args)]
@ -61,27 +66,38 @@ async fn try_start() -> Result<()> {
client
.send_command(DaemonCommand::Start(DaemonStartOptions::default()))
.await
.map(|_| ())
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[instrument]
async fn try_retrieve() -> Result<()> {
LogTracer::init().context("Failed to initialize LogTracer").unwrap();
if cfg!(target_os = "linux") || cfg!(target_vendor = "apple") {
let maybe_layer = system_log().unwrap();
if let Some(layer) = maybe_layer {
let logger = layer.with_subscriber(FmtSubscriber::new());
tracing::subscriber::set_global_default(logger).context("Failed to set the global tracing subscriber").unwrap();
}
}
burrow::ensureroot::ensure_root();
let iface2 = retrieve();
tracing::info!("{}", iface2);
Ok(())
}
async fn initialize_tracing() -> Result<()> {
LogTracer::init().context("Failed to initialize LogTracer")?;
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
{
let maybe_layer = system_log()?;
if let Some(layer) = maybe_layer {
let logger = layer.with_subscriber(
FmtSubscriber::builder()
.with_line_number(true)
.with_env_filter(EnvFilter::from_default_env())
.finish()
);
tracing::subscriber::set_global_default(logger).context("Failed to set the global tracing subscriber")?;
}
}
Ok(())
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
async fn try_stop() -> Result<()> {
let mut client = DaemonClient::new().await?;
@ -89,6 +105,44 @@ async fn try_stop() -> Result<()> {
Ok(())
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
async fn try_serverinfo() -> Result<()>{
let mut client = DaemonClient::new().await?;
let res = client.send_command(DaemonCommand::ServerInfo).await?;
match res.result {
Ok(DaemonResponseData::ServerInfo(si)) => {
println!("Got Result! {:?}", si);
}
Ok(DaemonResponseData::None) => {
println!("Server not started.")
}
Ok(res) => {println!("Unexpected Response: {:?}", res)}
Err(e) => {
println!("Error when retrieving from server: {}", e)
}
}
Ok(())
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
async fn try_serverconfig() -> Result<()>{
let mut client = DaemonClient::new().await?;
let res = client.send_command(DaemonCommand::ServerConfig).await?;
match res.result {
Ok(DaemonResponseData::ServerConfig(cfig)) => {
println!("Got Result! {:?}", cfig);
}
Ok(DaemonResponseData::None) => {
println!("Server not started.")
}
Ok(res) => {println!("Unexpected Response: {:?}", res)}
Err(e) => {
println!("Error when retrieving from server: {}", e)
}
}
Ok(())
}
#[cfg(not(any(target_os = "linux", target_vendor = "apple")))]
async fn try_start() -> Result<()> {
Ok(())
@ -104,24 +158,40 @@ async fn try_stop() -> Result<()> {
Ok(())
}
#[cfg(not(any(target_os = "linux", target_vendor = "apple")))]
async fn try_serverinfo() -> Result<()> {
Ok(())
}
#[cfg(not(any(target_os = "linux", target_vendor = "apple")))]
async fn try_serverconfig() -> Result<()> {
Ok(())
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
initialize_tracing().await?;
tracing::info!("Platform: {}", std::env::consts::OS);
let cli = Cli::parse();
match &cli.command {
Commands::Start(..) => {
try_start().await.unwrap();
try_start().await?;
tracing::info!("FINISHED");
}
Commands::Retrieve(..) => {
try_retrieve().await.unwrap();
try_retrieve().await?;
tracing::info!("FINISHED");
}
Commands::Stop => {
try_stop().await.unwrap();
try_stop().await?;
}
Commands::Daemon(_) => daemon::daemon_main().await?,
Commands::ServerInfo => {
try_serverinfo().await?
}
Commands::ServerConfig => {
try_serverconfig().await?
}
}
Ok(())
@ -141,5 +211,5 @@ fn system_log() -> anyhow::Result<Option<tracing_journald::Layer>> {
#[cfg(target_vendor = "apple")]
fn system_log() -> anyhow::Result<Option<OsLogger>> {
Ok(Some(OsLogger::new("com.hackclub.burrow", "default")))
Ok(Some(OsLogger::new("com.hackclub.burrow", "burrow-cli")))
}