use anyhow::Result;
use std::collections::HashMap;
use std::process::Command;
pub(crate) fn get_ld_cache() -> Result<HashMap<String, String>> {
let output = Command::new("/sbin/ldconfig").arg("-p").output()?;
if !output.status.success() {
anyhow::bail!("ldconfig -p failed with status: {}", output.status);
}
let stdout = String::from_utf8(output.stdout)?;
parse_ldconfig_output(&stdout)
}
fn parse_ldconfig_output(output: &str) -> Result<HashMap<String, String>> {
let mut cache = HashMap::new();
for line in output.lines().skip(1) {
let line = line.trim();
if let Some(arrow_pos) = line.find(" => ") {
let lib_name = line[..arrow_pos].trim();
let lib_path = line[arrow_pos + 4..].trim();
if let Some(paren_pos) = lib_name.find(" (") {
let clean_name = &lib_name[..paren_pos];
cache.insert(clean_name.to_string(), lib_path.to_string());
} else {
cache.insert(lib_name.to_string(), lib_path.to_string());
}
}
}
Ok(cache)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_ldconfig_output() {
let sample_output = r#"42 libs found in cache `/etc/ld.so.cache'
libz.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libz.so.1
libz.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libz.so
libyaml-0.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libyaml-0.so.2
libxtables.so.12 (libc6,x86-64) => /lib/x86_64-linux-gnu/libxtables.so.12
libxslt.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libxslt.so.1
libxml2.so.2 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libxml2.so.2
libwrap.so.0 (libc6,x86-64) => /lib/x86_64-linux-gnu/libwrap.so.0
libuuid.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libuuid.so.1
libc.so.6 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libc.so.6
libselinux.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libselinux.so.1
"#;
let cache = parse_ldconfig_output(sample_output).unwrap();
assert_eq!(cache.get("libz.so.1").unwrap(), "/lib/x86_64-linux-gnu/libz.so.1");
assert_eq!(cache.get("libz.so").unwrap(), "/lib/x86_64-linux-gnu/libz.so");
assert_eq!(cache.get("libyaml-0.so.2").unwrap(), "/lib/x86_64-linux-gnu/libyaml-0.so.2");
assert_eq!(cache.get("libxml2.so.2").unwrap(), "/lib/x86_64-linux-gnu/libxml2.so.2");
assert_eq!(cache.get("libc.so.6").unwrap(), "/lib/x86_64-linux-gnu/libc.so.6");
assert_eq!(cache.get("libselinux.so.1").unwrap(), "/lib/x86_64-linux-gnu/libselinux.so.1");
assert_eq!(cache.len(), 10);
}
#[test]
fn test_parse_ldconfig_output_empty() {
let sample_output = "0 libs found in cache `/etc/ld.so.cache'\n";
let cache = parse_ldconfig_output(sample_output).unwrap();
assert!(cache.is_empty());
}
#[test]
fn test_parse_ldconfig_output_no_parentheses() {
let sample_output = r#"1 libs found in cache `/etc/ld.so.cache'
libweird.so => /lib/libweird.so
"#;
let cache = parse_ldconfig_output(sample_output).unwrap();
assert_eq!(cache.get("libweird.so").unwrap(), "/lib/libweird.so");
}
#[test]
fn test_parse_ldconfig_output_skip_invalid_lines() {
let sample_output = r#"3 libs found in cache `/etc/ld.so.cache'
libvalid.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libvalid.so.1
this line has no arrow
libvalid2.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libvalid2.so.1
malformed =>
=> no lib name
"#;
let cache = parse_ldconfig_output(sample_output).unwrap();
assert_eq!(cache.len(), 2);
assert_eq!(cache.get("libvalid.so.1").unwrap(), "/lib/x86_64-linux-gnu/libvalid.so.1");
assert_eq!(cache.get("libvalid2.so.1").unwrap(), "/lib/x86_64-linux-gnu/libvalid2.so.1");
}
#[test]
fn test_parse_ldconfig_output_multiarch() {
let sample_output = r#"4 libs found in cache `/etc/ld.so.cache'
libc.so.6 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libc.so.6
libc.so.6 (libc6, OS ABI: Linux 3.2.0) => /lib/i386-linux-gnu/libc.so.6
libm.so.6 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libm.so.6
libm.so.6 (libc6, OS ABI: Linux 3.2.0) => /lib/i386-linux-gnu/libm.so.6
"#;
let cache = parse_ldconfig_output(sample_output).unwrap();
assert_eq!(cache.len(), 2);
assert!(cache.contains_key("libc.so.6"));
assert!(cache.contains_key("libm.so.6"));
}
}