tab_daemon/
lib.rs

1use daemonfile::DaemonFile;
2
3use crate::bus::DaemonBus;
4use log::{info, LevelFilter};
5
6use lifeline::{dyn_bus::DynBus, prelude::*};
7use message::daemon::DaemonShutdown;
8use service::daemon::DaemonService;
9use simplelog::{CombinedLogger, TermLogger, TerminalMode, WriteLogger};
10use std::time::Duration;
11use tab_api::{
12    config::{daemon_log, DaemonConfig},
13    launch::wait_for_shutdown,
14    log::get_level,
15};
16use tab_websocket::resource::listener::{WebsocketAuthToken, WebsocketListenerResource};
17use tokio::net::TcpListener;
18
19mod auth;
20mod bus;
21mod daemonfile;
22mod message;
23mod prelude;
24mod service;
25mod state;
26
27pub fn daemon_main(tab_version: &'static str) -> anyhow::Result<()> {
28    let mut runtime = tokio::runtime::Builder::new()
29        .threaded_scheduler()
30        .enable_io()
31        .enable_time()
32        .build()
33        .unwrap();
34
35    let result = runtime.block_on(async { main_async(tab_version).await });
36
37    runtime.shutdown_timeout(Duration::from_millis(25));
38
39    result?;
40
41    Ok(())
42}
43
44pub async fn new_bus(tab_version: &'static str) -> anyhow::Result<DaemonBus> {
45    let server = TcpListener::bind("127.0.0.1:0").await?;
46    let port = server.local_addr()?.port();
47    let websocket = WebsocketListenerResource(server);
48
49    let auth_token = auth::gen_token();
50    let pid = std::process::id();
51    let executable = std::env::current_exe()
52        .ok()
53        .map(|path| path.to_str().map(str::to_string))
54        .flatten();
55
56    let config = DaemonConfig {
57        pid: pid as i32,
58        port,
59        executable,
60        tab_version: Some(tab_version.to_string()),
61        auth_token: auth_token.clone(),
62    };
63
64    let bus = DaemonBus::default();
65    bus.store_resource::<DaemonConfig>(config);
66    bus.store_resource::<WebsocketAuthToken>(auth_token.into());
67    bus.store_resource::<WebsocketListenerResource>(websocket);
68
69    Ok(bus)
70}
71
72async fn main_async(tab_version: &'static str) -> anyhow::Result<()> {
73    let log_file = daemon_log()?;
74
75    let config = simplelog::ConfigBuilder::new()
76        .set_time_format_str("%H:%M:%S%.3f DAE")
77        .build();
78
79    let level = get_level().unwrap_or(LevelFilter::Info);
80    CombinedLogger::init(vec![
81        TermLogger::new(level, config.clone(), TerminalMode::Stderr),
82        WriteLogger::new(level, config, std::fs::File::create(log_file)?),
83    ])
84    .unwrap();
85
86    log_panics::init();
87
88    let bus = new_bus(tab_version).await?;
89    let config = bus.resource::<DaemonConfig>()?;
90
91    let daemon_file = DaemonFile::new(&config)?;
92    info!("Daemon started.");
93    info!("Daemon pid: {}", config.pid);
94    info!("Daemon port: {}", config.port);
95
96    let _service = DaemonService::spawn(&bus)?;
97    let shutdown = bus.rx::<DaemonShutdown>()?;
98
99    wait_for_shutdown(shutdown).await;
100
101    info!("Daemon shutdown.");
102    drop(daemon_file);
103
104    Ok(())
105}