#!/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" <&2 exit 1 fi echo "## $name u:$owner_user g:$owner_group" echo " $path ($source_dir, id=$source_id)" sqlite3 "$db_path" </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" <&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 "$@"