#!/bin/sh

# generate-crypt-services - Generate individual service files for each crypttab entry
# This script reads /etc/crypttab and creates individual dinit service files
# This is an alternative to the full crypttab services where per devices controls is wanted.
# Usage: /etc/dinit.d/cryptsetup.d/generate-crypt-services [output-dir]
# example: sudo /etc/dinit.d/cryptsetup.d/generate-crypt-services /etc/dinit.d

export PATH=/usr/bin:/usr/sbin:/bin:/sbin

set -e

OUTPUT_DIR="${1:-.}"
CRYPTTAB="/etc/crypttab"
TEMPLATE_DIR="${TEMPLATE_DIR:-.}"

log_info() {
    echo "[INFO] $*" >&2
}

log_error() {
    echo "[ERROR] $*" >&2
}

# Parse a crypttab line
parse_crypttab_line() {
    local line="$1"

    # Skip comments and empty lines
    [[ "$line" =~ ^[[:space:]]*# ]] && return 1
    [[ -z "$line" ]] && return 1

    # Split by whitespace
    local name device password options
    read -r name device password options <<<"$line"

    [[ -z "$name" ]] && return 1

    # Default values
    password="${password:--}"
    options="${options:=luks}"

    echo "$name|$device|$password|$options"
    return 0
}

# Generate service file for a device
generate_service_file() {
    local name="$1"
    local device="$2"
    local password="$3"
    local options="$4"

    local service_file="$OUTPUT_DIR/crypt-$name"

    log_info "Generating service file: $service_file"

    cat > "$service_file" << 'EOF'
# crypt-NAME_PLACEHOLDER.service - Unlock encrypted device NAME_PLACEHOLDER
# Auto-generated from /etc/crypttab entry
# Device: DEVICE_PLACEHOLDER
# Password: PASSWORD_PLACEHOLDER
# Options: OPTIONS_PLACEHOLDER

type = scripted
command = UNLOCK_COMMAND_PLACEHOLDER
stop-command = /usr/sbin/cryptsetup close NAME_PLACEHOLDER
restart = false
logfile = /var/log/crypt-NAME_PLACEHOLDER.log
logfile-permissions = 644
start-timeout = 300 # Allow time for passphrase entry
options: skippable
options: starts-on-console

depends-on: udevd
depends-on: modules
waits-for: udev-settle
EOF

    # Replace placeholders
    sed -i "s|NAME_PLACEHOLDER|$name|g" "$service_file"
    sed -i "s|DEVICE_PLACEHOLDER|$device|g" "$service_file"
    sed -i "s|PASSWORD_PLACEHOLDER|$password|g" "$service_file"
    sed -i "s|OPTIONS_PLACEHOLDER|$options|g" "$service_file"

    # Generate unlock command based on password type
    local unlock_cmd="/usr/sbin/cryptsetup open"

    # Parse options
    IFS=',' read -ra opts_array <<<"$options"
    for opt in "${opts_array[@]}"; do
        opt="${opt// /}"
        case "$opt" in
            discard)
                unlock_cmd+=" --allow-discards"
                ;;
            key-slot=*)
                unlock_cmd+=" --key-slot ${opt#key-slot=}"
                ;;
            tcrypt)
                unlock_cmd+=" --type tcrypt"
                ;;
        esac
    done

    # Add password/keyfile handling
    if [[ "$password" == "-" ]]; then
        unlock_cmd+=" --"
        unlock_cmd+="key-prompt"
    elif [[ "$password" == "none" ]] || [[ "$password" == "/dev/urandom" ]]; then
        unlock_cmd+=" --key-file=$password"
    elif [[ -f "$password" ]]; then
        unlock_cmd+=" --key-file=$password"
    fi

    unlock_cmd+=" $device $name"

    sed -i "s|UNLOCK_COMMAND_PLACEHOLDER|$unlock_cmd|g" "$service_file"

    chmod 644 "$service_file"
}

main() {
    if [[ ! -f "$CRYPTTAB" ]]; then
        log_info "No $CRYPTTAB found"
        return 0
    fi

    log_info "Reading $CRYPTTAB and generating service files in $OUTPUT_DIR"

    local count=0
    while IFS= read -r line; do
        parsed=$(parse_crypttab_line "$line") || continue

        IFS='|' read -r name device password options <<<"$parsed"

        # Generate the device service file (temporarily disable set -e to allow graceful error handling)
        set +e
        generate_service_file "$name" "$device" "$password" "$options"
        result=$?
        set -e
        if [[ $result -eq 0 ]]; then
            count=$((count + 1))
        fi
    done < "$CRYPTTAB"

    log_info "Generated $count service files"
    return 0
}

main "$@"
