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}