diff --git a/tools/pve/add-iptag.sh b/tools/pve/add-iptag.sh index e676cc3bb..7c33dc608 100644 --- a/tools/pve/add-iptag.sh +++ b/tools/pve/add-iptag.sh @@ -28,15 +28,22 @@ RD=$(echo "\033[01;31m") CL=$(echo "\033[m") BFR="\\r\\033[K" HOLD=" " -CM=" ✔️ ${CL}" -CROSS=" ✖️ ${CL}" +CM="${GN}✓${CL} " +CROSS="${RD}✗${CL} " + +# Stop any running spinner +stop_spinner() { + if [ -n "$SPINNER_PID" ] && kill -0 "$SPINNER_PID" 2>/dev/null; then + kill -TERM "$SPINNER_PID" 2>/dev/null + wait "$SPINNER_PID" 2>/dev/null + fi + SPINNER_PID="" + printf "\e[?25h\r" +} # Error handler for displaying error messages error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then - kill $SPINNER_PID >/dev/null - fi - printf "\e[?25h" + stop_spinner local exit_code="$?" local line_number="$1" local command="$2" @@ -46,57 +53,44 @@ error_handler() { # Spinner for progress indication spinner() { + local msg="$1" local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') local spin_i=0 local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" + + trap 'exit 0' TERM INT + printf "\e[?25l" 2>/dev/null while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" + printf "\r%s ${YW}%s${CL}" "${frames[spin_i]}" "$msg" 2>/dev/null || exit 0 spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" + sleep "$interval" || exit 0 done } # Info message msg_info() { local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & + stop_spinner + spinner "$msg" & SPINNER_PID=$! + disown $SPINNER_PID 2>/dev/null } # Success message msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then - kill $SPINNER_PID >/dev/null - fi - printf "\e[?25h" + stop_spinner local msg="$1" echo -e "${BFR}${CM}${GN}${msg}${CL}" } # Error message msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then - kill $SPINNER_PID >/dev/null - fi - printf "\e[?25h" + stop_spinner local msg="$1" echo -e "${BFR}${CROSS}${RD}${msg}${CL}" } -# Check if service exists -check_service_exists() { - if systemctl is-active --quiet iptag.service; then - return 0 - else - return 1 - fi -} - # Migrate configuration from old path to new migrate_config() { local old_config="/opt/lxc-iptag" @@ -113,6 +107,7 @@ migrate_config() { fi } + # Update existing installation update_installation() { msg_info "Updating IP-Tag Scripts" @@ -127,17 +122,19 @@ update_installation() { # Create new config file (check if exists and ask user) if [[ -f "/opt/iptag/iptag.conf" ]]; then echo -e "\n${YW}Configuration file already exists.${CL}" + echo -e "${YW}Note: No critical changes were made in this version.${CL}" while true; do read -p "Do you want to replace it with defaults? (y/n): " yn case $yn in [Yy]*) + interactive_config_setup msg_info "Replacing configuration file" generate_config >/opt/iptag/iptag.conf msg_ok "Configuration file replaced with defaults" break ;; [Nn]*) - echo -e "${GN}✔️ Keeping existing configuration file${CL}" + echo -e "${GN}✓ Keeping existing configuration file${CL}" break ;; *) @@ -146,6 +143,7 @@ update_installation() { esac done else + interactive_config_setup msg_info "Creating new configuration file" generate_config >/opt/iptag/iptag.conf msg_ok "Created new configuration file at /opt/iptag/iptag.conf" @@ -168,7 +166,7 @@ update_installation() { CONFIG_FILE="/opt/iptag/iptag.conf" SCRIPT_FILE="/opt/iptag/iptag" if [[ ! -f "$SCRIPT_FILE" ]]; then - echo "❌ Main script not found: $SCRIPT_FILE" + echo "✗ Main script not found: $SCRIPT_FILE" exit 1 fi export FORCE_SINGLE_RUN=true @@ -181,12 +179,193 @@ EOF systemctl daemon-reload &>/dev/null systemctl enable -q --now iptag.service &>/dev/null msg_ok "Updated IP-Tag Scripts" + + # Show configuration information after update + show_post_install_info +} + +# Install only command without service +install_command_only() { + msg_info "Installing IP-Tag Command Only" + + # Create directory if it doesn't exist + if [[ ! -d "/opt/iptag" ]]; then + mkdir -p /opt/iptag + fi + + # Migrate config if needed + migrate_config + + # Interactive configuration setup + if [[ ! -f /opt/iptag/iptag.conf ]]; then + interactive_config_setup_command + msg_info "Setup Configuration" + generate_config >/opt/iptag/iptag.conf + msg_ok "Created configuration file at /opt/iptag/iptag.conf" + else + stop_spinner + echo -e "\n${YW}Configuration file already exists.${CL}" + read -p "Do you want to reconfigure tag format? (y/n): " reconfigure + case $reconfigure in + [Yy]*) + interactive_config_setup_command + msg_info "Updating Configuration" + generate_config >/opt/iptag/iptag.conf + msg_ok "Updated configuration file" + ;; + *) + msg_ok "Keeping existing configuration file" + ;; + esac + fi + + # Setup main script + msg_info "Setup Main Script" + generate_main_script >/opt/iptag/iptag + chmod +x /opt/iptag/iptag + msg_ok "Created main script" + + # Create manual run command + msg_info "Creating iptag-run command" + cat <<'EOF' >/usr/local/bin/iptag-run +#!/usr/bin/env bash +CONFIG_FILE="/opt/iptag/iptag.conf" +SCRIPT_FILE="/opt/iptag/iptag" +if [[ ! -f "$SCRIPT_FILE" ]]; then + echo "✗ Main script not found: $SCRIPT_FILE" + exit 1 +fi +export FORCE_SINGLE_RUN=true +exec "$SCRIPT_FILE" +EOF + chmod +x /usr/local/bin/iptag-run + msg_ok "Created iptag-run command" + + msg_ok "IP-Tag Command installed successfully! Use 'iptag-run' to run manually." +} + +# Show post-installation information +show_post_install_info() { + stop_spinner + echo -e "\n${YW}=== Next Steps ===${CL}" + + # Show usage information + if command -v iptag-run >/dev/null 2>&1; then + echo -e "${YW}Run IP tagging manually: ${GN}iptag-run${CL}" + echo -e "${YW}Add to cron for scheduled execution if needed${CL}" + echo -e "" + fi + + echo -e "${RD}IMPORTANT: Configure your network subnets!${CL}" + echo -e "" + echo -e "${YW}Configuration file: ${GN}/opt/iptag/iptag.conf${CL}" + echo -e "" + echo -e "${YW}Edit CIDR_LIST with your actual subnets:${CL}" + echo -e "${GN}nano /opt/iptag/iptag.conf${CL} ${YW}or${CL} ${GN}vim /opt/iptag/iptag.conf${CL}" + echo -e "" + echo -e "${YW}Example configuration:${CL}" + echo -e "${GN}CIDR_LIST=(${CL}" + echo -e "${GN} 192.168.1.0/24 # Your actual subnet${CL}" + echo -e "${GN} 10.10.0.0/16 # Another subnet${CL}" + echo -e "${GN})${CL}" + echo -e "" +} + +# Interactive configuration setup for command-only (TAG_FORMAT only) +interactive_config_setup_command() { + echo -e "\n${YW}=== Configuration Setup ===${CL}" + + # TAG_FORMAT configuration + echo -e "\n${YW}Select tag format:${CL}" + echo -e "${GN}1)${CL} last_two_octets - Show last two octets (e.g., 0.100) [Default]" + echo -e "${GN}2)${CL} last_octet - Show only last octet (e.g., 100)" + echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)" + + while true; do + read -p "Enter your choice (1-3) [1]: " tag_choice + case ${tag_choice:-1} in + 1) + TAG_FORMAT="last_two_octets" + echo -e "${GN}✓ Selected: last_two_octets${CL}" + break + ;; + 2) + TAG_FORMAT="last_octet" + echo -e "${GN}✓ Selected: last_octet${CL}" + break + ;; + 3) + TAG_FORMAT="full" + echo -e "${GN}✓ Selected: full${CL}" + break + ;; + *) + echo -e "${RD}Please enter 1, 2, or 3.${CL}" + ;; + esac + done + + # Set default LOOP_INTERVAL for command mode + LOOP_INTERVAL=300 +} + +# Interactive configuration setup for service (TAG_FORMAT + LOOP_INTERVAL) +interactive_config_setup() { + echo -e "\n${YW}=== Configuration Setup ===${CL}" + + # TAG_FORMAT configuration + echo -e "\n${YW}Select tag format:${CL}" + echo -e "${GN}1)${CL} last_two_octets - Show last two octets (e.g., 0.100) [Default]" + echo -e "${GN}2)${CL} last_octet - Show only last octet (e.g., 100)" + echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)" + + while true; do + read -p "Enter your choice (1-3) [1]: " tag_choice + case ${tag_choice:-1} in + 1) + TAG_FORMAT="last_two_octets" + echo -e "${GN}✓ Selected: last_two_octets${CL}" + break + ;; + 2) + TAG_FORMAT="last_octet" + echo -e "${GN}✓ Selected: last_octet${CL}" + break + ;; + 3) + TAG_FORMAT="full" + echo -e "${GN}✓ Selected: full${CL}" + break + ;; + *) + echo -e "${RD}Please enter 1, 2, or 3.${CL}" + ;; + esac + done + + # LOOP_INTERVAL configuration + echo -e "\n${YW}Set check interval (in seconds):${CL}" + echo -e "${YW}Default: 300 seconds (5 minutes)${CL}" + echo -e "${YW}Recommended range: 300-3600 seconds${CL}" + + while true; do + read -p "Enter interval in seconds [300]: " interval_input + interval_input=${interval_input:-300} + + if [[ $interval_input =~ ^[0-9]+$ ]] && [ $interval_input -ge 300 ] && [ $interval_input -le 7200 ]; then + LOOP_INTERVAL=$interval_input + echo -e "${GN}✓ Selected: ${LOOP_INTERVAL} seconds${CL}" + break + else + echo -e "${RD}Please enter a valid number between 300 and 7200 seconds.${CL}" + fi + done } # Generate configuration file content generate_config() { cat </dev/null 2>&1; then vm_status=$(qm status "$vmid" 2>/dev/null | awk '{print $2}') fi if [[ "$vm_status" != "running" ]]; then - debug_log "vm $vmid: not running (status: $vm_status), skipping expensive detection" + debug_log "vm $vmid: not running (status: $vm_status)" return fi - # Cache for this execution - local cache_file="/tmp/iptag_vm_${vmid}_cache" - local cache_ttl=60 # 60 seconds cache - - # Check cache first - if [[ -f "$cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$cache_file" 2>/dev/null || echo 0))) -lt $cache_ttl ]]; then - local cached_ips=$(cat "$cache_file" 2>/dev/null) - if [[ -n "$cached_ips" ]]; then - debug_log "vm $vmid: using cached IPs: $cached_ips" - echo "$cached_ips" - return - fi - fi - - # Method 1: Quick ARP table lookup (fastest) + # Get MAC addresses from config local mac_addresses=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | head -3) debug_log "vm $vmid: found MACs: $mac_addresses" - # Quick ARP check without forced refresh (most common case) - for mac in $mac_addresses; do - local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') - local ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) - if [[ -n "$ip" && "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "vm $vmid: found IP $ip via quick ARP for MAC $mac_lower" - ips+="$ip " - fi - done - - # Early exit if we found IPs via ARP - if [[ -n "$ips" ]]; then - local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') - unique_ips="${unique_ips% }" - debug_log "vm $vmid: early exit with IPs: '$unique_ips'" - echo "$unique_ips" > "$cache_file" - echo "$unique_ips" - return - fi - - # Method 2: QM guest agent (fast if available) + # Method 1: QM guest agent (most reliable for current IP) if command -v qm >/dev/null 2>&1; then - local qm_ips=$(timeout 3 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -2) + debug_log "vm $vmid: trying qm guest agent first" + local qm_ips=$(timeout 8 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -3) for qm_ip in $qm_ips; do if [[ "$qm_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then debug_log "vm $vmid: found IP $qm_ip via qm guest cmd" @@ -458,130 +587,62 @@ get_vm_ips() { done fi - # Early exit if we found IPs via QM - if [[ -n "$ips" ]]; then - local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') - unique_ips="${unique_ips% }" - debug_log "vm $vmid: early exit with QM IPs: '$unique_ips'" - echo "$unique_ips" > "$cache_file" - echo "$unique_ips" - return - fi - - # Method 3: DHCP leases check (medium cost) - for mac in $mac_addresses; do - local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') - - for dhcp_file in "/var/lib/dhcp/dhcpd.leases" "/var/lib/dhcpcd5/dhcpcd.leases" "/tmp/dhcp.leases"; do - if [[ -f "$dhcp_file" ]]; then - local dhcp_ip=$(timeout 2 grep -A 10 "ethernet $mac_lower" "$dhcp_file" 2>/dev/null | grep "binding state active" -A 5 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | head -1) - if [[ -n "$dhcp_ip" && "$dhcp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "vm $vmid: found IP $dhcp_ip via DHCP leases for MAC $mac_lower" - ips+="$dhcp_ip " - break 2 + # Method 2: Fresh ARP table lookup (force refresh) + if [[ -n "$mac_addresses" ]]; then + debug_log "vm $vmid: refreshing ARP table and checking" + # Try to refresh ARP table by pinging network ranges + for mac in $mac_addresses; do + local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') + + # First check current ARP table + local current_ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) + + # If found in ARP, verify it's still valid by trying to ping + if [[ -n "$current_ip" && "$current_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found IP $current_ip in ARP table for MAC $mac_lower, verifying..." + # Quick ping test to verify IP is still active + if timeout 2 ping -c 1 "$current_ip" >/dev/null 2>&1; then + debug_log "vm $vmid: verified IP $current_ip is active via ping" + ips+="$current_ip " + else + debug_log "vm $vmid: IP $current_ip failed ping verification, removing from ARP" + # Remove stale ARP entry + ip neighbor del "$current_ip" dev $(ip route get "$current_ip" 2>/dev/null | grep -oE 'dev [^ ]+' | cut -d' ' -f2) 2>/dev/null || true fi fi done - done - - # Early exit if we found IPs via DHCP - if [[ -n "$ips" ]]; then - local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') - unique_ips="${unique_ips% }" - debug_log "vm $vmid: early exit with DHCP IPs: '$unique_ips'" - echo "$unique_ips" > "$cache_file" - echo "$unique_ips" - return fi - # Method 4: Limited network discovery (expensive - only if really needed) - debug_log "vm $vmid: falling back to limited network discovery" - - for mac in $mac_addresses; do - local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') - - # Get bridge interfaces - local bridges=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "bridge=\w+" | cut -d= -f2 | head -1) - for bridge in $bridges; do - if [[ -n "$bridge" && -d "/sys/class/net/$bridge" ]]; then - # Get bridge IP range - local bridge_ip=$(ip addr show "$bridge" 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]+' | head -1) - if [[ -n "$bridge_ip" ]]; then - local network=$(echo "$bridge_ip" | cut -d'/' -f1) - debug_log "vm $vmid: limited scan on bridge $bridge network $bridge_ip" - - # Force ARP refresh with broadcast ping (limited) - IFS='.' read -r a b c d <<< "$network" - local broadcast="$a.$b.$c.255" - timeout 1 ping -c 1 -b "$broadcast" >/dev/null 2>&1 || true - - # Check ARP again after refresh - sleep 0.5 - local ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) - if [[ -n "$ip" && "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "vm $vmid: found IP $ip via ARP after broadcast for MAC $mac_lower" - ips+="$ip " - break 2 - fi - - # Only do very limited ping scan (reduced range) - IFS='.' read -r a b c d <<< "$network" - local base_net="$a.$b.$c" - - # Try only most common ranges (much smaller than before) - for last_octet in {100..105} {200..205}; do - local test_ip="$base_net.$last_octet" - - # Very quick ping test (reduced timeout) - if timeout 0.2 ping -c 1 -W 1 "$test_ip" >/dev/null 2>&1; then - # Check if this IP corresponds to our MAC - sleep 0.1 - local found_mac=$(ip neighbor show "$test_ip" 2>/dev/null | grep -oE "([0-9a-f]{2}:){5}[0-9a-f]{2}") - if [[ "$found_mac" == "$mac_lower" ]]; then - debug_log "vm $vmid: found IP $test_ip via limited ping scan for MAC $mac_lower" - ips+="$test_ip " - break 2 - fi - fi - done - - # Skip extended scanning entirely (too expensive) - debug_log "vm $vmid: skipping extended scan to preserve CPU" - fi - fi - done - done - - # Method 5: Static configuration check (fast) + # Method 3: DHCP leases (backup method) if [[ -z "$ips" ]]; then - debug_log "vm $vmid: checking for static IP configuration" - - # Check cloud-init configuration if exists - local cloudinit_file="/var/lib/vz/snippets/${vmid}-cloud-init.yml" - if [[ -f "$cloudinit_file" ]]; then - local static_ip=$(grep -E "addresses?:" "$cloudinit_file" 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) - if [[ -n "$static_ip" && "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "vm $vmid: found static IP $static_ip in cloud-init config" - ips+="$static_ip " - fi - fi - - # Check VM config for any IP hints - local config_ip=$(grep -E "(ip=|gw=)" "$vm_config" 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) - if [[ -n "$config_ip" && "$config_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "vm $vmid: found IP hint $config_ip in VM config" - ips+="$config_ip " - fi + debug_log "vm $vmid: checking DHCP leases as fallback" + for mac in $mac_addresses; do + local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') + for dhcp_file in "/var/lib/dhcp/dhcpd.leases" "/var/lib/dhcpcd5/dhcpcd.leases"; do + if [[ -f "$dhcp_file" ]]; then + # Look for most recent lease for this MAC + local dhcp_ip=$(tac "$dhcp_file" 2>/dev/null | grep -A 10 "ethernet $mac_lower" | grep "binding state active" -A 5 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | head -1) + if [[ -n "$dhcp_ip" && "$dhcp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found IP $dhcp_ip via DHCP leases" + # Verify this IP responds + if timeout 2 ping -c 1 "$dhcp_ip" >/dev/null 2>&1; then + debug_log "vm $vmid: verified DHCP IP $dhcp_ip is active" + ips+="$dhcp_ip " + break 2 + else + debug_log "vm $vmid: DHCP IP $dhcp_ip failed ping test" + fi + fi + fi + done + done fi - # Remove duplicates and cache result + # Remove duplicates and clean up local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') unique_ips="${unique_ips% }" - # Cache the result (even if empty) - echo "$unique_ips" > "$cache_file" - - debug_log "vm $vmid: final optimized IPs: '$unique_ips'" + debug_log "vm $vmid: final IPs: '$unique_ips'" echo "$unique_ips" } @@ -646,10 +707,15 @@ update_tags() { return fi - # Add new IP tags + # Prepend new IP tags to the beginning of the tag list + local final_tags=() for new_ip in "${formatted_ips[@]}"; do - next_tags+=("$new_ip") + final_tags+=("$new_ip") done + for tag in "${next_tags[@]}"; do + final_tags+=("$tag") + done + next_tags=("${final_tags[@]}") # Update tags if there are changes local old_tags_str=$(IFS=';'; echo "${current_tags[*]}") @@ -764,23 +830,16 @@ update_all_tags() { # Display processing header with color if [[ "$type" == "lxc" ]]; then - log_info "Processing ${WHITE}${count}${NC} LXC container(s) in parallel" - - # Clean up old cache files before processing LXC - cleanup_vm_cache - - # Process LXC containers in parallel for better performance - process_lxc_parallel "${vmids[@]}" + log_info "Processing ${WHITE}${count}${NC} LXC container(s) sequentially" else - log_info "Processing ${WHITE}${count}${NC} virtual machine(s) in parallel" - - # Clean up old cache files before processing VMs - cleanup_vm_cache - - # Process VMs in parallel for better performance - process_vms_parallel "${vmids[@]}" + log_info "Processing ${WHITE}${count}${NC} virtual machine(s) sequentially" fi + # Process each VM/LXC container sequentially + for vmid in "${vmids[@]}"; do + update_tags "$type" "$vmid" + done + # Add completion message if [[ "$type" == "lxc" ]]; then log_success "Completed processing LXC containers" @@ -805,88 +864,26 @@ check_status_changed() { # Main check function check() { - local current_time changes_detected=false - current_time=$(date +%s) - - local update_lxc=false - local update_vm=false - - # Periodic cache cleanup (every 10 minutes) - local time_since_last_cleanup=$((current_time - ${last_cleanup_time:-0})) - if [[ $time_since_last_cleanup -ge 600 ]]; then - cleanup_vm_cache - last_cleanup_time=$current_time - debug_log "Performed periodic cache cleanup" - fi - - # Check LXC status - local time_since_last_lxc_check=$((current_time - last_lxc_status_check_time)) - if [[ "${LXC_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ - [[ "$time_since_last_lxc_check" -ge "${LXC_STATUS_CHECK_INTERVAL:-60}" ]]; then - last_lxc_status_check_time=$current_time - if check_status_changed "lxc"; then - update_lxc=true - log_warning "LXC status changes detected" - fi - fi - - # Check VM status - local time_since_last_vm_check=$((current_time - last_vm_status_check_time)) - if [[ "${VM_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ - [[ "$time_since_last_vm_check" -ge "${VM_STATUS_CHECK_INTERVAL:-60}" ]]; then - last_vm_status_check_time=$current_time - if check_status_changed "vm"; then - update_vm=true - log_warning "VM status changes detected" - fi - fi - - # Check network interface changes - local time_since_last_fw_check=$((current_time - last_fw_net_interface_check_time)) - if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" -gt 0 ]] && \ - [[ "$time_since_last_fw_check" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" ]]; then - last_fw_net_interface_check_time=$current_time - if check_status_changed "fw"; then - update_lxc=true - update_vm=true - log_warning "Network interface changes detected" - fi - fi - - # Force update if interval exceeded - for type in "lxc" "vm"; do - local last_update_var="last_update_${type}_time" - local time_since_last_update=$((current_time - ${!last_update_var})) - if [[ $time_since_last_update -ge ${FORCE_UPDATE_INTERVAL:-1800} ]]; then - if [[ "$type" == "lxc" ]]; then - update_lxc=true - log_info "Scheduled LXC update (every $((FORCE_UPDATE_INTERVAL / 60)) minutes)" - else - update_vm=true - log_info "Scheduled VM update (every $((FORCE_UPDATE_INTERVAL / 60)) minutes)" - fi - eval "${last_update_var}=${current_time}" - fi - done - - # Final execution - $update_lxc && update_all_tags "lxc" - $update_vm && update_all_tags "vm" + local current_time=$(date +%s) + + # Simple periodic check - always update both LXC and VM every loop + log_info "Starting periodic check" + + # Update LXC containers + update_all_tags "lxc" + + # Update VMs + update_all_tags "vm" } -# Initialize time variables -declare -g last_lxc_status="" last_vm_status="" last_fw_status="" -declare -g last_lxc_status_check_time=0 last_vm_status_check_time=0 last_fw_net_interface_check_time=0 -declare -g last_update_lxc_time=0 last_update_vm_time=0 last_cleanup_time=0 - # Main loop main() { # Display startup message echo -e "\n${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" log_success "IP-Tag service started successfully" - echo -e "${BLUE}ℹ${NC} Loop interval: ${WHITE}${LOOP_INTERVAL:-$DEFAULT_CHECK_INTERVAL}${NC} seconds" + echo -e "${BLUE}ℹ${NC} Loop interval: ${WHITE}${LOOP_INTERVAL:-300}${NC} seconds" echo -e "${BLUE}ℹ${NC} Debug mode: ${WHITE}${DEBUG:-false}${NC}" - echo -e "${BLUE}ℹ${NC} Tag format: ${WHITE}${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}${NC}" + echo -e "${BLUE}ℹ${NC} Tag format: ${WHITE}${TAG_FORMAT:-full}${NC}" echo -e "${BLUE}ℹ${NC} Allowed CIDRs: ${WHITE}${CIDR_LIST[*]}${NC}" echo -e "${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" @@ -902,336 +899,57 @@ main() { } -# Cache cleanup function -cleanup_vm_cache() { - local cache_dir="/tmp" - local vm_cache_ttl=${VM_IP_CACHE_TTL:-120} - local lxc_cache_ttl=${LXC_IP_CACHE_TTL:-120} - local status_cache_ttl=${LXC_STATUS_CACHE_TTL:-30} - local current_time=$(date +%s) - - debug_log "Starting extreme cache cleanup" - - # Clean VM cache files - for cache_file in "$cache_dir"/iptag_vm_*_cache; do - if [[ -f "$cache_file" ]]; then - local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) - if [[ $((current_time - file_time)) -gt $vm_cache_ttl ]]; then - rm -f "$cache_file" 2>/dev/null - debug_log "Cleaned up expired VM cache file: $cache_file" - fi - fi - done - - # Clean LXC IP cache files - for cache_file in "$cache_dir"/iptag_lxc_*_cache; do - if [[ -f "$cache_file" ]]; then - local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) - if [[ $((current_time - file_time)) -gt $lxc_cache_ttl ]]; then - rm -f "$cache_file" 2>/dev/null - # Also clean meta files - rm -f "${cache_file}.meta" 2>/dev/null - debug_log "Cleaned up expired LXC cache file: $cache_file" - fi - fi - done - - # Clean LXC status cache files (shorter TTL) - for cache_file in "$cache_dir"/iptag_lxc_status_*_cache; do - if [[ -f "$cache_file" ]]; then - local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) - if [[ $((current_time - file_time)) -gt $status_cache_ttl ]]; then - rm -f "$cache_file" 2>/dev/null - debug_log "Cleaned up expired LXC status cache: $cache_file" - fi - fi - done - - # Clean LXC PID cache files (60 second TTL) - for cache_file in "$cache_dir"/iptag_lxc_pid_*_cache; do - if [[ -f "$cache_file" ]]; then - local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) - if [[ $((current_time - file_time)) -gt 60 ]]; then - rm -f "$cache_file" 2>/dev/null - debug_log "Cleaned up expired LXC PID cache: $cache_file" - fi - fi - done - - # Clean any orphaned meta files - for meta_file in "$cache_dir"/iptag_*.meta; do - if [[ -f "$meta_file" ]]; then - local base_file="${meta_file%.meta}" - if [[ ! -f "$base_file" ]]; then - rm -f "$meta_file" 2>/dev/null - debug_log "Cleaned up orphaned meta file: $meta_file" - fi - fi - done - - debug_log "Completed extreme cache cleanup" -} -# Parallel VM processing function -process_vms_parallel() { - local vm_list=("$@") - local max_parallel=${MAX_PARALLEL_VM_CHECKS:-5} - local job_count=0 - local pids=() - local pid_start_times=() - - for vmid in "${vm_list[@]}"; do - if [[ $job_count -ge $max_parallel ]]; then - local pid_to_wait="${pids[0]}" - local start_time="${pid_start_times[0]}" - local waited=0 - while kill -0 "$pid_to_wait" 2>/dev/null && [[ $waited -lt 10 ]]; do - sleep 1 - ((waited++)) - done - if kill -0 "$pid_to_wait" 2>/dev/null; then - kill -9 "$pid_to_wait" 2>/dev/null - log_warning "VM parallel: killed stuck process $pid_to_wait after 10s timeout" - else - wait "$pid_to_wait" - fi - pids=("${pids[@]:1}") - pid_start_times=("${pid_start_times[@]:1}") - ((job_count--)) - fi - # Start background job - (update_tags "vm" "$vmid") & - pids+=($!) - pid_start_times+=("$(date +%s)") - ((job_count++)) - done - for i in "${!pids[@]}"; do - local pid="${pids[$i]}" - local waited=0 - while kill -0 "$pid" 2>/dev/null && [[ $waited -lt 10 ]]; do - sleep 1 - ((waited++)) - done - if kill -0 "$pid" 2>/dev/null; then - kill -9 "$pid" 2>/dev/null - log_warning "VM parallel: killed stuck process $pid after 10s timeout" - else - wait "$pid" - fi - done -} -# Parallel LXC processing function -process_lxc_parallel() { - local lxc_list=("$@") - local max_parallel=${MAX_PARALLEL_LXC_CHECKS:-2} - local batch_size=${LXC_BATCH_SIZE:-20} - local job_count=0 - local pids=() - local pid_start_times=() - - debug_log "Starting parallel LXC processing: ${#lxc_list[@]} containers, max_parallel=$max_parallel" - - if [[ ${#lxc_list[@]} -gt 5 ]]; then - debug_log "Pre-loading LXC statuses for ${#lxc_list[@]} containers" - local all_statuses=$(pct list 2>/dev/null) - for vmid in "${lxc_list[@]}"; do - local status=$(echo "$all_statuses" | grep "^$vmid" | awk '{print $2}') - if [[ -n "$status" ]]; then - local status_cache_file="/tmp/iptag_lxc_status_${vmid}_cache" - echo "$status" > "$status_cache_file" 2>/dev/null & - fi - done - wait - debug_log "Completed batch status pre-loading" - fi - for vmid in "${lxc_list[@]}"; do - if [[ $job_count -ge $max_parallel ]]; then - local pid_to_wait="${pids[0]}" - local start_time="${pid_start_times[0]}" - local waited=0 - while kill -0 "$pid_to_wait" 2>/dev/null && [[ $waited -lt 10 ]]; do - sleep 1 - ((waited++)) - done - if kill -0 "$pid_to_wait" 2>/dev/null; then - kill -9 "$pid_to_wait" 2>/dev/null - log_warning "LXC parallel: killed stuck process $pid_to_wait after 10s timeout" - else - wait "$pid_to_wait" - fi - pids=("${pids[@]:1}") - pid_start_times=("${pid_start_times[@]:1}") - ((job_count--)) - fi - # Start background job with higher priority - (update_tags "lxc" "$vmid") & - pids+=($!) - pid_start_times+=("$(date +%s)") - ((job_count++)) - done - for i in "${!pids[@]}"; do - local pid="${pids[$i]}" - local waited=0 - while kill -0 "$pid" 2>/dev/null && [[ $waited -lt 10 ]]; do - sleep 1 - ((waited++)) - done - if kill -0 "$pid" 2>/dev/null; then - kill -9 "$pid" 2>/dev/null - log_warning "LXC parallel: killed stuck process $pid after 10s timeout" - else - wait "$pid" - fi - done - debug_log "Completed parallel LXC processing" -} -# Optimized LXC IP detection with caching and alternative methods +# Simple LXC IP detection get_lxc_ips() { local vmid=$1 - local status_cache_file="/tmp/iptag_lxc_status_${vmid}_cache" - local status_cache_ttl=${LXC_STATUS_CACHE_TTL:-30} - debug_log "lxc $vmid: starting extreme optimized IP detection" - - # Check status cache first (avoid expensive pct status calls) - local lxc_status="" - if [[ -f "$status_cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$status_cache_file" 2>/dev/null || echo 0))) -lt $status_cache_ttl ]]; then - lxc_status=$(cat "$status_cache_file" 2>/dev/null) - debug_log "lxc $vmid: using cached status: $lxc_status" - else - lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}') - echo "$lxc_status" > "$status_cache_file" 2>/dev/null - debug_log "lxc $vmid: fetched fresh status: $lxc_status" - fi + debug_log "lxc $vmid: starting IP detection" + # Check if LXC is running + local lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}') if [[ "$lxc_status" != "running" ]]; then debug_log "lxc $vmid: not running (status: $lxc_status)" return fi local ips="" - local method_used="" - # EXTREME Method 1: Direct Proxmox config inspection (super fast) - debug_log "lxc $vmid: trying direct Proxmox config inspection" + # Method 1: Check Proxmox config for static IP local pve_lxc_config="/etc/pve/lxc/${vmid}.conf" if [[ -f "$pve_lxc_config" ]]; then local static_ip=$(grep -E "^net[0-9]+:" "$pve_lxc_config" 2>/dev/null | grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d'=' -f2 | head -1) - debug_log "lxc $vmid: [CONFIG] static_ip='$static_ip' (from $pve_lxc_config)" if [[ -n "$static_ip" && "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "lxc $vmid: found static IP $static_ip in Proxmox config" + debug_log "lxc $vmid: found static IP $static_ip in config" ips="$static_ip" - method_used="proxmox_config" fi - else - debug_log "lxc $vmid: [CONFIG] config file not found: $pve_lxc_config" fi - # EXTREME Method 2: Direct network namespace inspection (fastest dynamic) + # Method 2: ARP table lookup if no static IP + if [[ -z "$ips" && -f "$pve_lxc_config" ]]; then + local mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2) + if [[ -n "$mac_addr" ]]; then + local bridge_name=$(grep -Eo 'bridge=[^,]+' "$pve_lxc_config" | head -1 | cut -d'=' -f2) + local arp_ip=$(ip neighbor show | grep "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) + if [[ -n "$arp_ip" && "$arp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "lxc $vmid: found IP $arp_ip via ARP table" + ips="$arp_ip" + fi + fi + fi + + # Method 3: Direct container command if ARP failed if [[ -z "$ips" ]]; then - debug_log "lxc $vmid: trying optimized namespace inspection" - local ns_file="/var/lib/lxc/${vmid}/rootfs/proc/net/fib_trie" - if [[ -f "$ns_file" ]]; then - local ns_ip=$(timeout 1 grep -m1 -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' "$ns_file" 2>/dev/null | grep -v '127.0.0.1' | head -1) - debug_log "lxc $vmid: [NAMESPACE] ns_ip='$ns_ip'" - if [[ -n "$ns_ip" ]] && is_valid_ipv4 "$ns_ip"; then - debug_log "lxc $vmid: found IP $ns_ip via namespace inspection" - ips="$ns_ip" - method_used="namespace" - fi - else - debug_log "lxc $vmid: [NAMESPACE] ns_file not found: $ns_file" + local container_ip=$(timeout 5s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) + if [[ -n "$container_ip" ]] && is_valid_ipv4 "$container_ip"; then + debug_log "lxc $vmid: found IP $container_ip via pct exec" + ips="$container_ip" fi fi - # EXTREME Method 3: Batch ARP table lookup (if namespace failed) - if [[ -z "$ips" ]]; then - debug_log "lxc $vmid: trying batch ARP lookup" - local bridge_name=""; local mac_addr="" - if [[ -f "$pve_lxc_config" ]]; then - bridge_name=$(grep -Eo 'bridge=[^,]+' "$pve_lxc_config" | head -1 | cut -d'=' -f2) - mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2) - debug_log "lxc $vmid: [ARP] bridge_name='$bridge_name' mac_addr='$mac_addr' (from $pve_lxc_config)" - fi - if [[ -z "$bridge_name" || -z "$mac_addr" ]]; then - local lxc_config="/var/lib/lxc/${vmid}/config" - if [[ -f "$lxc_config" ]]; then - [[ -z "$bridge_name" ]] && bridge_name=$(grep "lxc.net.0.link" "$lxc_config" 2>/dev/null | cut -d'=' -f2 | tr -d ' ') - [[ -z "$mac_addr" ]] && mac_addr=$(grep "lxc.net.0.hwaddr" "$lxc_config" 2>/dev/null | cut -d'=' -f2 | tr -d ' ') - debug_log "lxc $vmid: [ARP] bridge_name='$bridge_name' mac_addr='$mac_addr' (from $lxc_config)" - else - debug_log "lxc $vmid: [ARP] lxc config not found: $lxc_config" - fi - fi - if [[ -n "$bridge_name" && -n "$mac_addr" ]]; then - local bridge_ip=$(ip neighbor show dev "$bridge_name" 2>/dev/null | grep "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) - debug_log "lxc $vmid: [ARP] bridge_ip='$bridge_ip'" - if [[ -n "$bridge_ip" && "$bridge_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "lxc $vmid: found IP $bridge_ip via ARP table" - ips="$bridge_ip" - method_used="arp_table" - fi - fi - fi - - # EXTREME Method 4: Fast process namespace (if ARP failed) - if [[ -z "$ips" ]] && [[ "${LXC_SKIP_SLOW_METHODS:-true}" != "true" ]]; then - debug_log "lxc $vmid: trying fast process namespace" - local pid_cache_file="/tmp/iptag_lxc_pid_${vmid}_cache" - local container_pid="" - if [[ -f "$pid_cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$pid_cache_file" 2>/dev/null || echo 0))) -lt 60 ]]; then - container_pid=$(cat "$pid_cache_file" 2>/dev/null) - else - container_pid=$(pct list 2>/dev/null | grep "^$vmid" | awk '{print $3}') - [[ -n "$container_pid" && "$container_pid" != "-" ]] && echo "$container_pid" > "$pid_cache_file" - fi - debug_log "lxc $vmid: [PROCESS_NS] container_pid='$container_pid'" - if [[ -n "$container_pid" && "$container_pid" != "-" ]]; then - local ns_ip=$(timeout 1 nsenter -t "$container_pid" -n ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) - debug_log "lxc $vmid: [PROCESS_NS] ns_ip='$ns_ip'" - if [[ -n "$ns_ip" ]] && is_valid_ipv4 "$ns_ip"; then - debug_log "lxc $vmid: found IP $ns_ip via process namespace" - ips="$ns_ip" - method_used="process_ns" - fi - fi - fi - - # Fallback: always do lxc-attach/pct exec with timeout if nothing found - if [[ -z "$ips" && "${LXC_ALLOW_FORCED_COMMANDS:-true}" == "true" ]]; then - debug_log "lxc $vmid: trying fallback lxc-attach (forced)" - local attach_ip="" - attach_ip=$(timeout 7s lxc-attach -n "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) - local attach_status=$? - debug_log "lxc $vmid: [LXC_ATTACH] attach_ip='$attach_ip' status=$attach_status" - if [[ $attach_status -eq 124 ]]; then - debug_log "lxc $vmid: lxc-attach timed out after 7s" - fi - if [[ -n "$attach_ip" ]] && is_valid_ipv4 "$attach_ip"; then - debug_log "lxc $vmid: found IP $attach_ip via lxc-attach (forced)" - ips="$attach_ip" - method_used="lxc_attach_forced" - fi - fi - if [[ -z "$ips" && "${LXC_ALLOW_FORCED_COMMANDS:-true}" == "true" ]]; then - debug_log "lxc $vmid: trying fallback pct exec (forced)" - local pct_ip="" - pct_ip=$(timeout 7s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) - local pct_status=$? - debug_log "lxc $vmid: [PCT_EXEC] pct_ip='$pct_ip' status=$pct_status" - if [[ $pct_status -eq 124 ]]; then - debug_log "lxc $vmid: pct exec timed out after 7s" - fi - if [[ -n "$pct_ip" ]] && is_valid_ipv4 "$pct_ip"; then - debug_log "lxc $vmid: found IP $pct_ip via pct exec (forced)" - ips="$pct_ip" - method_used="pct_exec_forced" - fi - fi - - debug_log "lxc $vmid: [RESULT] ips='$ips' method='$method_used'" + debug_log "lxc $vmid: final IPs: '$ips'" echo "$ips" } @@ -1239,28 +957,44 @@ main EOF } -# Main installation process -if check_service_exists; then - while true; do - read -p "IP-Tag service is already installed. Do you want to update it? (y/n): " yn - case $yn in - [Yy]*) +# Choose installation/update mode +echo -e "\n${YW}Choose action:${CL}" +echo -e "${GN}1)${CL} Install with automatic service (recommended)" +echo -e "${GN}2)${CL} Install command only (manual execution)" +echo -e "${GN}3)${CL} Update existing installation" +echo -e "${RD}4)${CL} Cancel" + +while true; do + read -p "Enter your choice (1-4): " choice + case $choice in + 1) + INSTALL_MODE="service" + echo -e "${GN}✓ Selected: Service installation${CL}" + break + ;; + 2) + INSTALL_MODE="command" + echo -e "${GN}✓ Selected: Command-only installation${CL}" + break + ;; + 3) + echo -e "${GN}✓ Selected: Update installation${CL}" update_installation exit 0 ;; - [Nn]*) - msg_error "Installation cancelled." + 4) + msg_error "Action cancelled." exit 0 ;; *) - msg_error "Please answer yes or no." + msg_error "Please enter 1, 2, 3, or 4." ;; - esac - done -fi + esac +done +echo -e "\n${YW}This will install ${APP} on ${hostname} in $INSTALL_MODE mode.${CL}" while true; do - read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn + read -p "Proceed? (y/n): " yn case $yn in [Yy]*) break @@ -1277,83 +1011,97 @@ done if ! pveversion | grep -Eq "pve-manager/(8\.[0-4]|9\.[0-9]+)(\.[0-9]+)*"; then msg_error "This version of Proxmox Virtual Environment is not supported" - msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0–8.4 or 9.x." + msg_error "⚠ Requires Proxmox Virtual Environment Version 8.0–8.4 or 9.x." msg_error "Exiting..." sleep 2 exit fi -FILE_PATH="/usr/local/bin/iptag" -if [[ -f "$FILE_PATH" ]]; then - msg_info "The file already exists: '$FILE_PATH'. Skipping installation." - exit 0 -fi - msg_info "Installing Dependencies" apt-get update &>/dev/null apt-get install -y ipcalc net-tools &>/dev/null msg_ok "Installed Dependencies" -msg_info "Setting up IP-Tag Scripts" -mkdir -p /opt/iptag -msg_ok "Setup IP-Tag Scripts" +# Execute installation based on selected mode +if [[ "$INSTALL_MODE" == "service" ]]; then + # Full service installation + msg_info "Setting up IP-Tag Scripts" + mkdir -p /opt/iptag + msg_ok "Setup IP-Tag Scripts" -# Migrate config if needed -migrate_config + # Migrate config if needed + migrate_config -msg_info "Setup Default Config" -if [[ ! -f /opt/iptag/iptag.conf ]]; then - generate_config >/opt/iptag/iptag.conf - msg_ok "Setup default config" -else - msg_ok "Default config already exists" -fi + # Interactive configuration setup + if [[ ! -f /opt/iptag/iptag.conf ]]; then + interactive_config_setup + msg_info "Setup Default Config" + generate_config >/opt/iptag/iptag.conf + msg_ok "Setup default config" + else + stop_spinner + echo -e "\n${YW}Configuration file already exists.${CL}" + read -p "Do you want to reconfigure tag format and loop interval? (y/n): " reconfigure + case $reconfigure in + [Yy]*) + interactive_config_setup + msg_info "Updating Configuration" + generate_config >/opt/iptag/iptag.conf + msg_ok "Updated configuration file" + ;; + *) + msg_ok "Keeping existing configuration file" + ;; + esac + fi -msg_info "Setup Main Function" -if [[ ! -f /opt/iptag/iptag ]]; then + msg_info "Setup Main Function" generate_main_script >/opt/iptag/iptag chmod +x /opt/iptag/iptag msg_ok "Setup Main Function" -else - msg_ok "Main Function already exists" -fi -msg_info "Creating Service" -if [[ ! -f /lib/systemd/system/iptag.service ]]; then + msg_info "Creating Service" generate_service >/lib/systemd/system/iptag.service msg_ok "Created Service" -else - msg_ok "Service already exists." -fi -msg_ok "Setup IP-Tag Scripts" + msg_info "Starting Service" + systemctl daemon-reload &>/dev/null + systemctl enable -q --now iptag.service &>/dev/null + msg_ok "Started Service" -msg_info "Starting Service" -systemctl daemon-reload &>/dev/null -systemctl enable -q --now iptag.service &>/dev/null -msg_ok "Started Service" - -msg_info "Restarting Service with optimizations" -systemctl restart iptag.service &>/dev/null -msg_ok "Service restarted with CPU optimizations" - -msg_info "Creating manual run command" -cat <<'EOF' >/usr/local/bin/iptag-run + msg_info "Creating manual run command" + cat <<'EOF' >/usr/local/bin/iptag-run #!/usr/bin/env bash CONFIG_FILE="/opt/iptag/iptag.conf" SCRIPT_FILE="/opt/iptag/iptag" if [[ ! -f "$SCRIPT_FILE" ]]; then - echo "❌ Main script not found: $SCRIPT_FILE" + echo "✗ Main script not found: $SCRIPT_FILE" exit 1 fi export FORCE_SINGLE_RUN=true exec "$SCRIPT_FILE" EOF -chmod +x /usr/local/bin/iptag-run -msg_ok "Created iptag-run executable - You can execute this manually by entering “iptag-run” in the Proxmox host, so the script is executed by hand." + chmod +x /usr/local/bin/iptag-run + msg_ok "Created iptag-run command" + + echo -e "\n${GN}${APP} service installation completed successfully! ${CL}" + echo -e "${YW}The service is now running automatically.${CL}" + echo -e "${YW}You can also run it manually with: ${GN}iptag-run${CL}\n" + + # Show configuration information + show_post_install_info + +elif [[ "$INSTALL_MODE" == "command" ]]; then + # Command-only installation + install_command_only + + stop_spinner + echo -e "\n${GN}${APP} command installation completed successfully! ${CL}" + + # Show configuration information + show_post_install_info +fi -SPINNER_PID="" -echo -e "\n${APP} installation completed successfully! ${CL}\n" - -# Proper script termination +# Clean up any running spinner and exit +stop_spinner exit 0