286 lines
No EOL
9.6 KiB
Rust
286 lines
No EOL
9.6 KiB
Rust
use std::path::Path;
|
|
use std::fs;
|
|
use std::io::Read;
|
|
use anyhow::{Result, Context};
|
|
use tracing::{debug, info, warn};
|
|
use ostree::gio;
|
|
|
|
/// OSTree environment detection module
|
|
///
|
|
/// This module provides functions to detect if apt-ostree is running
|
|
/// in an OSTree environment, following the same patterns as rpm-ostree.
|
|
pub struct OstreeDetection;
|
|
|
|
impl OstreeDetection {
|
|
/// Check if OSTree filesystem is present
|
|
///
|
|
/// This checks for the existence of `/ostree` directory, which indicates
|
|
/// that the OSTree filesystem layout is present.
|
|
///
|
|
/// Used by: Main daemon service (ConditionPathExists=/ostree)
|
|
pub fn is_ostree_filesystem() -> bool {
|
|
Path::new("/ostree").exists()
|
|
}
|
|
|
|
/// Check if system is booted from OSTree
|
|
///
|
|
/// This checks for the existence of `/run/ostree-booted` file, which indicates
|
|
/// that the system is currently booted from an OSTree deployment.
|
|
///
|
|
/// Used by: Boot status and monitoring services (ConditionPathExists=/run/ostree-booted)
|
|
pub fn is_ostree_booted() -> bool {
|
|
Path::new("/run/ostree-booted").exists()
|
|
}
|
|
|
|
/// Check if OSTree kernel parameter is present
|
|
///
|
|
/// This checks for the presence of "ostree" in the kernel command line,
|
|
/// which filters out non-traditional OSTree setups (e.g., live boots).
|
|
///
|
|
/// Used by: Security fix services (ConditionKernelCommandLine=ostree)
|
|
pub fn has_ostree_kernel_param() -> Result<bool> {
|
|
let mut cmdline = String::new();
|
|
fs::File::open("/proc/cmdline")
|
|
.context("Failed to open /proc/cmdline")?
|
|
.read_to_string(&mut cmdline)
|
|
.context("Failed to read kernel command line")?;
|
|
|
|
Ok(cmdline.contains("ostree"))
|
|
}
|
|
|
|
/// Check if OSTree sysroot can be loaded
|
|
///
|
|
/// This attempts to load the OSTree sysroot using the OSTree library,
|
|
/// which validates the OSTree repository structure.
|
|
///
|
|
/// Used by: Application-level detection
|
|
pub fn can_load_ostree_sysroot() -> Result<bool> {
|
|
// Use OSTree Rust bindings to check if sysroot can be loaded
|
|
let sysroot = ostree::Sysroot::new_default();
|
|
|
|
match sysroot.load(None::<&gio::Cancellable>) {
|
|
Ok(_) => {
|
|
debug!("OSTree sysroot loaded successfully");
|
|
Ok(true)
|
|
},
|
|
Err(e) => {
|
|
debug!("Failed to load OSTree sysroot: {}", e);
|
|
Ok(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check if there's a booted deployment
|
|
///
|
|
/// This checks if there's a valid booted deployment in the OSTree sysroot.
|
|
///
|
|
/// Used by: Application-level detection
|
|
pub fn has_booted_deployment() -> Result<bool> {
|
|
let sysroot = ostree::Sysroot::new_default();
|
|
|
|
match sysroot.load(None::<&gio::Cancellable>) {
|
|
Ok(_) => {
|
|
match sysroot.booted_deployment() {
|
|
Some(_) => {
|
|
debug!("Booted deployment found");
|
|
Ok(true)
|
|
},
|
|
None => {
|
|
debug!("No booted deployment found");
|
|
Ok(false)
|
|
}
|
|
}
|
|
},
|
|
Err(e) => {
|
|
debug!("Failed to load OSTree sysroot: {}", e);
|
|
Ok(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check if apt-ostree daemon is available
|
|
///
|
|
/// This checks for the availability of the apt-ostree daemon via D-Bus.
|
|
///
|
|
/// Used by: Daemon-level detection
|
|
pub async fn is_apt_ostree_daemon_available() -> Result<bool> {
|
|
match zbus::Connection::system().await {
|
|
Ok(conn) => {
|
|
match zbus::Proxy::new(
|
|
&conn,
|
|
"org.aptostree.dev",
|
|
"/org/aptostree/dev/Daemon",
|
|
"org.aptostree.dev.Daemon"
|
|
).await {
|
|
Ok(_) => {
|
|
debug!("apt-ostree daemon is available");
|
|
Ok(true)
|
|
},
|
|
Err(e) => {
|
|
debug!("apt-ostree daemon is not available: {}", e);
|
|
Ok(false)
|
|
}
|
|
}
|
|
},
|
|
Err(e) => {
|
|
debug!("Failed to connect to system D-Bus: {}", e);
|
|
Ok(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Comprehensive OSTree environment check
|
|
///
|
|
/// This performs all detection methods and returns a comprehensive
|
|
/// assessment of the OSTree environment.
|
|
pub async fn check_ostree_environment() -> Result<OstreeEnvironmentStatus> {
|
|
let filesystem = Self::is_ostree_filesystem();
|
|
let booted = Self::is_ostree_booted();
|
|
let kernel_param = Self::has_ostree_kernel_param()?;
|
|
let sysroot_loadable = Self::can_load_ostree_sysroot()?;
|
|
let has_deployment = Self::has_booted_deployment()?;
|
|
let daemon_available = Self::is_apt_ostree_daemon_available().await?;
|
|
|
|
let status = OstreeEnvironmentStatus {
|
|
filesystem,
|
|
booted,
|
|
kernel_param,
|
|
sysroot_loadable,
|
|
has_deployment,
|
|
daemon_available,
|
|
};
|
|
|
|
info!("OSTree environment status: {:?}", status);
|
|
Ok(status)
|
|
}
|
|
|
|
/// Check if apt-ostree can operate in the current environment
|
|
///
|
|
/// This determines if apt-ostree can function properly based on
|
|
/// the current environment detection.
|
|
pub async fn can_operate() -> Result<bool> {
|
|
let status = Self::check_ostree_environment().await?;
|
|
|
|
// Basic requirements: OSTree filesystem and booted deployment
|
|
let can_operate = status.filesystem && status.has_deployment;
|
|
|
|
if !can_operate {
|
|
warn!("apt-ostree cannot operate in this environment");
|
|
warn!("Filesystem: {}, Booted deployment: {}",
|
|
status.filesystem, status.has_deployment);
|
|
}
|
|
|
|
Ok(can_operate)
|
|
}
|
|
|
|
/// Validate environment and return user-friendly error if needed
|
|
///
|
|
/// This checks the environment and returns a helpful error message
|
|
/// if apt-ostree cannot operate.
|
|
pub async fn validate_environment() -> Result<()> {
|
|
if !Self::can_operate().await? {
|
|
return Err(anyhow::anyhow!(
|
|
"apt-ostree requires an OSTree environment to operate.\n\
|
|
\n\
|
|
This system does not appear to be running on an OSTree deployment.\n\
|
|
\n\
|
|
To use apt-ostree:\n\
|
|
1. Ensure you are running on an OSTree-based system\n\
|
|
2. Verify that /ostree directory exists\n\
|
|
3. Verify that /run/ostree-booted file exists\n\
|
|
4. Ensure you have a valid booted deployment\n\
|
|
\n\
|
|
For more information, see: https://github.com/your-org/apt-ostree"
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Status of OSTree environment detection
|
|
#[derive(Debug, Clone)]
|
|
pub struct OstreeEnvironmentStatus {
|
|
/// OSTree filesystem is present (/ostree directory exists)
|
|
pub filesystem: bool,
|
|
/// System is booted from OSTree (/run/ostree-booted exists)
|
|
pub booted: bool,
|
|
/// OSTree kernel parameter is present
|
|
pub kernel_param: bool,
|
|
/// OSTree sysroot can be loaded
|
|
pub sysroot_loadable: bool,
|
|
/// There's a valid booted deployment
|
|
pub has_deployment: bool,
|
|
/// apt-ostree daemon is available
|
|
pub daemon_available: bool,
|
|
}
|
|
|
|
impl OstreeEnvironmentStatus {
|
|
/// Check if this is a fully functional OSTree environment
|
|
pub fn is_fully_functional(&self) -> bool {
|
|
self.filesystem &&
|
|
self.booted &&
|
|
self.kernel_param &&
|
|
self.sysroot_loadable &&
|
|
self.has_deployment
|
|
}
|
|
|
|
/// Check if this is a minimal OSTree environment (can operate)
|
|
pub fn is_minimal(&self) -> bool {
|
|
self.filesystem && self.has_deployment
|
|
}
|
|
|
|
/// Get a human-readable description of the environment
|
|
pub fn description(&self) -> String {
|
|
if self.is_fully_functional() {
|
|
"Fully functional OSTree environment".to_string()
|
|
} else if self.is_minimal() {
|
|
"Minimal OSTree environment (can operate)".to_string()
|
|
} else if self.filesystem {
|
|
"Partial OSTree environment (filesystem only)".to_string()
|
|
} else {
|
|
"Non-OSTree environment".to_string()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_ostree_filesystem_detection() {
|
|
// This test will pass if /ostree exists, fail otherwise
|
|
// In a test environment, we can't guarantee the filesystem state
|
|
let _result = OstreeDetection::is_ostree_filesystem();
|
|
}
|
|
|
|
#[test]
|
|
fn test_ostree_booted_detection() {
|
|
// This test will pass if /run/ostree-booted exists, fail otherwise
|
|
let _result = OstreeDetection::is_ostree_booted();
|
|
}
|
|
|
|
#[test]
|
|
fn test_kernel_param_detection() {
|
|
// This test should always work since /proc/cmdline should exist
|
|
let result = OstreeDetection::has_ostree_kernel_param();
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_environment_status() {
|
|
let status = OstreeEnvironmentStatus {
|
|
filesystem: true,
|
|
booted: true,
|
|
kernel_param: true,
|
|
sysroot_loadable: true,
|
|
has_deployment: true,
|
|
daemon_available: true,
|
|
};
|
|
|
|
assert!(status.is_fully_functional());
|
|
assert!(status.is_minimal());
|
|
assert_eq!(status.description(), "Fully functional OSTree environment");
|
|
}
|
|
}
|