self_cmd 0.1.10

Self-executing command builder for Rust programs / Rust 程序自执行命令构建器
Documentation

English | 中文


self_cmd: Self-Executing Command Builder

Overview

self_cmd is a Rust library that creates Command instances for re-executing the current program with identical arguments and stdio inheritance. On Linux, it provides additional support for systemd socket activation by preserving file descriptor mappings through the LISTEN_FDS protocol.

Features

  • Self-Replication: Generate Command objects that execute the current program
  • Argument Preservation: Automatically captures and forwards command-line arguments
  • Stdio Inheritance: Maintains stdin, stdout, and stderr connections
  • Socket Activation Support: Linux-specific file descriptor mapping for systemd integration
  • Robust Error Handling: Custom error types with proper error propagation
  • Logging Integration: Built-in logging support for debugging and monitoring

Usage

Add to your Cargo.toml:

[dependencies]
self_cmd = "0.1.7"
use self_cmd;

fn main() -> self_cmd::Result<()> {
    // Create and execute a command that re-runs this program
    // Automatically handles stdio inheritance and Linux FD mappings for systemd socket activation
    let mut cmd = self_cmd::get()?;
    let status = cmd.status()?;
    println!("Child process exited with: {status}");
    
    Ok(())
}

On Linux systems, the library automatically handles systemd socket activation by checking LISTEN_FDS, duplicating file descriptors starting from FD 3, and configuring the child process environment. This makes it seamless for services that need to restart while maintaining their listening sockets.

Design Philosophy

The library follows a minimalist approach with a single public function that encapsulates the complexity of self-execution:

graph TD
    A[Program Start] --> B["self_cmd::get()"]
    B --> C["env::current_exe()"]
    C --> D["env::args().skip(1)"]
    D --> E["Command::new(program)"]
    E --> F["cmd.args(args)"]
    F --> G[Configure Stdio Inheritance]
    G --> H{Linux?}
    H -->|Yes| I["fd_mapping()"]
    I --> J[Check LISTEN_FDS env]
    J --> K[Duplicate FDs 3+]
    K --> L[Create FdMapping]
    L --> M[Set LISTEN_FDS for child]
    M --> N[Remove LISTEN_PID]
    N --> O[Return Command]
    H -->|No| O
    O --> P[Execute Command]

The design ensures:

  • Simplicity: Single function interface
  • Reliability: Proper error handling without panics
  • Flexibility: Returns Command for further customization
  • Performance: Minimal overhead with lazy evaluation

Technical Stack

  • Language: Rust 2024 Edition
  • Core Dependencies:
    • log for logging functionality
    • thiserror for structured error handling
  • Linux Dependencies:
    • command-fds for file descriptor mapping
    • libc for low-level system calls
  • Error Handling: Custom Error type with thiserror integration
  • Process Management: std::process::Command with FD mapping extensions
  • Environment Access: std::env for executable path and arguments

Project Structure

self_cmd/
├── src/
│   ├── lib.rs          # Core implementation and public API
│   ├── error.rs        # Error types and handling
│   └── fd_mapping.rs   # Linux-specific FD mapping (conditional)
├── tests/
│   └── main.rs         # Integration tests
├── readme/
│   ├── en.md          # English documentation
│   └── zh.md          # Chinese documentation
├── Cargo.toml         # Project configuration
└── test.sh           # Test runner script

API Reference

get() -> Result<Command>

Creates a Command instance configured to re-execute the current program with full context preservation.

Returns:

  • Ok(Command): Configured command ready for execution
  • Err(Error): If unable to determine executable path or configure FD mappings

Behavior:

  • Captures current executable path via env::current_exe()
  • Preserves all command-line arguments except program name
  • Configures stdio inheritance (stdin, stdout, stderr)
  • Linux only: Handles systemd socket activation FD mapping
  • Returns Command for further customization before execution

Example:

match self_cmd::get() {
    Ok(mut cmd) => {
        // Command is ready to use with full context preservation
        cmd.status()?;
    }
    Err(e) => {
        eprintln!("Failed to create self command: {e}");
    }
}

fd_mapping() -> Result<Vec<FdMapping>> (Linux only)

Available on Linux only - Creates file descriptor mappings for systemd socket activation.

Returns:

  • Ok(Vec<FdMapping>): List of FD mappings for socket activation
  • Err(Error): If FD duplication or validation fails

This function is automatically called by get() on Linux systems and typically doesn't need to be used directly.

Error Handling

The library uses a custom Error type built with thiserror for comprehensive error handling:

use self_cmd::{Error, Result};

#[derive(Error, Debug)]
pub enum Error {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    
    #[error("FD mapping collision: {0}")]
    FdMappingCollision(#[from] command_fds::FdMappingCollision),
}

All public functions return Result<T> which is an alias for std::result::Result<T, Error>. This provides:

  • Structured Errors: Clear error types for different failure modes
  • Error Chaining: Automatic conversion from underlying error types
  • Debugging Support: Rich error messages with context
  • Integration: Seamless integration with ? operator and error handling patterns

About

This project is an open-source component of js0.site ⋅ Refactoring the Internet Plan.

We are redefining the development paradigm of the Internet in a componentized way. Welcome to follow us:


self_cmd: 自执行命令构建器

概述

self_cmd 是 Rust 库,用于创建重新执行当前程序的 Command 实例,保持相同参数和标准输入输出继承。在 Linux 系统上,通过 LISTEN_FDS 协议保持文件描述符映射,提供对 systemd 套接字激活的额外支持。

功能特性

  • 自我复制: 生成执行当前程序的 Command 对象
  • 参数保持: 自动捕获并转发命令行参数
  • 标准输入输出继承: 维持 stdin、stdout、stderr 连接
  • 套接字激活支持: Linux 特定的文件描述符映射,用于 systemd 集成
  • 健壮的错误处理: 自定义错误类型与正确的错误传播
  • 日志集成: 内置日志支持,用于调试和监控

使用方法

添加到 Cargo.toml:

[dependencies]
self_cmd = "0.1.7"
use self_cmd;

fn main() -> self_cmd::Result<()> {
    // 创建并执行重新运行此程序的命令
    // 自动处理标准输入输出继承和 Linux systemd 套接字激活的 FD 映射
    let mut cmd = self_cmd::get()?;
    let status = cmd.status()?;
    println!("子进程退出状态: {status}");
    
    Ok(())
}

在 Linux 系统上,库自动处理 systemd 套接字激活,通过检查 LISTEN_FDS、从 FD 3 开始复制文件描述符并配置子进程环境。使需要重启但保持监听套接字的服务变得无缝。

设计理念

库采用极简主义方法,通过单个公共函数封装自执行的复杂性:

graph TD
    A[程序启动] --> B["self_cmd::get()"]
    B --> C["env::current_exe()"]
    C --> D["env::args().skip(1)"]
    D --> E["Command::new(program)"]
    E --> F["cmd.args(args)"]
    F --> G[配置标准输入输出继承]
    G --> H{Linux?}
    H -->|是| I["fd_mapping()"]
    I --> J[检查 LISTEN_FDS 环境变量]
    J --> K[复制 FD 3+]
    K --> L[创建 FdMapping]
    L --> M[为子进程设置 LISTEN_FDS]
    M --> N[移除 LISTEN_PID]
    N --> O[返回 Command]
    H -->|否| O
    O --> P[执行命令]

设计确保:

  • 简洁性: 单函数接口
  • 可靠性: 无恐慌的错误处理
  • 灵活性: 返回 Command 供进一步定制
  • 性能: 惰性求值的最小开销

技术栈

  • 语言: Rust 2024 版本
  • 核心依赖:
    • log 用于日志功能
    • thiserror 用于结构化错误处理
  • Linux 依赖:
    • command-fds 用于文件描述符映射
    • libc 用于底层系统调用
  • 错误处理: 自定义 Error 类型与 thiserror 集成
  • 进程管理: std::process::Command 与 FD 映射扩展
  • 环境访问: std::env 获取可执行文件路径和参数

项目结构

self_cmd/
├── src/
│   ├── lib.rs          # 核心实现和公共 API
│   ├── error.rs        # 错误类型和处理
│   └── fd_mapping.rs   # Linux 特定的 FD 映射(条件编译)
├── tests/
│   └── main.rs         # 集成测试
├── readme/
│   ├── en.md          # 英文文档
│   └── zh.md          # 中文文档
├── Cargo.toml         # 项目配置
└── test.sh           # 测试运行脚本

API 参考

get() -> Result<Command>

创建配置为重新执行当前程序的 Command 实例,完整保持上下文。

返回值:

  • Ok(Command): 配置好的命令,可直接执行
  • Err(Error): 无法确定可执行文件路径或配置 FD 映射时

行为:

  • 通过 env::current_exe() 捕获当前可执行文件路径
  • 保留除程序名外的所有命令行参数
  • 配置标准输入输出继承 (stdin, stdout, stderr)
  • 仅 Linux: 处理 systemd 套接字激活 FD 映射
  • 返回 Command 供执行前进一步定制

示例:

match self_cmd::get() {
    Ok(mut cmd) => {
        // 命令已准备就绪,完整保持上下文
        cmd.status()?;
    }
    Err(e) => {
        eprintln!("创建自执行命令失败: {e}");
    }
}

fd_mapping() -> Result<Vec<FdMapping>> (仅 Linux)

仅在 Linux 上可用 - 为 systemd 套接字激活创建文件描述符映射。

返回值:

  • Ok(Vec<FdMapping>): 套接字激活的 FD 映射列表
  • Err(Error): FD 复制或验证失败时

此函数在 Linux 系统上由 get() 自动调用,通常不需要直接使用。

错误处理

库使用基于 thiserror 构建的自定义 Error 类型进行全面的错误处理:

use self_cmd::{Error, Result};

#[derive(Error, Debug)]
pub enum Error {
    #[error("IO error: {0} / IO 错误: {0}")]
    Io(#[from] std::io::Error),
    
    #[error("FD mapping collision: {0} / FD 映射冲突: {0}")]
    FdMappingCollision(#[from] command_fds::FdMappingCollision),
}

所有公共函数返回 Result<T>,这是 std::result::Result<T, Error> 的别名。这提供了:

  • 结构化错误: 不同失败模式的清晰错误类型
  • 错误链: 从底层错误类型的自动转换
  • 调试支持: 带有上下文的丰富错误消息
  • 集成: 与 ? 操作符和错误处理模式的无缝集成

关于

本项目为 js0.site ⋅ 重构互联网计划 的开源组件。

我们正在以组件化的方式重新定义互联网的开发范式,欢迎关注: