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:

Constants:
  • PSUTIL_AVAILABLE: Boolean indicating if psutil 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() and popen() methods that mirror the standard subprocess.run and subprocess.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 stored self.command and passes along any additional **kwargs. The crucial difference is that it automatically sets the env keyword argument for subprocess.Popen to self.guard_env.

Parameters:

**kwargs (Any) – Keyword arguments to pass directly to subprocess.Popen. If env is provided in kwargs, 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 stored self.command and passes along any additional **kwargs. The crucial difference is that it automatically sets the env keyword argument for subprocess.run to self.guard_env.

Parameters:

**kwargs (Any) – Keyword arguments to pass directly to subprocess.run. If env is provided in kwargs, 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:
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:
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:

  1. Path construction for the server’s PID file using get_bedrock_server_pid_file_path().

  2. Reading the PID from this file via read_pid_from_file().

  3. Checking if the process with the read PID is running using is_process_running().

  4. If running, verifying the process’s identity using verify_process_identity(). The verification checks if the process executable path matches the expected bedrock_server or bedrock_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 returns None. 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 in None 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. Returns None if psutil 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:
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 an OSError 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:

  1. 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.

  2. 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 if psutil 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).