14 releases (6 breaking)

0.7.1 Jul 13, 2024
0.6.2 May 27, 2024
0.6.0 Feb 18, 2024
0.5.1 Nov 22, 2023
0.1.3 Jul 25, 2022

#12 in Configuration

Download history 1764/week @ 2024-08-18 2349/week @ 2024-08-25 2066/week @ 2024-09-01 2187/week @ 2024-09-08 3036/week @ 2024-09-15 2609/week @ 2024-09-22 2908/week @ 2024-09-29 2759/week @ 2024-10-06 3764/week @ 2024-10-13 4728/week @ 2024-10-20 3821/week @ 2024-10-27 3542/week @ 2024-11-03 2803/week @ 2024-11-10 1994/week @ 2024-11-17 2346/week @ 2024-11-24 3146/week @ 2024-12-01

10,684 downloads per month
Used in 11 crates (7 directly)

MIT/Apache

100KB
2.5K SLoC

Service Manager

Crates.io Docs CI

Rust library that provides an interface towards working with the following service management platforms:

Requires Rust 1.58.1 or higher!

Installation

Add the following to your Cargo.toml:

[dependencies]
service-manager = "0.7"

Examples

Generic service management

This crate provides a mechanism to detect and use the default service management platform of the current operating system. Each ServiceManager instance provides four key methods:

  • install - will install the service specified by a given context
  • uninstall - will uninstall the service specified by a given context
  • start - will start an installed service specified by a given context
  • stop - will stop a running service specified by a given context
use service_manager::*;
use std::ffi::OsString;
use std::path::PathBuf;

// Create a label for our service
let label: ServiceLabel = "com.example.my-service".parse().unwrap();

// Get generic service by detecting what is available on the platform
let manager = <dyn ServiceManager>::native()
    .expect("Failed to detect management platform");

// Install our service using the underlying service management platform
manager.install(ServiceInstallCtx {
    label: label.clone(),
    program: PathBuf::from("path/to/my-service-executable"),
    args: vec![OsString::from("--some-arg")],
    contents: None, // Optional String for system-specific service content.
    username: None, // Optional String for alternative user to run service.
    working_directory: None, // Optional String for the working directory for the service process.
    environment: None, // Optional list of environment variables to supply the service process.
    autostart: true, // Specify whether the service should automatically start upon OS reboot.
}).expect("Failed to install");

// Start our service using the underlying service management platform
manager.start(ServiceStartCtx {
    label: label.clone()
}).expect("Failed to start");

// Stop our service using the underlying service management platform
manager.stop(ServiceStopCtx {
    label: label.clone()
}).expect("Failed to stop");

// Uninstall our service using the underlying service management platform
manager.uninstall(ServiceUninstallCtx {
    label: label.clone()
}).expect("Failed to stop");

User-level service management

By default, service management platforms will interact with system-level services; however, some service management platforms like systemd and launchd support user-level services. To interact with services at the user level, you configure your manager using the generic ServiceManager::set_level function.

use service_manager::*;

// Create a label for our service
let label: ServiceLabel = "com.example.my-service".parse().unwrap();

// Get generic service by detecting what is available on the platform
let mut manager = <dyn ServiceManager>::native()
    .expect("Failed to detect management platform");

// Update our manager to work with user-level services
manager.set_level(ServiceLevel::User)
    .expect("Service manager does not support user-level services");

// Continue operating as usual via install/uninstall/start/stop
// ...

Specific service manager configurations

There are times where you need more control over the configuration of a service tied to a specific platform. To that end, you can create the service manager explicitly and set configuration properties appropriately.

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

// Create a label for our service
let label: ServiceLabel = "com.example.my-service".parse().unwrap();

// Instantiate a specific service manager
let mut manager = LaunchdServiceManager::system();

// Update an install configuration property where installing a service
// will NOT add the KeepAlive flag
manager.config.install.keep_alive = false;

// Install our service using the explicit service manager
manager.install(ServiceInstallCtx {
    label: label.clone(),
    program: PathBuf::from("path/to/my-service-executable"),
    args: vec![OsString::from("--some-arg")],
    contents: None, // Optional String for system-specific service content.
    username: None, // Optional String for alternative user to run service.
    working_directory: None, // Optional String for the working directory for the service process.
    environment: None, // Optional list of environment variables to supply the service process.
    autostart: true, // Specify whether the service should automatically start upon OS reboot.
}).expect("Failed to install");

Running tests

For testing purposes, we use a separate crate called system-tests and execute singular tests based on desired platform and level. From the root of the repository, execute the following to run a systemd user test:

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

Separately, run a systemd system test using the following (notice using of sudo -E to maintain permissions needed for system-level installation):

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

License

This project is licensed under either of

Apache License, Version 2.0, (LICENSE-APACHE or apache-license) MIT license (LICENSE-MIT or mit-license) at your option.

Dependencies

~5–14MB
~188K SLoC