JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
Loading...
Searching...
No Matches
process_launcher.h File Reference

Cross-platform process launcher aligned with std::thread semantics. More...

#include "jh/macros/platform.h"
#include "jh/metax/t_str.h"
#include "jh/synchronous/ipc/ipc_limits.h"
#include <string>
#include <stdexcept>
#include <filesystem>
#include <iostream>

Go to the source code of this file.

Classes

class  jh::sync::ipc::process_launcher< Path, IsBinary >
 Cross-platform process launcher. More...
struct  jh::sync::ipc::process_launcher< Path, IsBinary >::handle
 Process handle representing a single launched instance. More...

Namespaces

namespace  jh::sync
 Aggregated entry point for synchronization and coordination facilities.
namespace  jh::sync::ipc
 Synchronous inter-process coordination primitives.

Macros

#define JH_INTERPROCESS_ALLOW_PARENT_PATH   0

Detailed Description

Cross-platform process launcher aligned with std::thread semantics.

Rationale

This class encapsulates the platform-specific differences between POSIX fork() + execl() and Windows CreateProcess(), exposing a unified std::thread-like API.

Platform differences

  • POSIX (Linux & UNIX):
    • Any file with execute permission can be launched (binary or script).
    • fork() creates the child, execl() replaces its image.
    • wait() maps to waitpid().
  • Windows / MSYS2:
    • Child processes must originate from an executable image (e.g. .exe, .bat, .ps1).
    • CreateProcess() is used for launching.
    • wait() maps to WaitForSingleObject().

Binary flag

The template parameter IsBinary exists to simplify build workflows (especially for CMake-generated executables):

  • If true:
    • On Windows, ".exe" is appended automatically (so "writer""writer.exe").
    • On POSIX, the path is used directly (no extension manipulation).
  • If false:
    • The string is used as-is (Windows: may be .bat, .ps1; POSIX: may be script with shebang + execute permission).

Path rules

  • The template string must be a POSIX-style relative path:
    • No leading '/' (absolute paths forbidden).
    • "./" segments are meaningless and rejected.
    • ".." handling:
      • By default (JH_INTERPROCESS_ALLOW_PARENT_PATH == 0): any ".." is forbidden.
      • If JH_INTERPROCESS_ALLOW_PARENT_PATH == 1: leading "../" prefixes are permitted (one or more), but:
        • The entire path cannot consist only of "../" segments.
        • Once non-empty content has been appended, no further ".." segments are allowed.
    • Allowed characters: [A-Za-z0-9_.-/].
    • Length must be within [1, 128].
  • No need to prefix with "./" or use '\': paths are interpreted directly by the filesystem and resolved relative to the current working directory.

Path policy

  • Strict validation at compile time:
    • Illegal characters are rejected.
    • "./" or mid-path ".." segments are forbidden.
    • Absolute paths ("/foo/bar") are forbidden by design.
  • Parent path relaxation (JH_INTERPROCESS_ALLOW_PARENT_PATH):
    • Disabled (default = 0): any ".." usage is an error.
    • Enabled (= 1): only leading "../" prefixes are permitted; they must be followed by a valid non-empty subpath.
  • Cross-platform normalization:
    • POSIX: used as-is, relative to cwd.
    • Windows: forward slashes ('/') are translated automatically; backslashes are not required.
  • Security note:
    • Forbidding .. in the middle of paths prevents directory traversal vulnerabilities.
    • Restricting to relative paths avoids accidental execution of system binaries outside the project tree.

Semantics

  • Strictly aligned with std::thread:
    • A handle must be explicitly wait()-ed.
    • If destroyed without waiting, std::terminate() is invoked.
    • No kill or stop operations are provided.
  • The number and identity of launchers are fixed at compile time by the template string parameter.
  • The class itself is an empty static interface:
    • Cannot be instantiated.
    • Provides only start() for launching.

Handle semantics & security rationale

  • Each call to start() returns a unique, move-only handle representing one running process.
  • The handle enforces strict ownership:
    • It must be wait()-ed before destruction.
    • Destruction of an active handle triggers std::terminate().
    • Move transfers ownership; the source becomes invalid and cannot be moved or waited again.
    • Assigning into an active handle also terminates the program.
  • Each process_launcher<Path, IsBinary> has its own nested handle type, bound to its executable at compile time.
  • This binding is a deliberate design for both type safety and security:
    • Prevents handle reuse or hijacking across launchers.
    • Disallows runtime path injection or substitution attacks.
    • Guarantees the executable path is compile-time verified.
  • A unified generic handle is intentionally avoided because it would:
    • Break compile-time validation guarantees,
    • Enable runtime path injection,
    • And expand the attack surface for untrusted binaries.
  • By keeping the handle type bound to its launcher, the system enforces a static trust boundary: only pre-validated, known executables can run.