mysqladm/server/
command.rs

1use std::path::PathBuf;
2
3use anyhow::Context;
4use clap::Parser;
5use clap_verbosity_flag::Verbosity;
6use systemd_journal_logger::JournalLog;
7
8use crate::server::{
9    config::{ServerConfigArgs, read_config_from_path_with_arg_overrides},
10    server_loop::{
11        listen_for_incoming_connections_with_socket_path,
12        listen_for_incoming_connections_with_systemd_socket,
13    },
14};
15
16#[derive(Parser, Debug, Clone)]
17pub struct ServerArgs {
18    #[command(subcommand)]
19    subcmd: ServerCommand,
20
21    #[command(flatten)]
22    config_overrides: ServerConfigArgs,
23
24    #[arg(long)]
25    systemd: bool,
26}
27
28#[derive(Parser, Debug, Clone)]
29pub enum ServerCommand {
30    #[command()]
31    Listen,
32
33    #[command()]
34    SocketActivate,
35}
36
37const LOG_LEVEL_WARNING: &str = r#"
38===================================================
39== WARNING: LOG LEVEL IS SET TO 'TRACE'!         ==
40== THIS WILL CAUSE THE SERVER TO LOG SQL QUERIES ==
41== THAT MAY CONTAIN SENSITIVE INFORMATION LIKE   ==
42== PASSWORDS AND AUTHENTICATION TOKENS.          ==
43== THIS IS INTENDED FOR DEBUGGING PURPOSES ONLY  ==
44== AND SHOULD *NEVER* BE USED IN PRODUCTION.     ==
45===================================================
46"#;
47
48pub async fn handle_command(
49    socket_path: Option<PathBuf>,
50    config_path: Option<PathBuf>,
51    verbosity: Verbosity,
52    args: ServerArgs,
53) -> anyhow::Result<()> {
54    let mut auto_detected_systemd_mode = false;
55    let systemd_mode = args.systemd || {
56        if let Ok(true) = sd_notify::booted() {
57            auto_detected_systemd_mode = true;
58            true
59        } else {
60            false
61        }
62    };
63    if systemd_mode {
64        JournalLog::new()
65            .context("Failed to initialize journald logging")?
66            .install()
67            .context("Failed to install journald logger")?;
68
69        log::set_max_level(verbosity.log_level_filter());
70
71        if verbosity.log_level_filter() >= log::LevelFilter::Trace {
72            log::warn!("{}", LOG_LEVEL_WARNING.trim());
73        }
74
75        if auto_detected_systemd_mode {
76            log::info!("Running in systemd mode, auto-detected");
77        } else {
78            log::info!("Running in systemd mode");
79        }
80
81        start_watchdog_thread_if_enabled();
82    } else {
83        env_logger::Builder::new()
84            .filter_level(verbosity.log_level_filter())
85            .init();
86
87        log::info!("Running in standalone mode");
88    }
89
90    let config = read_config_from_path_with_arg_overrides(config_path, args.config_overrides)?;
91
92    match args.subcmd {
93        ServerCommand::Listen => {
94            listen_for_incoming_connections_with_socket_path(socket_path, config).await
95        }
96        ServerCommand::SocketActivate => {
97            if !args.systemd {
98                anyhow::bail!(concat!(
99                    "The `--systemd` flag must be used with the `socket-activate` command.\n",
100                    "This command currently only supports socket activation under systemd."
101                ));
102            }
103
104            listen_for_incoming_connections_with_systemd_socket(config).await
105        }
106    }
107}
108
109fn start_watchdog_thread_if_enabled() {
110    let mut micro_seconds: u64 = 0;
111    let watchdog_enabled = sd_notify::watchdog_enabled(false, &mut micro_seconds);
112
113    if watchdog_enabled {
114        micro_seconds = micro_seconds.max(2_000_000).div_ceil(2);
115
116        tokio::spawn(async move {
117            log::debug!(
118                "Starting systemd watchdog thread with {} millisecond interval",
119                micro_seconds.div_ceil(1000)
120            );
121            loop {
122                tokio::time::sleep(tokio::time::Duration::from_micros(micro_seconds)).await;
123                if let Err(err) = sd_notify::notify(false, &[sd_notify::NotifyState::Watchdog]) {
124                    log::warn!("Failed to notify systemd watchdog: {}", err);
125                } else {
126                    log::trace!("Ping sent to systemd watchdog");
127                }
128            }
129        });
130    } else {
131        log::debug!("Systemd watchdog not enabled, skipping watchdog thread");
132    }
133}