You've already forked AstralRinth
forked from didirus/AstralRinth
Child process manager api (#64)
* child process api * added hook to js * process API + restructured process state storage * formatting * added path-pid check and fixed probs * prettier * added profile checking function --------- Co-authored-by: Wyatt <wyatt@modrinth.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
//! API for interacting with Theseus
|
||||
pub mod auth;
|
||||
pub mod process;
|
||||
pub mod profile;
|
||||
pub mod profile_create;
|
||||
pub mod tags;
|
||||
pub mod settings;
|
||||
pub mod tags;
|
||||
|
||||
pub mod data {
|
||||
pub use crate::state::{
|
||||
@@ -16,6 +17,7 @@ pub mod prelude {
|
||||
pub use crate::{
|
||||
auth::{self, Credentials},
|
||||
data::*,
|
||||
process,
|
||||
profile::{self, Profile},
|
||||
profile_create, settings, State,
|
||||
};
|
||||
|
||||
161
theseus/src/api/process.rs
Normal file
161
theseus/src/api/process.rs
Normal file
@@ -0,0 +1,161 @@
|
||||
//! Theseus process management interface
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::state::MinecraftChild;
|
||||
pub use crate::{
|
||||
state::{
|
||||
Hooks, JavaSettings, MemorySettings, Profile, Settings, WindowSize,
|
||||
},
|
||||
State,
|
||||
};
|
||||
|
||||
// Gets whether a child process stored in the state by PID has finished
|
||||
#[tracing::instrument]
|
||||
pub async fn has_finished_by_pid(pid: u32) -> crate::Result<bool> {
|
||||
Ok(get_exit_status_by_pid(pid).await?.is_some())
|
||||
}
|
||||
|
||||
// Gets the exit status of a child process stored in the state by PID
|
||||
#[tracing::instrument]
|
||||
pub async fn get_exit_status_by_pid(pid: u32) -> crate::Result<Option<i32>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
Ok(children.exit_status(&pid).await?.and_then(|f| f.code()))
|
||||
}
|
||||
|
||||
// Gets the PID of each stored process in the state
|
||||
#[tracing::instrument]
|
||||
pub async fn get_all_pids() -> crate::Result<Vec<u32>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
Ok(children.keys())
|
||||
}
|
||||
|
||||
// Gets the PID of each *running* stored process in the state
|
||||
#[tracing::instrument]
|
||||
pub async fn get_all_running_pids() -> crate::Result<Vec<u32>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
children.running_keys().await
|
||||
}
|
||||
|
||||
// Gets the Profile paths of each *running* stored process in the state
|
||||
#[tracing::instrument]
|
||||
pub async fn get_all_running_profile_paths() -> crate::Result<Vec<PathBuf>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
children.running_profile_paths().await
|
||||
}
|
||||
|
||||
// Gets the Profiles (cloned) of each *running* stored process in the state
|
||||
#[tracing::instrument]
|
||||
pub async fn get_all_running_profiles() -> crate::Result<Vec<Profile>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
children.running_profiles().await
|
||||
}
|
||||
|
||||
// Gets the PID of each stored process in the state by profile path
|
||||
#[tracing::instrument]
|
||||
pub async fn get_pids_by_profile_path(
|
||||
profile_path: &Path,
|
||||
) -> crate::Result<Vec<u32>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
children.running_keys_with_profile(profile_path).await
|
||||
}
|
||||
|
||||
// Gets stdout of a child process stored in the state by PID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stdout_by_pid(pid: u32) -> crate::Result<String> {
|
||||
let state = State::get().await?;
|
||||
// Get stdout from child
|
||||
let children = state.children.read().await;
|
||||
|
||||
// Extract child or return crate::Error
|
||||
if let Some(child) = children.get(&pid) {
|
||||
let child = child.read().await;
|
||||
Ok(child.stdout.get_output().await?)
|
||||
} else {
|
||||
Err(crate::ErrorKind::LauncherError(format!(
|
||||
"No child process with PID {}",
|
||||
pid
|
||||
))
|
||||
.as_error())
|
||||
}
|
||||
}
|
||||
|
||||
// Gets stderr of a child process stored in the state by PID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stderr_by_pid(pid: u32) -> crate::Result<String> {
|
||||
let state = State::get().await?;
|
||||
// Get stdout from child
|
||||
let children = state.children.read().await;
|
||||
|
||||
// Extract child or return crate::Error
|
||||
if let Some(child) = children.get(&pid) {
|
||||
let child = child.read().await;
|
||||
Ok(child.stderr.get_output().await?)
|
||||
} else {
|
||||
Err(crate::ErrorKind::LauncherError(format!(
|
||||
"No child process with PID {}",
|
||||
pid
|
||||
))
|
||||
.as_error())
|
||||
}
|
||||
}
|
||||
|
||||
// Kill a child process stored in the state by PID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn kill_by_pid(pid: u32) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
if let Some(mchild) = children.get(&pid) {
|
||||
let mut mchild = mchild.write().await;
|
||||
kill(&mut mchild).await
|
||||
} else {
|
||||
// No error returned for already finished process
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a child process stored in the state by PID
|
||||
#[tracing::instrument]
|
||||
pub async fn wait_for_by_pid(pid: u32) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
// No error returned for already killed process
|
||||
if let Some(mchild) = children.get(&pid) {
|
||||
let mut mchild = mchild.write().await;
|
||||
wait_for(&mut mchild).await
|
||||
} else {
|
||||
// No error returned for already finished process
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Kill a running child process directly, and wait for it to be killed
|
||||
#[tracing::instrument]
|
||||
pub async fn kill(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||
running.child.kill().await?;
|
||||
wait_for(running).await
|
||||
}
|
||||
|
||||
// Await on the completion of a child process directly
|
||||
#[tracing::instrument]
|
||||
pub async fn wait_for(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||
let result = running.child.wait().await.map_err(|err| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Error running minecraft: {err}"
|
||||
))
|
||||
})?;
|
||||
|
||||
match result.success() {
|
||||
false => Err(crate::ErrorKind::LauncherError(format!(
|
||||
"Minecraft exited with non-zero code {}",
|
||||
result.code().unwrap_or(-1)
|
||||
))
|
||||
.as_error()),
|
||||
true => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Theseus profile management interface
|
||||
use crate::state::MinecraftChild;
|
||||
pub use crate::{
|
||||
state::{JavaSettings, Profile},
|
||||
State,
|
||||
@@ -9,10 +10,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::{
|
||||
process::{Child, Command},
|
||||
sync::RwLock,
|
||||
};
|
||||
use tokio::{process::Command, sync::RwLock};
|
||||
|
||||
/// Add a profile to the in-memory state
|
||||
#[tracing::instrument]
|
||||
@@ -114,7 +112,7 @@ pub async fn list(
|
||||
pub async fn run(
|
||||
path: &Path,
|
||||
credentials: &crate::auth::Credentials,
|
||||
) -> crate::Result<Arc<RwLock<Child>>> {
|
||||
) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
|
||||
let state = State::get().await.unwrap();
|
||||
let settings = state.settings.read().await;
|
||||
let profile = get(path).await?.ok_or_else(|| {
|
||||
@@ -227,31 +225,8 @@ pub async fn run(
|
||||
"Process failed to stay open.".to_string(),
|
||||
)
|
||||
})?;
|
||||
let child_arc = state_children.insert(pid, mc_process);
|
||||
let mchild_arc =
|
||||
state_children.insert_process(pid, path.to_path_buf(), mc_process);
|
||||
|
||||
Ok(child_arc)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn kill(running: &mut Child) -> crate::Result<()> {
|
||||
running.kill().await?;
|
||||
wait_for(running).await
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn wait_for(running: &mut Child) -> crate::Result<()> {
|
||||
let result = running.wait().await.map_err(|err| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Error running minecraft: {err}"
|
||||
))
|
||||
})?;
|
||||
|
||||
match result.success() {
|
||||
false => Err(crate::ErrorKind::LauncherError(format!(
|
||||
"Minecraft exited with non-zero code {}",
|
||||
result.code().unwrap_or(-1)
|
||||
))
|
||||
.as_error()),
|
||||
true => Ok(()),
|
||||
}
|
||||
Ok(mchild_arc)
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@ pub async fn set(settings: Settings) -> crate::Result<()> {
|
||||
// Replaces the settings struct in the RwLock with the passed argument
|
||||
*state.settings.write().await = settings;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user