Rust服务管理库service-manager的使用:跨平台守护进程与系统服务管理解决方案

Rust服务管理库service-manager的使用:跨平台守护进程与系统服务管理解决方案

简介

service-manager是一个Rust库,提供了与以下服务管理平台交互的接口:

  • Windows上的sc.exe(Windows服务)
  • Windows上的Winsw
  • MacOS上的Launchd
  • Linux上的systemd
  • Linux上的OpenRC
  • FreeBSD上的rc.d

要求Rust版本1.58.1或更高!

安装

Cargo.toml中添加以下依赖:

[dependencies]
service-manager = "0.8"

示例

通用服务管理

这个库提供了检测和使用当前操作系统默认服务管理平台的机制。每个ServiceManager实例提供四个关键方法:

  • install - 安装由给定上下文指定的服务
  • uninstall - 卸载由给定上下文指定的服务
  • start - 启动已安装的服务
  • stop - 停止正在运行的服务
use service_manager::*;
use std::ffi::OsString;
use std::path::PathBuf;

// 为我们的服务创建标签
let label: ServiceLabel = "com.example.my-service".parse().unwrap();

// 通过检测平台上的可用服务获取通用服务
let manager = <dyn ServiceManager>::native()
    .expect("Failed to detect management platform");

// 使用底层服务管理平台安装我们的服务
manager.install(ServiceInstallCtx {
    label: label.clone(),
    program: PathBuf::from("path/to/my-service-executable"),
    args: vec![OsString::from("--some-arg")],
    contents: None, // 可选String,用于系统特定服务内容
    username: None, // 可选String,指定运行服务的替代用户
    working_directory: None, // 可选String,指定服务进程的工作目录
    environment: None, // 可选环境变量列表
    autostart: true, // 指定服务是否应在OS重启时自动启动
    disable_restart_on_failure: false, // 服务默认在崩溃时重启
}).expect("Failed to install");

// 使用底层服务管理平台启动我们的服务
manager.start(ServiceStartCtx {
    label: label.clone()
}).expect("Failed to start");

// 使用底层服务管理平台停止我们的服务
manager.stop(ServiceStopCtx {
    label: label.clone()
}).expect("Failed to stop");

// 使用底层服务管理平台卸载我们的服务
manager.uninstall(ServiceUninstallCtx {
    label: label.clone
}).expect("Failed to stop");

用户级服务管理

默认情况下,服务管理平台会与系统级服务交互;然而,一些服务管理平台如systemdlaunchd支持用户级服务。要与用户级服务交互,您可以使用通用的ServiceManager::set_level函数配置您的管理器。

use service_manager::*;

// 为我们的服务创建标签
let label: ServiceLabel = "com.example.my-service".parse().unwrap();

// 通过检测平台上的可用服务获取通用服务
let mut manager = <dyn ServiceManager>::native()
    .expect("Failed to detect management platform");

// 更新我们的管理器以处理用户级服务
manager.set_level(ServiceLevel::User)
    .expect("Service manager does not support user-level services");

// 继续通过install/uninstall/start/stop进行操作
// ...

特定服务管理器配置

有时您需要对特定平台的服务配置进行更多控制。为此,您可以显式创建服务管理器并适当设置配置属性。

use service_manager::*;
use std::ffi::OsString;
use std::path::PathBuf;

// 为我们的服务创建标签
let label: ServiceLabel = "com.example.my-service".parse().unwrap();

// 实例化特定的服务管理器
let mut manager = LaunchdServiceManager::system();

// 更新安装配置属性,安装服务时将不添加KeepAlive标志
manager.config.install.keep_alive = false;

// 使用明确的服务管理器安装我们的服务
manager.install(ServiceInstallCtx {
    label: label.clone(),
    program: PathBuf::from("path/to/my-service-executable"),
    args: vec![OsString::from("--some-arg")],
    contents: None, // 可选String,用于系统特定服务内容
    username: None, // 可选String,指定运行服务的替代用户
    working_directory: None, // 可选String,指定服务进程的工作目录
    environment: None, // 可选环境变量列表
    autostart: true, // 指定服务是否应在OS重启时自动启动
    disable_restart_on_failure: false, // 服务默认在崩溃时重启
}).expect("Failed to install");

运行测试

出于测试目的,我们使用一个名为system-tests的独立crate,并根据所需的平台和级别执行单一测试。从存储库的根目录执行以下命令以运行systemd用户测试:

cargo test -p system-tests systemd_for_user -- --nocapture

单独地,使用以下命令运行systemd系统测试(注意使用sudo -E来保持系统级安装所需的权限):

sudo -E cargo test -p system-tests systemd_for_system -- --nocapture

完整示例代码

以下是一个完整的示例,展示了如何使用service-manager库来管理跨平台服务:

use service_manager::*;
use std::ffi::OsString;
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 创建服务标签
    let label: ServiceLabel = "com.example.my-service".parse()?;
    
    // 2. 检测并获取适合当前平台的服务管理器
    let mut manager = <dyn ServiceManager>::native()
        .expect("Failed to detect service management platform");
    
    // 3. 可选:设置为用户级服务
    if manager.capabilities().contains(ServiceManagerCapability::UserLevel) {
        manager.set_level(ServiceLevel::User)?;
    }
    
    // 4. 安装服务
    manager.install(ServiceInstallCtx {
        label: label.clone(),
        program: PathBuf::from("/usr/local/bin/my-service"),
        args: vec![OsString::from("--verbose")],
        contents: None,
        username: None,
        working_directory: None,
        environment: Some(vec![
            ("RUST_LOG".into(), "info".into()),
            ("CONFIG_PATH".into(), "/etc/my-service/config.toml".into())
        ]),
        autostart: true,
        disable_restart_on_failure: false,
    })?;
    
    // 5. 启动服务
    manager.start(ServiceStartCtx {
        label: label.clone()
    })?;
    
    println!("Service installed and started successfully!");
    
    // 6. 稍后可以停止和卸载服务
    // manager.stop(ServiceStopCtx { label: label.clone() })?;
    // manager.uninstall(ServiceUninstallCtx { label })?;
    
    Ok(())
}

许可证

这个项目采用以下任一种许可证:

  • Apache License, Version 2.0
  • MIT license

总结

service-manager库为Rust开发者提供了一个统一的API来管理跨平台的服务。无论您是在Windows、macOS还是Linux上部署服务,都可以使用相同的代码来安装、启动、停止和卸载服务。这使得构建需要在多个操作系统上作为服务运行的Rust应用程序变得更加简单。


1 回复

Rust服务管理库service-manager的使用:跨平台守护进程与系统服务管理解决方案

service-manager是一个Rust库,提供了跨平台的服务管理功能,允许开发者以统一的方式在不同操作系统上安装、卸载和管理后台服务。

功能特性

  • 支持多种服务类型:守护进程、系统服务等
  • 跨平台支持:Windows、Linux(多种init系统)、macOS
  • 统一的API接口
  • 服务生命周期管理

安装

在Cargo.toml中添加依赖:

[dependencies]
service-manager = "0.1"

基本使用方法

1. 创建服务配置

use service_manager::*;

// 创建服务管理器实例
let manager = ServiceManager::native()
    .expect("Failed to detect service manager");

2. 安装服务

// 定义服务配置
let service = Service {
    id: "my-service".to_string(),           // 服务ID
    name: "My Service".to_string(),         // 显示名称
    description: Some("My long running service".to_string()), // 服务描述
    executable: std::env::current_exe().unwrap(), // 可执行文件路径
    args: vec!["run".to_string()],          // 启动参数
    env: vec![],                            // 环境变量
    working_directory: None,                // 工作目录
};

// 安装服务
manager.install(service.clone()).expect("Failed to install service");

3. 启动服务

// 启动服务
manager.start(&service.id).expect("Failed to start service");

4. 停止服务

// 停止服务
manager.stop(&service.id).expect("Failed to stop service");

5. 卸载服务

// 卸载服务
manager.uninstall(&service.id).expect("Failed to uninstall service");

完整示例

下面是内容中提供的完整示例:

use service_manager::*;
use std::{env, process};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 获取当前可执行文件路径
    let exe = env::current_exe()?;
    
    // 创建服务管理器
    let manager = ServiceManager::native()?;
    
    // 定义服务配置
    let service = Service {
        id: "my-rust-service".to_string(),
        name: "My Rust Service".to_string(),
        description: Some("A sample Rust service".to_string()),
        executable: exe,
        args: vec!["run-as-service".to_string()],
        env: vec![],
        working_directory: None,
    };
    
    // 检查命令行参数
    let args: Vec<String> = env::args().collect();
    if args.len() > 1 {
        match args[1].as_str() {
            "install" => {
                manager.install(service.clone())?;
                println!("Service installed successfully");
                manager.start(&service.id)?;
                println!("Service started successfully");
            }
            "uninstall" => {
                manager.stop(&service.id)?;
                manager.uninstall(&service.id)?;
                println!("Service uninstalled successfully");
            }
            "start" => {
                manager.start(&service.id)?;
                println!("Service started successfully");
            }
            "stop" => {
                manager.stop(&service.id)?;
                println!("Service stopped successfully");
            }
            "run-as-service" => {
                // 这里是服务实际运行的代码
                println!("Service is running...");
                // 通常这里会有一个事件循环
                loop {
                    std::thread::sleep(std::time::Duration::from_secs(1));
                }
            }
            _ => {
                println!("Unknown command");
                process::exit(1);
            }
        }
    } else {
        println!("Usage: {} [install|uninstall|start|stop]", args[0]);
    }
    
    Ok(())
}

平台特定说明

Linux支持

在Linux上,service-manager支持多种init系统:

  • systemd
  • OpenRC
  • SysVinit

它会自动检测当前系统使用的init系统。

Windows服务

在Windows上,服务将被安装为标准的Windows服务,可以通过服务管理器访问。

macOS launchd

在macOS上,服务使用launchd进行管理。

高级配置

自定义服务用户

let service = Service {
    // ...其他配置...
    user: Some("myuser".to_string()), // Linux/macOS特定
    group: Some("mygroup".to_string()), // Linux/macOS特定
    ..service
};

设置服务依赖

let service = Service {
    // ...其他配置...
    dependencies: vec!["network.target".to_string()], // systemd特定
    ..service
};

配置服务重启行为

let service = Service {
    // ...其他配置...
    restart: Some(ServiceRestart::Always), // 总是重启
    // 或
    restart: Some(ServiceRestart::OnFailure), // 仅在失败时重启
    ..service
};

注意事项

  1. 在Linux上,安装服务通常需要root权限
  2. 服务可执行文件路径应该是绝对路径
  3. 卸载服务前最好先停止服务
  4. 不同平台的服务日志记录方式不同

service-manager为Rust开发者提供了一个简单统一的接口来管理系统服务,大大简化了跨平台服务管理的复杂性。

回到顶部