remote_server: Improve error reporting (#33770)
Closes #33736 Use `thiserror` to implement error stack and `anyhow` to report is to user. Also move some code from main to remote_server to have better crate isolation. Release Notes: - N/A --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
parent
99cee8778c
commit
ad25aba990
5 changed files with 216 additions and 105 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -13521,6 +13521,7 @@ dependencies = [
|
||||||
"smol",
|
"smol",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"telemetry_events",
|
"telemetry_events",
|
||||||
|
"thiserror 2.0.12",
|
||||||
"toml 0.8.20",
|
"toml 0.8.20",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
|
|
|
@ -65,6 +65,7 @@ telemetry_events.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
watch.workspace = true
|
watch.workspace = true
|
||||||
worktree.workspace = true
|
worktree.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
crashes.workspace = true
|
crashes.workspace = true
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
|
#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::Parser;
|
||||||
|
use remote_server::Commands;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
@ -21,105 +22,34 @@ struct Cli {
|
||||||
printenv: bool,
|
printenv: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
enum Commands {
|
|
||||||
Run {
|
|
||||||
#[arg(long)]
|
|
||||||
log_file: PathBuf,
|
|
||||||
#[arg(long)]
|
|
||||||
pid_file: PathBuf,
|
|
||||||
#[arg(long)]
|
|
||||||
stdin_socket: PathBuf,
|
|
||||||
#[arg(long)]
|
|
||||||
stdout_socket: PathBuf,
|
|
||||||
#[arg(long)]
|
|
||||||
stderr_socket: PathBuf,
|
|
||||||
},
|
|
||||||
Proxy {
|
|
||||||
#[arg(long)]
|
|
||||||
reconnect: bool,
|
|
||||||
#[arg(long)]
|
|
||||||
identifier: String,
|
|
||||||
},
|
|
||||||
Version,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn main() {
|
fn main() {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn main() {
|
fn main() -> anyhow::Result<()> {
|
||||||
use release_channel::{RELEASE_CHANNEL, ReleaseChannel};
|
|
||||||
use remote::proxy::ProxyLaunchError;
|
|
||||||
use remote_server::unix::{execute_proxy, execute_run};
|
|
||||||
|
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
if let Some(socket_path) = &cli.askpass {
|
if let Some(socket_path) = &cli.askpass {
|
||||||
askpass::main(socket_path);
|
askpass::main(socket_path);
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(socket) = &cli.crash_handler {
|
if let Some(socket) = &cli.crash_handler {
|
||||||
crashes::crash_server(socket.as_path());
|
crashes::crash_server(socket.as_path());
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if cli.printenv {
|
if cli.printenv {
|
||||||
util::shell_env::print_env();
|
util::shell_env::print_env();
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = match cli.command {
|
if let Some(command) = cli.command {
|
||||||
Some(Commands::Run {
|
remote_server::run(command)
|
||||||
log_file,
|
} else {
|
||||||
pid_file,
|
eprintln!("usage: remote <run|proxy|version>");
|
||||||
stdin_socket,
|
|
||||||
stdout_socket,
|
|
||||||
stderr_socket,
|
|
||||||
}) => execute_run(
|
|
||||||
log_file,
|
|
||||||
pid_file,
|
|
||||||
stdin_socket,
|
|
||||||
stdout_socket,
|
|
||||||
stderr_socket,
|
|
||||||
),
|
|
||||||
Some(Commands::Proxy {
|
|
||||||
identifier,
|
|
||||||
reconnect,
|
|
||||||
}) => match execute_proxy(identifier, reconnect) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(err) => {
|
|
||||||
if let Some(err) = err.downcast_ref::<ProxyLaunchError>() {
|
|
||||||
std::process::exit(err.to_exit_code());
|
|
||||||
}
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(Commands::Version) => {
|
|
||||||
let release_channel = *RELEASE_CHANNEL;
|
|
||||||
match release_channel {
|
|
||||||
ReleaseChannel::Stable | ReleaseChannel::Preview => {
|
|
||||||
println!("{}", env!("ZED_PKG_VERSION"))
|
|
||||||
}
|
|
||||||
ReleaseChannel::Nightly | ReleaseChannel::Dev => {
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
option_env!("ZED_COMMIT_SHA").unwrap_or(release_channel.dev_name())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
eprintln!("usage: remote <run|proxy|version>");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(error) = result {
|
|
||||||
log::error!("exiting due to error: {}", error);
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,78 @@ pub mod unix;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod remote_editing_tests;
|
mod remote_editing_tests;
|
||||||
|
|
||||||
|
use clap::Subcommand;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub use headless_project::{HeadlessAppState, HeadlessProject};
|
pub use headless_project::{HeadlessAppState, HeadlessProject};
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Commands {
|
||||||
|
Run {
|
||||||
|
#[arg(long)]
|
||||||
|
log_file: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
pid_file: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
stdin_socket: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
stdout_socket: PathBuf,
|
||||||
|
#[arg(long)]
|
||||||
|
stderr_socket: PathBuf,
|
||||||
|
},
|
||||||
|
Proxy {
|
||||||
|
#[arg(long)]
|
||||||
|
reconnect: bool,
|
||||||
|
#[arg(long)]
|
||||||
|
identifier: String,
|
||||||
|
},
|
||||||
|
Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
pub fn run(command: Commands) -> anyhow::Result<()> {
|
||||||
|
use anyhow::Context;
|
||||||
|
use release_channel::{RELEASE_CHANNEL, ReleaseChannel};
|
||||||
|
use unix::{ExecuteProxyError, execute_proxy, execute_run};
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Commands::Run {
|
||||||
|
log_file,
|
||||||
|
pid_file,
|
||||||
|
stdin_socket,
|
||||||
|
stdout_socket,
|
||||||
|
stderr_socket,
|
||||||
|
} => execute_run(
|
||||||
|
log_file,
|
||||||
|
pid_file,
|
||||||
|
stdin_socket,
|
||||||
|
stdout_socket,
|
||||||
|
stderr_socket,
|
||||||
|
),
|
||||||
|
Commands::Proxy {
|
||||||
|
identifier,
|
||||||
|
reconnect,
|
||||||
|
} => execute_proxy(identifier, reconnect)
|
||||||
|
.inspect_err(|err| {
|
||||||
|
if let ExecuteProxyError::ServerNotRunning(err) = err {
|
||||||
|
std::process::exit(err.to_exit_code());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context("running proxy on the remote server"),
|
||||||
|
Commands::Version => {
|
||||||
|
let release_channel = *RELEASE_CHANNEL;
|
||||||
|
match release_channel {
|
||||||
|
ReleaseChannel::Stable | ReleaseChannel::Preview => {
|
||||||
|
println!("{}", env!("ZED_PKG_VERSION"))
|
||||||
|
}
|
||||||
|
ReleaseChannel::Nightly | ReleaseChannel::Dev => {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
option_env!("ZED_COMMIT_SHA").unwrap_or(release_channel.dev_name())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ use smol::Async;
|
||||||
use smol::{net::unix::UnixListener, stream::StreamExt as _};
|
use smol::{net::unix::UnixListener, stream::StreamExt as _};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
use std::process::ExitStatus;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use std::{env, thread};
|
use std::{env, thread};
|
||||||
|
@ -46,6 +47,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use telemetry_events::LocationData;
|
use telemetry_events::LocationData;
|
||||||
|
use thiserror::Error;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
pub static VERSION: LazyLock<&str> = LazyLock::new(|| match *RELEASE_CHANNEL {
|
pub static VERSION: LazyLock<&str> = LazyLock::new(|| match *RELEASE_CHANNEL {
|
||||||
|
@ -526,7 +528,23 @@ pub fn execute_run(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Error)]
|
||||||
|
pub(crate) enum ServerPathError {
|
||||||
|
#[error("Failed to create server_dir `{path}`")]
|
||||||
|
CreateServerDir {
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
#[error("Failed to create logs_dir `{path}`")]
|
||||||
|
CreateLogsDir {
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
struct ServerPaths {
|
struct ServerPaths {
|
||||||
log_file: PathBuf,
|
log_file: PathBuf,
|
||||||
pid_file: PathBuf,
|
pid_file: PathBuf,
|
||||||
|
@ -536,10 +554,19 @@ struct ServerPaths {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerPaths {
|
impl ServerPaths {
|
||||||
fn new(identifier: &str) -> Result<Self> {
|
fn new(identifier: &str) -> Result<Self, ServerPathError> {
|
||||||
let server_dir = paths::remote_server_state_dir().join(identifier);
|
let server_dir = paths::remote_server_state_dir().join(identifier);
|
||||||
std::fs::create_dir_all(&server_dir)?;
|
std::fs::create_dir_all(&server_dir).map_err(|source| {
|
||||||
std::fs::create_dir_all(&logs_dir())?;
|
ServerPathError::CreateServerDir {
|
||||||
|
source,
|
||||||
|
path: server_dir.clone(),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
let log_dir = logs_dir();
|
||||||
|
std::fs::create_dir_all(log_dir).map_err(|source| ServerPathError::CreateLogsDir {
|
||||||
|
source: source,
|
||||||
|
path: log_dir.clone(),
|
||||||
|
})?;
|
||||||
|
|
||||||
let pid_file = server_dir.join("server.pid");
|
let pid_file = server_dir.join("server.pid");
|
||||||
let stdin_socket = server_dir.join("stdin.sock");
|
let stdin_socket = server_dir.join("stdin.sock");
|
||||||
|
@ -557,7 +584,43 @@ impl ServerPaths {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_proxy(identifier: String, is_reconnecting: bool) -> Result<()> {
|
#[derive(Debug, Error)]
|
||||||
|
pub(crate) enum ExecuteProxyError {
|
||||||
|
#[error("Failed to init server paths")]
|
||||||
|
ServerPath(#[from] ServerPathError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
ServerNotRunning(#[from] ProxyLaunchError),
|
||||||
|
|
||||||
|
#[error("Failed to check PidFile '{path}'")]
|
||||||
|
CheckPidFile {
|
||||||
|
#[source]
|
||||||
|
source: CheckPidError,
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Failed to kill existing server with pid '{pid}'")]
|
||||||
|
KillRunningServer {
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
pid: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("failed to spawn server")]
|
||||||
|
SpawnServer(#[source] SpawnServerError),
|
||||||
|
|
||||||
|
#[error("stdin_task failed")]
|
||||||
|
StdinTask(#[source] anyhow::Error),
|
||||||
|
#[error("stdout_task failed")]
|
||||||
|
StdoutTask(#[source] anyhow::Error),
|
||||||
|
#[error("stderr_task failed")]
|
||||||
|
StderrTask(#[source] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn execute_proxy(
|
||||||
|
identifier: String,
|
||||||
|
is_reconnecting: bool,
|
||||||
|
) -> Result<(), ExecuteProxyError> {
|
||||||
init_logging_proxy();
|
init_logging_proxy();
|
||||||
|
|
||||||
let server_paths = ServerPaths::new(&identifier)?;
|
let server_paths = ServerPaths::new(&identifier)?;
|
||||||
|
@ -574,12 +637,19 @@ pub fn execute_proxy(identifier: String, is_reconnecting: bool) -> Result<()> {
|
||||||
|
|
||||||
log::info!("starting proxy process. PID: {}", std::process::id());
|
log::info!("starting proxy process. PID: {}", std::process::id());
|
||||||
|
|
||||||
let server_pid = check_pid_file(&server_paths.pid_file)?;
|
let server_pid = check_pid_file(&server_paths.pid_file).map_err(|source| {
|
||||||
|
ExecuteProxyError::CheckPidFile {
|
||||||
|
source,
|
||||||
|
path: server_paths.pid_file.clone(),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
let server_running = server_pid.is_some();
|
let server_running = server_pid.is_some();
|
||||||
if is_reconnecting {
|
if is_reconnecting {
|
||||||
if !server_running {
|
if !server_running {
|
||||||
log::error!("attempted to reconnect, but no server running");
|
log::error!("attempted to reconnect, but no server running");
|
||||||
anyhow::bail!(ProxyLaunchError::ServerNotRunning);
|
return Err(ExecuteProxyError::ServerNotRunning(
|
||||||
|
ProxyLaunchError::ServerNotRunning,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(pid) = server_pid {
|
if let Some(pid) = server_pid {
|
||||||
|
@ -590,7 +660,7 @@ pub fn execute_proxy(identifier: String, is_reconnecting: bool) -> Result<()> {
|
||||||
kill_running_server(pid, &server_paths)?;
|
kill_running_server(pid, &server_paths)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
spawn_server(&server_paths)?;
|
spawn_server(&server_paths).map_err(ExecuteProxyError::SpawnServer)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
let stdin_task = smol::spawn(async move {
|
let stdin_task = smol::spawn(async move {
|
||||||
|
@ -630,9 +700,9 @@ pub fn execute_proxy(identifier: String, is_reconnecting: bool) -> Result<()> {
|
||||||
|
|
||||||
if let Err(forwarding_result) = smol::block_on(async move {
|
if let Err(forwarding_result) = smol::block_on(async move {
|
||||||
futures::select! {
|
futures::select! {
|
||||||
result = stdin_task.fuse() => result.context("stdin_task failed"),
|
result = stdin_task.fuse() => result.map_err(ExecuteProxyError::StdinTask),
|
||||||
result = stdout_task.fuse() => result.context("stdout_task failed"),
|
result = stdout_task.fuse() => result.map_err(ExecuteProxyError::StdoutTask),
|
||||||
result = stderr_task.fuse() => result.context("stderr_task failed"),
|
result = stderr_task.fuse() => result.map_err(ExecuteProxyError::StderrTask),
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
log::error!(
|
log::error!(
|
||||||
|
@ -645,12 +715,12 @@ pub fn execute_proxy(identifier: String, is_reconnecting: bool) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kill_running_server(pid: u32, paths: &ServerPaths) -> Result<()> {
|
fn kill_running_server(pid: u32, paths: &ServerPaths) -> Result<(), ExecuteProxyError> {
|
||||||
log::info!("killing existing server with PID {}", pid);
|
log::info!("killing existing server with PID {}", pid);
|
||||||
std::process::Command::new("kill")
|
std::process::Command::new("kill")
|
||||||
.arg(pid.to_string())
|
.arg(pid.to_string())
|
||||||
.output()
|
.output()
|
||||||
.context("failed to kill existing server")?;
|
.map_err(|source| ExecuteProxyError::KillRunningServer { source, pid })?;
|
||||||
|
|
||||||
for file in [
|
for file in [
|
||||||
&paths.pid_file,
|
&paths.pid_file,
|
||||||
|
@ -664,18 +734,39 @@ fn kill_running_server(pid: u32, paths: &ServerPaths) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_server(paths: &ServerPaths) -> Result<()> {
|
#[derive(Debug, Error)]
|
||||||
|
pub(crate) enum SpawnServerError {
|
||||||
|
#[error("failed to remove stdin socket")]
|
||||||
|
RemoveStdinSocket(#[source] std::io::Error),
|
||||||
|
|
||||||
|
#[error("failed to remove stdout socket")]
|
||||||
|
RemoveStdoutSocket(#[source] std::io::Error),
|
||||||
|
|
||||||
|
#[error("failed to remove stderr socket")]
|
||||||
|
RemoveStderrSocket(#[source] std::io::Error),
|
||||||
|
|
||||||
|
#[error("failed to get current_exe")]
|
||||||
|
CurrentExe(#[source] std::io::Error),
|
||||||
|
|
||||||
|
#[error("failed to launch server process")]
|
||||||
|
ProcessStatus(#[source] std::io::Error),
|
||||||
|
|
||||||
|
#[error("failed to launch and detach server process: {status}\n{paths}")]
|
||||||
|
LaunchStatus { status: ExitStatus, paths: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_server(paths: &ServerPaths) -> Result<(), SpawnServerError> {
|
||||||
if paths.stdin_socket.exists() {
|
if paths.stdin_socket.exists() {
|
||||||
std::fs::remove_file(&paths.stdin_socket)?;
|
std::fs::remove_file(&paths.stdin_socket).map_err(SpawnServerError::RemoveStdinSocket)?;
|
||||||
}
|
}
|
||||||
if paths.stdout_socket.exists() {
|
if paths.stdout_socket.exists() {
|
||||||
std::fs::remove_file(&paths.stdout_socket)?;
|
std::fs::remove_file(&paths.stdout_socket).map_err(SpawnServerError::RemoveStdoutSocket)?;
|
||||||
}
|
}
|
||||||
if paths.stderr_socket.exists() {
|
if paths.stderr_socket.exists() {
|
||||||
std::fs::remove_file(&paths.stderr_socket)?;
|
std::fs::remove_file(&paths.stderr_socket).map_err(SpawnServerError::RemoveStderrSocket)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let binary_name = std::env::current_exe()?;
|
let binary_name = std::env::current_exe().map_err(SpawnServerError::CurrentExe)?;
|
||||||
let mut server_process = std::process::Command::new(binary_name);
|
let mut server_process = std::process::Command::new(binary_name);
|
||||||
server_process
|
server_process
|
||||||
.arg("run")
|
.arg("run")
|
||||||
|
@ -692,11 +783,17 @@ fn spawn_server(paths: &ServerPaths) -> Result<()> {
|
||||||
|
|
||||||
let status = server_process
|
let status = server_process
|
||||||
.status()
|
.status()
|
||||||
.context("failed to launch server process")?;
|
.map_err(SpawnServerError::ProcessStatus)?;
|
||||||
anyhow::ensure!(
|
|
||||||
status.success(),
|
if !status.success() {
|
||||||
"failed to launch and detach server process"
|
return Err(SpawnServerError::LaunchStatus {
|
||||||
);
|
status,
|
||||||
|
paths: format!(
|
||||||
|
"log file: {:?}, pid file: {:?}",
|
||||||
|
paths.log_file, paths.pid_file,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let mut total_time_waited = std::time::Duration::from_secs(0);
|
let mut total_time_waited = std::time::Duration::from_secs(0);
|
||||||
let wait_duration = std::time::Duration::from_millis(20);
|
let wait_duration = std::time::Duration::from_millis(20);
|
||||||
|
@ -717,7 +814,15 @@ fn spawn_server(paths: &ServerPaths) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_pid_file(path: &Path) -> Result<Option<u32>> {
|
#[derive(Debug, Error)]
|
||||||
|
#[error("Failed to remove PID file for missing process (pid `{pid}`")]
|
||||||
|
pub(crate) struct CheckPidError {
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
pid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_pid_file(path: &Path) -> Result<Option<u32>, CheckPidError> {
|
||||||
let Some(pid) = std::fs::read_to_string(&path)
|
let Some(pid) = std::fs::read_to_string(&path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|contents| contents.parse::<u32>().ok())
|
.and_then(|contents| contents.parse::<u32>().ok())
|
||||||
|
@ -742,7 +847,7 @@ fn check_pid_file(path: &Path) -> Result<Option<u32>> {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Found PID file, but process with that PID does not exist. Removing PID file."
|
"Found PID file, but process with that PID does not exist. Removing PID file."
|
||||||
);
|
);
|
||||||
std::fs::remove_file(&path).context("Failed to remove PID file")?;
|
std::fs::remove_file(&path).map_err(|source| CheckPidError { source, pid })?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue