sysinit
is a Python library for defining, managing, and interacting with systemd-like service units using simple YAML configurations. It allows you to programmatically control the lifecycle (start, stop, enable, disable, reload) of background processes or tasks.
- Operating System: This library only works on Linux distributions that use systemd. It fundamentally relies on the
systemctl
command and the systemd directory structure. It will not work on macOS, Windows, or non-systemd Linux systems. - System Mode (
sudo
Required): By default, or whenmanage_as_user: false
is set for a service,sysinit
interacts with the system-wide systemd instance. This requiressudo
(root) privileges for most operations:- Placing/removing service files in
/etc/systemd/system/
. - Running
systemctl start/stop/enable/disable/restart/daemon-reload
for system services. - Security Risk: Granting
sudo
access to any script carries inherent security risks. Understand the commands being run (primarilysystemctl
and file operations in system directories) before using this mode.sysinit
attempts to usesudo
only when necessary for system-level interaction.
- Placing/removing service files in
- Define services declaratively in YAML.
- Manage systemd system services (requires
sudo
). - Manage systemd user services (
sudo
-free). - Control service lifecycle:
start
,stop
,restart
,reload
. - Enable/disable services for boot/login start.
- Load/unload service definition files.
- Pythonic API for programmatic control (
Unit
,UnitManager
). - Dry-run mode for safe testing.
- Basic logging of operations.
# Install from PyPI
pip install sysinit
# uv install
uv add sysinit
# For development (includes testing and formatting tools)
pip install "sysinit[dev]"
# Or: pip install black pytest pyyaml
The primary way to interact via the command line is through the sysinit-term
command, which provides an interactive shell.
-
Ensure
ipython
is installed (see Installation section above). -
(Optional) Create
config.yaml
: Define your services as described in the sections below. Ifconfig.yaml
doesn't exist in the current directory, the shell will start with an empty manager. -
Launch the Interactive Shell:
# Use default config.yaml (if it exists) sysinit-term shell # Specify a different configuration file sysinit-term --config /path/to/your/config.yaml shell # Get help sysinit-term --help sysinit-term shell --help
-
Inside the Shell: You'll get an IPython prompt. Use the available objects and helper functions (like
manager
,Unit
,Command
,start()
,stop()
,status()
,services()
,add_unit()
) to manage your services interactively. Typeexit()
or pressCtrl+D
to quit.# Example session inside the shell: In [1]: services() # List services loaded from config (or 'No services...') Managed services: - my-web-server - my-dev-tool In [2]: status('my-dev-tool') # Status output... In [3]: stop('my-web-server') # May require sudo password if not run as root Stopping 'my-web-server'... Service 'my-web-server' stopped. In [4]: u = Unit(name='temp-ls', exec_start='ls -l /tmp', manage_as_user=True) In [5]: add_unit(u) Unit 'temp-ls' added to manager. In [6]: start('temp-ls') Starting 'temp-ls'... # Output... Service 'temp-ls' started.
(Note: While the interactive shell is the primary focus of sysinit-term
, you could extend sysinit/term.py
to include direct, non-interactive commands like sysinit-term start <service>
if desired.)
Create a YAML file (e.g., config.yaml) to define your services. Specify whether each service should be managed at the system level or user level.
services:
# Example 1: A system-wide service (requires sudo)
- my-web-server:
unit_config:
name: my-web-server # Base name for service file (my-web-server.service)
description: My Sample Web Server
exec_start: "/usr/bin/python -m http.server 8000" # Command to start
working_directory: "/opt/my-web-app" # Optional: Directory to run in
user: "www-data" # Optional: Run as specific user
Type: "simple" # Systemd service type
Restart: "on-failure" # Optional: Restart policy
# WantedBy: "multi-user.target" # Default for system services
command_config:
dry_run: false # Set to true to just print commands
verbose: true # More detailed logging
manage_as_user: false # Explicitly system mode (default)
enable_service: true # Attempt to enable on first start
# Example 2: A user-specific service (sudo-free)
- my-dev-tool:
unit_config:
name: my-dev-tool # my-dev-tool.service
description: My Development Helper Tool
exec_start: "/home/user/scripts/my_dev_tool.py" # MUST be accessible by the user
working_directory: "/home/user/dev/my-tool"
Type: "oneshot"
RemainAfterExit: yes
# WantedBy: "default.target" # Default for user services
command_config:
dry_run: false
verbose: true
manage_as_user: true # CRITICAL: Run as systemd --user service
enable_service: true # Enable for user login start
# Add more services as needed...
from sysinit.core.unit_manager import UnitManager
import logging
# Configure logging level if needed (optional)
# logging.basicConfig(level=logging.DEBUG)
# Initialize the manager, loading services from the config
try:
manager = UnitManager("config.yaml")
except FileNotFoundError:
print("Error: config.yaml not found!")
exit(1)
except ValueError as e:
print(f"Error loading config: {e}")
exit(1)
# --- Lifecycle Operations ---
# Start all defined services (respects manage_as_user for each)
print("Starting all services...")
manager.start_all()
# Note: Starting system services will likely prompt for sudo password if not run as root
# Start a specific service
print("\nStarting my-dev-tool...")
try:
manager.start_service("my-dev-tool")
except Exception as e:
print(f"Error starting service: {e}")
# Stop a specific service
print("\nStopping my-web-server...")
try:
manager.stop_service("my-web-server") # This will likely require sudo
except Exception as e:
print(f"Error stopping service: {e}")
# Check status of all services
print("\nChecking status...")
manager.status_all()
# Restart a specific service
print("\nRestarting my-dev-tool...")
manager.restart_service("my-dev-tool")
# Disable a service (won't start on boot/login)
print("\nDisabling my-web-server...")
manager.disable_service("my-web-server") # Requires sudo
# Enable a service (will start on boot/login)
print("\nEnabling my-web-server...")
manager.enable_service("my-web-server") # Requires sudo
# Reload configuration for a service (rewrites .service file, reloads daemon)
# Use after changing config in code or if the service file needs refreshing
print("\nReloading my-dev-tool definition...")
manager.reload_service("my-dev-tool")
# Stop all services
print("\nStopping all services...")
manager.stop_all()
# Unload all service files (removes from systemd dirs)
print("\nUnloading all services...")
manager.unload_all() # Requires sudo for system services
The config.yaml
file has a top-level services
key, which is a list of service definitions. Each service definition is a dictionary with a single key being the logical service name used within sysinit
(e.g., my-web-server
). The value is another dictionary containing unit_config
and command_config
.
These parameters map closely to systemd .service
file options within the [Unit]
and [Service]
sections.
name
: (Required) The base name for the service (e.g.,my-app
results inmy-app.service
).description
: (Optional) Service description (Description=
).exec_start
: (Required) The command to run to start the service (ExecStart=
).exec_stop
: (Optional) The command to run to stop the service (ExecStop=
).working_directory
: (Optional) The working directory for the service (WorkingDirectory=
).user
: (Optional) System user to run the service as (User=
). Only effective in system mode.group
: (Optional) System group to run the service as (Group=
). Only effective in system mode.Type
: (Optional) Service type (simple
,forking
,oneshot
, etc.). Defaults tooneshot
. (Type=
).Restart
: (Optional) Restart policy (no
,on-success
,on-failure
,always
, etc.) (Restart=
).RemainAfterExit
: (Optional, boolean) Useful foroneshot
services. Defaults tofalse
. (RemainAfterExit=
).Environment
: (Optional, dict) Environment variables (Environment="KEY=value"
).After
: (Optional, string) Run after specified units (After=
).Requires
: (Optional, string) Depends on specified units (Requires=
).WantedBy
: (Optional) Install target. Defaults tomulti-user.target
(system) ordefault.target
(user). (WantedBy=
).unit
: (Optional) Explicitly set the service filename (e.g.,my-custom-name.service
). Overridesname
.
These parameters control how sysinit
itself behaves when managing the unit.
dry_run
: (Optional, boolean) Iftrue
, print commands instead of executing them. Defaults tofalse
.verbose
: (Optional, boolean) Enable more detailed logging output. Defaults tofalse
.enable_service
: (Optional, boolean) Iftrue
,sysinit
will attempt toenable
the service during the firststart
operation if it's not already enabled. Defaults tofalse
.