|
#!/usr/bin/env bash |
|
|
|
# === VMware IP Address Updater === |
|
# Cross-platform script to update VMware Workstation IP in hosts file |
|
# Supports Windows (Git Bash/MSYS), WSL, Linux, and macOS |
|
|
|
# === DEFAULT CONFIG === |
|
DEFAULT_IP="192.168.1.100" # Default placeholder IP - update as needed |
|
DEFAULT_DOMAIN="vmware.local" # Default domain name |
|
|
|
# Accept environment variables for remote execution |
|
if [[ -n "$VMWARE_IP" ]]; then |
|
DEFAULT_IP="$VMWARE_IP" |
|
print_info "Using IP from environment: $VMWARE_IP" |
|
fi |
|
|
|
if [[ -n "$VMWARE_DOMAIN" ]]; then |
|
DEFAULT_DOMAIN="$VMWARE_DOMAIN" |
|
print_info "Using domain from environment: $VMWARE_DOMAIN" |
|
fi |
|
|
|
# Colors for output |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
BLUE='\033[0;34m' |
|
NC='\033[0m' # No Color |
|
|
|
# === FUNCTIONS === |
|
print_info() { |
|
echo -e "${BLUE}[INFO]${NC} $1" |
|
} |
|
|
|
print_success() { |
|
echo -e "${GREEN}[✅]${NC} $1" |
|
} |
|
|
|
print_warning() { |
|
echo -e "${YELLOW}[⚠️]${NC} $1" |
|
} |
|
|
|
print_error() { |
|
echo -e "${RED}[❌]${NC} $1" |
|
} |
|
|
|
validate_ip() { |
|
local ip="$1" |
|
local regex='^([0-9]{1,3}\.){3}[0-9]{1,3}$' |
|
|
|
if [[ $ip =~ $regex ]]; then |
|
# Check each octet is between 0-255 |
|
IFS='.' read -ra ADDR <<< "$ip" |
|
for i in "${ADDR[@]}"; do |
|
if [[ $i -lt 0 ]] || [[ $i -gt 255 ]]; then |
|
return 1 |
|
fi |
|
done |
|
return 0 |
|
fi |
|
return 1 |
|
} |
|
|
|
# Load configuration from .env file if it exists |
|
load_env_config() { |
|
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|
local env_file="$script_dir/.env" |
|
|
|
if [[ -f "$env_file" ]]; then |
|
print_info "Loading configuration from .env file..." |
|
|
|
# Source the .env file safely |
|
while IFS='=' read -r key value; do |
|
# Skip empty lines and comments |
|
[[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue |
|
|
|
# Remove quotes and whitespace |
|
key=$(echo "$key" | xargs) |
|
value=$(echo "$value" | sed 's/^["'\'']//' | sed 's/["'\'']$//' | xargs) |
|
|
|
case "$key" in |
|
"VMWARE_IP") |
|
if validate_ip "$value"; then |
|
DEFAULT_IP="$value" |
|
print_info "Loaded IP from .env: $DEFAULT_IP" |
|
else |
|
print_warning "Invalid IP in .env file: $value (using default)" |
|
fi |
|
;; |
|
"VMWARE_DOMAIN") |
|
if [[ -n "$value" ]]; then |
|
DEFAULT_DOMAIN="$value" |
|
print_info "Loaded domain from .env: $DEFAULT_DOMAIN" |
|
fi |
|
;; |
|
esac |
|
done < "$env_file" |
|
else |
|
print_info "No .env file found. Using default configuration." |
|
print_info "Create .env file with VMWARE_IP and VMWARE_DOMAIN to customize defaults." |
|
fi |
|
} |
|
|
|
create_sample_env() { |
|
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|
local env_file="$script_dir/.env" |
|
local sample_file="$script_dir/.env.example" |
|
|
|
print_info "Creating sample .env.example file..." |
|
|
|
cat > "$sample_file" << EOF |
|
# VMware IP Address Updater Configuration |
|
# Copy this file to .env and modify as needed |
|
|
|
# Default IP address for your VMware VM |
|
VMWARE_IP=192.168.1.100 |
|
|
|
# Default domain name for your VMware VM |
|
VMWARE_DOMAIN=vmware.local |
|
|
|
# Examples: |
|
# VMWARE_IP=192.168.170.133 |
|
# VMWARE_DOMAIN=dev.mycompany.local |
|
# VMWARE_DOMAIN=vm.personal.network |
|
EOF |
|
|
|
print_success "Created .env.example file" |
|
print_info "Copy to .env and customize: cp .env.example .env" |
|
} |
|
|
|
check_admin_rights() { |
|
local hosts_file="$1" |
|
if [[ ! -w "$hosts_file" ]]; then |
|
return 1 |
|
fi |
|
return 0 |
|
} |
|
|
|
detect_platform() { |
|
if grep -qi microsoft /proc/version 2>/dev/null; then |
|
# WSL - use Windows hosts file |
|
echo "wsl" |
|
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then |
|
# Git Bash on Windows (MSYS) |
|
echo "windows" |
|
elif [[ "$OSTYPE" == "darwin"* ]]; then |
|
# macOS |
|
echo "macos" |
|
else |
|
# Native Linux |
|
echo "linux" |
|
fi |
|
} |
|
|
|
get_hosts_config() { |
|
local platform="$1" |
|
|
|
case "$platform" in |
|
"wsl") |
|
HOSTS_FILE="/mnt/c/Windows/System32/drivers/etc/hosts" |
|
SUDO="sudo" |
|
BACKUP_SUFFIX=".bak" |
|
;; |
|
"windows") |
|
HOSTS_FILE="/c/Windows/System32/drivers/etc/hosts" |
|
SUDO="" |
|
BACKUP_SUFFIX=".bak" |
|
;; |
|
"macos") |
|
HOSTS_FILE="/etc/hosts" |
|
SUDO="sudo" |
|
BACKUP_SUFFIX=".bak" |
|
;; |
|
"linux") |
|
HOSTS_FILE="/etc/hosts" |
|
SUDO="sudo" |
|
BACKUP_SUFFIX=".bak" |
|
;; |
|
*) |
|
print_error "Unsupported platform: $platform" |
|
exit 1 |
|
;; |
|
esac |
|
} |
|
|
|
backup_hosts_file() { |
|
local hosts_file="$1" |
|
local backup_file="${hosts_file}${BACKUP_SUFFIX}" |
|
|
|
if [[ -f "$hosts_file" ]]; then |
|
print_info "Creating backup: $backup_file" |
|
if [[ -n "$SUDO" ]]; then |
|
$SUDO cp "$hosts_file" "$backup_file" |
|
else |
|
cp "$hosts_file" "$backup_file" 2>/dev/null || { |
|
print_warning "Could not create backup (insufficient permissions)" |
|
} |
|
fi |
|
fi |
|
} |
|
|
|
prompt_for_ip() { |
|
local current_ip="$1" |
|
local domain="$2" |
|
|
|
echo "" |
|
print_info "Current configuration:" |
|
echo " Domain: $domain" |
|
echo " Current IP: $current_ip" |
|
echo "" |
|
|
|
# Check if domain exists in hosts file and show current entry |
|
local existing_entry=$(grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[[:space:]]+$domain" "$HOSTS_FILE" 2>/dev/null) |
|
if [[ -n "$existing_entry" ]]; then |
|
print_info "Existing hosts entry found:" |
|
echo " $existing_entry" |
|
echo "" |
|
fi |
|
|
|
while true; do |
|
read -p "Enter new IP address (or press Enter to use $current_ip): " user_input |
|
|
|
# If user just pressed Enter, use current IP |
|
if [[ -z "$user_input" ]]; then |
|
echo "$current_ip" |
|
return 0 |
|
fi |
|
|
|
# Validate IP address format |
|
if validate_ip "$user_input"; then |
|
echo "$user_input" |
|
return 0 |
|
else |
|
print_error "Invalid IP address format. Please enter a valid IP (e.g., 192.168.1.100)" |
|
fi |
|
done |
|
} |
|
|
|
remove_duplicate_entries() { |
|
local hosts_file="$1" |
|
local domain="$2" |
|
local sudo_cmd="$3" |
|
|
|
# Count existing entries for the domain |
|
local count=$(grep -c -E "[[:space:]]$domain([[:space:]]|$)" "$hosts_file" 2>/dev/null || echo "0") |
|
|
|
if [[ $count -gt 1 ]]; then |
|
print_warning "Found $count duplicate entries for $domain. Removing duplicates..." |
|
|
|
# Create a temporary file to store the cleaned content |
|
local temp_file=$(mktemp) |
|
|
|
# Remove all lines containing the domain |
|
grep -v -E "[[:space:]]$domain([[:space:]]|$)" "$hosts_file" > "$temp_file" 2>/dev/null |
|
|
|
# Copy cleaned content back |
|
if [[ -n "$sudo_cmd" ]]; then |
|
$sudo_cmd cp "$temp_file" "$hosts_file" |
|
else |
|
cp "$temp_file" "$hosts_file" |
|
fi |
|
|
|
# Clean up |
|
rm -f "$temp_file" |
|
|
|
print_info "Removed $count duplicate entries" |
|
fi |
|
} |
|
|
|
update_hosts_entry() { |
|
local hosts_file="$1" |
|
local domain="$2" |
|
local entry="$3" |
|
local sudo_cmd="$4" |
|
|
|
# Check if we can write to the file |
|
if ! check_admin_rights "$hosts_file" && [[ -z "$sudo_cmd" ]]; then |
|
print_error "Insufficient permissions to modify $hosts_file" |
|
print_info "Please run this script as Administrator (Windows) or with sudo (Linux/macOS)" |
|
return 1 |
|
fi |
|
|
|
# First, remove any duplicate entries for this domain |
|
remove_duplicate_entries "$hosts_file" "$domain" "$sudo_cmd" |
|
|
|
# Check if domain exists and update or append |
|
local existing_entry=$(grep -E "^[^#]*[[:space:]]$domain([[:space:]]|$)" "$hosts_file" 2>/dev/null) |
|
|
|
if [[ -n "$existing_entry" ]]; then |
|
print_info "Updating existing entry for $domain..." |
|
print_info "Old entry: $existing_entry" |
|
|
|
# Create a more robust sed command that only matches the exact domain |
|
local escaped_domain=$(echo "$domain" | sed 's/[.[\*^$()+?{|]/\\&/g') |
|
|
|
# Use different sed syntax for different platforms |
|
if [[ "$OSTYPE" == "darwin"* ]]; then |
|
# macOS requires different sed syntax |
|
$sudo_cmd sed -i "$BACKUP_SUFFIX" "s/^[^#]*[[:space:]]$escaped_domain\([[:space:]].*\)\{0,1\}$/$entry/" "$hosts_file" |
|
else |
|
# Linux/Windows |
|
$sudo_cmd sed -i"$BACKUP_SUFFIX" "s/^[^#]*[[:space:]]$escaped_domain\([[:space:]].*\)\{0,1\}$/$entry/" "$hosts_file" |
|
fi |
|
else |
|
print_info "Adding new entry for $domain..." |
|
|
|
# Add entry to hosts file |
|
if [[ -n "$sudo_cmd" ]]; then |
|
echo "$entry" | $sudo_cmd tee -a "$hosts_file" >/dev/null |
|
else |
|
echo "$entry" >> "$hosts_file" 2>/dev/null || { |
|
print_error "Failed to add entry (insufficient permissions)" |
|
return 1 |
|
} |
|
fi |
|
fi |
|
|
|
# Final check to ensure no duplicates were created |
|
local final_count=$(grep -c -E "[[:space:]]$domain([[:space:]]|$)" "$hosts_file" 2>/dev/null || echo "0") |
|
if [[ $final_count -gt 1 ]]; then |
|
print_warning "Duplicate entries detected after update. Cleaning up..." |
|
remove_duplicate_entries "$hosts_file" "$domain" "$sudo_cmd" |
|
|
|
# Re-add the correct entry |
|
if [[ -n "$sudo_cmd" ]]; then |
|
echo "$entry" | $sudo_cmd tee -a "$hosts_file" >/dev/null |
|
else |
|
echo "$entry" >> "$hosts_file" |
|
fi |
|
fi |
|
|
|
return 0 |
|
} |
|
|
|
verify_entry() { |
|
local hosts_file="$1" |
|
local domain="$2" |
|
|
|
local result=$(grep "$domain" "$hosts_file" 2>/dev/null) |
|
if [[ -n "$result" ]]; then |
|
print_success "Hosts file updated successfully:" |
|
echo " $result" |
|
return 0 |
|
else |
|
print_error "Failed to verify entry in hosts file" |
|
return 1 |
|
fi |
|
} |
|
|
|
show_usage() { |
|
echo "Usage: $0 [OPTIONS] [IP_ADDRESS] [DOMAIN]" |
|
echo "" |
|
echo "Options:" |
|
echo " -i, --interactive Prompt for IP address input" |
|
echo " -c, --create-env Create sample .env.example file" |
|
echo " -h, --help Show this help message" |
|
echo "" |
|
echo "Examples:" |
|
echo " $0 # Use default/env values" |
|
echo " $0 -i # Interactive mode - prompt for IP" |
|
echo " $0 -c # Create .env.example file" |
|
echo " $0 192.168.1.100 # Update IP only" |
|
echo " $0 192.168.1.100 vm.example.com # Update both IP and domain" |
|
echo " $0 --interactive vm.custom.domain # Interactive IP with custom domain" |
|
echo "" |
|
echo "Configuration:" |
|
echo " Default IP: $DEFAULT_IP" |
|
echo " Default Domain: $DEFAULT_DOMAIN" |
|
echo "" |
|
echo "Environment File (.env):" |
|
echo " VMWARE_IP=192.168.1.100 # Set default IP address" |
|
echo " VMWARE_DOMAIN=vmware.local # Set default domain name" |
|
echo "" |
|
echo "Note: This script ensures no duplicate entries are created in the hosts file." |
|
echo " Configuration from .env file overrides built-in defaults." |
|
} |
|
|
|
# === MAIN SCRIPT === |
|
main() { |
|
local interactive_mode=false |
|
local create_env_mode=false |
|
local args=() |
|
|
|
# Parse command line arguments |
|
while [[ $# -gt 0 ]]; do |
|
case $1 in |
|
-h|--help) |
|
show_usage |
|
exit 0 |
|
;; |
|
-i|--interactive) |
|
interactive_mode=true |
|
shift |
|
;; |
|
-c|--create-env) |
|
create_env_mode=true |
|
shift |
|
;; |
|
-*) |
|
print_error "Unknown option: $1" |
|
show_usage |
|
exit 1 |
|
;; |
|
*) |
|
args+=("$1") |
|
shift |
|
;; |
|
esac |
|
done |
|
|
|
# Handle create-env mode |
|
if [[ "$create_env_mode" == true ]]; then |
|
create_sample_env |
|
exit 0 |
|
fi |
|
|
|
# Load environment configuration |
|
load_env_config |
|
|
|
# Initialize configuration after loading .env |
|
IP="$DEFAULT_IP" |
|
DOMAIN="$DEFAULT_DOMAIN" |
|
ENTRY="$IP $DOMAIN" |
|
|
|
# Restore positional parameters |
|
set -- "${args[@]}" |
|
|
|
print_info "VMware IP Address Updater" |
|
echo "==================================" |
|
|
|
# Handle domain argument first (if provided) |
|
if [[ -n "$2" ]]; then |
|
DOMAIN="$2" |
|
elif [[ -n "$1" ]] && [[ "$interactive_mode" == true ]]; then |
|
# If interactive mode and only one argument, treat it as domain |
|
if ! validate_ip "$1"; then |
|
DOMAIN="$1" |
|
set -- # Clear arguments since we used it as domain |
|
fi |
|
fi |
|
|
|
# Handle IP address |
|
if [[ "$interactive_mode" == true ]]; then |
|
# Interactive mode - prompt for IP |
|
IP=$(prompt_for_ip "$IP" "$DOMAIN") |
|
elif [[ -n "$1" ]]; then |
|
# Command line argument provided |
|
if validate_ip "$1"; then |
|
IP="$1" |
|
else |
|
print_error "Invalid IP address format: $1" |
|
print_info "Use -i for interactive mode or provide a valid IP address" |
|
exit 1 |
|
fi |
|
fi |
|
|
|
# Update entry string |
|
ENTRY="$IP $DOMAIN" |
|
|
|
echo "" |
|
print_info "Configuration:" |
|
echo " Target: $ENTRY" |
|
|
|
# Detect platform |
|
PLATFORM=$(detect_platform) |
|
print_info "Detected platform: $PLATFORM" |
|
|
|
# Get platform-specific configuration |
|
get_hosts_config "$PLATFORM" |
|
print_info "Hosts file: $HOSTS_FILE" |
|
|
|
# Check if hosts file exists |
|
if [[ ! -f "$HOSTS_FILE" ]]; then |
|
print_error "Hosts file not found: $HOSTS_FILE" |
|
exit 1 |
|
fi |
|
|
|
# Show current state before modification |
|
echo "" |
|
print_info "Current hosts file entries for '$DOMAIN':" |
|
local current_entries=$(grep -E "[[:space:]]$DOMAIN([[:space:]]|$)" "$HOSTS_FILE" 2>/dev/null || echo " (none found)") |
|
if [[ "$current_entries" != " (none found)" ]]; then |
|
echo "$current_entries" | sed 's/^/ /' |
|
else |
|
echo " (none found)" |
|
fi |
|
|
|
# Create backup |
|
backup_hosts_file "$HOSTS_FILE" |
|
|
|
echo "" |
|
print_info "Updating hosts file..." |
|
|
|
# Update hosts entry |
|
if update_hosts_entry "$HOSTS_FILE" "$DOMAIN" "$ENTRY" "$SUDO"; then |
|
echo "" |
|
# Verify the change |
|
verify_entry "$HOSTS_FILE" "$DOMAIN" |
|
|
|
# Check for any remaining duplicates and warn |
|
local final_count=$(grep -c -E "[[:space:]]$DOMAIN([[:space:]]|$)" "$HOSTS_FILE" 2>/dev/null || echo "0") |
|
if [[ $final_count -gt 1 ]]; then |
|
print_warning "Multiple entries still exist for $DOMAIN. Manual cleanup may be required." |
|
elif [[ $final_count -eq 1 ]]; then |
|
print_success "Single entry confirmed - no duplicates detected" |
|
fi |
|
|
|
echo "" |
|
# Platform-specific additional notes |
|
case "$PLATFORM" in |
|
"wsl"|"windows") |
|
print_info "Note: Changes will affect both Windows and WSL" |
|
print_info "You may need to flush DNS cache: ipconfig /flushdns" |
|
;; |
|
"macos") |
|
print_info "You may need to flush DNS cache: sudo dscacheutil -flushcache" |
|
;; |
|
"linux") |
|
print_info "You may need to restart network services if using systemd-resolved" |
|
;; |
|
esac |
|
else |
|
print_error "Failed to update hosts file" |
|
exit 1 |
|
fi |
|
} |
|
|
|
# Run main function with all arguments |
|
main "$@" |