qbit_manage/entrypoint.sh
bobokun 385d0c633d
feat(entrypoint): enhance script with validation.
- Add structured logging function for consistent output
- Implement robust numeric validation for PUID/PGID environment variables
- Improve safe_copy with atomic operations and comprehensive error handling
- Optimize permission fixing with better performance and root user checks
- Restructure code with main function for improved organization
- Remove default ENV vars from Dockerfile to simplify conf
- Default PUID, PGID, and UMASK environment variables removed from Dockerfile - users must now explicitly set these if needed
2025-08-29 09:34:49 -04:00

179 lines
5.1 KiB
Bash

#!/bin/bash
set -euo pipefail # Exit on error, undefined vars, pipe failures
# Configuration
readonly SOURCE_FILE="/app/config/config.yml.sample"
readonly DEST_FILE="/config/config.yml.sample"
# Logging function for consistent output
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
}
# Validate numeric environment variables
validate_numeric_env() {
local var_name="$1"
local var_value="$2"
if [[ -n "$var_value" ]] && ! [[ "$var_value" =~ ^[0-9]+$ ]]; then
log "Warning: $var_name must be numeric. Got $var_name='$var_value' - ignoring PUID/PGID and running as root"
return 1
fi
return 0
}
# Validate and set PUID/PGID
validate_user_group_ids() {
local puid_valid=0
local pgid_valid=0
if ! validate_numeric_env "PUID" "${PUID:-}"; then
puid_valid=1
fi
if ! validate_numeric_env "PGID" "${PGID:-}"; then
pgid_valid=1
fi
# If either is invalid, clear both
if [[ $puid_valid -eq 1 ]] || [[ $pgid_valid -eq 1 ]]; then
PUID=""
PGID=""
return 1
fi
return 0
}
# Safely copy file with atomic operation and error handling
safe_copy() {
local src="$1"
local dest="$2"
local temp_file="${dest}.tmp"
# Validate source file exists and is readable
if [[ ! -f "$src" ]] || [[ ! -r "$src" ]]; then
log "Error: Source file '$src' does not exist or is not readable"
return 1
fi
# Create parent directory if it doesn't exist
local dest_dir
dest_dir="$(dirname "$dest")"
if [[ ! -d "$dest_dir" ]]; then
mkdir -p "$dest_dir" || {
log "Error: Could not create destination directory '$dest_dir'"
return 1
}
fi
# Atomic copy operation
if cp "$src" "$temp_file" && mv "$temp_file" "$dest"; then
log "Successfully copied $src to $dest"
return 0
else
# Clean up temp file on failure
[[ -f "$temp_file" ]] && rm -f "$temp_file"
log "Error: Failed to copy $src to $dest"
return 1
fi
}
# Optimized permission fixing with better performance
fix_permissions() {
local path="$1"
# Skip if PUID or PGID are not set
if [[ -z "${PUID:-}" ]] || [[ -z "${PGID:-}" ]]; then
log "Skipping permission fix for $path - PUID or PGID not set"
return 0
fi
# Check if we're running as root
if [[ "$(id -u)" != "0" ]]; then
log "Skipping permission fix for $path - not running as root"
return 0
fi
local needs_fix=0
if [[ -d "$path" ]]; then
# Check if any files in directory need ownership change
if find "$path" -xdev \( -not -user "$PUID" -o -not -group "$PGID" \) -print -quit 2>/dev/null | grep -q .; then
needs_fix=1
fi
elif [[ -e "$path" ]]; then
# Check if file needs ownership change
if [[ "$(stat -c '%u:%g' "$path" 2>/dev/null || echo "0:0")" != "$PUID:$PGID" ]]; then
needs_fix=1
fi
else
log "Warning: Path '$path' does not exist, skipping permission fix"
return 0
fi
if [[ $needs_fix -eq 1 ]]; then
if chown -R "$PUID:$PGID" "$path" 2>/dev/null; then
local type_msg="file"
[[ -d "$path" ]] && type_msg="directory"
log "Corrected ownership of $type_msg $path to $PUID:$PGID"
return 0
else
log "Warning: Could not change ownership of $path"
return 1
fi
fi
return 0
}
# Execute command with appropriate privilege level
execute_command() {
local current_uid
current_uid="$(id -u)"
if [[ "$current_uid" = "0" ]]; then
if [[ -n "${PUID:-}" ]] && [[ -n "${PGID:-}" ]]; then
log "Changing privileges to PUID:PGID = $PUID:$PGID"
exec /sbin/su-exec "${PUID}:${PGID}" "$@" || {
log "Warning: Could not drop privileges to ${PUID}:${PGID}, continuing as root"
exec "$@"
}
else
log "PUID/PGID not set, running as root"
exec "$@"
fi
else
log "Already running as non-root user (UID: $current_uid), executing command"
exec "$@"
fi
}
# Main execution
main() {
# Validate user/group IDs
validate_user_group_ids
# Handle config file setup
if [[ -d "/config" ]]; then
if [[ -f "$SOURCE_FILE" ]] && [[ -s "$SOURCE_FILE" ]]; then
# Check if destination needs updating
if [[ ! -f "$DEST_FILE" ]] || ! cmp -s "$SOURCE_FILE" "$DEST_FILE" 2>/dev/null; then
if safe_copy "$SOURCE_FILE" "$DEST_FILE"; then
# Fix permissions if running as root and IDs are set
if [[ "$(id -u)" = "0" ]] && [[ -n "${PUID:-}" ]] && [[ -n "${PGID:-}" ]]; then
fix_permissions "$DEST_FILE"
fi
fi
fi
elif [[ ! -f "$SOURCE_FILE" ]]; then
log "Warning: Source file $SOURCE_FILE does not exist, skipping config setup"
fi
fi
# Execute the main command
execute_command "$@"
}
# Run main function with all arguments
main "$@"