mysqladm/core/
bootstrap.rsuse std::{fs, path::PathBuf};
use anyhow::Context;
use nix::libc::{exit, EXIT_SUCCESS};
use std::os::unix::net::UnixStream as StdUnixStream;
use tokio::net::UnixStream as TokioUnixStream;
use crate::{
core::common::{UnixUser, DEFAULT_CONFIG_PATH, DEFAULT_SOCKET_PATH},
server::{config::read_config_from_path, server_loop::handle_requests_for_single_session},
};
pub fn drop_privs() -> anyhow::Result<()> {
log::debug!("Dropping privileges");
let real_uid = nix::unistd::getuid();
let real_gid = nix::unistd::getgid();
nix::unistd::setuid(real_uid).context("Failed to drop privileges")?;
nix::unistd::setgid(real_gid).context("Failed to drop privileges")?;
debug_assert_eq!(nix::unistd::getuid(), real_uid);
debug_assert_eq!(nix::unistd::getgid(), real_gid);
log::debug!("Privileges dropped successfully");
Ok(())
}
pub fn bootstrap_server_connection_and_drop_privileges(
server_socket_path: Option<PathBuf>,
config_path: Option<PathBuf>,
) -> anyhow::Result<StdUnixStream> {
if server_socket_path.is_some() && config_path.is_some() {
anyhow::bail!("Cannot provide both a socket path and a config path");
}
log::debug!("Starting the server connection bootstrap process");
let socket = bootstrap_server_connection(server_socket_path, config_path)?;
drop_privs()?;
Ok(socket)
}
fn bootstrap_server_connection(
socket_path: Option<PathBuf>,
config_path: Option<PathBuf>,
) -> anyhow::Result<StdUnixStream> {
if let Some(socket_path) = socket_path {
log::debug!("Connecting to socket at {:?}", socket_path);
return match StdUnixStream::connect(socket_path) {
Ok(socket) => Ok(socket),
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => Err(anyhow::anyhow!("Socket not found")),
std::io::ErrorKind::PermissionDenied => Err(anyhow::anyhow!("Permission denied")),
_ => Err(anyhow::anyhow!("Failed to connect to socket: {}", e)),
},
};
}
if let Some(config_path) = config_path {
if fs::metadata(&config_path).is_err() {
return Err(anyhow::anyhow!("Config file not found or not readable"));
}
log::debug!("Starting server with config at {:?}", config_path);
return invoke_server_with_config(config_path);
}
if fs::metadata(DEFAULT_SOCKET_PATH).is_ok() {
log::debug!("Connecting to default socket at {:?}", DEFAULT_SOCKET_PATH);
return match StdUnixStream::connect(DEFAULT_SOCKET_PATH) {
Ok(socket) => Ok(socket),
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => Err(anyhow::anyhow!("Socket not found")),
std::io::ErrorKind::PermissionDenied => Err(anyhow::anyhow!("Permission denied")),
_ => Err(anyhow::anyhow!("Failed to connect to socket: {}", e)),
},
};
}
let config_path = PathBuf::from(DEFAULT_CONFIG_PATH);
if fs::metadata(&config_path).is_ok() {
log::debug!("Starting server with default config at {:?}", config_path);
return invoke_server_with_config(config_path);
}
anyhow::bail!("No socket path or config path provided, and no default socket or config found");
}
fn invoke_server_with_config(config_path: PathBuf) -> anyhow::Result<StdUnixStream> {
let (server_socket, client_socket) = StdUnixStream::pair()?;
let unix_user = UnixUser::from_uid(nix::unistd::getuid().as_raw())?;
match (unsafe { nix::unistd::fork() }).context("Failed to fork")? {
nix::unistd::ForkResult::Parent { child } => {
log::debug!("Forked child process with PID {}", child);
Ok(client_socket)
}
nix::unistd::ForkResult::Child => {
log::debug!("Running server in child process");
match run_forked_server(config_path, server_socket, unix_user) {
Err(e) => Err(e),
Ok(_) => unreachable!(),
}
}
}
}
fn run_forked_server(
config_path: PathBuf,
server_socket: StdUnixStream,
unix_user: UnixUser,
) -> anyhow::Result<()> {
let config = read_config_from_path(Some(config_path))?;
let result: anyhow::Result<()> = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let socket = TokioUnixStream::from_std(server_socket)?;
handle_requests_for_single_session(socket, &unix_user, &config).await?;
Ok(())
});
result?;
unsafe {
exit(EXIT_SUCCESS);
}
}