System Process Core Documentation
Provides generic, cross-platform process management utilities.
This module abstracts common tasks related to system process interaction, aiming to provide a consistent interface regardless of the underlying OS (though some behaviors might be platform-specific, as noted in function docstrings). It is crucial for managing Bedrock server processes, launcher processes, and potentially other child processes.
The module heavily relies on the psutil
library for many of its
capabilities. Its availability is checked by the PSUTIL_AVAILABLE
flag,
and functions requiring psutil
will typically raise a SystemError
if it’s not installed.
Key Functionality Groups:
- Process Status & Verification:
- Process Creation & Termination:
- Guarded Subprocessing:
GuardedProcess
: A wrapper aroundsubprocess
to inject a recursion guard environment variable, preventing re-initialization loops when the application calls itself.
- Constants:
PSUTIL_AVAILABLE
: Boolean indicating ifpsutil
was imported.
- class bedrock_server_manager.core.system.process.GuardedProcess(command: List[str | PathLike])
Wraps
subprocess
calls to inject a recursion guard environment variable.When the application needs to launch a new instance of itself as a subprocess (e.g., to start a server in a detached background process via
launch_detached_process()
), this class is used to manage the subprocess creation.Its primary purpose is to set a specific environment variable, defined by
GUARD_VARIABLE
(e.g.,BSM_GUARDED_EXECUTION="1"
), in the environment of the child process. The application’s main entry point or initialization logic can then check for the presence of this variable. If detected, it can skip certain initialization steps (like reloading plugins, re-parsing arguments for the main CLI) that are unnecessary or problematic for a guarded, secondary instance. This helps prevent issues like duplicate plugin loading or unintended recursive behavior.The class provides
run()
andpopen()
methods that mirror the standardsubprocess.run
andsubprocess.Popen
functions but ensure the guarded environment is passed to the child process.- command
The command and its arguments.
- Type:
List[Union[str, os.PathLike]]
- guard_env
A copy of the current environment with the guard variable added.
- Type:
Dict[str, str]
- popen(**kwargs: Any) Popen
Wraps
subprocess.Popen
, injecting the guarded environment.This method calls
subprocess.Popen
with the storedself.command
and passes along any additional**kwargs
. The crucial difference is that it automatically sets theenv
keyword argument forsubprocess.Popen
toself.guard_env
.- Parameters:
**kwargs (Any) – Keyword arguments to pass directly to
subprocess.Popen
. Ifenv
is provided inkwargs
, it will be overwritten.- Returns:
The
subprocess.Popen
object for the new process.- Return type:
subprocess.Popen
- run(**kwargs: Any) CompletedProcess
Wraps
subprocess.run
, injecting the guarded environment.This method calls
subprocess.run
with the storedself.command
and passes along any additional**kwargs
. The crucial difference is that it automatically sets theenv
keyword argument forsubprocess.run
toself.guard_env
.- Parameters:
**kwargs (Any) – Keyword arguments to pass directly to
subprocess.run
. Ifenv
is provided inkwargs
, it will be overwritten.- Returns:
The result of the
subprocess.run
call.- Return type:
subprocess.CompletedProcess
- bedrock_server_manager.core.system.process.get_bedrock_launcher_pid_file_path(server_name: str, config_dir: str) str
Constructs the path for a Bedrock server’s LAUNCHER process PID file.
This PID file is intended for the “launcher” or “wrapper” process that manages the actual Bedrock server (e.g., a process started by this application to run the server in the background or foreground with IPC). The PID file is typically named
bedrock_<server_name>_launcher.pid
and is placed directly in the provided config_dir (unlike the server’s own PID file which might be in a subdirectory).If config_dir does not exist, this function will attempt to create it.
- Parameters:
server_name (str) – The unique name of the server instance this launcher is associated with.
config_dir (str) – The main configuration directory for the application. If it doesn’t exist, an attempt will be made to create it.
- Returns:
The absolute path to where the launcher’s PID file should be located.
- Return type:
str
- Raises:
MissingArgumentError – If server_name or config_dir are empty or not strings.
AppFileNotFoundError – If config_dir does not exist and cannot be created.
- bedrock_server_manager.core.system.process.get_bedrock_server_pid_file_path(server_name: str, config_dir: str) str
Constructs the standardized path to a Bedrock server’s main process PID file.
This function generates a path for a PID file specific to a Bedrock server instance. The PID file is typically located in a subdirectory named after the server_name within the main config_dir. The filename itself is
bedrock_<server_name>.pid
.Example
If server_name is “MyServer” and config_dir is “/opt/bsm/.config”, the returned path might be “/opt/bsm/.config/MyServer/bedrock_MyServer.pid”.
- Parameters:
server_name (str) – The unique name of the server instance.
config_dir (str) – The main configuration directory for the application. This directory must exist.
- Returns:
The absolute path to where the server’s PID file should be located.
- Return type:
str
- Raises:
MissingArgumentError – If server_name or config_dir are empty or not strings.
AppFileNotFoundError – If the config_dir does not exist, or if the derived server-specific config subdirectory (e.g., <config_dir>/<server_name>) does not exist.
- bedrock_server_manager.core.system.process.get_pid_file_path(config_dir: str, pid_filename: str) str
Constructs the full, absolute path for a generic PID file.
This utility function joins the provided configuration directory and PID filename to produce a standardized, absolute path for a PID file.
- Parameters:
config_dir (str) – The absolute path to the application’s (or a specific component’s) configuration directory where the PID file should reside.
pid_filename (str) – The base name of the PID file (e.g., “web_server.pid”, “my_process.pid”).
- Returns:
The absolute path to where the PID file should be stored.
- Return type:
str
- Raises:
AppFileNotFoundError – If config_dir is not provided, is not a string, or is not an existing directory.
MissingArgumentError – If pid_filename is not provided or is an empty string.
- bedrock_server_manager.core.system.process.get_verified_bedrock_process(server_name: str, server_dir: str, config_dir: str) Process | None
Finds, verifies, and returns the Bedrock server process using its PID file.
This high-level function combines several steps to reliably identify if a Bedrock server, identified by server_name, is currently running and is indeed the correct process. It performs:
Path construction for the server’s PID file using
get_bedrock_server_pid_file_path()
.Reading the PID from this file via
read_pid_from_file()
.Checking if the process with the read PID is running using
is_process_running()
.If running, verifying the process’s identity using
verify_process_identity()
. The verification checks if the process executable path matches the expectedbedrock_server
orbedrock_server.exe
in the server_dir, and if its current working directory is server_dir.
If
psutil
is unavailable, this function logs an error and returnsNone
. Most other exceptions encountered during the process (e.g., PID file not found, process not running, verification mismatch, permissions issues) are caught, logged at DEBUG level, and result inNone
being returned, as these are often considered normal states indicating the server is not running as expected. More severe or unexpected errors are logged at ERROR level.- Parameters:
server_name (str) – The unique name of the server instance.
server_dir (str) – The server’s installation directory, used for verifying the executable path and CWD.
config_dir (str) – The main application configuration directory where the server’s PID file (and its parent subdirectory) are located.
- Returns:
A
psutil.Process
object representing the verified, running Bedrock server process if all checks pass. ReturnsNone
ifpsutil
is unavailable, the server is not running, the PID file is missing/invalid, or if process verification fails.- Return type:
Optional[psutil.Process]
- bedrock_server_manager.core.system.process.is_process_running(pid: int) bool
Checks if a process with the given PID is currently running.
This function relies on
psutil.pid_exists()
to determine if a process with the specified pid is active on the system.- Parameters:
pid (int) – The process ID to check.
- Returns:
True
if a process with the given pid exists and is running,False
otherwise.- Return type:
bool
- Raises:
SystemError – If the
psutil
library is not available (i.e.,PSUTIL_AVAILABLE
isFalse
).MissingArgumentError – If pid is not an integer.
- bedrock_server_manager.core.system.process.launch_detached_process(command: List[str], launcher_pid_file_path: str) int
Launches a command as a detached background process and records its PID.
This function uses the
GuardedProcess
wrapper to execute the given command. The GuardedProcess injects a recursion guard environment variable into the child process.Platform-specific
subprocess.Popen
flags are used to ensure the new process is fully detached from the parent and runs independently in the background:On Windows:
subprocess.CREATE_NO_WINDOW
is used.On POSIX systems (Linux, macOS):
start_new_session=True
is used.
Standard input, output, and error streams of the new process are redirected to
subprocess.DEVNULL
.The PID of the newly launched detached process is written to the file specified by launcher_pid_file_path using
write_pid_to_file()
.- Parameters:
command (List[str]) – The command and its arguments as a list of strings (e.g.,
['python', 'my_script.py', '--daemon']
). The first element should be the executable.launcher_pid_file_path (str) – The absolute path to the file where the PID of the newly launched launcher/detached process should be written.
- Returns:
The Process ID (PID) of the newly launched detached process.
- Return type:
int
- Raises:
MissingArgumentError – If command is empty, its first element (executable) is empty, or if launcher_pid_file_path is empty.
AppFileNotFoundError – If the executable specified in command[0] is not found on the system.
SystemError – For other OS-level errors that occur during process creation (e.g., permission issues, resource limits).
FileOperationError – If writing the PID to launcher_pid_file_path fails.
- bedrock_server_manager.core.system.process.read_pid_from_file(pid_file_path: str) int | None
Reads and validates a Process ID (PID) from a specified file.
If the PID file exists, this function attempts to read its content, strip any leading/trailing whitespace, and convert it to an integer.
- Parameters:
pid_file_path (str) – The absolute path to the PID file.
- Returns:
The PID as an integer if the file exists, is readable, and contains a valid integer. Returns
None
if the PID file does not exist at pid_file_path.- Return type:
Optional[int]
- Raises:
MissingArgumentError – If pid_file_path is not provided or is empty.
FileOperationError – If the file exists but cannot be read (e.g., due to permissions) or if its content is not a valid integer.
- bedrock_server_manager.core.system.process.remove_pid_file_if_exists(pid_file_path: str) bool
Removes the specified PID file if it exists, logging outcomes.
This function checks for the existence of a file at pid_file_path. If it exists, an attempt is made to delete it. Deletion failures due to
OSError
(e.g., permission issues) are logged as warnings but do not propagate the exception.- Parameters:
pid_file_path (str) – The absolute path to the PID file to remove.
- Returns:
True
if the file was successfully removed or if it did not exist initially.False
if anOSError
occurred during an attempted removal.- Return type:
bool
- Raises:
MissingArgumentError – If pid_file_path is not provided or is empty.
- bedrock_server_manager.core.system.process.terminate_process_by_pid(pid: int, terminate_timeout: int = 5, kill_timeout: int = 2)
Attempts to gracefully terminate, then forcefully kill, a process by PID.
This function implements a two-stage termination strategy:
Graceful Termination (SIGTERM): It first sends a SIGTERM signal (
process.terminate()
) to the process with the given pid. It then waits for terminate_timeout seconds for the process to exit cleanly.Forceful Kill (SIGKILL): If the process does not terminate within the terminate_timeout, a SIGKILL signal (
process.kill()
) is sent to forcibly stop it. It then waits for kill_timeout seconds.
This approach gives the target process a chance to shut down gracefully (e.g., save data, release resources) before resorting to a forceful kill.
- Parameters:
pid (int) – The Process ID of the process to terminate.
terminate_timeout (int, optional) – Seconds to wait for graceful termination after SIGTERM. Defaults to 5.
kill_timeout (int, optional) – Seconds to wait for the process to die after SIGKILL. Defaults to 2.
- Raises:
SystemError – If the
psutil
library is not available.MissingArgumentError – If pid is not an integer.
PermissionsError – If
psutil
is denied access when trying to terminate the process.ServerStopError – For other
psutil
errors (e.g., process already exited before kill, unexpected errors) or if timeouts are invalid.
- bedrock_server_manager.core.system.process.verify_process_identity(pid: int, expected_executable_path: str | None = None, expected_cwd: str | None = None, expected_command_args: str | List[str] | None = None)
Verifies if a running process matches an expected signature.
This function checks a process, identified by its pid, against one or more provided criteria: its executable path, its current working directory (CWD), and/or specific command-line arguments. This is useful for confirming that a PID read from a file indeed corresponds to the expected application or server process, and not some other unrelated process that happens to have the same PID if the original process died and its PID was recycled.
It uses
psutil.Process(pid).oneshot()
for efficient information retrieval. Path comparisons (for executable and CWD) are case-insensitive and normalized.- Parameters:
pid (int) – The Process ID of the process to verify.
expected_executable_path (Optional[str], optional) – The expected absolute path of the main executable. Defaults to
None
.expected_cwd (Optional[str], optional) – The expected absolute current working directory of the process. Defaults to
None
.expected_command_args (Optional[Union[str, List[str]]], optional) – A specific string or a list of strings that are expected to be present in the process’s command-line arguments. Defaults to
None
.
- Raises:
SystemError – If the
psutil
library is not available or ifpsutil
encounters an internal error retrieving process information.ServerProcessError – If the process with the given pid does not exist, or if it exists but does not match one or more of the provided expected criteria (mismatch details are included in the error message).
PermissionsError – If
psutil
is denied access when trying to get information for the specified pid.MissingArgumentError – If pid is not an integer or if none of the optional verification criteria (expected_executable_path, expected_cwd, expected_command_args) are provided.
- bedrock_server_manager.core.system.process.write_pid_to_file(pid_file_path: str, pid: int)
Writes a process ID (PID) to the specified file, creating directories if needed.
This function writes the given pid (converted to a string) to the file at pid_file_path. If the parent directory (or directories) for pid_file_path do not exist, they will be created. Any existing content in the file will be overwritten.
- Parameters:
pid_file_path (str) – The absolute path to the PID file where the PID should be written.
pid (int) – The process ID to write to the file.
- Raises:
MissingArgumentError – If pid_file_path is not provided or is empty, or if pid is not an integer.
FileOperationError – If an
OSError
occurs while creating directories or writing to the file (e.g., permission issues).