1mod config;
7mod pty;
8mod session;
9mod terminal;
10mod web;
11
12use clap::Parser;
13use tokio::net::TcpListener;
14use tracing_subscriber::EnvFilter;
15
16use crate::config::{Config, LogFormat};
17use crate::session::SessionStore;
18
19#[tokio::main]
20async fn main() {
21 let config = Config::parse();
22
23 let filter =
24 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&config.log_level));
25
26 match config.log_format {
27 LogFormat::Text => tracing_subscriber::fmt().with_env_filter(filter).init(),
28 LogFormat::Json => tracing_subscriber::fmt()
29 .json()
30 .with_env_filter(filter)
31 .init(),
32 };
33
34 let sessions = SessionStore::new();
35 let addr = std::net::SocketAddr::new(config.address, config.port);
36 let app = web::router(
37 config.shell,
38 config.pwd,
39 config.scrollback_limit * 1024,
40 sessions,
41 );
42
43 let listener = TcpListener::bind(addr).await.unwrap_or_else(|e| {
44 tracing::error!("failed to bind to {}: {}", addr, e);
45 std::process::exit(1);
46 });
47
48 tracing::info!("listening on http://{}", addr);
49
50 axum::serve(listener, app)
51 .with_graceful_shutdown(shutdown_signal())
52 .await
53 .unwrap_or_else(|e| {
54 tracing::error!("server error: {}", e);
55 std::process::exit(1);
56 });
57}
58
59async fn shutdown_signal() {
60 let ctrl_c = tokio::signal::ctrl_c();
61 let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
62 .expect("failed to install SIGTERM handler");
63
64 tokio::select! {
65 _ = ctrl_c => {
66 tracing::info!("received Ctrl+C, shutting down");
67 }
68 _ = sigterm.recv() => {
69 tracing::info!("received SIGTERM, shutting down");
70 }
71 }
72}