diff options
| -rw-r--r-- | src/manifest.rs | 23 | ||||
| -rw-r--r-- | tests/compare.rs | 22 |
2 files changed, 39 insertions, 6 deletions
diff --git a/src/manifest.rs b/src/manifest.rs index b3e489d..3c34ade 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -170,15 +170,26 @@ fn collect_files(rootfs: &Path) -> Result<Vec<FileRecord>> { } fn collect_services(rootfs: &Path) -> Result<Vec<ServiceRecord>> { - let wants = rootfs.join("etc/systemd/system/multi-user.target.wants"); - if !wants.exists() { + let system = rootfs.join("etc/systemd/system"); + if !system.is_dir() { return Ok(Vec::new()); } let mut services = Vec::new(); - for entry in fs::read_dir(&wants).with_context(|| format!("read service state directory {}", wants.display()))? { - let entry = entry?; - let name = entry.file_name().into_string().map_err(|_| anyhow::anyhow!("non-UTF-8 service name in {}", wants.display()))?; - if entry.file_type()?.is_symlink() && name.ends_with(".service") { + for entry in WalkDir::new(&system).follow_links(false).min_depth(2) { + let entry = entry.with_context(|| format!("walk service state directory {}", system.display()))?; + let parent_is_wants_directory = entry + .path() + .parent() + .and_then(Path::file_name) + .is_some_and(|name| name.to_string_lossy().ends_with(".target.wants")); + if !parent_is_wants_directory || !entry.file_type().is_symlink() { + continue; + } + let name = entry + .file_name() + .to_str() + .ok_or_else(|| anyhow::anyhow!("non-UTF-8 service name in {}", entry.path().display()))?; + if name.ends_with(".service") { services.push(ServiceRecord::new(name, true)); } } diff --git a/tests/compare.rs b/tests/compare.rs index fded37f..37135a7 100644 --- a/tests/compare.rs +++ b/tests/compare.rs @@ -291,6 +291,28 @@ fn native_manifest_collection_excludes_internal_members_from_its_archive_facts() } #[test] +fn rootfs_collection_recognizes_services_enabled_by_non_default_targets() { + let fixture = tempdir().expect("temporary directory"); + let rootfs = fixture.path().join("rootfs"); + let wants = rootfs.join("etc/systemd/system/graphical.target.wants"); + fs::create_dir_all(&wants).expect("create service state directory"); + symlink( + "/usr/lib/systemd/system/controller.service", + wants.join("controller.service"), + ) + .expect("enable service"); + let artifact = fixture.path().join("controller.tar"); + NativeTarWriter::new() + .write(&rootfs, &artifact) + .expect("write native archive"); + + let manifest = ArtifactManifest::collect(&rootfs, vec![], None, &artifact) + .expect("collect native filesystem and archive facts"); + + assert_eq!(manifest.services, vec![ServiceRecord::new("controller.service", true)]); +} + +#[test] fn archive_collection_coalesces_a_service_enabled_by_multiple_targets() { let fixture = tempdir().expect("temporary directory"); let rootfs = fixture.path().join("rootfs"); |