#!/bin/bash
#
# Copyright(C) 2024 @ ZENETYS
# This script is licensed under MIT License (http://opensource.org/licenses/MIT)
#

set -f
export LC_ALL=C
QUIET=
PROGNAME=${0##*/}
ZLC_ENV=${ZLC_ENV:-/etc/logcenter/logcenter.conf}

function exit_usage() {
    local status=${1:-0}
    [[ "$status" != "0" ]] && exec >&2
    echo "\
Usage: $PROGNAME [-n]
Update by-name log archives symlinks

Available options:
  -n, --dry-run     Dry-run mode, do not change anything.
  -q, --quiet       Display errors only.
  -h, --help        Display this help."
    exit "$status"
}

function _log() {
    local now=$(date +%Y-%m-%dT%H:%M:%S.%3N%:z)
    local LOGGER_SEVERITY=${LOGGER_SEVERITY:-info}
    local STDOUT_LABEL=${STDOUT_LABEL:-INFO}
    [[ -t 0 ]] || logger -t "$PROGNAME[$$]" -p "$LOGGER_SEVERITY" -- "${DRY_RUN:+DRY-RUN ** }$*"
    [[ -n $QUIET && $STDOUT_LABEL == INFO ]] && return 0
    echo "$now $STDOUT_LABEL $PROGNAME: ${DRY_RUN:+DRY-RUN ** }$*"
}

function info() { LOGGER_SEVERITY=info STDOUT_LABEL=INFO _log "$@"; }
function warning() { LOGGER_SEVERITY=warning STDOUT_LABEL=WARNING _log "$@"; }
function error() { LOGGER_SEVERITY=err STDOUT_LABEL=ERROR _log "$@"; }
function fatal() { LOGGER_SEVERITY=crit STDOUT_LABEL=FATAL _log "$@"; exit 2; }

function load_env_or_fatal() {
    source "$ZLC_ENV" || fatal "Failed to source $ZLC_ENV"
    [[ -z $ZLC_ARCHIVES_DIR ]] && fatal 'Bad environment, ZLC_ARCHIVES_DIR not set'
    return 0
}

[[ $UID == 0 ]] ||
    fatal 'This script must be run as root'

load_env_or_fatal

while (( $# > 0 )); do
    case "$1" in
        -n|--dry-run) DRY_RUN=1 ;;
        -q|--quiet) QUIET=1 ;;
        -h|--help) exit_usage ;;
        *) exit_usage 1 ;;
    esac
    shift
done

if [[ -n $DRY_RUN ]]; then
    function ln() { info "EXEC: $FUNCNAME $*"; }
fi

BY_NAME_DIR="$ZLC_ARCHIVES_DIR/@by-name"
BY_IP_DIR="$ZLC_ARCHIVES_DIR/@by-ip"

# $1: target
# $2: symlink
function make_symlink() {
    local target=$1; [[ $target == */* ]] || target="./$target"
    local symlink=$2; [[ $symlink == */* ]] || symlink="./$symlink"
    if [[ -e "$symlink" && $(readlink "$symlink") == "$target" ]]; then
        info "Symlink $symlink already exists"
        return 0
    fi
    if ! [[ -d ${symlink%/*} ]] &&
       ! install -d -o "$ZLC_ARCHIVES_USER" -g "$ZLC_ARCHIVES_GROUP" -m 0750 "${symlink%/*}"; then
        error "mkdir failed: ${symlink%/*}"
        return 1
    fi
    if ! ln -Tsnf "$target" "$symlink"; then
        error "ln failed: $symlink"
        return 1
    fi
    return 0
}

info "Option ZLC_ARCHIVES_FQDN_SRC=${ZLC_ARCHIVES_FQDN_SRC:-<empty>}"
find "$ZLC_ARCHIVES_DIR/" -mindepth 1 -maxdepth 1 \
        -not -name "${BY_NAME_DIR##*/}" \
        -not -name "${BY_IP_DIR##*/}" \
        -printf '%P\n' |
    sort -V |
    while read -r dir; do
        if [[ $dir =~ ^[0-9]+(\.[0-9]+){3}$ ]]; then
            if [[ $dir == 127.0.0.1 ]]; then
                by_name=$(hostname --fqdn)
                if [[ $by_name == *.* ]]; then
                    by_name="${by_name%%.*}-localhost.${by_name#*.}"
                else
                    by_name+="-localhost"
                fi
            else
                by_name=( $(host -t PTR "$dir" |
                    sed -nre 's,^.* domain name pointer (.+)\.$,\1,p' |
                    tr '[A-Z]' '[a-z]') )
            fi
            info "IP: $dir, PTR: ${by_name[*]:-<not-found>}"
            by_ip=( "$dir" )
        else
            by_name=( "$dir" )
            by_ip=( $({ host -t A "$dir"; host -t AAAA "$dir"; } |
                sed -nre 's,^.* has address (.+)$,\1,p' |
                tr '[A-Z]' '[a-z]') )
            info "Name: $dir, IP: ${by_ip[*]:-<not-found>}"
        fi

        if [[ $ZLC_ARCHIVES_FQDN_SRC != on ]]; then
            by_name=( "${by_name[@]%%.*}" )
        fi
        for l in "${by_name[@]}"; do
            make_symlink "../$dir" "$BY_NAME_DIR/$l"
        done
        for l in "${by_ip[@]}"; do
            make_symlink "../$dir" "$BY_IP_DIR/$l"
        done
    done

# delete broken links
for i in "$BY_NAME_DIR" "$BY_IP_DIR"; do
    [[ -d $i/. ]] || continue
    find -L "$i/" -mindepth 1 -maxdepth 1 -type l |
        while read -r; do
            info "Remove dangling symlink $REPLY"
            rm "$REPLY"
        done
done
