The backup engine already drops to the backup user (sudo -E -u $docker_install_user) and backupLocationOwner == $docker_install_user, which is exactly what runFileOp/runFileWrite resolve to in both modes. So convert the raw-sudo data ops (mkdir/chmod/rm/find/cat/grep/mv/chown/tee on backup repos, location configs, keys, manifests) to runFileOp/runFileWrite — creating files as the owner directly, no root chown. backup_verify creates its scratch as the backup user (runFileOp mktemp) instead of chown-after. Binary installs (kopia tar/install, borg dnf) -> runSystem. The 44 sudo -u engine drops stay (already least-privilege; the scoped sudoers will grant them). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
82 lines
2.7 KiB
Bash
82 lines
2.7 KiB
Bash
#!/bin/bash
|
|
|
|
manifestCollect()
|
|
{
|
|
local app_name="$1"
|
|
local app_dir="$containers_dir$app_name"
|
|
|
|
local libreportal_commit
|
|
libreportal_commit=$(git -C "${install_scripts_dir%/scripts/}" rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
|
|
local compose_file=""
|
|
for candidate in "$app_dir/docker-compose.yml" "$app_dir/compose.yml"; do
|
|
[[ -f "$candidate" ]] && compose_file="$candidate" && break
|
|
done
|
|
|
|
local compose_hash="unknown"
|
|
[[ -n "$compose_file" ]] && compose_hash=$(sha256sum "$compose_file" 2>/dev/null | cut -d' ' -f1)
|
|
|
|
local images_json="[]"
|
|
if [[ -n "$compose_file" ]]; then
|
|
local images=()
|
|
while IFS= read -r line; do
|
|
local img
|
|
img=$(echo "$line" | sed -E 's/^[[:space:]]*image:[[:space:]]*["'\'']?([^"'\'' ]+)["'\'']?.*/\1/')
|
|
[[ -n "$img" ]] && images+=("\"$img\"")
|
|
done < <(grep -E '^[[:space:]]+image:' "$compose_file" 2>/dev/null)
|
|
if [[ ${#images[@]} -gt 0 ]]; then
|
|
images_json="[$(IFS=,; echo "${images[*]}")]"
|
|
fi
|
|
fi
|
|
|
|
local volumes_json="[]"
|
|
if [[ -d "$app_dir" ]]; then
|
|
local vols=()
|
|
while IFS= read -r d; do
|
|
[[ -n "$d" ]] && vols+=("\"$(basename "$d")\"")
|
|
done < <(find "$app_dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
|
|
if [[ ${#vols[@]} -gt 0 ]]; then
|
|
volumes_json="[$(IFS=,; echo "${vols[*]}")]"
|
|
fi
|
|
fi
|
|
|
|
local size_bytes
|
|
size_bytes=$(runFileOp du -sb "$app_dir" 2>/dev/null | awk '{print $1}')
|
|
[[ -z "$size_bytes" ]] && size_bytes=0
|
|
|
|
local file_count
|
|
file_count=$(runFileOp find "$app_dir" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
|
|
local strategy="${CFG_BACKUP_STRATEGY:-auto}"
|
|
declare -f backupResolveStrategy >/dev/null 2>&1 && strategy=$(backupResolveStrategy "$app_name")
|
|
|
|
local databases_json="[]"
|
|
if declare -f backupDbDescriptors >/dev/null 2>&1; then
|
|
local dbs=() desc kind container datadir path
|
|
while IFS= read -r desc; do
|
|
[[ -z "$desc" ]] && continue
|
|
IFS=':' read -r kind container datadir path <<< "$desc"
|
|
dbs+=("{\"kind\":\"$kind\",\"container\":\"$container\",\"path\":\"$path\"}")
|
|
done < <(backupDbDescriptors "$app_name")
|
|
[[ ${#dbs[@]} -gt 0 ]] && databases_json="[$(IFS=,; echo "${dbs[*]}")]"
|
|
fi
|
|
|
|
cat <<EOF
|
|
{
|
|
"version": 2,
|
|
"app": "$app_name",
|
|
"host": "${CFG_INSTALL_NAME:-libreportal}",
|
|
"created_at": "$(date -Iseconds)",
|
|
"libreportal_commit": "$libreportal_commit",
|
|
"engine": "restic",
|
|
"compose_hash": "$compose_hash",
|
|
"images": $images_json,
|
|
"volumes": $volumes_json,
|
|
"size_bytes": $size_bytes,
|
|
"file_count": $file_count,
|
|
"strategy": "$strategy",
|
|
"databases": $databases_json
|
|
}
|
|
EOF
|
|
}
|