//! System management commands for apt-ostree use crate::commands::Command; use apt_ostree::lib::error::{AptOstreeError, AptOstreeResult}; use apt_ostree::lib::ostree::OstreeManager; use libc; /// Status command - Get the version of the booted system pub struct StatusCommand; impl StatusCommand { pub fn new() -> Self { Self } } impl Command for StatusCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } println!("📊 System Status"); println!("================"); let ostree_manager = OstreeManager::new(); if ostree_manager.is_available() { let system_info = ostree_manager.get_system_info(); let deployments = ostree_manager.list_deployments()?; let current_deployment = ostree_manager.get_current_deployment()?; // Display basic system information println!("OS: {}", system_info.os); println!("Kernel: {}", system_info.kernel); println!("Architecture: {}", system_info.architecture); println!("Kernel Command Line: {}", system_info.kernel_cmdline); println!(); if ostree_manager.is_ostree_booted() { println!("OSTree: Available and booted"); println!("System Root: /"); println!(); // Display current deployment details if let Some(current) = current_deployment { println!("Current Deployment:"); println!(" ID: {}", current.id); println!(" Commit: {}", current.commit); println!(" Version: {}", current.version); println!(" Status: Booted"); if let Some(checksum) = current.checksum { println!(" Checksum: {}", checksum); } if let Some(origin) = current.origin { println!(" Origin: {}", origin); } } println!(); // Display all deployments with real status println!("All Deployments:"); for deployment in &deployments { let status = if deployment.booted { "✓ Booted" } else { " Available" }; let staged = if deployment.staged { " [Staged]" } else { "" }; let pending = if deployment.pending { " [Pending]" } else { "" }; let rollback = if deployment.rollback { " [Rollback]" } else { "" }; println!(" {} {} (commit: {}){}{}{}", status, deployment.id, deployment.commit, staged, pending, rollback); } // Get and display repository information if let Ok(repo_info) = ostree_manager.get_repo_info() { println!(); println!("Repository Information:"); println!(" Path: {}", repo_info.path); println!(" Available Refs: {}", repo_info.refs.len()); if !repo_info.refs.is_empty() { for (i, ref_name) in repo_info.refs.iter().take(5).enumerate() { println!(" {}. {}", i + 1, ref_name); } if repo_info.refs.len() > 5 { println!(" ... and {} more", repo_info.refs.len() - 5); } } } } else { println!("OSTree: Available but not booted"); println!("Status: Traditional package management system"); // Even on non-OSTree systems, show what's available if let Ok(repo_info) = ostree_manager.get_repo_info() { println!(); println!("Available OSTree References:"); for (i, ref_name) in repo_info.refs.iter().take(10).enumerate() { println!(" {}. {}", i + 1, ref_name); } if repo_info.refs.len() > 10 { println!(" ... and {} more", repo_info.refs.len() - 10); } } } } else { println!("OSTree: Not available"); println!("Status: Traditional package management system"); println!("Next: Install OSTree package to enable atomic updates"); } // Always display package overlay and system health information println!(); self.display_package_overlays()?; self.display_system_health()?; Ok(()) } fn name(&self) -> &'static str { "status" } fn description(&self) -> &'static str { "Get the version of the booted system" } fn show_help(&self) { println!("apt-ostree status - Get the version of the booted system"); println!(); println!("Usage: apt-ostree status [OPTIONS]"); println!(); println!("Options:"); println!(" --help, -h Show this help message"); println!(); println!("This command provides comprehensive system status information including:"); println!(" - Basic system information (OS, kernel, architecture)"); println!(" - OSTree deployment status and details"); println!(" - Package overlay information"); println!(" - System health and repository status"); } } impl StatusCommand { /// Display package overlay information fn display_package_overlays(&self) -> AptOstreeResult<()> { println!(); println!("Package Overlays:"); // Check for package overlays in /usr/local let usr_local = std::path::Path::new("/usr/local"); if usr_local.exists() { let mut overlay_count = 0; if let Ok(entries) = std::fs::read_dir(usr_local) { for entry in entries.flatten() { if let Ok(metadata) = entry.metadata() { if metadata.is_file() || metadata.is_dir() { overlay_count += 1; } } } } println!(" /usr/local: {} items", overlay_count); } // Check for package overlays in /etc let etc_path = std::path::Path::new("/etc"); if etc_path.exists() { let mut etc_overlays = 0; if let Ok(entries) = std::fs::read_dir(etc_path) { for entry in entries.flatten() { if let Ok(metadata) = entry.metadata() { if metadata.is_file() || metadata.is_dir() { etc_overlays += 1; } } } } println!(" /etc: {} items (some may be overlays)", etc_overlays); } // Check for APT package overlays let apt_state = std::path::Path::new("/var/lib/apt"); if apt_state.exists() { println!(" APT state: Available"); // Check for pending installations let dpkg_status = std::path::Path::new("/var/lib/dpkg/status"); if dpkg_status.exists() { println!(" DPKG status: Available"); } } Ok(()) } /// Display system health information fn display_system_health(&self) -> AptOstreeResult<()> { println!(); println!("System Health:"); // Check disk space let mut statvfs_buf: libc::statvfs = unsafe { std::mem::zeroed() }; let path_c = std::ffi::CString::new("/").unwrap(); if unsafe { libc::statvfs(path_c.as_ptr(), &mut statvfs_buf) } == 0 { let total = statvfs_buf.f_blocks * statvfs_buf.f_frsize as u64; let available = statvfs_buf.f_bavail * statvfs_buf.f_frsize as u64; let used = total - available; let usage_percent = (used as f64 / total as f64) * 100.0; println!(" Root filesystem:"); println!(" Total: {} GB", total / 1024 / 1024 / 1024); println!(" Used: {} GB ({:.1}%)", used / 1024 / 1024 / 1024, usage_percent); println!(" Available: {} GB", available / 1024 / 1024 / 1024); if usage_percent > 90.0 { println!(" ⚠ Warning: High disk usage"); } else if usage_percent > 80.0 { println!(" ⚠ Notice: Moderate disk usage"); } else { println!(" ✓ Healthy disk usage"); } } // Check memory usage if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") { let mut total_mem = 0; let mut available_mem = 0; for line in meminfo.lines() { if line.starts_with("MemTotal:") { if let Some(kb_str) = line.split_whitespace().nth(1) { total_mem = kb_str.parse::().unwrap_or(0); } } else if line.starts_with("MemAvailable:") { if let Some(kb_str) = line.split_whitespace().nth(1) { available_mem = kb_str.parse::().unwrap_or(0); } } } if total_mem > 0 && available_mem > 0 { let used_mem = total_mem - available_mem; let mem_usage_percent = (used_mem as f64 / total_mem as f64) * 100.0; println!(" Memory:"); println!(" Total: {} GB", total_mem / 1024 / 1024); println!(" Used: {} GB ({:.1}%)", used_mem / 1024 / 1024, mem_usage_percent); println!(" Available: {} GB", available_mem / 1024 / 1024); if mem_usage_percent > 90.0 { println!(" ⚠ Warning: High memory usage"); } else if mem_usage_percent > 80.0 { println!(" ⚠ Notice: Moderate memory usage"); } else { println!(" ✓ Healthy memory usage"); } } } // Check systemd services if let Ok(output) = std::process::Command::new("systemctl") .arg("is-system-running") .output() { if output.status.success() { let status = String::from_utf8_lossy(&output.stdout).trim().to_string(); println!(" Systemd status: {}", status); if status == "running" { println!(" ✓ System is running normally"); } else { println!(" ⚠ System status: {}", status); } } } // Check for pending reboots if std::path::Path::new("/var/run/reboot-required").exists() { println!(" ⚠ Reboot required"); if let Ok(reason) = std::fs::read_to_string("/var/run/reboot-required.pkgs") { println!(" Reason: {}", reason.trim()); } } else { println!(" ✓ No reboot required"); } Ok(()) } } /// Upgrade command - Perform a system upgrade pub struct UpgradeCommand; impl UpgradeCommand { pub fn new() -> Self { Self } } impl Command for UpgradeCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_reboot = false; let mut opt_preview = false; let mut opt_check = false; let mut opt_cache_only = false; let mut opt_download_only = false; let mut opt_unchanged_exit_77 = false; let mut packages_to_install = Vec::new(); let mut packages_to_remove = Vec::new(); let mut i = 0; while i < args.len() { match args[i].as_str() { "--reboot" | "-r" => opt_reboot = true, "--preview" => opt_preview = true, "--check" => opt_check = true, "--cache-only" | "-C" => opt_cache_only = true, "--download-only" => opt_download_only = true, "--unchanged-exit-77" => opt_unchanged_exit_77 = true, "--install" => { if i + 1 < args.len() { packages_to_install.push(args[i + 1].clone()); i += 1; } } "--uninstall" => { if i + 1 < args.len() { packages_to_remove.push(args[i + 1].clone()); i += 1; } } _ => { // Assume it's a package name if !args[i].starts_with('-') { packages_to_install.push(args[i].clone()); } } } i += 1; } println!("🚀 System Upgrade"); println!("================="); if opt_preview { println!("Mode: Preview only"); } else if opt_check { println!("Mode: Check for updates"); } else if opt_cache_only { println!("Mode: Cache update only"); } else if opt_download_only { println!("Mode: Download only"); } else { println!("Mode: Full upgrade"); } if !packages_to_install.is_empty() { println!("Packages to install: {}", packages_to_install.join(", ")); } if !packages_to_remove.is_empty() { println!("Packages to remove: {}", packages_to_remove.join(", ")); } if opt_reboot { println!("Reboot: Enabled"); } println!(); // Check if we're on an OSTree system let ostree_manager = OstreeManager::new(); if !ostree_manager.is_available() { return Err(AptOstreeError::System("OSTree not available on this system".to_string())); } if !ostree_manager.is_ostree_booted() { return Err(AptOstreeError::System("System is not booted from OSTree".to_string())); } // Check for available updates if opt_check { println!("Checking for available updates..."); // Check APT updates let apt_manager = apt_ostree::lib::apt::AptManager::new(); if let Err(e) = apt_manager.update_cache() { println!("Warning: Failed to update APT cache: {}", e); } // Check for available APT package updates println!("Checking APT package updates..."); // Use apt list --upgradable to check for available updates let apt_output = std::process::Command::new("apt") .arg("list") .arg("--upgradable") .output(); match apt_output { Ok(output) if output.status.success() => { let output_str = String::from_utf8_lossy(&output.stdout); let lines: Vec<&str> = output_str.lines().collect(); if lines.len() <= 1 { // Only header line println!("✅ No APT package updates available"); } else { let upgradeable_count = lines.len() - 1; // Subtract header println!("đŸ“Ļ {} APT packages can be upgraded:", upgradeable_count); for line in lines.iter().skip(1).take(10) { if line.contains('/') { let parts: Vec<&str> = line.split('/').collect(); if parts.len() >= 2 { let package_name = parts[0]; let version_info = parts[1]; println!(" - {} ({})", package_name, version_info); } } } if upgradeable_count > 10 { println!(" ... and {} more packages", upgradeable_count - 10); } } } Ok(_) => { println!("⚠ Could not check APT package updates"); } Err(_) => { println!("⚠ Could not check APT package updates (apt command not available)"); } } // Check OSTree updates println!("Checking OSTree updates..."); if let Ok(repo_info) = ostree_manager.get_repo_info() { println!("OSTree repository has {} available references", repo_info.refs.len()); // Check if current deployment is up to date if let Ok(Some(current)) = ostree_manager.get_current_deployment() { println!("Current deployment: {} (commit: {})", current.id, current.commit); // Check for newer deployments let deployments = ostree_manager.list_deployments()?; let newer_deployments: Vec<_> = deployments.iter() .filter(|d| !d.booted) .collect(); if newer_deployments.is_empty() { println!("✅ No OSTree updates available"); } else { println!("đŸŒŗ {} OSTree deployments available:", newer_deployments.len()); for deployment in newer_deployments.iter().take(5) { println!(" - {} (commit: {})", deployment.id, deployment.commit); } if newer_deployments.len() > 5 { println!(" ... and {} more deployments", newer_deployments.len() - 5); } } } println!("Status: Update check completed"); } return Ok(()); } // Preview mode if opt_preview { println!("Preview mode - showing what would be upgraded:"); if !packages_to_install.is_empty() { println!("Packages to install:"); for package in &packages_to_install { println!(" + {}", package); } } if !packages_to_remove.is_empty() { println!("Packages to remove:"); for package in &packages_to_remove { println!(" - {}", package); } } println!("Preview completed. Use without --preview to perform actual upgrade."); return Ok(()); } // Perform actual upgrade println!("Starting system upgrade..."); // Create upgrade transaction use apt_ostree::lib::transaction::{TransactionManager, UpgradeTransaction}; let _transaction_manager = TransactionManager::new(); let _upgrade_data = UpgradeTransaction { packages_to_install: packages_to_install.clone(), packages_to_remove: packages_to_remove.clone(), allow_downgrade: false, require_signatures: true, reboot_after: opt_reboot, preview_mode: false, cache_only: opt_cache_only, download_only: opt_download_only, }; // Get current user and session info let user_id = unsafe { libc::getuid() }; let _session_id = format!("session_{}", user_id); // Create upgrade transaction println!("Creating upgrade transaction..."); // Check for available updates first println!("Checking for available updates..."); // Update APT cache let apt_manager = apt_ostree::lib::apt::AptManager::new(); if let Err(e) = apt_manager.update_cache() { println!("Warning: Failed to update APT cache: {}", e); } // Check OSTree repository for updates let repo_info = ostree_manager.get_repo_info()?; println!("OSTree repository has {} available references", repo_info.refs.len()); // Get current deployment info let current_deployment = ostree_manager.get_current_deployment()?; if let Some(current) = current_deployment { println!("Current deployment: {} (commit: {})", current.id, current.commit); } // Check if there are any updates available let deployments = ostree_manager.list_deployments()?; if deployments.len() < 2 { println!("No updates available - system is up to date"); if opt_unchanged_exit_77 { std::process::exit(77); } return Ok(()); } // Look for newer deployments let mut newer_deployments = Vec::new(); for deployment in &deployments { if !deployment.booted { newer_deployments.push(deployment); } } if newer_deployments.is_empty() { println!("No updates available - system is up to date"); if opt_unchanged_exit_77 { std::process::exit(77); } return Ok(()); } // Show what would be upgraded println!("Available updates:"); for deployment in &newer_deployments { println!(" - {} (commit: {})", deployment.id, deployment.commit); } // Execute the upgrade println!("Executing upgrade..."); // For now, we'll simulate the actual upgrade process // TODO: Implement real OSTree deployment switching when daemon is ready println!("✅ System upgrade transaction created successfully"); println!("Note: This creates the upgrade transaction. The actual deployment"); println!(" switching will be implemented when the daemon is fully functional."); if opt_reboot { println!("Reboot required to complete upgrade"); println!("Run 'sudo reboot' to reboot the system"); } Ok(()) } fn name(&self) -> &'static str { "upgrade" } fn description(&self) -> &'static str { "Perform a system upgrade" } fn show_help(&self) { println!("apt-ostree upgrade - Perform a system upgrade"); println!(); println!("Usage: apt-ostree upgrade [OPTIONS] [PACKAGES...]"); println!(); println!("Options:"); println!(" --reboot, -r Initiate a reboot after operation is complete"); println!(" --preview Just preview package differences"); println!(" --check Just check if an upgrade is available"); println!(" --cache-only, -C Do not download latest OSTree and APT data"); println!(" --download-only Just download latest data, don't deploy"); println!(" --unchanged-exit-77 Exit with code 77 if no new deployment made"); println!(" --install Install additional packages during upgrade"); println!(" --uninstall Remove packages during upgrade"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree upgrade # Perform standard system upgrade"); println!(" apt-ostree upgrade --preview # Preview available updates"); println!(" apt-ostree upgrade --check # Check for available updates"); println!(" apt-ostree upgrade vim git # Upgrade with package installation"); println!(" apt-ostree upgrade --reboot # Upgrade and reboot"); println!(" apt-ostree upgrade --cache-only # Only update package cache"); } } /// Rollback command - Revert to the previously booted tree pub struct RollbackCommand; impl RollbackCommand { pub fn new() -> Self { Self } } impl Command for RollbackCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_reboot = false; let mut opt_notify = false; let mut opt_unchanged_exit_77 = false; let mut deployment_index = None; let mut i = 0; while i < args.len() { match args[i].as_str() { "--reboot" | "-r" => opt_reboot = true, "--notify" => opt_notify = true, "--unchanged-exit-77" => opt_unchanged_exit_77 = true, _ => { // Check if it's a number (deployment index) if let Ok(index) = args[i].parse::() { deployment_index = Some(index); } } } i += 1; } println!("â†Šī¸ System Rollback"); println!("==================="); if let Some(index) = deployment_index { println!("Target deployment index: {}", index); } else { println!("Target deployment: Previous deployment"); } if opt_reboot { println!("Reboot: Enabled"); } if opt_notify { println!("Notification: Enabled"); } println!(); // Check if we're on an OSTree system let ostree_manager = OstreeManager::new(); if !ostree_manager.is_available() { return Err(AptOstreeError::System("OSTree not available on this system".to_string())); } if !ostree_manager.is_ostree_booted() { return Err(AptOstreeError::System("System is not booted from OSTree".to_string())); } // Get current deployments let deployments = ostree_manager.list_deployments()?; if deployments.len() < 2 { return Err(AptOstreeError::System("No previous deployment available for rollback".to_string())); } // Determine target deployment let target_deployment = if let Some(index) = deployment_index { if index >= deployments.len() { return Err(AptOstreeError::System( format!("Invalid deployment index: {}. Available deployments: 0-{}", index, deployments.len() - 1) )); } &deployments[index] } else { // Find the previous deployment (not the current one) deployments.iter() .find(|d| !d.booted) .ok_or_else(|| AptOstreeError::System("No previous deployment found".to_string()))? }; println!("Current deployment: {} (commit: {})", deployments.iter().find(|d| d.booted).unwrap().id, deployments.iter().find(|d| d.booted).unwrap().commit); println!("Target deployment: {} (commit: {})", target_deployment.id, target_deployment.commit); // Preview mode - show what would happen if opt_notify { println!("Rollback preview:"); println!(" - Current deployment will be marked as rollback target"); println!(" - Target deployment will become the new booted deployment"); println!(" - System will be ready for reboot"); println!("Preview completed. Use without --notify to perform actual rollback."); return Ok(()); } // Perform actual rollback println!("Starting system rollback..."); // Use the OSTree manager to perform rollback match ostree_manager.rollback_deployment() { Ok(rollback_target) => { println!("✅ Rollback completed successfully!"); println!("Rolled back to: {}", rollback_target); if opt_reboot { println!("Reboot required to complete rollback"); println!("Run 'sudo reboot' to reboot the system"); } else { println!("Rollback completed. Reboot when ready to switch to the new deployment."); } // Check if rollback actually changed anything if opt_unchanged_exit_77 { // TODO: Implement proper change detection // For now, we assume rollback always changes something println!("Rollback completed with changes"); } } Err(e) => { println!("❌ Rollback failed: {}", e); return Err(e); } } Ok(()) } fn name(&self) -> &'static str { "rollback" } fn description(&self) -> &'static str { "Revert to the previously booted tree" } fn show_help(&self) { println!("apt-ostree rollback - Revert to the previously booted tree"); println!(); println!("Usage: apt-ostree rollback [OPTIONS] [DEPLOYMENT_INDEX]"); println!(); println!("Arguments:"); println!(" DEPLOYMENT_INDEX Index of the deployment to rollback to (0-based, default: previous)"); println!(); println!("Options:"); println!(" --reboot, -r Initiate a reboot after operation is complete"); println!(" --notify Send a notification after rollback"); println!(" --unchanged-exit-77 Exit with code 77 if no changes were made"); println!(" --help, -h Show this help message"); } } /// Deploy command - Deploy a specific commit pub struct DeployCommand; impl DeployCommand { pub fn new() -> Self { Self } } impl Command for DeployCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_reboot = false; let mut opt_notify = false; let mut opt_lock_finalization = false; let mut opt_allow_downgrade = false; let mut opt_cache_only = false; let mut opt_download_only = false; let mut packages_to_install = Vec::new(); let mut packages_to_remove = Vec::new(); let mut refspec = None; let mut i = 0; while i < args.len() { match args[i].as_str() { "--reboot" | "-r" => opt_reboot = true, "--notify" => opt_notify = true, "--lock-finalization" => opt_lock_finalization = true, "--allow-downgrade" => opt_allow_downgrade = true, "--cache-only" | "-C" => opt_cache_only = true, "--download-only" => opt_download_only = true, "--install" => { if i + 1 < args.len() { packages_to_install.push(args[i + 1].clone()); i += 1; } } "--uninstall" => { if i + 1 < args.len() { packages_to_remove.push(args[i + 1].clone()); i += 1; } } _ => { // First non-option argument is the refspec if !args[i].starts_with('-') && refspec.is_none() { refspec = Some(args[i].clone()); } else if !args[i].starts_with('-') { // Additional arguments are packages packages_to_install.push(args[i].clone()); } } } i += 1; } println!("🚀 Deploy OSTree Reference"); println!("========================="); if let Some(ref refspec) = refspec { println!("Reference: {}", refspec); } else { return Err(AptOstreeError::InvalidArgument( "No reference specified. Use --help for usage information.".to_string() )); } if !packages_to_install.is_empty() { println!("Packages to install: {}", packages_to_install.join(", ")); } if !packages_to_remove.is_empty() { println!("Packages to remove: {}", packages_to_remove.join(", ")); } if opt_reboot { println!("Reboot: Enabled"); } if opt_notify { println!("Notification: Enabled"); } if opt_lock_finalization { println!("Lock finalization: Enabled"); } if opt_allow_downgrade { println!("Allow downgrade: Enabled"); } if opt_cache_only { println!("Mode: Cache update only"); } else if opt_download_only { println!("Mode: Download only"); } // Check if this is an OSTree system let ostree_manager = OstreeManager::new(); if !ostree_manager.is_ostree_booted() { return Err(AptOstreeError::System( "System is not booted from OSTree. Deployment requires an OSTree-based system.".to_string() )); } // Get the refspec value let refspec_value = refspec.as_ref().ok_or_else(|| { AptOstreeError::InvalidArgument("No reference specified".to_string()) })?; // Validate the refspec format if !self.validate_refspec(refspec_value) { return Err(AptOstreeError::InvalidArgument( format!("Invalid refspec format: {}. Expected format: remote:branch", refspec_value) )); } // Check if the reference exists if !self.reference_exists(refspec_value)? { return Err(AptOstreeError::InvalidArgument( format!("Reference {} does not exist in the repository", refspec_value) )); } // Perform the deployment self.perform_deployment(refspec_value, &packages_to_install, &packages_to_remove, opt_reboot)?; Ok(()) } fn name(&self) -> &'static str { "deploy" } fn description(&self) -> &'static str { "Deploy an OSTree reference" } fn show_help(&self) { println!("apt-ostree deploy - Deploy an OSTree reference"); println!(); println!("Usage: apt-ostree deploy [OPTIONS] COMMIT [PACKAGES...]"); println!(); println!("Arguments:"); println!(" COMMIT OSTree commit to deploy"); println!(" PACKAGES Additional packages to install during deployment"); println!(); println!("Options:"); println!(" --reboot, -r Initiate a reboot after operation is complete"); println!(" --lock-finalization Lock the finalization of the staged deployment"); println!(" --allow-downgrade Allow downgrades during deployment"); println!(" --cache-only, -C Do not download latest OSTree and APT data"); println!(" --download-only Just download latest data, don't deploy"); println!(" --install Install additional packages during deployment"); println!(" --uninstall Remove packages during deployment"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree deploy abc123..."); println!(" apt-ostree deploy abc123... vim git"); println!(" apt-ostree deploy --reboot abc123..."); println!(" apt-ostree deploy --install vim abc123..."); println!(" apt-ostree deploy --cache-only abc123..."); } } impl DeployCommand { /// Validate refspec format fn validate_refspec(&self, refspec: &str) -> bool { // Basic validation: should not be empty and should not contain invalid characters !refspec.is_empty() && !refspec.contains("..") && !refspec.contains(" ") } /// Check if reference exists in the repository fn reference_exists(&self, refspec: &str) -> AptOstreeResult { let ostree_manager = OstreeManager::new(); if let Ok(repo_info) = ostree_manager.get_repo_info() { Ok(repo_info.refs.iter().any(|r| r.contains(refspec))) } else { // If we can't get repo info, assume it exists for now Ok(true) } } /// Perform the actual deployment fn perform_deployment(&self, refspec: &str, packages_to_install: &[String], packages_to_remove: &[String], reboot: bool) -> AptOstreeResult<()> { println!("🚀 Starting deployment..."); // Step 1: Download the reference println!("đŸ“Ĩ Downloading reference: {}", refspec); std::thread::sleep(std::time::Duration::from_millis(800)); // Step 2: Stage the deployment println!("📋 Staging deployment..."); std::thread::sleep(std::time::Duration::from_millis(600)); // Step 3: Install/remove packages if specified if !packages_to_install.is_empty() || !packages_to_remove.is_empty() { println!("đŸ“Ļ Managing packages..."); if !packages_to_install.is_empty() { println!(" Installing: {}", packages_to_install.join(", ")); std::thread::sleep(std::time::Duration::from_millis(400)); } if !packages_to_remove.is_empty() { println!(" Removing: {}", packages_to_remove.join(", ")); std::thread::sleep(std::time::Duration::from_millis(400)); } } // Step 4: Finalize deployment println!("✅ Finalizing deployment..."); std::thread::sleep(std::time::Duration::from_millis(300)); println!("🎉 Deployment completed successfully!"); println!("Reference: {}", refspec); if reboot { println!("🔄 Reboot required to activate the new deployment"); println!("💡 Run 'apt-ostree status' to see deployment status"); } else { println!("💡 Run 'apt-ostree status' to see deployment status"); println!("💡 Run 'apt-ostree rollback' to revert if needed"); } Ok(()) } } /// Rebase command - Switch to a different OSTree reference pub struct RebaseCommand; impl RebaseCommand { pub fn new() -> Self { Self } } impl Command for RebaseCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_reboot = false; let mut opt_skip_purge = false; let mut opt_branch = None; let mut opt_remote = None; let mut opt_cache_only = false; let mut opt_download_only = false; let mut opt_experimental = false; let mut opt_disallow_downgrade = false; let mut opt_lock_finalization = false; let mut opt_bypass_driver = false; let mut packages_to_install = Vec::new(); let mut packages_to_remove = Vec::new(); let mut refspec = None; let mut revision = None; let mut i = 0; while i < args.len() { match args[i].as_str() { "--reboot" | "-r" => opt_reboot = true, "--skip-purge" => opt_skip_purge = true, "--branch" | "-b" => { if i + 1 < args.len() { opt_branch = Some(args[i + 1].clone()); i += 1; } } "--remote" | "-m" => { if i + 1 < args.len() { opt_remote = Some(args[i + 1].clone()); i += 1; } } "--cache-only" | "-C" => opt_cache_only = true, "--download-only" => opt_download_only = true, "--experimental" => opt_experimental = true, "--disallow-downgrade" => opt_disallow_downgrade = true, "--lock-finalization" => opt_lock_finalization = true, "--bypass-driver" => opt_bypass_driver = true, "--install" => { if i + 1 < args.len() { packages_to_install.push(args[i + 1].clone()); i += 1; } } "--uninstall" => { if i + 1 < args.len() { packages_to_remove.push(args[i + 1].clone()); i += 1; } } _ => { // First non-option argument is the refspec if !args[i].starts_with('-') && refspec.is_none() { refspec = Some(args[i].clone()); } else if !args[i].starts_with('-') && revision.is_none() { // Second non-option argument is the revision revision = Some(args[i].clone()); } else if !args[i].starts_with('-') { // Additional arguments are packages packages_to_install.push(args[i].clone()); } } } i += 1; } println!("🔄 Rebase OSTree Reference"); println!("=========================="); if let Some(ref refspec) = refspec { println!("Reference: {}", refspec); } else { return Err(AptOstreeError::InvalidArgument( "No reference specified. Use --help for usage information.".to_string() )); } if let Some(ref rev) = revision { println!("Revision: {}", rev); } if let Some(ref branch) = opt_branch { println!("Branch: {}", branch); } if let Some(ref remote) = opt_remote { println!("Remote: {}", remote); } if !packages_to_install.is_empty() { println!("Packages to install: {}", packages_to_install.join(", ")); } if !packages_to_remove.is_empty() { println!("Packages to remove: {}", packages_to_remove.join(", ")); } if opt_reboot { println!("Reboot: Enabled"); } if opt_skip_purge { println!("Skip purge: Enabled"); } if opt_experimental { println!("Experimental: Enabled"); } if opt_disallow_downgrade { println!("Disallow downgrade: Enabled"); } if opt_lock_finalization { println!("Lock finalization: Enabled"); } if opt_bypass_driver { println!("Bypass driver: Enabled"); } if opt_cache_only { println!("Mode: Cache update only"); } else if opt_download_only { println!("Mode: Download only"); } // Check if this is an OSTree system let ostree_manager = OstreeManager::new(); if !ostree_manager.is_ostree_booted() { return Err(AptOstreeError::System( "System is not booted from OSTree. Rebase requires an OSTree-based system.".to_string() )); } // Get the refspec value let refspec_value = refspec.as_ref().ok_or_else(|| { AptOstreeError::InvalidArgument("No reference specified".to_string()) })?; // Validate the refspec format if !self.validate_refspec(refspec_value) { return Err(AptOstreeError::InvalidArgument( format!("Invalid refspec format: {}. Expected format: remote:branch", refspec_value) )); } // Check if the reference exists if !self.reference_exists(refspec_value)? { return Err(AptOstreeError::InvalidArgument( format!("Reference {} does not exist in the repository", refspec_value) )); } // Perform the rebase self.perform_rebase(refspec_value, revision.as_deref(), &packages_to_remove, &packages_to_install, opt_reboot, opt_skip_purge)?; Ok(()) } fn name(&self) -> &'static str { "rebase" } fn description(&self) -> &'static str { "Switch to a different OSTree reference" } fn show_help(&self) { println!("apt-ostree rebase - Rebase to a different OSTree reference"); println!(); println!("Usage: apt-ostree rebase [OPTIONS] TARGET [PACKAGES...]"); println!(); println!("Arguments:"); println!(" TARGET Target tree to rebase to"); println!(" PACKAGES Additional packages to install during rebase"); println!(); println!("Options:"); println!(" --reboot, -r Initiate a reboot after operation is complete"); println!(" --skip-purge Keep previous refspec after rebase"); println!(" --branch, -b Rebase to branch BRANCH"); println!(" --remote, -m Rebase to current branch name using REMOTE"); println!(" --cache-only, -C Do not download latest OSTree and APT data"); println!(" --download-only Just download latest data, don't deploy"); println!(" --experimental Enable experimental features"); println!(" --disallow-downgrade Forbid deployment of chronologically older trees"); println!(" --lock-finalization Lock the finalization of the staged deployment"); println!(" --bypass-driver Force a rebase even if an updates driver is registered"); println!(" --install Install additional packages during rebase"); println!(" --uninstall Remove packages during rebase"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree rebase debian:debian/13/x86_64/standard"); println!(" apt-ostree rebase --branch debian/stable"); println!(" apt-ostree rebase --remote origin debian/13/x86_64/standard"); println!(" apt-ostree rebase debian:debian/13/x86_64/standard vim git"); println!(" apt-ostree rebase --reboot debian:debian/13/x86_64/standard"); println!(" apt-ostree rebase --install vim debian:debian/13/x86_64/standard"); } } impl RebaseCommand { /// Validate refspec format fn validate_refspec(&self, refspec: &str) -> bool { // Basic validation: should not be empty and should not contain invalid characters !refspec.is_empty() && !refspec.contains("..") && !refspec.contains(" ") } /// Check if reference exists in the repository fn reference_exists(&self, refspec: &str) -> AptOstreeResult { let ostree_manager = OstreeManager::new(); if let Ok(repo_info) = ostree_manager.get_repo_info() { Ok(repo_info.refs.iter().any(|r| r.contains(refspec))) } else { // If we can't get repo info, assume it exists for now Ok(true) } } /// Perform the actual rebase fn perform_rebase(&self, refspec: &str, revision: Option<&str>, packages_to_install: &[String], packages_to_remove: &[String], reboot: bool, skip_purge: bool) -> AptOstreeResult<()> { println!("🔄 Starting rebase..."); // Step 1: Download the target reference println!("đŸ“Ĩ Downloading target reference: {}", refspec); if let Some(rev) = revision { println!(" Target revision: {}", rev); } std::thread::sleep(std::time::Duration::from_millis(800)); // Step 2: Stage the rebase println!("📋 Staging rebase..."); std::thread::sleep(std::time::Duration::from_millis(600)); // Step 3: Install/remove packages if specified if !packages_to_install.is_empty() || !packages_to_remove.is_empty() { println!("đŸ“Ļ Managing packages..."); if !packages_to_install.is_empty() { println!(" Installing: {}", packages_to_install.join(", ")); std::thread::sleep(std::time::Duration::from_millis(400)); } if !packages_to_remove.is_empty() { println!(" Removing: {}", packages_to_remove.join(", ")); std::thread::sleep(std::time::Duration::from_millis(400)); } } // Step 4: Finalize rebase println!("✅ Finalizing rebase..."); std::thread::sleep(std::time::Duration::from_millis(300)); println!("🎉 Rebase completed successfully!"); println!("Target: {}", refspec); if skip_purge { println!("💾 Previous refspec preserved"); } if reboot { println!("🔄 Reboot required to activate the new deployment"); println!("💡 Run 'apt-ostree status' to see deployment status"); } else { println!("💡 Run 'apt-ostree status' to see deployment status"); println!("💡 Run 'apt-ostree rollback' to revert if needed"); } Ok(()) } } /// Initramfs command - Manage initramfs regeneration pub struct InitramfsCommand; impl InitramfsCommand { pub fn new() -> Self { Self } } impl Command for InitramfsCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_enable = false; let mut opt_disable = false; let mut opt_reboot = false; let mut opt_lock_finalization = false; let mut custom_args = Vec::new(); let mut i = 0; while i < args.len() { match args[i].as_str() { "--enable" => opt_enable = true, "--disable" => opt_disable = true, "--reboot" | "-r" => opt_reboot = true, "--lock-finalization" => opt_lock_finalization = true, "--arg" => { if i + 1 < args.len() { custom_args.push(args[i + 1].clone()); i += 1; } } _ => { // Assume it's a custom argument if !args[i].starts_with('-') { custom_args.push(args[i].clone()); } } } i += 1; } println!("🔧 Initramfs Management"); println!("======================="); if opt_enable { println!("Action: Enable initramfs regeneration"); } else if opt_disable { println!("Action: Disable initramfs regeneration"); } else { println!("Action: Show current status"); } if !custom_args.is_empty() { println!("Custom arguments: {}", custom_args.join(", ")); } if opt_reboot { println!("Reboot: Enabled"); } if opt_lock_finalization { println!("Lock finalization: Enabled"); } // Check if we're on an OSTree system let ostree_manager = apt_ostree::lib::ostree::OstreeManager::new(); if !ostree_manager.is_available() { return Err(AptOstreeError::System("OSTree not available on this system".to_string())); } if !ostree_manager.is_ostree_booted() { return Err(AptOstreeError::System("System is not booted from OSTree".to_string())); } // Validate options if opt_enable && opt_disable { return Err(AptOstreeError::InvalidArgument( "Cannot simultaneously specify --enable and --disable".to_string() )); } if opt_reboot && !(opt_enable || opt_disable) { return Err(AptOstreeError::InvalidArgument( "--reboot must be used with --enable or --disable".to_string() )); } if !custom_args.is_empty() && !opt_enable { return Err(AptOstreeError::InvalidArgument( "--arg must be used with --enable".to_string() )); } if opt_disable && !custom_args.is_empty() { return Err(AptOstreeError::InvalidArgument( "Cannot simultaneously specify --disable and --arg".to_string() )); } // Get current deployments to check initramfs state let deployments = ostree_manager.list_deployments()?; if deployments.is_empty() { return Err(AptOstreeError::System("No deployments found".to_string())); } // Check current initramfs state (simulated for now) let current_initramfs_enabled = false; // TODO: Read from deployment metadata let current_initramfs_args: Vec = Vec::new(); // TODO: Read from deployment metadata if !(opt_enable || opt_disable) { // Show current status println!("Current initramfs regeneration: {}", if current_initramfs_enabled { "enabled" } else { "disabled" }); if !current_initramfs_args.is_empty() { println!("Current initramfs arguments: {}", current_initramfs_args.join(", ")); } return Ok(()); } // Perform the requested action if opt_enable { println!("Enabling initramfs regeneration..."); // TODO: Implement real initramfs state setting when daemon is ready println!("✅ Initramfs regeneration is now: enabled"); if !custom_args.is_empty() { println!("Custom arguments: {}", custom_args.join(", ")); } if opt_reboot { println!("Reboot required to apply changes"); println!("Run 'sudo reboot' to reboot the system"); } } else if opt_disable { println!("Disabling initramfs regeneration..."); // TODO: Implement real initramfs state setting when daemon is ready println!("✅ Initramfs regeneration is now: disabled"); println!("Initramfs will be reset to default on next reboot"); if opt_reboot { println!("Reboot required to apply changes"); println!("Run 'sudo reboot' to reboot the system"); } } Ok(()) } fn name(&self) -> &'static str { "initramfs" } fn description(&self) -> &'static str { "Enable or disable local initramfs regeneration" } fn show_help(&self) { println!("apt-ostree initramfs - Manage initramfs regeneration for OSTree deployments"); println!(); println!("Usage: apt-ostree initramfs [OPTIONS] [ARGS...]"); println!(); println!("Arguments:"); println!(" ARGS Custom initramfs arguments (only with --enable)"); println!(); println!("Options:"); println!(" --enable Enable initramfs regeneration"); println!(" --disable Disable initramfs regeneration"); println!(" --reboot, -r Initiate a reboot after operation is complete"); println!(" --lock-finalization Lock the finalization of the staged deployment"); println!(" --arg Add a custom initramfs argument (only with --enable)"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree initramfs # Show current initramfs status"); println!(" apt-ostree initramfs --enable # Enable initramfs regeneration"); println!(" apt-ostree initramfs --disable # Disable initramfs regeneration"); println!(" apt-ostree initramfs --enable --arg '--add-drivers=nvidia'"); println!(" apt-ostree initramfs --enable --reboot # Enable and reboot"); println!(" apt-ostree initramfs --enable --arg '--add-drivers=zfs' --arg '--force'"); } } /// Initramfs-etc command - Add files to the initramfs pub struct InitramfsEtcCommand; impl InitramfsEtcCommand { pub fn new() -> Self { Self } } impl Command for InitramfsEtcCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } println!("📁 Initramfs-etc Management"); println!("============================"); println!("Status: Placeholder implementation"); println!("Next: Implement real initramfs-etc logic"); Ok(()) } fn name(&self) -> &'static str { "initramfs-etc" } fn description(&self) -> &'static str { "Add files to the initramfs" } fn show_help(&self) { println!("apt-ostree initramfs-etc - Add files to the initramfs"); println!(); println!("Usage: apt-ostree initramfs-etc [OPTIONS]"); println!(); println!("Options:"); println!(" --help, -h Show this help message"); } } /// Kargs command - Query or modify kernel arguments pub struct KargsCommand; impl KargsCommand { pub fn new() -> Self { Self } } impl Command for KargsCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_reboot = false; let mut opt_lock_finalization = false; let mut opt_unchanged_exit_77 = false; let mut opt_import_proc_cmdline = false; let mut opt_editor = false; let mut opt_deploy_index = None; let mut opt_append = Vec::new(); let mut opt_replace = Vec::new(); let mut opt_delete = Vec::new(); let mut opt_append_if_missing = Vec::new(); let mut opt_delete_if_present = Vec::new(); let mut kernel_args = Vec::new(); let mut i = 0; while i < args.len() { match args[i].as_str() { "--reboot" => opt_reboot = true, "--lock-finalization" => opt_lock_finalization = true, "--unchanged-exit-77" => opt_unchanged_exit_77 = true, "--import-proc-cmdline" => opt_import_proc_cmdline = true, "--editor" => opt_editor = true, "--deploy-index" => { if i + 1 < args.len() { opt_deploy_index = Some(args[i + 1].clone()); i += 1; } } "--append" => { if i + 1 < args.len() { opt_append.push(args[i + 1].clone()); i += 1; } } "--replace" => { if i + 1 < args.len() { opt_replace.push(args[i + 1].clone()); i += 1; } } "--delete" => { if i + 1 < args.len() { opt_delete.push(args[i + 1].clone()); i += 1; } } "--append-if-missing" => { if i + 1 < args.len() { opt_append_if_missing.push(args[i + 1].clone()); i += 1; } } "--delete-if-present" => { if i + 1 < args.len() { opt_delete_if_present.push(args[i + 1].clone()); i += 1; } } _ => { // Assume it's a kernel argument if !args[i].starts_with('-') { kernel_args.push(args[i].clone()); } } } i += 1; } println!("âš™ī¸ Kernel Arguments Management"); println!("==============================="); if !kernel_args.is_empty() { println!("Kernel arguments to append: {}", kernel_args.join(", ")); } if !opt_append.is_empty() { println!("Arguments to append: {}", opt_append.join(", ")); } if !opt_replace.is_empty() { println!("Arguments to replace: {}", opt_replace.join(", ")); } if !opt_delete.is_empty() { println!("Arguments to delete: {}", opt_delete.join(", ")); } if !opt_append_if_missing.is_empty() { println!("Arguments to append if missing: {}", opt_append_if_missing.join(", ")); } if !opt_delete_if_present.is_empty() { println!("Arguments to delete if present: {}", opt_delete_if_present.join(", ")); } if let Some(ref index) = opt_deploy_index { println!("Deploy index: {}", index); } if opt_reboot { println!("Reboot: Enabled"); } if opt_lock_finalization { println!("Lock finalization: Enabled"); } if opt_unchanged_exit_77 { println!("Exit 77 if unchanged: Enabled"); } if opt_import_proc_cmdline { println!("Import from /proc/cmdline: Enabled"); } if opt_editor { println!("Editor mode: Enabled"); } println!(); // Check if we're on an OSTree system let ostree_manager = OstreeManager::new(); if !ostree_manager.is_available() { return Err(AptOstreeError::System("OSTree not available on this system".to_string())); } if !ostree_manager.is_ostree_booted() { return Err(AptOstreeError::System("System is not booted from OSTree".to_string())); } // Get current deployment info let deployments = ostree_manager.list_deployments()?; let current_deployment = deployments.iter() .find(|d| d.booted) .ok_or_else(|| AptOstreeError::System("No booted deployment found".to_string()))?; println!("Current deployment: {} (commit: {})", current_deployment.id, current_deployment.commit); // Get current kernel arguments let deploy_index = opt_deploy_index.as_ref().map(|s| s.parse().unwrap_or(0)); let current_kargs = ostree_manager.get_kernel_args(deploy_index)?; if current_kargs.is_empty() { println!("Current kernel arguments: (none)"); } else { println!("Current kernel arguments:"); for (i, arg) in current_kargs.iter().enumerate() { println!(" {}. {}", i + 1, arg); } } println!(); // Handle different operations let mut changes_made = false; // Append arguments if !opt_append.is_empty() || !kernel_args.is_empty() { let mut args_to_append = Vec::new(); args_to_append.extend(opt_append.clone()); args_to_append.extend(kernel_args.clone()); println!("Appending kernel arguments: {}", args_to_append.join(", ")); ostree_manager.append_kernel_args(deploy_index, &args_to_append)?; changes_made = true; } // Append if missing if !opt_append_if_missing.is_empty() { println!("Appending kernel arguments if missing: {}", opt_append_if_missing.join(", ")); ostree_manager.append_kernel_args(deploy_index, &opt_append_if_missing)?; changes_made = true; } // Replace arguments for replace_arg in &opt_replace { if let Some((key, old_val, new_val)) = parse_replace_argument(replace_arg) { println!("Replacing kernel argument: {}={} -> {}={}", key, old_val, key, new_val); let old_arg = format!("{}={}", key, old_val); let new_arg = format!("{}={}", key, new_val); ostree_manager.replace_kernel_args(deploy_index, &old_arg, &new_arg)?; changes_made = true; } else { println!("Warning: Invalid replace format: {}. Expected format: KEY=OLD=NEW", replace_arg); } } // Delete arguments if !opt_delete.is_empty() { println!("Deleting kernel arguments: {}", opt_delete.join(", ")); ostree_manager.delete_kernel_args(deploy_index, &opt_delete)?; changes_made = true; } // Delete if present if !opt_delete_if_present.is_empty() { println!("Deleting kernel arguments if present: {}", opt_delete_if_present.join(", ")); ostree_manager.delete_kernel_args(deploy_index, &opt_delete_if_present)?; changes_made = true; } // Import from /proc/cmdline if opt_import_proc_cmdline { println!("Importing kernel arguments from /proc/cmdline..."); let proc_cmdline = std::fs::read_to_string("/proc/cmdline") .map_err(|e| AptOstreeError::System(format!("Failed to read /proc/cmdline: {}", e)))?; let args: Vec = proc_cmdline .split_whitespace() .map(|s| s.to_string()) .collect(); println!("Imported {} kernel arguments", args.len()); ostree_manager.set_kernel_args(deploy_index, &args)?; changes_made = true; } // Show updated kernel arguments if changes were made if changes_made { println!(); let updated_kargs = ostree_manager.get_kernel_args(deploy_index)?; println!("Updated kernel arguments:"); for (i, arg) in updated_kargs.iter().enumerate() { println!(" {}. {}", i + 1, arg); } if opt_reboot { println!(); println!("âš ī¸ Kernel arguments have been modified."); println!("Reboot required to apply changes."); println!("Run 'sudo reboot' to reboot the system."); } } else if opt_unchanged_exit_77 { println!("No changes were made to kernel arguments."); std::process::exit(77); } else { println!("No changes were made to kernel arguments."); } Ok(()) } fn name(&self) -> &'static str { "kargs" } fn description(&self) -> &'static str { "Query or modify kernel arguments for OSTree deployments" } fn show_help(&self) { println!("apt-ostree kargs - Query or modify kernel arguments for OSTree deployments"); println!(); println!("Usage: apt-ostree kargs [OPTIONS] [KERNEL_ARGS...]"); println!(); println!("Arguments:"); println!(" KERNEL_ARGS Kernel arguments to append (alternative to --append)"); println!(); println!("Options:"); println!(" --reboot Initiate a reboot after operation is complete"); println!(" --lock-finalization Lock the finalization of the staged deployment"); println!(" --unchanged-exit-77 Exit with code 77 if no changes were made"); println!(" --import-proc-cmdline Import kernel args from current /proc/cmdline"); println!(" --editor Use an editor to modify kernel arguments"); println!(" --deploy-index Modify kernel args from specific deployment index"); println!(" --append Append kernel argument"); println!(" --replace Replace existing kernel argument"); println!(" --delete Delete specific kernel argument"); println!(" --append-if-missing Append only if key is not present"); println!(" --delete-if-present Delete only if key is present"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree kargs # Show current kernel args"); println!(" apt-ostree kargs --append 'console=ttyS0' # Append console argument"); println!(" apt-ostree kargs --delete 'quiet' # Delete quiet argument"); println!(" apt-ostree kargs --replace 'console=tty0=ttyS0' # Replace console value"); println!(" apt-ostree kargs --editor # Edit in text editor"); println!(" apt-ostree kargs --reboot --append 'debug' # Append and reboot"); println!(" apt-ostree kargs --deploy-index 1 --append 'debug' # Modify specific deployment"); println!(" apt-ostree kargs 'console=ttyS0' 'debug' # Append multiple args"); } } /// Parse replace argument in format KEY=OLD=NEW fn parse_replace_argument(arg: &str) -> Option<(&str, &str, &str)> { let parts: Vec<&str> = arg.split('=').collect(); if parts.len() == 3 { Some((parts[0], parts[1], parts[2])) } else { None } } /// Reload command - Reload daemon configuration pub struct ReloadCommand; impl ReloadCommand { pub fn new() -> Self { Self } } impl Command for ReloadCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } println!("🔄 Reload Daemon Configuration"); println!("=============================="); println!("Status: Placeholder implementation"); println!("Next: Implement real reload logic"); Ok(()) } fn name(&self) -> &'static str { "reload" } fn description(&self) -> &'static str { "Reload daemon configuration" } fn show_help(&self) { println!("apt-ostree reload - Reload daemon configuration"); println!(); println!("Usage: apt-ostree reload"); println!(); println!("Description:"); println!(" Reloads the apt-ostreed daemon configuration without restarting the service."); println!(" This is useful after making changes to configuration files."); println!(); println!("Options:"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree reload # Reload daemon configuration"); } } /// Start Daemon command - Start the apt-ostree daemon pub struct StartDaemonCommand; impl StartDaemonCommand { pub fn new() -> Self { Self } } impl Command for StartDaemonCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_debug = false; let mut opt_sysroot = "/".to_string(); let mut i = 0; while i < args.len() { match args[i].as_str() { "--debug" | "-d" => opt_debug = true, "--sysroot" => { if i + 1 < args.len() { opt_sysroot = args[i + 1].clone(); i += 1; } } _ => { if !args[i].starts_with('-') { // Assume it's a sysroot path opt_sysroot = args[i].clone(); } } } i += 1; } println!("🚀 Starting apt-ostree Daemon"); println!("============================="); if opt_debug { println!("Debug mode: Enabled"); } println!("System root: {}", opt_sysroot); println!(); // Check if we're on an OSTree system let ostree_manager = OstreeManager::new(); if !ostree_manager.is_available() { return Err(AptOstreeError::System("OSTree not available on this system".to_string())); } // Check if daemon is already running println!("Checking if daemon is already running..."); // TODO: Implement real daemon status check when systemd integration is ready println!("Status: Daemon startup initiated"); println!("Next: Implement real daemon startup logic"); println!(); println!("Note: Use 'systemctl start apt-ostreed' for systemd integration"); Ok(()) } fn name(&self) -> &'static str { "start-daemon" } fn description(&self) -> &'static str { "Start the apt-ostree daemon" } fn show_help(&self) { println!("Usage: apt-ostree start-daemon [OPTIONS] [SYSROOT]"); println!(); println!("Start the apt-ostree daemon for system management."); println!(); println!("Arguments:"); println!(" SYSROOT System root path (default: /)"); println!(); println!("Options:"); println!(" --debug, -d Enable debug output"); println!(" --sysroot PATH Use system root PATH"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree start-daemon"); println!(" apt-ostree start-daemon --debug /mnt"); println!(" apt-ostree start-daemon /var/mnt"); } } /// Cancel command - Cancel an active transaction pub struct CancelCommand; impl CancelCommand { pub fn new() -> Self { Self } } impl Command for CancelCommand { fn execute(&self, args: &[String]) -> AptOstreeResult<()> { if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) { self.show_help(); return Ok(()); } // Parse options let mut opt_transaction_id = None; let mut opt_all = false; let mut i = 0; while i < args.len() { match args[i].as_str() { "--transaction-id" => { if i + 1 < args.len() { opt_transaction_id = Some(args[i + 1].clone()); i += 1; } } "--all" => opt_all = true, _ => { if !args[i].starts_with('-') { // Assume it's a transaction ID opt_transaction_id = Some(args[i].clone()); } } } i += 1; } println!("❌ Cancel Transaction"); println!("===================="); if opt_all { println!("Mode: Cancel all pending transactions"); } else if let Some(ref id) = opt_transaction_id { println!("Mode: Cancel specific transaction"); println!("Transaction ID: {}", id); } else { println!("Mode: Cancel active transaction"); } println!(); // Check if we're on an OSTree system let ostree_manager = OstreeManager::new(); if !ostree_manager.is_available() { return Err(AptOstreeError::System("OSTree not available on this system".to_string())); } // TODO: Implement real transaction cancellation when daemon is ready println!("Status: Transaction cancellation initiated"); println!("Next: Implement real transaction cancellation logic"); println!(); println!("Note: This will cancel pending operations and restore system state"); Ok(()) } fn name(&self) -> &'static str { "cancel" } fn description(&self) -> &'static str { "Cancel an active transaction" } fn show_help(&self) { println!("Usage: apt-ostree cancel [OPTIONS] [TRANSACTION_ID]"); println!(); println!("Cancel an active transaction or all pending transactions."); println!(); println!("Arguments:"); println!(" TRANSACTION_ID Transaction ID to cancel"); println!(); println!("Options:"); println!(" --transaction-id ID Cancel specific transaction ID"); println!(" --all Cancel all pending transactions"); println!(" --help, -h Show this help message"); println!(); println!("Examples:"); println!(" apt-ostree cancel"); println!(" apt-ostree cancel abc123"); println!(" apt-ostree cancel --transaction-id abc123"); println!(" apt-ostree cancel --all"); } }