use atuin_client::database::Sqlite as HistoryDatabase;
use atuin_client::record::sqlite_store::SqliteStore;
use atuin_client::settings::{Settings, watcher::global_settings_watcher};
use eyre::Result;
pub mod client;
pub mod components;
pub mod control;
pub mod daemon;
pub mod events;
pub mod history;
pub mod search;
pub mod server;
pub use daemon::{Component, Daemon, DaemonBuilder, DaemonHandle};
pub use events::DaemonEvent;
pub use components::{HistoryComponent, SearchComponent, SyncComponent};
pub use client::{ControlClient, emit_event, emit_event_with_settings};
pub async fn boot(
settings: Settings,
store: SqliteStore,
history_db: HistoryDatabase,
) -> Result<()> {
let history_component = HistoryComponent::new();
let search_component = SearchComponent::new();
let sync_component = SyncComponent::new();
let history_service = history_component.grpc_service();
let search_service = search_component.grpc_service();
let mut daemon = Daemon::builder(settings.clone())
.store(store)
.history_db(history_db)
.component(history_component)
.component(search_component)
.component(sync_component)
.build()
.await?;
let handle = daemon.handle();
let control_service = control::ControlService::new(handle.clone());
daemon.start_components().await?;
if let Ok(watcher) = global_settings_watcher() {
let mut settings_rx = watcher.subscribe();
let watcher_handle = handle.clone();
tokio::spawn(async move {
tracing::info!("config file watcher started");
while settings_rx.changed().await.is_ok() {
let new_settings = (*settings_rx.borrow()).clone();
watcher_handle.apply_settings((*new_settings).clone()).await;
}
tracing::debug!("config file watcher stopped");
});
} else {
tracing::warn!(
"failed to start config file watcher; settings changes will require daemon restart"
);
}
let signal_handle = handle.clone();
tokio::spawn(async move {
shutdown_signal().await;
tracing::info!("received shutdown signal");
signal_handle.shutdown();
});
server::run_grpc_server(
settings,
history_service,
search_service,
control_service.into_server(),
handle,
)
.await?;
daemon.run_event_loop().await?;
daemon.stop_components().await;
tracing::info!("daemon shut down complete");
Ok(())
}
#[cfg(unix)]
async fn shutdown_signal() {
let mut term = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to register sigterm handler");
let mut int = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt())
.expect("failed to register sigint handler");
tokio::select! {
_ = term.recv() => {},
_ = int.recv() => {},
}
}
#[cfg(not(unix))]
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("failed to listen for ctrl+c");
}