diff --git a/service_manager/examples/README.md b/service_manager/examples/README.md new file mode 100644 index 0000000..7c755fa --- /dev/null +++ b/service_manager/examples/README.md @@ -0,0 +1,47 @@ +# Service Manager Examples + +This directory contains examples demonstrating the usage of the `sal-service-manager` crate. + +## Running Examples + +To run any example, use the following command structure from the `service_manager` crate's root directory: + +```sh +cargo run --example +``` + +--- + +### 1. `simple_service` + +This example demonstrates the ideal, clean lifecycle of a service using the separated `create` and `start` steps. + +**Behavior:** +1. Creates a new service definition. +2. Starts the newly created service. +3. Checks its status to confirm it's running. +4. Stops the service. +5. Checks its status again to confirm it's stopped. +6. Removes the service definition. + +**Run it:** +```sh +cargo run --example simple_service +``` + +### 2. `service_spaghetti` + +This example demonstrates how the service manager handles "messy" or improper sequences of operations, showcasing its error handling and robustness. + +**Behavior:** +1. Creates a service. +2. Starts the service. +3. Tries to start the **same service again** (which should fail as it's already running). +4. Removes the service **without stopping it first** (the manager should handle this gracefully). +5. Tries to stop the **already removed** service (which should fail). +6. Tries to remove the service **again** (which should also fail). + +**Run it:** +```sh +cargo run --example service_spaghetti +``` diff --git a/service_manager/examples/service_spaghetti.rs b/service_manager/examples/service_spaghetti.rs new file mode 100644 index 0000000..c5d3c7e --- /dev/null +++ b/service_manager/examples/service_spaghetti.rs @@ -0,0 +1,95 @@ +//! service_spaghetti - An example of messy service management. +//! +//! This example demonstrates how the service manager behaves when commands +//! are issued in a less-than-ideal order, such as starting a service that's +//! already running or removing a service that hasn't been stopped. + +use sal_service_manager::{create_service_manager, ServiceConfig}; +use std::collections::HashMap; +use std::thread; +use std::time::Duration; + +fn main() { + let manager = create_service_manager(None).expect("Failed to create service manager"); + let service_name = "com.herocode.examples.spaghetti"; + + let service_config = ServiceConfig { + name: service_name.to_string(), + binary_path: "/bin/sh".to_string(), + args: vec![ + "-c".to_string(), + "while true; do echo 'Spaghetti service is running...'; sleep 5; done".to_string(), + ], + working_directory: None, + environment: HashMap::new(), + auto_restart: false, + }; + + println!("--- Service Spaghetti Example ---"); + println!("This example demonstrates messy, error-prone service management."); + + // Cleanup from previous runs to ensure a clean slate + if let Ok(true) = manager.exists(service_name) { + println!("\nService '{}' found from a previous run. Cleaning up first.", service_name); + let _ = manager.stop(service_name); + let _ = manager.remove(service_name); + println!("Cleanup complete."); + } + + // 1. Create the service + println!("\n1. Creating the service..."); + match manager.create(&service_config) { + Ok(()) => println!(" -> Success: Service '{}' created.", service_name), + Err(e) => { + eprintln!(" -> Error: Failed to create service: {}. Halting example.", e); + return; + } + } + + // 2. Start the service + println!("\n2. Starting the service for the first time..."); + match manager.start(service_name) { + Ok(()) => println!(" -> Success: Service '{}' started.", service_name), + Err(e) => { + eprintln!(" -> Error: Failed to start service: {}. Halting example.", e); + return; + } + } + + thread::sleep(Duration::from_secs(2)); + + // 3. Try to start the service again while it's already running + println!("\n3. Trying to start the *same service* again..."); + match manager.start(service_name) { + Ok(()) => println!(" -> Unexpected Success: Service started again."), + Err(e) => eprintln!(" -> Expected Error: {}. The manager should detect it is already running.", e), + } + + // 3. Let it run for a bit + println!("\n3. Letting the service run for 5 seconds..."); + thread::sleep(Duration::from_secs(5)); + + // 4. Remove the service without stopping it first + // The `remove` function is designed to stop the service if it's running. + println!("\n4. Removing the service without explicitly stopping it first..."); + match manager.remove(service_name) { + Ok(()) => println!(" -> Success: Service was stopped and removed."), + Err(e) => eprintln!(" -> Error: Failed to remove service: {}", e), + } + + // 5. Try to stop the service after it has been removed + println!("\n5. Trying to stop the service that was just removed..."); + match manager.stop(service_name) { + Ok(()) => println!(" -> Unexpected Success: Stopped a removed service."), + Err(e) => eprintln!(" -> Expected Error: {}. The manager knows the service is gone.", e), + } + + // 6. Try to remove the service again + println!("\n6. Trying to remove the service again..."); + match manager.remove(service_name) { + Ok(()) => println!(" -> Unexpected Success: Removed a non-existent service."), + Err(e) => eprintln!(" -> Expected Error: {}. The manager correctly reports it's not found.", e), + } + + println!("\n--- Spaghetti Example Finished ---"); +} diff --git a/service_manager/examples/simple_service.rs b/service_manager/examples/simple_service.rs new file mode 100644 index 0000000..37a39bf --- /dev/null +++ b/service_manager/examples/simple_service.rs @@ -0,0 +1,105 @@ +use sal_service_manager::{create_service_manager, ServiceConfig}; +use std::collections::HashMap; +use std::thread; +use std::time::Duration; + +fn main() { + // 1. Create a service manager for the current platform + let manager = match create_service_manager(None) { + Ok(manager) => manager, + Err(e) => { + eprintln!("Error: Failed to create service manager: {}", e); + return; + } + }; + + // 2. Define the configuration for our new service + let service_name = "com.herocode.examples.simpleservice"; + let service_config = ServiceConfig { + name: service_name.to_string(), + // A simple command that runs in a loop + binary_path: "/bin/sh".to_string(), + args: vec![ + "-c".to_string(), + "while true; do echo 'Simple service is running...'; date; sleep 5; done".to_string(), + ], + working_directory: None, + environment: HashMap::new(), + auto_restart: false, + }; + + println!("--- Service Manager Example ---"); + + // Cleanup from previous runs, if necessary + if let Ok(true) = manager.exists(service_name) { + println!("Service '{}' already exists. Cleaning up before starting.", service_name); + if let Err(e) = manager.stop(service_name) { + println!("Note: could not stop existing service (it might not be running): {}", e); + } + if let Err(e) = manager.remove(service_name) { + eprintln!("Error: failed to remove existing service: {}", e); + return; + } + println!("Cleanup complete."); + } + + // 3. Create the service + println!("\n1. Creating service: '{}'", service_name); + match manager.create(&service_config) { + Ok(()) => println!("Service '{}' created successfully.", service_name), + Err(e) => { + eprintln!("Error: Failed to create service '{}': {}", service_name, e); + return; + } + } + + // 4. Start the service + println!("\n2. Starting service: '{}'", service_name); + match manager.start(service_name) { + Ok(()) => println!("Service '{}' started successfully.", service_name), + Err(e) => { + eprintln!("Error: Failed to start service '{}': {}", service_name, e); + return; + } + } + + // Give it a moment to run + println!("\nWaiting for 2 seconds for the service to initialize..."); + thread::sleep(Duration::from_secs(2)); + + // 4. Check the status of the service + println!("\n2. Checking service status..."); + match manager.status(service_name) { + Ok(status) => println!("Service status: {:?}", status), + Err(e) => eprintln!("Error: Failed to get status for service '{}': {}", service_name, e), + } + + println!("\nLetting the service run for 10 seconds. Check logs if you can."); + thread::sleep(Duration::from_secs(10)); + + // 5. Stop the service + println!("\n3. Stopping service: '{}'", service_name); + match manager.stop(service_name) { + Ok(()) => println!("Service '{}' stopped successfully.", service_name), + Err(e) => eprintln!("Error: Failed to stop service '{}': {}", service_name, e), + } + + println!("\nWaiting for 2 seconds for the service to stop..."); + thread::sleep(Duration::from_secs(2)); + + // Check status again + println!("\n4. Checking status after stopping..."); + match manager.status(service_name) { + Ok(status) => println!("Service status: {:?}", status), + Err(e) => eprintln!("Error: Failed to get status for service '{}': {}", service_name, e), + } + + // 6. Remove the service + println!("\n5. Removing service: '{}'", service_name); + match manager.remove(service_name) { + Ok(()) => println!("Service '{}' removed successfully.", service_name), + Err(e) => eprintln!("Error: Failed to remove service '{}': {}", service_name, e), + } + + println!("\n--- Example Finished ---"); +}