# SAL Zinit Client (`sal-zinit-client`) A Rust client library for interacting with [Zinit](https://github.com/systeminit/zinit), a process supervisor daemon for Linux systems. This package provides both a Rust API and Rhai scripting integration for comprehensive service management. ## Features - **Async Operations**: Built on tokio for non-blocking communication - **Unix Socket Communication**: Connects to Zinit daemon via Unix domain sockets - **Global Client Management**: Efficient connection reuse with lazy initialization - **Comprehensive Service Management**: Full lifecycle control (start, stop, restart, monitor, etc.) - **Service Configuration**: Create, delete, and retrieve service configurations - **Real-time Log Streaming**: Retrieve logs with filtering support - **Rhai Integration**: Complete scripting support for automation - **Production Ready**: Real-world tested with comprehensive error handling ## Installation Add this to your `Cargo.toml`: ```toml [dependencies] sal-zinit-client = "0.1.0" ``` ## Quick Start ### Rust API ```rust use sal_zinit_client::{list, status, create_service, start, stop}; #[tokio::main] async fn main() -> Result<(), Box> { let socket_path = "/var/run/zinit.sock"; // List all services let services = list(socket_path).await?; println!("Services: {:?}", services); // Create a new service create_service(socket_path, "my-service", "echo 'Hello World'", true).await?; // Start the service start(socket_path, "my-service").await?; // Get service status let service_status = status(socket_path, "my-service").await?; println!("Status: {:?}", service_status); Ok(()) } ``` ### Rhai Scripting ```rhai // Zinit socket path let socket_path = "/var/run/zinit.sock"; // List all services let services = zinit_list(socket_path); print(`Found ${services.len()} services`); // Create and manage a service let service_name = "rhai-test-service"; let exec_command = "echo 'Hello from Rhai'"; // Create service zinit_create_service(socket_path, service_name, exec_command, true); // Monitor and start zinit_monitor(socket_path, service_name); zinit_start(socket_path, service_name); // Get status let status = zinit_status(socket_path, service_name); print(`Service state: ${status.state}`); // Clean up zinit_stop(socket_path, service_name); zinit_forget(socket_path, service_name); zinit_delete_service(socket_path, service_name); ``` ## API Reference ### Core Functions #### Service Management - `list(socket_path)` - List all services and their states - `status(socket_path, name)` - Get detailed status of a specific service - `start(socket_path, name)` - Start a service - `stop(socket_path, name)` - Stop a service - `restart(socket_path, name)` - Restart a service - `monitor(socket_path, name)` - Start monitoring a service - `forget(socket_path, name)` - Stop monitoring a service - `kill(socket_path, name, signal)` - Send a signal to a service #### Service Configuration - `create_service(socket_path, name, exec, oneshot)` - Create a simple service - `create_service_full(socket_path, name, exec, oneshot, after, env, log, test)` - Create service with full options - `delete_service(socket_path, name)` - Delete a service - `get_service(socket_path, name)` - Get service configuration #### Logs - `logs(socket_path, filter)` - Get logs with optional filtering - `logs(socket_path, None)` - Get all logs ### Rhai Functions All Rust functions are available in Rhai with `zinit_` prefix: - `zinit_list(socket_path)` → Map - `zinit_status(socket_path, name)` → Map - `zinit_start(socket_path, name)` → bool - `zinit_stop(socket_path, name)` → bool - `zinit_restart(socket_path, name)` → bool - `zinit_monitor(socket_path, name)` → bool - `zinit_forget(socket_path, name)` → bool - `zinit_kill(socket_path, name, signal)` → bool - `zinit_create_service(socket_path, name, exec, oneshot)` → String - `zinit_delete_service(socket_path, name)` → String - `zinit_get_service(socket_path, name)` → Dynamic - `zinit_logs(socket_path, filter)` → Array - `zinit_logs_all(socket_path)` → Array ## Configuration ### Socket Paths Common Zinit socket locations: - `/var/run/zinit.sock` (default system location) - `/tmp/zinit.sock` (temporary/testing) - `/run/zinit.sock` (alternative system location) ### Environment Variables The client respects standard environment configurations and handles connection failures gracefully. ## Testing The package includes comprehensive tests that work with real Zinit servers: ```bash # Run all tests cargo test # Run only unit tests cargo test --test zinit_client_tests # Run only Rhai integration tests cargo test --test rhai_integration_tests ``` ### Test Requirements **IMPORTANT**: For full test coverage, you must start a Zinit server before running tests: ```bash # Start Zinit for testing (recommended for development) zinit -s /tmp/zinit.sock init # Alternative: Start with system socket (requires sudo) sudo zinit --socket /var/run/zinit.sock init # Or use systemd (if available) sudo systemctl start zinit ``` **Without a running Zinit server:** - Tests will gracefully skip when no socket is available - You'll see messages like "⚠ No Zinit socket found. Tests will be skipped." - This is expected behavior and not a test failure **With a running Zinit server:** - Tests will connect to the server and perform real operations - Service creation, management, and deletion will be tested - Log retrieval and signal handling will be validated ## Examples ### Service Lifecycle Management ```rust use sal_zinit_client::*; async fn manage_web_server() -> Result<(), Box> { let socket = "/var/run/zinit.sock"; let service = "web-server"; // Create web server service create_service(socket, service, "python3 -m http.server 8080", false).await?; // Start monitoring and run monitor(socket, service).await?; start(socket, service).await?; // Check if running let status = status(socket, service).await?; println!("Web server PID: {}", status.pid); // Graceful shutdown stop(socket, service).await?; forget(socket, service).await?; delete_service(socket, service).await?; Ok(()) } ``` ### Log Monitoring ```rust use sal_zinit_client::logs; async fn monitor_logs() -> Result<(), Box> { let socket = "/var/run/zinit.sock"; // Get all logs let all_logs = logs(socket, None).await?; println!("Total log entries: {}", all_logs.len()); // Get filtered logs let error_logs = logs(socket, Some("error".to_string())).await?; println!("Error log entries: {}", error_logs.len()); Ok(()) } ``` ## Error Handling The client provides comprehensive error handling: ```rust use sal_zinit_client::{list, ZinitError}; async fn handle_errors() { let socket = "/invalid/path/zinit.sock"; match list(socket).await { Ok(services) => println!("Services: {:?}", services), Err(e) => { eprintln!("Zinit error: {}", e); // Handle specific error types } } } ``` ## Integration with SAL This package is part of the SAL (System Abstraction Layer) ecosystem: ```rust use sal::zinit_client; // Access through SAL let services = sal::zinit_client::list("/var/run/zinit.sock").await?; ``` ## Contributing This package follows SAL's strict quality standards: - Real functionality only (no placeholders or stubs) - Comprehensive test coverage with actual behavior validation - Production-ready error handling and logging - Security considerations for credential handling ## License Apache-2.0