osal.systemd #
SystemD Module
A V module for managing systemd services with comprehensive error handling and monitoring capabilities.
Features
- Create, start, stop, and delete systemd services
- Service status monitoring with detailed error reporting
- Journal log retrieval with filtering options
- Health checks for service validation
- Automatic retry logic for service operations
Quick Start
import incubaid.herolib.lib.osal.systemd
// Create systemd factory
mut systemd := systemd.new()!
// Create a new service
mut redis_service := systemd.new(
name: 'redis_custom'
cmd: 'redis-server /etc/redis/redis.conf'
description: 'Custom Redis server'
start: true
)!
// Check service status
status := redis_service.status()!
println('Redis service status: ${status}')
// Get service logs
logs := redis_service.get_logs(50)!
println('Recent logs:\n${logs}')
Creating Services
Basic Service
mut service := systemd.new(
name: 'my_service'
cmd: '/usr/bin/my_application --config /etc/my_app.conf'
description: 'My custom application'
start: true
)!
Service with Environment Variables
mut service := systemd.new(
name: 'web_app'
cmd: '/usr/bin/webapp'
description: 'Web application server'
env: {
'PORT': '8080'
'ENV': 'production'
'DB_HOST': 'localhost'
}
start: true
)!
Service with Complex Command
// For multi-line commands, systemd will create a script file
mut service := systemd.new(
name: 'backup_service'
cmd: '
#!/bin/bash
cd /var/backups
tar -czf backup_$(date +%Y%m%d).tar.gz /home/data/
aws s3 cp backup_$(date +%Y%m%d).tar.gz s3://my-bucket/
'
description: 'Daily backup service'
start: true
)!
Service Management
Starting and Stopping Services
// Start service (with automatic verification)
service.start()! // Will wait and verify service started successfully
// Stop service (with verification)
service.stop()! // Will wait and verify service stopped
// Restart service
service.restart()!
Checking Service Status
// Get simple status
status := service.status()!
match status {
.active { println('Service is running') }
.failed { println('Service has failed') }
.inactive { println('Service is stopped') }
else { println('Service status: ${status}') }
}
// Get detailed status information
detailed_status := service.status_detailed()!
println(detailed_status)
Log Management
Basic Log Retrieval
// Get last 100 lines
logs := service.get_logs(100)!
// Using journalctl directly
logs := systemd.journalctl(service: 'my_service', limit: 50)!
Advanced Log Filtering
// Get error logs only
error_logs := systemd.journalctl_errors('my_service')!
// Get logs since specific time
recent_logs := systemd.journalctl_recent('my_service', '1 hour ago')!
// Custom log filtering
filtered_logs := systemd.journalctl(
service: 'my_service'
since: '2024-01-01'
priority: 'warning'
grep: 'connection'
limit: 200
)!
Health Monitoring
Individual Service Health Check
is_healthy := systemd.validate_service('my_service')!
if !is_healthy {
println('Service needs attention')
}
System-wide Health Check
health_results := systemd.health_check()!
for service_name, is_healthy in health_results {
if !is_healthy {
println('Service ${service_name} is not healthy')
}
}
Error Handling
The module provides detailed error messages with log context:
// Service creation with error handling
mut service := systemd.new(
name: 'problematic_service'
cmd: '/nonexistent/binary'
start: true
) or {
println('Failed to create service: ${err}')
// Error will include recent logs showing why service failed
return
}
Service Deletion
// Stop and remove service completely
service.delete()!
// Or using systemd factory
systemd.destroy('service_name')!
Best Practices
- Always handle errors: Service operations can fail, always use
!
oror
blocks - Use descriptive names: Service names should be clear and unique
- Check logs on failure: When services fail, check logs for diagnostic information
- Validate service health: Regularly check service status in production
- Use environment variables: Keep configuration flexible with environment variables
Common Patterns
Conditional Service Creation
if !systemd.exists('my_service') {
mut service := systemd.new(
name: 'my_service'
cmd: 'my_application'
start: true
)!
}
Service with Dependency
// Ensure dependency is running first
redis_status := systemd.get('redis')!.status()!
if redis_status != .active {
return error('Redis must be running before starting web service')
}
mut web_service := systemd.new(
name: 'web_service'
cmd: 'web_server --redis-host localhost:6379'
start: true
)!
Troubleshooting
Service Won't Start
- Check service logs:
service.get_logs(100)!
- Verify command exists:
osal.cmd_exists('your_command')
- Check file permissions and paths
- Review systemd unit file:
cat /etc/systemd/system/service_name.service
Service Keeps Failing
- Get error logs:
systemd.journalctl_errors('service_name')!
- Check if command is executable
- Verify environment variables and working directory
- Test command manually:
your_command_here
Testing
// Test module
vtest ~/code/github/incubaid/herolib/lib/osal/systemd/systemd_process_test.v
fn check #
fn check() !bool
check if systemd is on system, returns True if yes
fn journalctl #
fn journalctl(args JournalArgs) !string
fn journalctl_errors #
fn journalctl_errors(service string) !string
Add convenience methods
fn journalctl_recent #
fn journalctl_recent(service string, since string) !string
fn new #
fn new() !&Systemd
fn process_list #
fn process_list() ![]SystemdProcessInfo
enum ActiveState #
enum ActiveState {
active // The unit has been started successfully and is running as expected.
inactive // The unit is not running.
activating // The unit is in the process of being started.
deactivating // The unit is in the process of being stopped.
failed // The unit tried to start but failed.
}
enum LoadState #
enum LoadState {
loaded // The unit's configuration file has been successfully loaded into memory.
not_found // The unit's configuration file could not be found.
error // There was an error loading the unit's configuration file.
masked // The unit has been masked, which means it has been explicitly disabled and cannot be started.
}
enum SubState #
enum SubState {
unknown
start
running // The service is currently running.
exited // The service has completed its process and exited. For services that do something at startup and then exit (oneshot services), this is a normal state.
failed // The service has failed after starting.
waiting // The service is waiting for some condition to be met.
autorestart
dead
}
This provides more detailed information about the unit's state, often referred to as the "sub-state". This can vary significantly between different types of units (services, sockets, timers, etc.)
enum SystemdFactoryStatus #
enum SystemdFactoryStatus {
init
ok
error
}
struct JournalArgs #
@[params]
struct JournalArgs {
pub:
service string // name of service for which logs will be retrieved
limit int = 100 // number of last log lines to be shown
since string // time since when to show logs (e.g., "1 hour ago", "2024-01-01")
follow bool // follow logs in real-time
priority string // log priority (emerg, alert, crit, err, warning, notice, info, debug)
grep string // filter logs containing this text
}
Add more flexible journalctl options
struct Systemd #
@[heap]
struct Systemd {
pub mut:
processes []&SystemdProcess
path pathlib.Path
path_cmd pathlib.Path
status SystemdFactoryStatus
}
fn (Systemd) reload #
fn (mut systemd Systemd) reload() !
fn (Systemd) new #
fn (mut systemd Systemd) new(args_ SystemdProcessNewArgs) !SystemdProcess
name string @[required]
cmd string @[required]
description string @[required]
fn (Systemd) names #
fn (mut systemd Systemd) names() []string
fn (Systemd) get #
fn (mut systemd Systemd) get(name_ string) !&SystemdProcess
fn (Systemd) exists #
fn (mut systemd Systemd) exists(name_ string) bool
fn (Systemd) destroy #
fn (mut systemd Systemd) destroy(name_ string) !
fn (Systemd) validate_service #
fn (mut systemd Systemd) validate_service(name string) !bool
Add validation method
fn (Systemd) health_check #
fn (mut systemd Systemd) health_check() !map[string]bool
Add method to check all services
struct SystemdProcess #
@[heap]
struct SystemdProcess {
pub mut:
name string
unit string // as generated or used by systemd
cmd string
pid int
env map[string]string
systemd &Systemd @[skip; str: skip]
description string
info SystemdProcessInfo
restart bool = true // whether process will be restarted upon failure
}
fn (SystemdProcess) servicefile_path #
fn (mut self SystemdProcess) servicefile_path() string
fn (SystemdProcess) write #
fn (mut self SystemdProcess) write() !
fn (SystemdProcess) start #
fn (mut self SystemdProcess) start() !
fn (SystemdProcess) refresh #
fn (mut self SystemdProcess) refresh() !
get status from system
fn (SystemdProcess) delete #
fn (mut self SystemdProcess) delete() !
fn (SystemdProcess) stop #
fn (mut self SystemdProcess) stop() !
fn (SystemdProcess) restart #
fn (mut self SystemdProcess) restart() !
fn (SystemdProcess) get_logs #
fn (self SystemdProcess) get_logs(lines int) !string
fn (SystemdProcess) status #
fn (self SystemdProcess) status() !SystemdStatus
Improve status method with better error handling
fn (SystemdProcess) status_detailed #
fn (self SystemdProcess) status_detailed() !string
Add detailed status method
struct SystemdProcessInfo #
struct SystemdProcessInfo {
pub mut:
unit string
load_state LoadState
active_state ActiveState
sub_state SubState
description string
}
struct SystemdProcessNewArgs #
@[params]
struct SystemdProcessNewArgs {
pub mut:
name string @[required]
cmd string @[required]
description string
env map[string]string
start bool = true
restart bool = true
}