agentmux_srv/bootstrap/
shutdown.rs1use super::*;
7
8pub fn emit_estart(ws_port: u16, web_port: u16, version: &str, build_time: &str, instance_id: &str) {
13 let pending_migrations = migrations::count_pending_migrations(&base::get_mux_data_dir());
14 eprintln!(
15 "AGENTMUXSRV-ESTART ws:127.0.0.1:{} web:127.0.0.1:{} version:{} buildtime:{} instance:{} pending_migrations:{}",
16 ws_port, web_port, version, build_time, instance_id, pending_migrations
17 );
18}
19
20pub fn install_shutdown_handlers(watch_stdin: bool) -> tokio_util::sync::CancellationToken {
25 let stdin_token = tokio_util::sync::CancellationToken::new();
29 let stdin_shutdown = stdin_token.clone();
30 if watch_stdin {
31 std::thread::spawn(move || {
32 use std::io::Read;
33 let mut stdin = std::io::stdin().lock();
34 let mut buf = [0u8; 1024];
35 loop {
36 match stdin.read(&mut buf) {
37 Ok(0) => {
38 eprintln_lossy!("stdin closed, shutting down");
39 stdin_shutdown.cancel();
40 break;
41 }
42 Ok(_) => {}
43 Err(e) => {
44 eprintln_lossy!("stdin read error: {}, shutting down", e);
45 stdin_shutdown.cancel();
46 break;
47 }
48 }
49 }
50 });
51 }
52
53 let signal_token = stdin_token.clone();
55 tokio::spawn(async move {
56 let ctrl_c = tokio::signal::ctrl_c();
57 #[cfg(unix)]
58 {
59 let mut sigterm =
60 tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).unwrap();
61 tokio::select! {
62 _ = ctrl_c => {
63 tracing::info!("received SIGINT, shutting down");
64 }
65 _ = sigterm.recv() => {
66 tracing::info!("received SIGTERM, shutting down");
67 }
68 }
69 }
70 #[cfg(not(unix))]
71 {
72 ctrl_c.await.ok();
73 tracing::info!("received Ctrl+C, shutting down");
74 }
75 signal_token.cancel();
76 });
77
78 stdin_token
79}
80
81pub fn spawn_wal_checkpoint_loop(
87 token: tokio_util::sync::CancellationToken,
88 wal_mstore: Arc<Store>,
89 wal_filestore: Arc<FileStore>,
90) {
91 tokio::spawn(async move {
92 const INTERVAL: std::time::Duration = std::time::Duration::from_secs(30 * 60);
93 loop {
94 tokio::select! {
95 _ = tokio::time::sleep(INTERVAL) => {}
96 _ = token.cancelled() => break,
97 }
98 if let Err(e) = wal_mstore.checkpoint() {
99 tracing::warn!(error = %e, "wal_checkpoint(TRUNCATE) on objects.db failed");
100 } else {
101 tracing::debug!("wal_checkpoint(TRUNCATE): objects.db ok");
102 }
103 if let Err(e) = wal_filestore.checkpoint() {
104 tracing::warn!(error = %e, "wal_checkpoint(TRUNCATE) on filestore.db failed");
105 } else {
106 tracing::debug!("wal_checkpoint(TRUNCATE): filestore.db ok");
107 }
108 }
109 });
110}