support admin server restart --force
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
7658387a74
commit
5edd391e83
4 changed files with 62 additions and 6 deletions
|
@ -1,4 +1,4 @@
|
||||||
use conduit::{warn, Result};
|
use conduit::{warn, Error, Result};
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
use crate::services;
|
use crate::services;
|
||||||
|
@ -102,7 +102,17 @@ pub(super) async fn reload(_body: Vec<&str>) -> Result<RoomMessageEventContent>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub(super) async fn restart(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
pub(super) async fn restart(_body: Vec<&str>, force: bool) -> Result<RoomMessageEventContent> {
|
||||||
|
use conduit::utils::sys::current_exe_deleted;
|
||||||
|
|
||||||
|
if !force && current_exe_deleted() {
|
||||||
|
return Err(Error::Err(
|
||||||
|
"The server cannot be restarted because the executable was tampered with. If this is expected use --force \
|
||||||
|
to override."
|
||||||
|
.to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
services().server.restart()?;
|
services().server.restart()?;
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
|
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
|
||||||
|
|
|
@ -51,7 +51,10 @@ pub(super) enum ServerCommand {
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
/// - Restart the server
|
/// - Restart the server
|
||||||
Restart,
|
Restart {
|
||||||
|
#[arg(short, long)]
|
||||||
|
force: bool,
|
||||||
|
},
|
||||||
|
|
||||||
/// - Shutdown the server
|
/// - Shutdown the server
|
||||||
Shutdown,
|
Shutdown,
|
||||||
|
@ -77,7 +80,9 @@ pub(super) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<R
|
||||||
#[cfg(conduit_mods)]
|
#[cfg(conduit_mods)]
|
||||||
ServerCommand::Reload => reload(body).await?,
|
ServerCommand::Reload => reload(body).await?,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ServerCommand::Restart => restart(body).await?,
|
ServerCommand::Restart {
|
||||||
|
force,
|
||||||
|
} => restart(body, force).await?,
|
||||||
ServerCommand::Shutdown => shutdown(body).await?,
|
ServerCommand::Shutdown => shutdown(body).await?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,3 +34,34 @@ pub fn available_parallelism() -> usize {
|
||||||
.expect("Unable to query for available parallelism.")
|
.expect("Unable to query for available parallelism.")
|
||||||
.get()
|
.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a possibly corrected std::env::current_exe() even if the path is
|
||||||
|
/// marked deleted.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This function is declared unsafe because the original result was altered for
|
||||||
|
/// security purposes, and altering it back ignores those urposes and should be
|
||||||
|
/// understood by the user.
|
||||||
|
pub unsafe fn current_exe() -> Result<std::path::PathBuf> {
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
let exe = std::env::current_exe()?;
|
||||||
|
match exe.to_str() {
|
||||||
|
None => Ok(exe),
|
||||||
|
Some(str) => Ok(str
|
||||||
|
.strip_suffix(" (deleted)")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or(exe)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine if the server's executable was removed or replaced. This is a
|
||||||
|
/// specific check; useful for successful restarts. May not be available or
|
||||||
|
/// accurate on all platforms; defaults to false.
|
||||||
|
#[must_use]
|
||||||
|
pub fn current_exe_deleted() -> bool {
|
||||||
|
std::env::current_exe().map_or(false, |exe| {
|
||||||
|
exe.to_str()
|
||||||
|
.map_or(false, |exe| exe.ends_with(" (deleted)"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -2,10 +2,20 @@
|
||||||
|
|
||||||
use std::{env, os::unix::process::CommandExt, process::Command};
|
use std::{env, os::unix::process::CommandExt, process::Command};
|
||||||
|
|
||||||
use conduit::{debug, info};
|
use conduit::{debug, info, utils};
|
||||||
|
|
||||||
pub(super) fn restart() -> ! {
|
pub(super) fn restart() -> ! {
|
||||||
let exe = env::current_exe().expect("program path must be identified and available");
|
// SAFETY: We have allowed an override for the case where the current_exe() has
|
||||||
|
// been replaced or removed. By default the server will fail to restart if the
|
||||||
|
// binary has been replaced (i.e. by cargo); this is for security purposes.
|
||||||
|
// Command::exec() used to panic in that case.
|
||||||
|
//
|
||||||
|
// We can (and do) prevent that panic by checking the result of current_exe()
|
||||||
|
// prior to committing to restart, returning an error to the user without any
|
||||||
|
// unexpected shutdown. In a nutshell that is the execuse for this unsafety.
|
||||||
|
// Nevertheless, we still want a way to override the restart preventation (i.e.
|
||||||
|
// admin server restart --force).
|
||||||
|
let exe = unsafe { utils::sys::current_exe().expect("program path must be available") };
|
||||||
let envs = env::vars();
|
let envs = env::vars();
|
||||||
let args = env::args().skip(1);
|
let args = env::args().skip(1);
|
||||||
debug!(?exe, ?args, ?envs, "Restart");
|
debug!(?exe, ?args, ?envs, "Restart");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue