depscollector Administration
List dependencies of a User or a binary
This is specially useful for packageuser LFS systems
dependencies: depscollector
Synopsis
depscollector [-u] [-d db_path] [-b bin_dir]
DESCRIPTION
-u Update mode (remove stale entries, add new only)
-d db_path Path to SQLite database (default: /var/lib/depscollector/depscollector.db)
-b bin_dir Directory to scan for binaries (can be used multiple times)
-h Show this help message
wget https://git.christianimmanuel.de/scripts/raw/Administration/depscollector && chmod 740 depscollectordepscollector raw
#!/bin/bash
#################################################
### List dependencies of a User or a binary
### This is specially useful for packageuser LFS systems
###
### dependencies: depscollector
### Synopsis
### depscollector [-u] [-d db_path] [-b bin_dir]
### DESCRIPTION
### -u Update mode (remove stale entries, add new only)
### -d db_path Path to SQLite database (default: /var/lib/depscollector/depscollector.db)
### -b bin_dir Directory to scan for binaries (can be used multiple times)
### -h Show this help message
init_db() {
echo "INIT DB"
mkdir -p "$(dirname "$db_path")"
echo "Creating DB"
sqlite3 "$db_path" <<'EOF'
CREATE TABLE IF NOT EXISTS sources (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dir TEXT UNIQUE NOT NULL
);
CREATE TABLE IF NOT EXISTS binaries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
owner_user TEXT,
owner_group TEXT,
source_id INTEGER NOT NULL,
FOREIGN KEY (source_id) REFERENCES sources(id)
);
CREATE TABLE IF NOT EXISTS libraries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
owner_user TEXT,
owner_group TEXT
);
CREATE TABLE IF NOT EXISTS binary_dependencies (
binary_id INTEGER NOT NULL,
library_id INTEGER NOT NULL,
PRIMARY KEY (binary_id, library_id),
FOREIGN KEY (binary_id) REFERENCES binaries(id) ON DELETE CASCADE,
FOREIGN KEY (library_id) REFERENCES libraries(id) ON DELETE CASCADE
);
EOF
local expected_tables=("sources" "binaries" "libraries" "binary_dependencies")
for table in "${expected_tables[@]}"; do
if ! sqlite3 "$db_path" ".schema $table" | grep -q "^CREATE TABLE"; then
echo "Error: Table '$table' was not created successfully in $db_path" >&2
rm -f "$db_path"
exit 1
else
echo "Table $table exists."
fi
done
}
get_owner() {
stat -c '%U:%G' "$1"
}
insert_binary() {
local path="$1"
local name owner_user owner_group source_dir source_id
name="$(basename "$path")"
IFS=: read -r owner_user owner_group < <(get_owner "$path")
source_dir="$(dirname "$path")"
# Insert or find source_id
sqlite3 "$db_path" <<EOF
INSERT OR IGNORE INTO sources (dir) VALUES ('$source_dir');
EOF
source_id=$(sqlite3 "$db_path" "SELECT id FROM sources WHERE dir = '$source_dir';")
if [[ -z "$source_id" ]]; then
echo "Error: Could not resolve source_id for $source_dir" >&2
exit 1
fi
echo "## $name u:$owner_user g:$owner_group"
echo " $path ($source_dir, id=$source_id)"
sqlite3 "$db_path" <<EOF
INSERT OR IGNORE INTO binaries (path, name, owner_user, owner_group, source_id)
VALUES ('$path', '$name', '$owner_user', '$owner_group', $source_id);
EOF
}
insert_library() {
local lib_path="$1"
[[ ! -e "$lib_path" ]] && return
local name owner_user owner_group
name="$(basename "$lib_path")"
IFS=: read -r owner_user owner_group < <(get_owner "$lib_path")
echo " $name $owner_user $owner_group"
sqlite3 "$db_path" <<EOF
INSERT OR IGNORE INTO libraries (path, name, owner_user, owner_group)
VALUES ('$lib_path', '$name', '$owner_user', '$owner_group');
EOF
}
link_binary_to_library() {
local binary_path="$1"
local lib_path="$2"
local binary_id lib_id
binary_id=$(sqlite3 "$db_path" "SELECT id FROM binaries WHERE path = '$binary_path';")
lib_id=$(sqlite3 "$db_path" "SELECT id FROM libraries WHERE path = '$lib_path';")
[[ -n "$binary_id" && -n "$lib_id" ]] || return
sqlite3 "$db_path" <<EOF
INSERT OR IGNORE INTO binary_dependencies (binary_id, library_id)
VALUES ($binary_id, $lib_id);
EOF
}
find_library_path() {
local lib="$1"
ldconfig -p | awk -v lib="$lib" '$1 == lib { print $NF; exit }'
}
process_binary() {
local binary="$1"
insert_binary "$binary"
if file "$binary" | grep -q ELF; then
local libs
libs=$(readelf -d "$binary" 2>/dev/null | awk '/NEEDED/ { gsub(/\[|\]/, "", $NF); print $NF }')
echo
for lib in $libs; do
echo " $lib"
local lib_path
lib_path="$(find_library_path "$lib")"
if [[ -n "$lib_path" ]]; then
insert_library "$lib_path"
link_binary_to_library "$binary" "$lib_path"
fi
done
fi
}
remove_stale_entries() {
echo "Collecting current binaries..."
current_bins=()
for dir in "${bin_dirs[@]}"; do
[[ -d "$dir" ]] && mapfile -t found < <(find "$dir" -type f -executable)
current_bins+=("${found[@]}")
done
echo "Collecting tracked binaries from DB..."
mapfile -t db_bins < <(sqlite3 "$db_path" "SELECT path FROM binaries;" | sort)
# Use temp files to diff
local tmp_current tmp_db
tmp_current=$(mktemp)
tmp_db=$(mktemp)
printf "%s\n" "${current_bins[@]}" | sort > "$tmp_current"
printf "%s\n" "${db_bins[@]}" | sort > "$tmp_db"
# Remove binaries that are no longer found
while IFS= read -r stale_bin; do
echo "Removing stale binary: $stale_bin"
sqlite3 "$db_path" "DELETE FROM binaries WHERE path = '$stale_bin';"
done < <(comm -23 "$tmp_db" "$tmp_current")
rm -f "$tmp_current" "$tmp_db"
echo "Cleaning unused libraries..."
sqlite3 "$db_path" <<EOF
DELETE FROM libraries
WHERE id NOT IN (SELECT library_id FROM binary_dependencies);
EOF
}
main() {
local update=0
db_path="/var/lib/depscollector/depscollector.db"
bin_dirs=()
bin_dir="/usr/bin"
while [[ $# -gt 0 ]]; do
case "$1" in
-u) update=1; shift ;;
-d) db_path="$2"; shift 2 ;;
-b) bin_dirs+=("$2"); shift 2 ;;
-h)
echo "Usage: $0 [-u] [-d db_path] [-b bin_dir]"
echo " -u Update mode (remove stale entries, add new only)"
echo " -d db_path Path to SQLite database (default: /var/lib/depscollector/depscollector.db)"
echo " -b bin_dir Directory to scan for binaries (can be used multiple times)"
echo " -h Show this help message"
exit 0
;;
*) echo "Invalid option: $1" >&2; echo "Try '$0 -h' for help." >&2; exit 1 ;;
esac
done
if [[ ${#bin_dirs[@]} -eq 0 ]]; then
mapfile -t bin_dirs < <(sqlite3 "$db_path" "SELECT DISTINCT dir FROM sources;")
fi
if [[ ${#bin_dirs[@]} -eq 0 ]]; then
bin_dirs=("/usr/bin")
fi
echo "Using database: $db_path"
echo "Scanning binaries in: ${bin_dirs[*]}"
init_db
echo "Collecting current binaries from ${bin_dirs[@]}..."
all_bins=()
for dir in "${bin_dirs[@]}"; do
[[ -d "$dir" ]] && mapfile -t found < <(find "$dir" -type f -executable)
all_bins+=("${found[@]}")
done
IFS=$'\n' all_bins=($(printf "%s\n" "${all_bins[@]}" | sort -u))
if (( update )); then
echo "Updating DB: removing stale entries..."
current_bins=("${all_bins[@]}")
remove_stale_entries
echo "Collecting tracked binaries from DB after cleanup..."
mapfile -t db_bins < <(sqlite3 "$db_path" "SELECT path FROM binaries ORDER BY path;")
# Save to tmp files to diff
tmp_all=$(mktemp)
tmp_db=$(mktemp)
printf "%s\n" "${all_bins[@]}" | sort > "$tmp_all"
printf "%s\n" "${db_bins[@]}" | sort > "$tmp_db"
echo "Processing new binaries only..."
while IFS= read -r new_bin; do
echo -e "\n###############################################"
process_binary "$new_bin"
done < <(comm -13 "$tmp_db" "$tmp_all")
rm -f "$tmp_all" "$tmp_db"
else
# Full fresh scan: insert all
for bin in "${all_bins[@]}"; do
echo -e "\n###############################################"
process_binary "$bin"
done
fi
}
main "$@"