#!/usr/bin/bash

# CloudLinux Netconsole Management Script
# This script manages the netconsole kernel module for remote kernel logging

NETCONSOLE_MODULE="netconsole"
NETCONSOLE_CONFIG_DIR="/sys/kernel/config/netconsole"
NETCONSOLE_TARGET="cloudlinux-kpr"

# Hardcoded configuration values
SRC_IP=""
SRC_PORT="6666"
DST_IP="sentrykernel.cloudlinux.com"
DST_PORT="514"
DEV_INTERFACE=""

# Function to get the primary network interface if not specified
get_primary_interface() {
    local dev=""

    if [ -n "$DEV_INTERFACE" ]; then
        echo "$DEV_INTERFACE"
        return
    fi

    local route via target

    route=$(LANG=C ip -o route get to $DST_IP/32)

    dev=$(echo $route | sed "s|.* dev \([^ ]*\).*|\1|")
    echo "$dev"
}

# Function to get the IP address of an interface
get_interface_ip() {
    local iface="$1"
    if [ -n "$iface" ]; then
        ip addr show "$iface" | grep -E "inet [0-9]" | head -1 | awk '{print $2}' | cut -d/ -f1
    fi
}

# Function to check if netconsole configfs is available
check_configfs() {
    if [ ! -d "$NETCONSOLE_CONFIG_DIR" ]; then
        echo "Error: netconsole configfs not available at $NETCONSOLE_CONFIG_DIR" >&2
        return 1
    fi
    return 0
}

# Function to start netconsole
start_netconsole() {
    echo "Starting CloudLinux netconsole..."
    
    # Check if netconsole module is available
    if ! modinfo "$NETCONSOLE_MODULE" >/dev/null 2>&1; then
        echo "Error: netconsole module is not available" >&2
        exit 1
    fi

    # Load the netconsole module if not already loaded
    if ! lsmod | grep -q "^$NETCONSOLE_MODULE "; then
        echo "Loading netconsole module..."
        if ! modprobe "$NETCONSOLE_MODULE"; then
            echo "Error: Failed to load netconsole module" >&2
            exit 1
        fi
    else
        echo "Netconsole module already loaded"
    fi

    # Configuring oops_only mode
    if ! echo 1 > /sys/module/$NETCONSOLE_MODULE/parameters/oops_only; then
        echo "Error: Failed to set oops_only mode" >&2
        exit 1
    fi

    # Check if netconsole configfs is available
    if ! check_configfs; then
        echo "Error: netconsole configfs not available" >&2
        exit 1
    fi

    if [ -n "$DST_IP" ]; then
        # IPv6 regex also covers 4to6, zero-compressed, and link-local addresses with zone-index addresses.
        # It should also cover IPv4-embedded, IPv4-mapped, and IPv4-translated IPv6 addresses.
        # Taken from: http://stackoverflow.com/a/17871737/3481531
        IPv4_regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
        IPv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
        if ! [[ "$DST_IP" =~ $IPv4_regex ]] && ! [[ "$DST_IP" =~ $IPv6_regex ]]; then
            # Use IPv4 by default:
            DST_IP="$(LANG=C getent ahostsv4 $DST_IP 2> /dev/null)"

            # Try IPv6 in case IPv4 resolution has failed:
            if [[ $? -eq 2 ]]; then
                DST_IP="$(LANG=C getent ahostsv6 $DST_IP 2> /dev/null)"
            fi

            if [[ $? -ne 0 ]]; then
                echo $"Unable to resolve IP address specified in params" 1>&2
                exit 2
            fi

            DST_IP="$(echo "$DST_IP" | head -1 | cut --delimiter=' ' --fields=1)"
        fi
    fi
    if [ -z "$DST_IP" ] ; then
        echo $"Server address not specified in params" 1>&2
        exit 2
    fi
    
    # Get the primary interface
    local primary_interface=$(get_primary_interface)
    if [ -z "$primary_interface" ]; then
        echo "Error: No network interface found" >&2
        exit 1
    fi
    
    # Get the source IP if not specified
    local src_ip="$SRC_IP"
    if [ -z "$src_ip" ]; then
        src_ip=$(get_interface_ip "$primary_interface")
        if [ -z "$src_ip" ]; then
            echo "Error: Could not determine IP address for interface $primary_interface" >&2
            exit 1
        fi
    fi
    
    # Check if target already exists
    if [ -d "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" ]; then
        echo "Netconsole target already exists, removing it first..."
        rmdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" 2>/dev/null || true
    fi
    
    # Create the netconsole target
    echo "Creating netconsole target..."
    if ! mkdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET"; then
        echo "Error: Failed to create netconsole target" >&2
        exit 1
    fi
    
    # Configure the netconsole target
    echo "Configuring netconsole target..."
    
    # Set the device
    echo "$primary_interface" > "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/dev_name" 2>/dev/null || {
        echo "Error: Failed to set dev_name" >&2
        rmdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" 2>/dev/null || true
        exit 1
    }
    
    # Set the remote IP
    echo "$DST_IP" > "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/remote_ip" 2>/dev/null || {
        echo "Error: Failed to set remote_ip" >&2
        rmdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" 2>/dev/null || true
        exit 1
    }

    # Set the remote port
    echo "$DST_PORT" > "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/remote_port" 2>/dev/null || {
        echo "Error: Failed to set remote_ip" >&2
        rmdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" 2>/dev/null || true
        exit 1
    }
    
    # Set the local port
    echo "$SRC_PORT" > "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/local_port" 2>/dev/null || {
        echo "Error: Failed to set local_port" >&2
        rmdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" 2>/dev/null || true
        exit 1
    }
    
    # Enable the target
    echo "1" > "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/enabled" 2>/dev/null || {
        echo "Error: Failed to enable netconsole target" >&2
        rmdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" 2>/dev/null || true
        exit 1
    }
    
    echo "Netconsole started successfully"
    echo "  Source: $src_ip:$SRC_PORT (interface: $primary_interface)"
    echo "  Destination: $DST_IP:$DST_PORT"
}

# Function to stop netconsole
stop_netconsole() {
    echo "Stopping CloudLinux netconsole..."
    
    # Check if netconsole configfs is available
    if ! check_configfs; then
        echo "Netconsole configfs not available, nothing to stop"
        return 0
    fi
    
    # Check if target exists
    if [ ! -d "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" ]; then
        echo "Netconsole target does not exist, nothing to stop"
        return 0
    fi
    
    # Disable the target first
    echo "0" > "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/enabled" 2>/dev/null || true
    
    # Remove the target
    echo "Removing netconsole target..."
    if rmdir "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" 2>/dev/null; then
        echo "Netconsole stopped successfully"
    else
        echo "Error: Failed to remove netconsole target" >&2
        exit 1
    fi
}

# Function to show status
show_status() {
    # Also show module status
    if lsmod | grep -q "^$NETCONSOLE_MODULE "; then
        echo "Netconsole module is loaded"
    else
        echo "Netconsole module is not loaded"
    fi

    if [ ! -d "$NETCONSOLE_CONFIG_DIR" ]; then
        echo "Netconsole configfs not available"
        return 0
    fi
    
    if [ -d "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET" ]; then
        echo "Netconsole is active"
        echo "Current configuration:"
        
        if [ -f "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/dev_name" ]; then
            echo "  Device: $(cat "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/dev_name")"
        fi
        
        if [ -f "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/remote_ip" ]; then
            echo "  Remote: $(cat "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/remote_ip"):$(cat "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/remote_port")"
        fi
        
        if [ -f "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/local_port" ]; then
            echo "  Local port: $(cat "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/local_port")"
        fi
        
        if [ -f "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/enabled" ]; then
            local enabled=$(cat "$NETCONSOLE_CONFIG_DIR/$NETCONSOLE_TARGET/enabled")
            echo "  Enabled: $enabled"
        fi
    else
        echo "Netconsole is not active"
    fi
    
}

# Main script logic
case "$1" in
    start)
        start_netconsole
        ;;
    stop)
        stop_netconsole
        ;;
    restart)
        stop_netconsole
        sleep 1
        start_netconsole
        ;;
    status)
        show_status
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}" >&2
        echo ""
        echo "This script manages the CloudLinux netconsole service."
        echo ""
        echo "Hardcoded configuration:"
        echo "  Source port: $SRC_PORT"
        echo "  Destination IP: $DST_IP"
        echo "  Destination port: $DST_PORT"
        echo "  Source IP and interface: auto-detected"
        echo ""
        echo "The script uses /sys/kernel/config/netconsole/ for configuration"
        echo "and does not unload the netconsole module on stop."
        exit 1
        ;;
esac
