Add log-init-function and websocket retry

This commit is contained in:
Tobias Reisinger 2023-11-29 01:03:04 +01:00
parent 6536ff0792
commit 50bcac2a1b
Signed by: serguzim
GPG key ID: 13AD60C237A28DFE
12 changed files with 72 additions and 68 deletions

BIN
Cargo.lock generated

Binary file not shown.

View file

@ -1,19 +1,20 @@
use std::str; use std::time::Duration;
use crate::relay_loop::run_relay_loop; use crate::relay_loop::run_relay_loop;
use crate::settings::Settings; use crate::settings::Settings;
use emgauwa_lib::db; use emgauwa_lib::constants::WEBSOCKET_RETRY_TIMEOUT;
use emgauwa_lib::db::{DbController, DbRelay}; use emgauwa_lib::db::{DbController, DbRelay};
use emgauwa_lib::handlers::v1::ws::controllers::ControllerWsAction; use emgauwa_lib::handlers::v1::ws::controllers::ControllerWsAction;
use emgauwa_lib::models::{Controller, FromDbModel}; use emgauwa_lib::models::{Controller, FromDbModel};
use emgauwa_lib::types::ControllerUid; use emgauwa_lib::types::ControllerUid;
use futures::channel::mpsc; use emgauwa_lib::{db, utils};
use futures::{future, pin_mut, SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use sqlx::pool::PoolConnection; use sqlx::pool::PoolConnection;
use sqlx::Sqlite; use sqlx::Sqlite;
use tokio::io::AsyncReadExt; use tokio::time;
use tokio_tungstenite::tungstenite::Error; use tokio_tungstenite::tungstenite::Error;
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
use utils::init_logging;
mod driver; mod driver;
mod relay_loop; mod relay_loop;
@ -51,6 +52,7 @@ async fn create_this_relay(
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let settings = settings::init(); let settings = settings::init();
init_logging(&settings.logging.level);
let pool = db::init(&settings.database).await; let pool = db::init(&settings.database).await;
@ -87,46 +89,47 @@ async fn main() {
settings.core.host, settings.core.port settings.core.host, settings.core.port
); );
let (stdin_tx, stdin_rx) = mpsc::unbounded(); tokio::spawn(run_relay_loop(settings));
tokio::spawn(read_stdin(stdin_tx));
let (ws_stream, _) = connect_async(url).await.expect("Failed to connect"); loop {
time::sleep(WEBSOCKET_RETRY_TIMEOUT).await;
let connect_result = connect_async(&url).await;
if let Err(err) = connect_result {
log::warn!(
"Failed to connect to websocket: {}. Retrying in {} seconds...",
err,
WEBSOCKET_RETRY_TIMEOUT.as_secs()
);
continue;
}
let (ws_stream, _) = connect_result.unwrap();
let (mut write, read) = ws_stream.split(); let (mut write, read) = ws_stream.split();
let ws_action = ControllerWsAction::Register(this); let ws_action = ControllerWsAction::Register(this.clone());
println!("Sending action: {:?}", ws_action);
let ws_action_json = serde_json::to_string(&ws_action).unwrap(); let ws_action_json = serde_json::to_string(&ws_action).unwrap();
println!("Sending json: {}", ws_action_json);
write.send(Message::text(ws_action_json)).await.unwrap(); write.send(Message::text(ws_action_json)).await.unwrap();
let ws_to_stdout = read.for_each(handle_message);
let stdin_to_ws = stdin_rx.map(Ok).forward(write);
tokio::spawn(run_relay_loop(settings)); let read_handler = read.for_each(handle_message);
pin_mut!(stdin_to_ws, ws_to_stdout); read_handler.await;
future::select(stdin_to_ws, ws_to_stdout).await;
}
// Our helper method which will read data from stdin and send it along the log::warn!(
// sender provided. "Lost connection to websocket. Retrying in {} seconds...",
async fn read_stdin(tx: mpsc::UnboundedSender<Message>) { WEBSOCKET_RETRY_TIMEOUT.as_secs()
let mut stdin = tokio::io::stdin(); );
loop {
let mut buf = vec![0; 1024];
let n = match stdin.read(&mut buf).await {
Err(_) | Ok(0) => break,
Ok(n) => n,
};
buf.truncate(n);
tx.unbounded_send(Message::text(str::from_utf8(&buf).unwrap()))
.unwrap();
} }
} }
pub async fn handle_message(message_result: Result<Message, Error>) { pub async fn handle_message(message_result: Result<Message, Error>) {
match message_result { match message_result {
Ok(message) => println!("{}", message.into_text().unwrap()), Ok(message) => {
Err(err) => println!("Error: {}", err), if let Message::Text(msg_text) = message {
log::debug!("{}", msg_text)
}
}
Err(err) => log::debug!("Error: {}", err),
} }
} }

View file

@ -9,6 +9,6 @@ pub async fn run_relay_loop(settings: Settings) {
loop { loop {
let next_timestamp = Local::now().naive_local().time() + default_duration; let next_timestamp = Local::now().naive_local().time() + default_duration;
time::sleep(default_duration).await; time::sleep(default_duration).await;
println!("Relay loop: {}", next_timestamp) log::debug!("Relay loop: {}", next_timestamp)
} }
} }

View file

@ -12,7 +12,6 @@ actix-web = "4.4"
actix-web-actors = "4.2" actix-web-actors = "4.2"
actix-cors = "0.6" actix-cors = "0.6"
simple_logger = "4.2"
log = "0.4" log = "0.4"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }

View file

@ -10,8 +10,7 @@ use actix_web::{middleware, web, App, HttpServer};
use emgauwa_lib::db::DbController; use emgauwa_lib::db::DbController;
use emgauwa_lib::handlers; use emgauwa_lib::handlers;
use emgauwa_lib::types::ConnectedControllersType; use emgauwa_lib::types::ConnectedControllersType;
use log::{trace, LevelFilter}; use emgauwa_lib::utils::init_logging;
use simple_logger::SimpleLogger;
mod settings; mod settings;
mod utils; mod utils;
@ -19,15 +18,7 @@ mod utils;
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
let settings = settings::init(); let settings = settings::init();
init_logging(&settings.logging.level);
let log_level: LevelFilter =
LevelFilter::from_str(&settings.logging.level).expect("Error parsing log level.");
trace!("Log level set to {:?}", log_level);
SimpleLogger::new()
.with_level(log_level)
.init()
.expect("Error initializing logger.");
let listener = TcpListener::bind(format!("{}:{}", settings.host, settings.port)) let listener = TcpListener::bind(format!("{}:{}", settings.host, settings.port))
.expect("Error creating listener"); .expect("Error creating listener");

View file

@ -1,4 +1,5 @@
use crate::settings::Settings; use crate::settings::Settings;
use log::log;
use std::ffi::CString; use std::ffi::CString;
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
@ -26,11 +27,11 @@ fn drop_privileges_group(group: &str) -> Result<(), Error> {
if let Ok(cstr) = CString::new(group.as_bytes()) { if let Ok(cstr) = CString::new(group.as_bytes()) {
let p = unsafe { libc::getgrnam(cstr.as_ptr()) }; let p = unsafe { libc::getgrnam(cstr.as_ptr()) };
if p.is_null() { if p.is_null() {
eprintln!("Unable to getgrnam of group: {}", group); log::error!("Unable to getgrnam of group: {}", group);
return Err(Error::last_os_error()); return Err(Error::last_os_error());
} }
if unsafe { libc::setgid((*p).gr_gid) } != 0 { if unsafe { libc::setgid((*p).gr_gid) } != 0 {
eprintln!("Unable to setgid of group: {}", group); log::error!("Unable to setgid of group: {}", group);
return Err(Error::last_os_error()); return Err(Error::last_os_error());
} }
} else { } else {
@ -47,11 +48,11 @@ fn drop_privileges_user(user: &str) -> Result<(), Error> {
if let Ok(cstr) = CString::new(user.as_bytes()) { if let Ok(cstr) = CString::new(user.as_bytes()) {
let p = unsafe { libc::getpwnam(cstr.as_ptr()) }; let p = unsafe { libc::getpwnam(cstr.as_ptr()) };
if p.is_null() { if p.is_null() {
eprintln!("Unable to getpwnam of user: {}", user); log::error!("Unable to getpwnam of user: {}", user);
return Err(Error::last_os_error()); return Err(Error::last_os_error());
} }
if unsafe { libc::setuid((*p).pw_uid) } != 0 { if unsafe { libc::setuid((*p).pw_uid) } != 0 {
eprintln!("Unable to setuid of user: {}", user); log::error!("Unable to setuid of user: {}", user);
return Err(Error::last_os_error()); return Err(Error::last_os_error());
} }
} else { } else {

View file

@ -14,13 +14,14 @@ serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
simple_logger = "4.2"
log = "0.4"
config = "0.13" config = "0.13"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] } sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio", "macros", "chrono"] }
libsqlite3-sys = { version = "*", features = ["bundled"] } libsqlite3-sys = { version = "*", features = ["bundled"] }
log = "0.4"
uuid = "1.6" uuid = "1.6"
futures = "0.3" futures = "0.3"

View file

@ -3,3 +3,5 @@ use std::time::Duration;
pub const DEFAULT_PORT: u16 = 4419; pub const DEFAULT_PORT: u16 = 4419;
pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
pub const HEARTBEAT_TIMEOUT: Duration = Duration::from_secs(15); pub const HEARTBEAT_TIMEOUT: Duration = Duration::from_secs(15);
pub const WEBSOCKET_RETRY_TIMEOUT: Duration = Duration::from_secs(5);

View file

@ -9,18 +9,12 @@ use crate::models::{convert_db_list, Controller};
use crate::types::ConnectedControllersType; use crate::types::ConnectedControllersType;
#[get("/api/v1/controllers")] #[get("/api/v1/controllers")]
pub async fn index( pub async fn index(pool: web::Data<Pool<Sqlite>>) -> Result<HttpResponse, ApiError> {
pool: web::Data<Pool<Sqlite>>,
connected_controllers: web::Data<ConnectedControllersType>,
) -> Result<HttpResponse, ApiError> {
let mut pool_conn = pool.acquire().await?; let mut pool_conn = pool.acquire().await?;
let db_controllers = DbController::get_all(&mut pool_conn).await?; let db_controllers = DbController::get_all(&mut pool_conn).await?;
let controllers: Vec<Controller> = convert_db_list(&mut pool_conn, db_controllers)?; let controllers: Vec<Controller> = convert_db_list(&mut pool_conn, db_controllers)?;
let data = connected_controllers.lock().unwrap();
println!("{:?}", *data);
Ok(HttpResponse::Ok().json(controllers)) Ok(HttpResponse::Ok().json(controllers))
} }

View file

@ -26,6 +26,5 @@ pub async fn ws_controllers(
stream, stream,
) )
.map_err(|_| ApiError::InternalError(String::from("error starting websocket"))); .map_err(|_| ApiError::InternalError(String::from("error starting websocket")));
println!("{:?}", resp);
resp resp
} }

View file

@ -17,14 +17,14 @@ pub trait FromDbModel {
Self: Sized; Self: Sized;
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Schedule { pub struct Schedule {
#[serde(flatten)] #[serde(flatten)]
pub s: DbSchedule, pub s: DbSchedule,
pub tags: Vec<String>, pub tags: Vec<String>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Relay { pub struct Relay {
#[serde(flatten)] #[serde(flatten)]
pub r: DbRelay, pub r: DbRelay,
@ -33,7 +33,7 @@ pub struct Relay {
pub tags: Vec<String>, pub tags: Vec<String>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Controller { pub struct Controller {
#[serde(flatten)] #[serde(flatten)]
pub c: DbController, pub c: DbController,

View file

@ -1,3 +1,7 @@
use log::LevelFilter;
use simple_logger::SimpleLogger;
use std::str::FromStr;
pub fn load_settings<T>(config_name: &str, env_prefix: &str) -> T pub fn load_settings<T>(config_name: &str, env_prefix: &str) -> T
where where
for<'de> T: serde::Deserialize<'de>, for<'de> T: serde::Deserialize<'de>,
@ -16,3 +20,13 @@ where
.try_deserialize::<T>() .try_deserialize::<T>()
.expect("Error reading settings") .expect("Error reading settings")
} }
pub fn init_logging(level: &str) {
let log_level: LevelFilter = LevelFilter::from_str(level).expect("Error parsing log level.");
log::trace!("Log level set to {:?}", log_level);
SimpleLogger::new()
.with_level(log_level)
.init()
.expect("Error initializing logger.");
}