#!/bin/bash # Initial release date: October 2022 # Authors: @mativ00 & @sudoalx on Telegram # Description: This bash script was created to automate the installation of images hosted on labhub.eu.org # Version: 3.5.8-main # Last Updated: 2024-08-08 yyyy-mm-dd # Default ishare2 configuration: this configuration will be overwritten when defined in the configuration file. USE_ARIA2C=false # Set to true to use aria2c for faster downloads. Set to false to use wget instead. SSL_CHECK="true" # Set to true to check SSL certificate. Set to false to disable SSL certificate check. CHANNEL="main" # Set to main to use the main channel. Set to main to use the main channel. MIRRORS=("https://raw.githubusercontent.com/ishare2-org/mirrors/main/index.gd.json" "https://raw.githubusercontent.com/ishare2-org/mirrors/main/index.od.json") # List of mirrors JSON_URL=${MIRRORS[0]} # Set to the default mirror # End of ishare2 configuration # Initialize variables ISHARE2_DIR="/opt/ishare2/cli" # ishare2 directory LOG_FILE="$ISHARE2_DIR/ishare2.log" # Log file TEMP_JSON="$ISHARE2_DIR/index.json" # Temporary JSON file VERSION="3.5.8-main" # Current version of ishare2 SOURCES_URL="https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/sources.list" # Sources.list URL MOTD_FILE="$ISHARE2_DIR/motd.json" # MOTD file REMOTE_MOTD_URL="https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/motd.json" # Remote MOTD URL # Colors RED='\033[31m' YELLOW='\033[1;33m' GREEN='\033[32m' BLUE='\033[34m' NO_COLOR='\033[0m' # Script name SCR_NAME_EXEC=$0 SCR_NAME_EXEC_FP=$(realpath "$0") SCR_NAME=$(basename "$SCR_NAME_EXEC") SCR_NAME=${SCR_NAME%.*} # Configuration file ISHARE2_CONF_FILE=$ISHARE2_DIR/ishare2.conf logger() { local log_level=${1^^} # Convert log level to uppercase shift # Remove log level from arguments # Create LOG_FILE if it does not exist if [[ ! -f "$LOG_FILE" ]]; then touch "$LOG_FILE" fi # Ensure LOG_FILE is set and writable if [[ -z "$LOG_FILE" ]]; then echo "LOG_FILE is not set" >&2 return 1 elif [[ ! -w "$LOG_FILE" && ! -e "$LOG_FILE" ]]; then echo "Cannot write to LOG_FILE: $LOG_FILE" >&2 # Attempt to create the log file touch "$LOG_FILE" || return 1 # Attempt to fix the permissions chmod 644 "$LOG_FILE" || return 1 return 1 fi # Ensure SCR_NAME is set if [[ -z "$SCR_NAME" ]]; then SCR_NAME="UNKNOWN_SCRIPT" fi # Log the output to the log file echo -e "[$log_level][$SCR_NAME] $(date '+%Y-%m-%d %H:%M:%S'): $*" >>"$LOG_FILE" } # Function to fetch the latest list of sources needed for ishare2 ishare2_sources() { check_installed "curl" # Check if the sources.list file exists in the ishare2 directory if [ ! -f "$ISHARE2_DIR/sources.list" ]; then # If the sources.list file is older than 1 day, backup the file and download the latest sources.list if [ ! -f "$ISHARE2_DIR/sources.list" ] || [ $(find "$ISHARE2_DIR/sources.list" -mtime +1 -print) ]; then # Backup the sources.list file cp $ISHARE2_DIR/sources.list "$ISHARE2_DIR/sources.list.bak" # Download the latest sources.list curl -sL "$SOURCES_URL" >"$ISHARE2_DIR/sources.list" # Check if the sources.list file was downloaded successfully if [ $? -eq 0 ]; then logger info "The sources.list file has been downloaded successfully." else logger error "Failed to download the sources.list file." logger info "Restoring the backup sources.list file..." # Restore the backup sources.list file if [ -f "$ISHARE2_DIR/sources.list.bak" ]; then cp "$ISHARE2_DIR/sources.list.bak" $ISHARE2_DIR/sources.list logger info "The backup sources.list file has been restored." echo -e "${RED}[-] Failed to download the sources.list file.${NO_COLOR}" echo -e "${YELLOW}[!] Using the old sources.list file.${NO_COLOR}" echo -e "${YELLOW}[!] Please check your internet connection and try again.${NO_COLOR}" else logger info "The backup sources.list file does not exist." echo -e "${RED}[-] Failed to download the sources.list file.${NO_COLOR}" echo -e "${YELLOW}[!] Please check your internet connection and try again.${NO_COLOR}" exit 1 fi fi fi fi # Source the sources.list file source $ISHARE2_DIR/sources.list } # Function to initialize motd checks check_motd() { # Fetch the latest MOTD from the remote source fetch_latest_motd # Check if the fetched MOTD is different from the current one if motds_are_different; then # Display the updated MOTD show_motd # Update the MOTD information update_motd_info else logger info "MOTD is up to date. No changes were made." if should_show_motd; then # Display the current MOTD show_motd # Update the MOTD information update_motd_info fi fi } # Function to check if the MOTD should be shown should_show_motd() { current_date=$(date +"%Y-%m-%d") last_shown_date=$(jq -r '.last_shown_date' "$MOTD_FILE") show_count=$(jq -r '.show_count' "$MOTD_FILE") # Check if MOTD has not been shown today or shown less than 3 times if [ "$current_date" != "$last_shown_date" ] || [ "$show_count" -lt 3 ]; then return 0 # Show MOTD else return 1 # Do not show MOTD fi } # Function to display the MOTD show_motd() { message=$(jq -r '.message' "$MOTD_FILE") messages=( "MOTD from the ishare2 team:" "$message" "" "Telegram: https://t.me/NetLabHub" "Donate: https://buymeacoffee.com/sudoalex" "GitHub: https://github.com/ishare2-org/ishare2-cli" ) print_box "${messages[@]}" } # Function to update the MOTD information update_motd_info() { current_date=$(date +"%Y-%m-%d") jq '.last_shown_date = $date | .show_count += 1' --arg date "$current_date" "$MOTD_FILE" >$ISHARE2_DIR/motd.tmp.json times_shown=$(jq -r '.show_count' "$ISHARE2_DIR/motd.tmp.json") logger info "MOTD has been shown $times_shown out of 3 times today." mv $ISHARE2_DIR/motd.tmp.json "$MOTD_FILE" } # Function to fetch the latest MOTD from a remote source fetch_latest_motd() { if [ ! -f "$MOTD_FILE" ]; then logger info "MOTD file does not exist. Fetching the latest MOTD from the remote source." curl -s "$REMOTE_MOTD_URL" >$ISHARE2_DIR/motd.json return fi curl -s "$REMOTE_MOTD_URL" >$ISHARE2_DIR/motd.tmp.json # if curl failed then write custom message to motd file if [ $? -ne 0 ]; then echo -e "{\"message\":\"Failed to fetch the latest MOTD. Please check your internet connection.\"}" >$ISHARE2_DIR/motd.tmp.json fi } # Function to compare the current MOTD with the fetched MOTD motds_are_different() { if [ ! -f "$MOTD_FILE" ] || [ ! -f "$ISHARE2_DIR/motd.tmp.json" ]; then return 0 fi local current_motd=$(jq -r '.message' "$MOTD_FILE") local fetched_motd=$(jq -r '.message' "$ISHARE2_DIR/motd.tmp.json") # Compare the current MOTD with the fetched MOTD if [ "$current_motd" != "$fetched_motd" ]; then mv $ISHARE2_DIR/motd.tmp.json "$MOTD_FILE" fi [ "$current_motd" != "$fetched_motd" ] } # Function to check if there is a new version of ishare2 available check_updates() { LATEST_VERSION=$(curl -sL https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/version) # Check if the version file could be fetched successfully if [ -z "$LATEST_VERSION" ]; then echo -e "${RED}[-] Failed to fetch the latest version information. Please check your internet connection.${NO_COLOR}" logger error "Failed to fetch the latest version information from: $LATEST_VERSION" return fi if [[ $LATEST_VERSION != $VERSION ]]; then # Make an array of messages messages=( "A newer version of ishare2 is available." "Current version: $VERSION" "Latest version: $LATEST_VERSION" "Run: ishare2 upgrade to update to the latest version." "See more methods to update on GitHub:" "https://github.com/ishare2-org/ishare2-cli" ) print_box "${messages[@]}" fi } # Function to print a box-like component with adjusted width print_box() { messages=("$@") # Split messages on line breaks and find the length of the longest line max_length=0 IFS=$'\n' # Internal Field Separator set to newline for splitting all_lines=() # Array to hold all lines of all messages for message in "${messages[@]}"; do # Split message into lines readarray -t lines <<<"$message" for line in "${lines[@]}"; do all_lines+=("$line") length=${#line} if ((length > max_length)); then max_length=$length fi done done IFS=$' \t\n' # Reset IFS to default # Display information in a box-like component with adjusted width echo -e "${YELLOW}┌$(printf '─%.0s' $(seq 1 $((max_length + 4))))┐${NO_COLOR}" for line in "${all_lines[@]}"; do printf "${YELLOW}│${NO_COLOR} %-$(($max_length + 2))s ${YELLOW}│${NO_COLOR}\n" "$line" done echo -e "${YELLOW}└$(printf '─%.0s' $(seq 1 $((max_length + 4))))┘${NO_COLOR}" } # Function to rotate among the available mirrors rotate_mirrors() { # Rotate mirrors source $ISHARE2_CONF_FILE # Create unset JSON_URL if it does not exist if ! grep -q "JSON_URL" "$ISHARE2_CONF_FILE"; then echo "JSON_URL=" >>"$ISHARE2_CONF_FILE" fi if [[ $ROTATE == true ]]; then logger info "Rotating mirrors..." if [[ $JSON_URL == ${MIRRORS[0]} ]]; then logger info "Setting JSON_URL to ${MIRRORS[1]}" JSON_URL=${MIRRORS[1]} sed -i "s|JSON_URL=.*|JSON_URL=${MIRRORS[1]}|" "$ISHARE2_CONF_FILE" elif [[ $JSON_URL == ${MIRRORS[1]} ]]; then logger info "Setting JSON_URL to ${MIRRORS[0]}" JSON_URL=${MIRRORS[0]} sed -i "s|JSON_URL=.*|JSON_URL=${MIRRORS[0]}|" "$ISHARE2_CONF_FILE" elif [[ -z $JSON_URL ]]; then # If JSON_URL is empty, set it to the first mirror logger info "Setting JSON_URL to ${MIRRORS[0]}" JSON_URL=${MIRRORS[0]} sed -i "s|JSON_URL=.*|JSON_URL=${MIRRORS[0]}|" "$ISHARE2_CONF_FILE" fi fi } # Function to print a separator with a title print_separator() { local TITLE=$1 local WIDTH=${#TITLE} local SEPARATOR="" # Calculate the required number of '=' characters to create the separator for ((i = 0; i < WIDTH + 8; i++)); do SEPARATOR+="=" done echo -e "${BLUE}$SEPARATOR${NO_COLOR}" echo -e "\033[1;33m ${TITLE} \033[0m" echo -e "${BLUE}$SEPARATOR${NO_COLOR}" } # Function to check if PNetLab is installed is_pnetlab_installed() { # Check if pnetlab is installed data=$(mysql -uroot -ppnetlab -D pnetlab_db -e "SELECT control_value FROM control WHERE control_value>1;" 2>/dev/null) data_array=("$data") pnetlab_version=${data_array[1]} if [[ -z $pnetlab_version ]]; then echo -e "${YELLOW}[!] WARN: PNetLab was not found.${NO_COLOR}" echo -e "${YELLOW}[!]${NO_COLOR} This script has only been tested on PNetLab. If you are not using PNetLab, you may encounter issues." logger warn "PNetLab is not installed." return 1 fi return 0 } # Function to initialize the ishare2 directory checks check_ishare2_dir() { # check if ishare2 directory exists otherwise create it if [[ ! -d $ISHARE2_DIR ]]; then create_ishare2_dir fi } # Set max log file lines to 1000 set_max_log_lines() { if [[ -f $LOG_FILE ]]; then if [[ $(wc -l <"$LOG_FILE") -gt 1000 ]]; then tail -n 1000 $LOG_FILE >$ISHARE2_DIR/tmp.txt mv $ISHARE2_DIR/tmp.txt $LOG_FILE fi fi } create_default_conf() { install_dependencies # If the --init argument is passed, display a message to the user messages=( "The default configuration will be used. Feel free to modify it later" "by running: ishare2 config" "Press Enter to continue." ) print_box "${messages[@]}" read -n 1 # Write the default configuration to the configuration file echo "USE_ARIA2C=false" >$ISHARE2_CONF_FILE echo "SSL_CHECK=true" >>$ISHARE2_CONF_FILE echo "CHANNEL=$CHANNEL" >>$ISHARE2_CONF_FILE echo "ROTATE=true" >>$ISHARE2_CONF_FILE echo "JSON_URL=${MIRRORS[0]}" >>$ISHARE2_CONF_FILE # Display values used messages=( "Configuration values:" "USE_ARIA2C: false" "SSL_CHECK: true" "CHANNEL: $CHANNEL" "ROTATE: true" ) print_box "${messages[@]}" } # Function to check if the configuration file exists, and validate it check_config() { # get first argument local first_arg=$1 if [[ $first_arg == "--init" ]]; then # check if ishare2 directory exists otherwise create it check_ishare2_dir # create default configuration create_default_conf return fi # check if config file exists if [[ ! -f $ISHARE2_CONF_FILE ]]; then create_config fi # check if config file is empty if [[ ! -s $ISHARE2_CONF_FILE ]]; then echo -e "${RED}[!] Configuration file is empty. Please configure ishare2.${NO_COLOR}" create_config fi # check if config file is valid validate_conf_file } # Function to create the configuration file create_config() { fix_repositories clear messages=( "Welcome to the ishare2!" "This script will guide you through the initial configuration process." "But first, let's check if the required dependencies are installed." ) print_box "${messages[@]}" sleep 3 is_pnetlab_installed # check if pnetlab is installed if [[ -z $pnetlab_version ]]; then logger warn "PNetLab is not installed." else echo -e "${GREEN}[+] PNetLab version: $pnetlab_version found.${NO_COLOR}" check_pnetlab_sources fi echo -e "${BLUE}[*] Installing ishare2 dependencies...${NO_COLOR}" MAX_RETRIES=3 retry_counter=0 while [[ $retry_counter -lt $MAX_RETRIES ]]; do if install_dependencies; then break fi echo -e "${RED}[-] Failed to install dependencies. Retrying...${NO_COLOR}" logger error "Failed to install dependencies. Retrying..." retry_counter=$((retry_counter + 1)) sleep 10 done if [[ $retry_counter -eq $MAX_RETRIES ]]; then MAX_RETRIES=3 retry_counter=0 while [[ $retry_counter -lt $MAX_RETRIES ]]; do if check_pnetlab_sources; then break fi echo -e "${RED}[-] Failed to replace pnetlab sources. Retrying...${NO_COLOR}" logger error "Failed to replace PNetLab sources. Retrying..." retry_counter=$((retry_counter + 1)) sleep 10 done fi logger info "Starting configuration wizard..." # Create the configuration file clear messages=( "Welcome to the ishare2 configuration wizard." "- This wizard will guide you through the configuration process." "- Press Enter to accept the default value." "- You can modify the configuration later by running: ishare2 config." "- Press Ctrl+C to cancel." ) print_box "${messages[@]}" sleep 3 # Ask whether to use aria2c or not echo -e "${YELLOW}[+] Use aria2c for faster downloads? (default: no)${NO_COLOR}" read -p "[+] (y/n): " -r if [[ $REPLY =~ ^[Yy]$ ]]; then echo -e "${YELLOW}[!] Using aria2c allows you to download files faster, however, many users reported having issues with it.\nTo disable it, run: ishare2 config and answer no to this question.${NO_COLOR}" echo "USE_ARIA2C=true" >$ISHARE2_CONF_FILE install_package "aria2" read -n 1 else echo "USE_ARIA2C=false" >$ISHARE2_CONF_FILE fi # Ask if SSL certificate should be checked echo -e "${YELLOW}[+] Check SSL certificate? (default: yes)${NO_COLOR}" read -p "[+] (y/n): " -r case $REPLY in [Nn]) SSL_CHECK=false ;; *) SSL_CHECK=true ;; esac echo "SSL_CHECK=\"$SSL_CHECK\"" >>$ISHARE2_CONF_FILE echo -e "${YELLOW}[+] Choose the update channel.${NO_COLOR}" # Get branches as an array instead of a single string with spaces mapfile -t branches < <(curl -sL https://api.github.com/repos/ishare2-org/ishare2-cli/branches | jq -r '.[].name') # Display branches with correct indexing for ((i = 0; i < ${#branches[@]}; i++)); do echo " $((i + 1))) ${branches[i]}" done # Ask the user to select a branch local current_channel=$CHANNEL read -p "[*] Enter the number of the branch you want to use (default: $CHANNEL): " -r if [[ -z $REPLY ]]; then echo -e "${YELLOW}[!] Using the default branch.${NO_COLOR}" elif ! [[ $REPLY =~ ^[0-9]+$ ]] || ((REPLY < 1 || REPLY > ${#branches[@]})); then echo -e "${RED}[!] Invalid input. Using the default branch.${NO_COLOR}" else CHANNEL=${branches[REPLY - 1]} echo -e "${YELLOW}[!] Using the $CHANNEL branch.${NO_COLOR}" echo -e "${YELLOW}[!] Running: ishare2 upgrade, will update ishare2 to the $CHANNEL branch.${NO_COLOR}" fi echo "CHANNEL=\"$CHANNEL\"" >>$ISHARE2_CONF_FILE # Provisional mirror selection feature. Will be replaced with a better solution in the future. # Ask if the user wants to use the default mirror, another mirror or a custom mirror echo -e "${YELLOW}[+] Choose a mirror. (default: Rotate mirrors)${NO_COLOR}" # Show list of mirrors echo " 1) Rotate mirrors (recommended)" echo " 2) Google Drive mirror" echo " 3) Onedrive mirror" echo " 4) Custom mirror" # Ask the user to select a mirror read -p "[*] Enter the number of the mirror you want to use (default: 1): " -r case $REPLY in 2) JSON_URL=${MIRRORS[0]} echo -e "${YELLOW}[!] Using Google Drive mirror.${NO_COLOR}" ;; 3) JSON_URL=${MIRRORS[1]} echo -e "${YELLOW}[!] Using Onedrive mirror.${NO_COLOR}" ;; 4) echo -e "${YELLOW}[+] Enter the URL of the mirror.${NO_COLOR}" read -p "[*] Enter the URL of the mirror you want to use: " -r JSON_URL="$REPLY" echo -e "${YELLOW}[!] Using custom mirror: $JSON_URL.${NO_COLOR}" ;; *) echo -e "${YELLOW}[!] ishare2 will rotate among the available mirrors.${NO_COLOR}" echo "ROTATE=true" >>$ISHARE2_CONF_FILE ;; esac echo "JSON_URL=\"$JSON_URL\"" >>$ISHARE2_CONF_FILE # Validate the configuration file if [ ! -f "$ISHARE2_CONF_FILE" ]; then echo -e "${RED}[!] Failed to create configuration file. Please try again.${NO_COLOR}" logger error "Failed to create configuration file." exit 1 fi remove_duplicates validate_conf_file sleep 1 clear messages=( "Configuration completed successfully." "You can start using ishare2!" "[!] IMPORTANT NOTICES:" "- ishare2 is a free and open-source project. If you paid" " for it, you have been scammed." "- Do not download ishare2 from unofficial sources as they" " may contain arbitrary code." ) print_box "${messages[@]}" # Update to the latest version on the selected channel if [[ $current_channel != $CHANNEL ]]; then echo -e "${YELLOW}[!] Installing the latest version of ishare2 on the $CHANNEL channel...${NO_COLOR}" curl -sL https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/ishare2 >/usr/sbin/ishare2 chmod +x /usr/sbin/ishare2 echo -e "${GREEN}[+] ishare2 has been updated to the latest version on the $CHANNEL channel.${NO_COLOR}" fi } # Function to remove duplicate lines from the configuration file remove_duplicates() { local config_file="$ISHARE2_CONF_FILE" # Check if the configuration file exists if [ ! -f "$config_file" ]; then echo -e "${RED}[!] Configuration file does not exist.${NO_COLOR}" logger error "Configuration file does not exist." return fi # Remove duplicate lines from the configuration file awk '!seen[$0]++' "$config_file" >"$config_file.tmp" && mv "$config_file.tmp" "$config_file" logger info "Removed duplicate lines from the configuration file." } # Function to validate the configuration file validate_conf_file() { # Check if the configuration file exists if [ ! -f "$ISHARE2_CONF_FILE" ]; then echo -e "${RED}[!] Configuration file does not exist.${NO_COLOR}" logger error "Configuration file does not exist." return 1 fi # Array of allowed variables local allowed_vars=("USE_ARIA2C" "CHANNEL" "SSL_CHECK" "JSON_URL" "ROTATE" "MOTD") # Read the configuration file line by line and filter out invalid variables local filtered_content="" while IFS= read -r line; do # Skip empty lines and comments if [[ -z "$line" || "$line" == \#* ]]; then continue fi # Extract variable name by trimming leading and trailing whitespaces local var_name="$(echo "$line" | awk -F '=' '{gsub(/^[ \t]+|[ \t]+$/, "", $1); print $1}')" # Check if the variable is allowed is_allowed=false for allowed_var in "${allowed_vars[@]}"; do if [[ "$allowed_var" == "$var_name" ]]; then is_allowed=true break fi done if $is_allowed || [[ "$line" != *=* ]]; then filtered_content+="$line"$'\n' else logger warn "Removed invalid variable \"$var_name\" from the configuration file." fi done <"$ISHARE2_CONF_FILE" # Write the filtered content back to the configuration file echo "$filtered_content" >"$ISHARE2_CONF_FILE" logger info "Validated the configuration file." # shellcheck source=/opt/ishare2/cli/ishare2.conf source "$ISHARE2_CONF_FILE" return 0 } # Function to remove i-share.top apt sources.list remove_ishare_sources() { # Remove i-share.top line from sources.list sed -i '/i-share.top/d' /etc/apt/sources.list } # Function to install dependencies required for ishare2 install_dependencies() { remove_ishare_sources echo -e "${YELLOW}[+] ishare2 needs to install some dependencies to work properly.${NO_COLOR}" local packages=("curl" "wget" "jq" "unrar" "tree" "unzip") echo -e "${YELLOW}[+] List of packages to be installed: ${NO_COLOR}" for package in "${packages[@]}"; do echo -e "${YELLOW} -${NO_COLOR} $package" done echo -e "${YELLOW}[+] Starting to install dependencies in 5 seconds... This may take a while. Press Ctrl+C to cancel.${NO_COLOR}" sleep 5 for package in "${packages[@]}"; do install_package "$package" done } # Function to check if a package is installed and install it if it's not check_installed() { PACKAGE=$1 # Check if package is installed if ! command -v "$PACKAGE" &>/dev/null; then echo -e "${YELLOW}[!] WARN: $PACKAGE is not installed.${NO_COLOR}" install_package "$PACKAGE" # Check success of package installation if ! command -v "$PACKAGE" &>/dev/null; then echo -e "${RED}[-] Error: Failed to install $PACKAGE.${NO_COLOR}" logger error "Failed to install $PACKAGE." exit 1 else # package is installed echo -e "${GREEN}[+] $PACKAGE has been installed successfully.${NO_COLOR}" fi fi } # Function to check if a site is accessible is_site_accessible() { local url=$1 if ! curl -s --head --request GET "$url" | grep "200 OK\|301 Moved Permanently" >/dev/null; then return 1 # Site is not accessible else return 0 # Site is accessible fi } # Function to check if the sources.list file is using the correct sources fix_repositories() { # Immediately check for http://repo.pnetlab.com and replace it with https if found if grep -q "deb \[trusted=yes\] http://repo.pnetlab.com ./" /etc/apt/sources.list; then echo -e "${YELLOW}[+] Found http://repo.pnetlab.com in sources.list, replacing with https...${NO_COLOR}" sed -i 's|deb \[trusted=yes\] http://repo.pnetlab.com ./|deb [trusted=yes] https://repo.pnetlab.com ./|g' /etc/apt/sources.list fi # Check if https://repo.pnetlab.com is accessible if ! is_site_accessible "https://repo.pnetlab.com"; then echo -e "${YELLOW}[!] repo.pnetlab.com is not accessible.${NO_COLOR}" echo -e "${YELLOW}[!] Checking alternative...${NO_COLOR}" # The script previously modified any http to https if necessary, so just check for https existence now if grep -q "deb \[trusted=yes\] https://repo.pnetlab.com ./" /etc/apt/sources.list; then echo -e "${YELLOW}[!] Your sources.list is already using https for repo.pnetlab.com.${NO_COLOR}" fi # Check if https://pnetlab.labhub.eu.org is accessible if ! is_site_accessible "https://pnetlab.labhub.eu.org"; then echo -e "${RED}[-] Both repo.pnetlab.com and the alternative pnetlab.labhub.eu.org are inaccessible.${NO_COLOR}" echo -e "${YELLOW}[!] Please check your internet connection and try again.${NO_COLOR}" # Ask the user if they want to continue anyway echo -e "${YELLOW}[?] Do you want to continue anyway?${NO_COLOR}" read -p "[?] (y/n): " -r if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi fi # Ask the user if they want to replace the sources echo -e "${YELLOW}[!] The official pnetlab.com repository is not accessible.${NO_COLOR}" echo -e "${YELLOW}[?] Do you want to replace the pnetlab sources with the LabHub mirror?${NO_COLOR}" read -p "[?] (y/n): " -r if [[ $REPLY =~ ^[Yy]$ ]]; then # Comment out "deb [trusted=yes] https://repo.pnetlab.com ./" and add "deb [trusted=yes] https://pnetlab.labhub.eu.org ./" echo -e "${YELLOW}[+] Replacing pnetlab sources with labhub mirror...${NO_COLOR}" sed -i 's|deb \[trusted=yes\] https://repo.pnetlab.com ./|#deb \[trusted=yes\] https://repo.pnetlab.com ./|g' /etc/apt/sources.list echo "deb [trusted=yes] https://pnetlab.labhub.eu.org ./" >>/etc/apt/sources.list fi else echo -e "${GREEN}[+] repo.pnetlab.com is accessible.${NO_COLOR}" # No further action needed if the site is accessible, assuming the earlier check already ensured https is used fi } # Function to run apt-get update apt_get_update() { echo -e "${YELLOW}[+] Updating package lists...${NO_COLOR}" if ! apt-get update -y -qq; then return 1 fi } # Function to install a package install_package() { local package_name=$1 # check if $package_name is installed otherwise install it if ! command -v "$package_name" &>/dev/null; then echo -e "${YELLOW}[+] $package_name is not installed.${NO_COLOR}" if ! apt_get_update; then echo -e "${RED}[-] Failed to update package lists.${NO_COLOR}" echo -e "${YELLOW}[!] Attempting to fix APT sources...${NO_COLOR}" fix_repositories if ! apt_get_update; then echo -e "${RED}[-] Failed to fix APT sources.${NO_COLOR}" logger error "Failed to fix APT sources." exit 1 fi fi echo -e "${YELLOW}[+] Installing $package_name...${NO_COLOR}" if ! apt-get install -qq -y "$package_name"; then echo -e "${RED}[-] Failed to install $package_name. Please install it manually or you will likely encounter issues in the future.${NO_COLOR}" echo -e "${YELLOW}[!] Run: apt-get install -y $package_name${NO_COLOR}" logger error "Failed to install $package_name." exit 1 fi else echo -e "${GREEN}[+] $package_name is already installed.${NO_COLOR}" logger info "Package $package_name is already installed." logger info "Updating $package_name..." if ! apt-get install --only-upgrade -qq "$package_name"; then logger error "Failed to update $package_name." exit 1 fi fi } # Function to check if APT or dpkg is running and unlock dpkg unlock_dpkg() { # Check if APT or dpkg is running if pgrep -f '^[/]usr[/]bin[/](apt|dpkg)' >/dev/null 2>&1; then echo "APT or dpkg seems to be running in the background." echo -e "${YELLOW}[?] Do you want to unlock dpkg?${NO_COLOR}" # Confirm with the user echo -e "${YELLOW}[!] Warning: Unlocking dpkg while APT or dpkg processes are active can lead to a broken system.${NO_COLOR}" echo "Proceed with caution. It's recommended to try this only if you're sure that no APT or dpkg processes are running." echo "Proceed? (y/n)" read -r answer if [[ $answer != "y" && $answer != "Y" ]]; then echo "Operation cancelled by the user." fi # Attempt to remove lock files echo "Attempting to remove lock files..." local lock_files=( "/var/lib/dpkg/lock" "/var/cache/apt/archives/lock" "/var/lib/apt/lists/lock" "/var/lib/dpkg/lock-frontend" ) for file in "${lock_files[@]}"; do if [ -f "$file" ]; then sudo rm "$file" >/dev/null 2>&1 echo "Removed $file" else echo "$file does not exist or has already been removed." fi done # Attempt to reconfigure dpkg echo "Reconfiguring dpkg..." sudo dpkg --configure -a echo "Operation completed. Please try running your APT or dpkg command again." fi } # Function to create the ishare2 directory create_ishare2_dir() { echo -e "${YELLOW}[!] Creating ishare2 directory...${NO_COLOR}" if ! mkdir -p $ISHARE2_DIR 2>/dev/null; then error_message=$(mkdir -p $ISHARE2_DIR 2>&1 >/dev/null) if [[ $error_message == *"Permission denied"* ]]; then echo -e "${RED}[-] Failed to create ishare2 directory: Permission denied.${NO_COLOR}" else echo -e "${RED}[-] Failed to create ishare2 directory. Error: $error_message${NO_COLOR}" fi exit 1 fi logger info "ishare2 directory created successfully." } # Function to check image size against the downloaded image check_image_size() { local image_path=$1 local image_name=$2 local image_size_from_json=$3 # Check if the image file exists if [ ! -f "$image_path$image_name" ]; then echo -e "${RED}[-] Error: The image file does not exist.${NO_COLOR}" logger error "The image file does not exist: $image_path$image_name" exit 1 fi # Get the size of the downloaded image local image_size=$(stat -c%s "$image_path$image_name") # Check if the image size is less than 20KB if [ $image_size -lt 20000 ]; then echo -e "${RED}[-] Error: The image file size is too small. The file may be corrupted.${NO_COLOR}" logger error "The image file size is too small: $image_path$image_name. Expected size: $image_size_from_json. Actual size: $image_size" exit 1 fi # Check if the image size matches the size from the JSON file if [ $image_size -ne $image_size_from_json ]; then echo -e "${RED}[-] Error: The image file size does not match the size expected by the index.${NO_COLOR}" echo -e "${RED}[!] Debug information:${NO_COLOR}" echo -e "${YELLOW} Expected size: $image_size_from_json${NO_COLOR}" echo -e "${YELLOW} Actual size: $image_size${NO_COLOR}" echo -e "${YELLOW} JSON file URL: $JSON_URL${NO_COLOR}" logger error "The image file size does not match the size from the JSON index: $image_path$image_name. Expected size: $image_size_from_json. Actual size: $image_size" exit 1 fi echo -e "${GREEN}[+] Basic integrity checks passed. The downloaded image is the expected size.${NO_COLOR}" } # Function to fetch JSON file with the list of files to download fetch_json() { check_installed "jq" # Create a temporary directory logger info "Downloading JSON file from $JSON_URL..." curl -sL "$JSON_URL" -o "$TEMP_JSON" # Check if the curl command failed if [ $? -ne 0 ]; then logger error "Failed to download the JSON file. From: $JSON_URL" echo -e "${RED}[-] Failed to download the JSON index file. Check your internet connection.${NO_COLOR}" echo -e "${YELLOW}[!] Running connection test to check your internet connection.${NO_COLOR}" connection_tests exit 1 fi # Check if the JSON file is valid if ! jq -e . >/dev/null 2>&1 <"$TEMP_JSON"; then logger error "Error: Invalid JSON index file." echo -e "${RED}[-] Error: Invalid JSON index file.${NO_COLOR}" echo -e "${RED}[!] Debug information:${NO_COLOR}" echo -e "${YELLOW} - JSON file URL: $JSON_URL${NO_COLOR}" echo -e "${YELLOW} - JSON file location: $TEMP_JSON${NO_COLOR}" echo -e "${RED}[!] Troubleshooting:${NO_COLOR}" echo -e "${YELLOW} 1) Check if the JSON file is accessible through your web browser.${NO_COLOR}" echo -e "${YELLOW} 2) If the issue persists, report the issue to the ishare2 team on Telegram: https://t.me/NetLabHub${NO_COLOR}" exit 1 else logger info "JSON file downloaded successfully." fi } # Function to download a file using wget or aria2c download_file() { local url="$1" local output="$2" local options="$3" USE_ARIA2C=$(grep "USE_ARIA2C" $ISHARE2_CONF_FILE | cut -d "=" -f2) if [[ $USE_ARIA2C == "true" ]]; then # Check if aria2c is installed if ! command -v aria2c &>/dev/null; then echo -e "${RED}[-] Error: aria2c is not installed. Attempting to install it...${NO_COLOR}" install_package "aria2" >/dev/null # Check if aria2c is installed if ! command -v aria2c &>/dev/null; then echo -e "${RED}[-] Error: aria2c is not installed. Please install it manually and try again.${NO_COLOR}" exit 1 else # aria2c is installed echo -e "${GREEN}[+] aria2c has been installed successfully.${NO_COLOR}" fi fi fi if command -v aria2c &>/dev/null && [[ $USE_ARIA2C == "true" ]]; then download_file_aria2 "$url" "$output" "$options" else download_file_wget "$url" "$output" "$options" fi } # Function to download a file using aria2c download_file_aria2() { local url="$1" local output_dir="$2" local output_name="$3" logger info "Downloading file from $url..." rm -f error.log if [ "$SSL_CHECK" = true ]; then aria2c --console-log-level=warn -j 2 -c --split=4 --min-split-size=20M --max-connection-per-server=4 --download-result=hide --allow-overwrite=true --dir="$output_dir" --out="$output_name" --log="error.log" "$url" else aria2c --console-log-level=warn -j 2 -c --split=4 --min-split-size=20M --max-connection-per-server=4 --download-result=hide --check-certificate=false --allow-overwrite=true --dir="$output_dir" --out="$output_name" --log="error.log" "$url" fi if [ $? -eq 0 ]; then # line break echo echo -e "${YELLOW}[+] ${GREEN}The file has been downloaded successfully to $output_dir$output_name ${NO_COLOR}" rm -f error.log else echo -e "${RED}[-] Error: Unable to download the file. Check the error.log file for more information.${NO_COLOR}" exit 1 fi # Check if file size is less than 20KB and its extension is not excluded local excluded_extensions=("html" "txt" "php" "yml" "md" "json" "sh" "yaml" "txt") if [ -f "$output_dir$output_name" ] && [ $(stat -c%s "$output_dir$output_name") -lt 20000 ]; then is_excluded=false for ext in "${excluded_extensions[@]}"; do if [ "${output_name##*.}" = "$ext" ]; then is_excluded=true break fi done if [ "$is_excluded" = false ]; then echo -e "${RED}[-] The file size is too small. The file may be corrupted.${NO_COLOR}" # Check if html file is downloaded instead of the actual file, key words: "DOCTYPE", "", Bhadoo if grep -q "DOCTYPE" "$output_dir$output_name" || grep -q "" "$output_dir$output_name" || grep -q "Bhadoo" "$output_dir$output_name"; then echo -e "${RED}[-] The file may be corrupted. The file may be an HTML file instead of the actual file.${NO_COLOR}" fi # Show troubleshooting steps echo -e "${YELLOW}[!] Troubleshooting:${NO_COLOR}" echo -e "${YELLOW}[!] 1) Switch mirrors, if you have mirrors rotation enabled then run the command again using the --overwrite flag.${NO_COLOR}" echo -e "${YELLOW}[!] 2) If the issue persists, visit the live repository through your web browser and download the file manually.${NO_COLOR}" echo -e "${YELLOW}[!] 3) If the issue persists, report the issue to the ishare2 team on Telegram.${NO_COLOR}" fi fi } # Function to download a file using curl download_file_curl() { # $1 = url, $2 = output # Create the directory if it does not exist mkdir -p $ISHARE2_DIR/tmp # Create the file if it does not exist touch $ISHARE2_DIR/tmp/curl_error local url="$1" local output="$2" logger info "Downloading file from $url..." if [ "$SSL_CHECK" = true ]; then curl -s -o "$output" "$url" 2>$ISHARE2_DIR/tmp/curl_error else curl -s --insecure -o "$output" "$url" 2>$ISHARE2_DIR/tmp/curl_error fi if [ $? -eq 0 ]; then logger info "The file has been downloaded successfully to $output." rm -f $ISHARE2_DIR/tmp/curl_error else echo -e "${RED}[-] Error: Unable to download the file. ${NO_COLOR}" # Read the error message from the curl_error file ERROR_MESSAGE=$(cat $ISHARE2_DIR/tmp/curl_error) # Print the error message echo -e "${RED}[-] Logs:\n $ERROR_MESSAGE ${NO_COLOR}" exit 1 fi } # Function to download a file using wget download_file_wget() { # Create the directory if it does not exist mkdir -p $ISHARE2_DIR/tmp # Create the file if it does not exist touch $ISHARE2_DIR/tmp/wget_error local url="$1" local output_dir="$2" local output_name="$3" if [[ -z "$output_name" ]]; then # if output name is not provided, use the basename of the URL output_name=$(basename "$url") fi local output="$output_dir/$output_name" logger info "Downloading file from $url..." if [ "$SSL_CHECK" = true ]; then wget -O "$output" "$url" -q --show-progress --content-disposition -T 300 else wget -O "$output" "$url" -q --show-progress --content-disposition --no-check-certificate -T 300 fi # Check if wget command failed if [ $? -eq 0 ]; then logger info "The file has been downloaded successfully to $output." rm -f $ISHARE2_DIR/tmp/wget_error else echo -e "${RED}[-] Error: Unable to download the file. ${NO_COLOR}" # Read the error message from the wget_error file ERROR_MESSAGE=$(cat $ISHARE2_DIR/tmp/wget_error) # Print the error message echo -e "${RED}[-] Read the logs above for more information. ${NO_COLOR}" logger error "Failed to download the file. Error: $ERROR_MESSAGE" exit 1 fi } # Function to check if the user is root check_user_is_root() { if ! [[ "$(id -u)" == 0 ]]; then user=$(whoami) echo -e "${RED}[!] This script requires root privileges to be executed. The current user, \"$user\", does not have enough permissions. Please, switch to the root user to run the script.${NO_COLOR}" exit 1 fi } # Function to get the yaml templates directory based on the server platform get_server_platform() { grep -q vmx /proc/cpuinfo && echo -n vmx >/opt/unetlab/platform grep -q svm /proc/cpuinfo && echo -n svm >/opt/unetlab/platform platform=$(cat /opt/unetlab/platform) if [[ $platform == "vmx" ]]; then YML_DIR="/opt/unetlab/html/templates/intel/"; fi if [[ $platform == "svm" ]]; then YML_DIR="/opt/unetlab/html/templates/amd/"; fi } # Function to set the yaml templates directory based on the PNETLab version set_yml_template_folder_location() { data=$(mysql -uroot -ppnetlab -D pnetlab_db -e "SELECT control_value FROM control WHERE control_value>1;" 2>/dev/null) data_array=("$data") pnetlab_version=${data_array[1]} if [[ $pnetlab_version == "4.2.10" ]] || [[ $pnetlab_version == "5.0.1" ]]; then YML_DIR="/opt/unetlab/html/templates/" fi if [[ $pnetlab_version == *5.2* ]] || [[ $pnetlab_version == *5.3* ]] || [[ $pnetlab_version == *6.* ]]; then get_server_platform fi } # Function to list the labs available in the default or custom labs directory list_labs() { # Check if the user passed a custom path for Labs directory if [[ $1 == "mylabs" ]]; then # If the command is "ishare2 mylabs" LABS_DIR=$2 CUSTOM_PATH_FLAG=1 RELATIVE_PATH=$LABS_DIR if [[ ! "$LABS_DIR" == /* ]]; then # If the path is not absolute # Convert the path to absolute LABS_DIR=$(realpath "$LABS_DIR") # Check if the path is a directory and add '/' if it's missing if [[ -d "$LABS_DIR" && "$LABS_DIR" != */ ]]; then LABS_DIR="$LABS_DIR/" fi fi NOT_FOUND_MESSAGE="Make sure you are passing the correct path to the folder where your labs are located" echo -e "${BLUE}Custom path for Labs directory:${NO_COLOR} ${YELLOW}$LABS_DIR${NO_COLOR}" else # Use default path for PNETLab Store LABS_DIR="/opt/unetlab/labs/Your labs from PNETLab Store/" NOT_FOUND_MESSAGE="This folder is automatically created when a lab from PNETLab Store is downloaded" fi if ! [[ -d $LABS_DIR ]]; then echo "\"$LABS_DIR\" directory has not been found" echo "$NOT_FOUND_MESSAGE" exit 0 fi if [[ -z "$(ls -A "$LABS_DIR")" ]]; then echo "No labs found in \"$LABS_DIR\" directory" exit 0 fi counter_all_labs_installed_on_server=0 for file in "$LABS_DIR"*; do counter_all_labs_installed_on_server=$((counter_all_labs_installed_on_server + 1)) done echo -e "Labs currently available in \"$RELATIVE_PATH\" directory: $counter_all_labs_installed_on_server" declare -a readableLabsArray=() declare -a authorLabsArray=() for file in "$LABS_DIR"*; do check=$(grep -Pio 'encoding="UTF-8"' "$file") SUB="encoding" if [[ $check == *"$SUB"* ]]; then readableLabsArray+=("$file") author_lab_name=$(grep -Pio '.*author="\K[^"]*' "$file" | sort -u) if [[ -z $author_lab_name ]]; then # Author name not specified author_lab_name=N/A fi authorLabsArray+=("$author_lab_name") fi done echo "Labs in plain text: ${#readableLabsArray[@]}" if [[ ${#readableLabsArray[@]} -eq 0 ]]; then STR="No readable labs were found, so images cannot be downloaded for any lab" echo -e "${RED}[-]$STR\033[0m" exit 0 fi echo -e "\nList\n" counter_for_readable_labs=0 for f in "${readableLabsArray[@]}"; do counter_for_readable_labs=$((counter_for_readable_labs + 1)) echo -e "$counter_for_readable_labs) ${f##*/} - ${authorLabsArray[$((counter_for_readable_labs - 1))]}" done if [[ $CUSTOM_PATH_FLAG -eq 1 ]]; then CMD_TO_EXECUTE="ishare2 mylabs $RELATIVE_PATH " CMD_TO_EXECUTE_ALL="ishare2 mylabs $RELATIVE_PATH all" else CMD_TO_EXECUTE="ishare2 labs " CMD_TO_EXECUTE_ALL="ishare2 labs all" fi echo -e "\nTo install images for a specific lab, use the following command:\n\033[32m$CMD_TO_EXECUTE \033[0m" echo -e "\nTo install images for all of the labs, use the following command:\n\033[32m$CMD_TO_EXECUTE_ALL\033[0m" } # Function to pull images for a lab install_images_from_lab() { # Check if the user passed a custom path for Labs directory if [[ $1 == "mylabs" ]]; then # If the command is "ishare2 mylabs" LABS_DIR=$2 RELATIVE_PATH=$LABS_DIR if [[ ! "$LABS_DIR" == /* ]]; then # If the path is not absolute # Convert the path to absolute LABS_DIR=$(realpath "$LABS_DIR") # Check if the path is a directory and add '/' if it's missing if [[ -d "$LABS_DIR" && "$LABS_DIR" != */ ]]; then LABS_DIR="$LABS_DIR/" fi fi NUMBER=$3 echo -e "${BLUE}Custom path for Labs directory:${NO_COLOR} ${YELLOW}$LABS_DIR${NO_COLOR}" echo -e "${BLUE}Lab number:${NO_COLOR} ${YELLOW}$NUMBER${NO_COLOR}" else # Use default path for PNETLab Store LABS_DIR="/opt/unetlab/labs/Your labs from PNETLab Store/" NUMBER=$1 fi if ! [[ -d $LABS_DIR ]]; then echo "\"Your labs from PNETLab Store\" directory has not been found" echo "This folder is automatically created when a lab from PNETLab Store is downloaded" exit 0 fi if [[ -z "$(ls -A "$LABS_DIR")" ]]; then echo "No labs were found at PNETLab Store folder in server" exit 0 fi declare -a readableLabsArray=() for file in "$LABS_DIR"*; do check=$(grep -Pio 'encoding="UTF-8"' "$file") SUB="encoding" if [[ $check == *"$SUB"* ]]; then readableLabsArray+=("$file") fi done counter_for_readable_labs=0 for f in "${readableLabsArray[@]}"; do counter_for_readable_labs=$((counter_for_readable_labs + 1)) done if [[ ${#readableLabsArray[@]} -eq 0 ]]; then STR="No readable labs were found so we cannot download images for any lab" echo -e "${RED}[-]$STR\033[0m" exit 0 fi if [[ $NUMBER -gt $counter_for_readable_labs || $NUMBER -le 0 ]]; then if [[ $counter_for_readable_labs -eq 1 ]]; then STR="Last parameter must be 1 because you have only one lab" echo -e "${YELLOW}$STR${NO_COLOR}" else STR="Last parameter must be a number between 1 and $counter_for_readable_labs" echo -e "${YELLOW}$STR${NO_COLOR}" fi echo -e list_labs "$1" "$LABS_DIR" exit 0 fi file=${readableLabsArray[$((NUMBER - 1))]} echo -e "File selected: $file" TYPES=(iol dynamips qemu docker) for type in "${TYPES[@]}"; do images=$(grep -Pio "type=\"$type\".*image=\"\K[^\"]*" "$file" | sort -u) echo "$images" >/opt/unetlab/labs/"$type"_images.txt done list_iol_images=/opt/unetlab/labs/iol_images.txt list_dynamips_images=/opt/unetlab/labs/dynamips_images.txt list_qemu_images=/opt/unetlab/labs/qemu_images.txt list_docker_images=/opt/unetlab/labs/docker_images.txt BIN_FLAG=0 if ! grep -q '[^[:space:]]' $list_iol_images; then BIN_FLAG=1; fi DYNAMIPS_FLAG=0 if ! grep -q '[^[:space:]]' $list_dynamips_images; then DYNAMIPS_FLAG=1; fi QEMU_FLAG=0 if ! grep -q '[^[:space:]]' $list_qemu_images; then QEMU_FLAG=1; fi DOCKER_FLAG=0 if ! grep -q '[^[:space:]]' $list_docker_images; then DOCKER_FLAG=1; fi print_separator "List of images found on lab" for type in "${TYPES[@]}"; do print_separator "List of ${type^^} images" list_file="/opt/unetlab/labs/${type}_images.txt" if ! grep -qv -e '^\s*$' "$list_file"; then echo "No ${type} images found" else n=1 while read -r line; do echo "File $n : $line" n=$((n + 1)) done <"$list_file" fi done TYPES=(iol dynamips qemu) for type in "${TYPES[@]}"; do print_separator "Start downloading ${type^^} images for this lab" pull_lab_images "$file" "$type" done print_separator "Start downloading DOCKER images for this lab" echo -e "\033[32m$STR_DOCKER\033[0m" if [[ DOCKER_FLAG -eq 1 ]]; then echo -e "${YELLOW}- Nothing to download${NO_COLOR}" else echo "Docker images" download_lab_docker_images fi # Remove csv file rm "$(pwd)"/"$FILENAME" >/dev/null 2>&1 # Remove files rm /opt/unetlab/labs/{iol_images.txt,dynamips_images.txt,qemu_images.txt,docker_images.txt} >/dev/null 2>&1 } # Function to download images for all labs install_images_all_labs() { # Check if the user passed a custom path for Labs directory if [[ $1 == "mylabs" ]]; then # If the command is "ishare2 mylabs" LABS_DIR=$2 CUSTOM_PATH_FLAG=1 if [[ ! "$LABS_DIR" == /* ]]; then # If the path is not absolute # Convert the path to absolute LABS_DIR=$(realpath "$LABS_DIR") # Check if the path is a directory and add '/' if it's missing if [[ -d "$LABS_DIR" && "$LABS_DIR" != */ ]]; then LABS_DIR="$LABS_DIR/" fi fi NOT_FOUND_MESSAGE="Make sure you are passing the correct path to the folder where your labs are located" echo -e "${BLUE}Custom path for Labs directory:${NO_COLOR} ${YELLOW}$LABS_DIR${NO_COLOR}" else # Use default path for PNETLab Store LABS_DIR="/opt/unetlab/labs/Your labs from PNETLab Store/" NOT_FOUND_MESSAGE="This folder is automatically created when a lab from PNETLab Store is downloaded" fi if ! [[ -d $LABS_DIR ]]; then echo "\"$LABS_DIR\" directory has not been found" echo "$NOT_FOUND_MESSAGE" exit 0 fi if [[ -z "$(ls -A "$LABS_DIR")" ]]; then echo "No labs found in \"$LABS_DIR\" directory" exit 0 fi declare -a readableLabsArray=() for file in "$LABS_DIR"*; do check=$(grep -Pio 'encoding="UTF-8"' "$file") SUB="encoding" if [[ $check == *"$SUB"* ]]; then readableLabsArray+=("$file") fi done N=${#readableLabsArray[@]} if [[ $N -eq 0 ]]; then STR="No readable labs have been found so there is no way to continue with this command" echo -e "${RED}[-]$STR\033[0m" exit 0 fi if [[ $N -eq 1 ]]; then echo "Starting to download images for $N lab" else echo "Starting to download images for $N labs" fi for ((i = 1; i <= N; i++)); do echo -e "\n\033[32mLab $i/$N\033[0m" if [[ $CUSTOM_PATH_FLAG -eq 1 ]]; then install_images_from_lab "mylabs" "$LABS_DIR" "$i" else install_images_from_lab "$i" fi done echo -e "\nDone" } # Function to download all images of a specific type (IOL, DYNAMIPS, QEMU, DOCKER) pull_all() { TYPE=${1^^} if [[ $TYPE == "BIN" ]]; then TYPE="IOL" fi if [[ $TYPE == "QEMU" ]]; then echo -e "${YELLOW}[!] WARNING: Pulling all QEMU images will take a long time.\n [!] Make sure you have enough disk space and bandwidth!${NO_COLOR}" # Enter confirmation text CONFIRMATION="I understand that pulling all QEMU images will take a long time. I have enough disk space and bandwidth." # Ask the user to enter the confirmation text read -p "[?] Please enter the following text to confirm: \"$CONFIRMATION\" (case sensitive): " -r # If the confirmation text is incorrect then exit if [[ ! $REPLY =~ ^$CONFIRMATION$ ]]; then echo -e "${RED}[!] Aborting.${NO_COLOR}" exit 1 fi fi echo -e "${YELLOW}[+] Pulling all $TYPE images...${NO_COLOR}" # Ask for conirmation read -p "[?] Are you sure you want to pull all $TYPE images? (y/n): " -r # If reply was no Y or y then exit if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo -e "${RED}[!] Aborting.${NO_COLOR}" exit 1 fi # Get the list of all images fetch_json # Get all the IDs for the images of the specified type IDS=$(jq -r --arg type "$TYPE" '.[$type][] | .id' "$TEMP_JSON") # Pull all the images i=1 total=$(echo "$IDS" | wc -l) for ID in $IDS; do echo -e "${YELLOW}[+] Pulling image $i/$total...${NO_COLOR}" ishare2 pull ${TYPE,,} $ID i=$((i + 1)) done } # Function to pull lab images pull_lab_images() { # Pending to implement: corrections_for_bin_images_in_lab_"$IMAGE_NAME" "$LAB_PATH" # Pending to implement: corrections_for_qemu_images_in_lab_"$QEMU_NAME" LAB_PATH="$1" IMAGE_TYPE="$2" IMAGE_TYPE_UPPERCASE=$(echo "$IMAGE_TYPE" | tr '[:lower:]' '[:upper:]') filename1="/opt/unetlab/labs/${IMAGE_TYPE}_images.txt" n=1 while read -r line; do n=$((n + 1)) IMAGE_NAME="$line" if [[ -z $IMAGE_NAME ]]; then echo "No $IMAGE_TYPE images found" else fetch_json # Clear the ID variable at the start of each iteration ID="" # Search for the image in the "IMAGE_TYPE" section of the JSON file and get the ID ID=$(jq -r --arg image_name "$IMAGE_NAME" '.["'"$IMAGE_TYPE_UPPERCASE"'"][] | select(.name == $image_name) | .id' "$TEMP_JSON") if [ -n "$ID" ]; then # The image was found in the "IMAGE_TYPE" section of the JSON data echo "Image '$IMAGE_NAME' found in JSON data. ID: $ID" # Download the image ishare2 pull "$IMAGE_TYPE" "$ID" else echo "Image '$IMAGE_NAME' not found in JSON data." fi fi done <"$filename1" } # Function to check docker service status check_docker_service_status() { local status=$(service docker status) if echo "$status" | grep -q "active (running)"; then return 0 fi if echo "$status" | grep -q "inactive (dead)"; then echo -e "${RED}Detected: Docker service is down. Trying to restart it${NO_COLOR}" service docker restart status=$(service docker status) fi if echo "$status" | grep -q "active (running)"; then echo -e "${GREEN}Docker service has been restarted successfully${NO_COLOR}" return 0 else echo -e "${RED}There was a problem trying to start docker service${NO_COLOR}" echo -e "Information about service docker status command:\n" echo "$status" fi } # Function to list docker images list_dockers() { echo -e "\nDocker images in server" docker images } # Function to count docker images count_dockers() { data=$(docker images | wc -l) docker_count=$((data - 1)) if [[ $docker_count -eq 1 ]]; then echo -e "\n$docker_count docker image found in server" else echo -e "\n$docker_count docker images found in server" fi } # Function to download lab docker images using the correct docker image name corrections_for_docker_images_in_lab_function() { DOCKER_NAME=$1 LAB_PATH=$2 declare -a array=( "eve-ostinato" "eve-ostinato-bionic" "eve-ostinato-focal" "eve-dind" "eve-dind-focal" "eve-chrome" "eve-chrome-bionic" "eve-chrome-focal" "eve-firefox" "eve-firefox-bionic" "eve-firefox-focal" "eve-desktop" "eve-desktop-bionic" "eve-desktop-focal" "eve-gui-server" "eve-gui-server-bionic" "eve-gui-server-focal" "eve-wireshark" "eve-wireshark-bionic" "eve-wireshark-focal" "eve-kali" "eve-kali-large" "eve-napalm-focal" "eve-ansible-focal" "dockergui-rdp" "eve-gui-server:latest" "eve-gui-server-bionic:latest" ) for i in "${array[@]}"; do if [[ $DOCKER_NAME == "$i" ]]; then OLD_FILENAME=$DOCKER_NAME NEW_FILENAME=eveng/$DOCKER_NAME DOCKER_NAME=$NEW_FILENAME echo -e "\nDocker image name changed from:\n$OLD_FILENAME" echo -e "to\n$NEW_FILENAME\n" echo "Changing docker image name inside .unl lab file" echo Lab file: "$LAB_PATH" sed -i -e s+"$OLD_FILENAME"+"$NEW_FILENAME"+g "$LAB_PATH" echo -e "Changing: OK\n" fi done } # Function to download lab docker images download_lab_docker_images() { filename1=/opt/unetlab/labs/docker_images.txt # Checking docker service status before pull any docker image if check_docker_service_status; then # When 0 is returned while read -r line; do DOCKER_NAME=$line corrections_for_docker_images_in_lab_"$DOCKER_NAME" "$LAB_PATH" STR="Docker requested: " echo -e "\033[33m$STR\033[0m $DOCKER_NAME" docker pull "$DOCKER_NAME" done <"$filename1" fi list_dockers count_dockers } # Function to get the version of ishare2 ishare2_version() { # Check if file is empty or not exists or different from installed $VERSION echo $VERSION >$ISHARE2_DIR/ishare2_version # Set CHANNEL on ishare2.conf sed -i "s/CHANNEL=.*/CHANNEL=$CHANNEL/" $ISHARE2_CONF_FILE if [[ $1 == "show" ]]; then echo -e "ishare2 version: $(cat $ISHARE2_DIR/ishare2_version)" echo -e "Latest version: $(curl -s --insecure "$URL_VERSION")" fi } # Function to upgrade ishare2 upgrade_ishare2() { LOCAL_VALUE=$(cat $ISHARE2_DIR/ishare2_version) UPSTREAM_VERSION=$(curl -s --insecure "$URL_VERSION") if [[ $LOCAL_VALUE == "$UPSTREAM_VERSION" ]]; then echo -e "${YELLOW}[+] ishare2 $UPSTREAM_VERSION is already in the newest version available. ${NO_COLOR}" echo -e "${YELLOW}[+] Do you want to reinstall it? (y/n) ${NO_COLOR}" read -r answer if [[ $answer == "y" || $answer == "Y" ]]; then echo -e "${YELLOW}[+] Reinstalling ishare2... ${NO_COLOR}" else exit 0 fi fi rm /usr/sbin/ishare2 download_file_wget "$URL_ISHARE2" /usr/sbin/ "ishare2" echo -e "${GREEN}[+] Successfully downloaded the latest version of ishare2!${NO_COLOR}" # Make ishare2 executable echo -e "${YELLOW}[+] Setting ishare2 permissions${NO_COLOR}" chmod +x /usr/sbin/ishare2 # Show success message echo -e "${GREEN}[+] ishare2 was upgraded from $LOCAL_VALUE to $UPSTREAM_VERSION ${NO_COLOR}" } # Function to show the ishare2 upgrade menu menu_ishare2_upgrade_pnetlab() { # Define the menu options options=( "Upgrade PNETLab v5 STABLE" "Upgrade PNETLab v5 BETA" "Upgrade PNETLab v6 BETA" ) # Set the prompt for the select menu PS3="Please, select an option: " # Print the menu and wait for user input select opt in "${options[@]}" "Exit"; do case "$REPLY" in 1) # Upgrade PNETLab v5 bash -c "$(curl -sL --insecure "$URL_PNETLAB_V5")" ;; 2) # Upgrade PNETLab v5 Beta bash -c "$(curl -sL --insecure "$URL_PNETLAB_V5_BETA")" ;; 3) # Upgrade PNETLab v6 bash -c "$(curl -sL --insecure "$URL_PNETLAB_V6")" ;; $((${#options[@]} + 1))) break ;; *) # Invalid option total_options=${#options[@]} echo -e "${RED}Invalid option: Select a number from 1 to $total_options${NO_COLOR}" continue ;; esac done } # Function to show the ishare2 upgrade menu menu_ishare2_upgrade() { prompt="Please, select an option: " options=( "Upgrade ishare2" "Upgrade PNETLab" "Upgrade ishare2 GUI" ) PS3="$prompt" select opt in "${options[@]}" "Exit"; do case "$REPLY" in 1) upgrade_ishare2 break ;; 2) menu_ishare2_upgrade_pnetlab break ;; 3) ishare2 gui install break ;; $((${#options[@]} + 1))) break ;; *) STR="Invalid option: Select a number from 1 to 4" echo -e "${RED}$STR${NO_COLOR}" continue ;; esac done } # Function to show the ishare2 changelog show_changelog() { FILE=/root/CHANGELOG.md if [[ -e $FILE ]]; then rm $FILE; fi wget -q "$URL_CHANGELOG_MD" -P /root head -n 15 $FILE rm $FILE } # Function to install ishare2 GUI ishare2_gui_install() { curl -sL "$URL_ISHARE2_GUI_INSTALL" | bash } # Function to start ishare2 GUI ishare2_gui_start() { systemctl start ishare2_gui.service if [[ $? -ne 0 ]]; then echo -e "${RED}[-] Error starting ishare2 GUI service. ${NO_COLOR}" echo -e "${RED}[-] Please see the logs for troubleshooting. Run: systemctl status ishare2_gui.service ${NO_COLOR}" exit 1 fi } # Function to stop ishare2 GUI ishare2_gui_stop() { systemctl stop ishare2_gui.service if [[ $? -ne 0 ]]; then echo -e "${RED}[-] Error stopping ishare2 GUI service. ${NO_COLOR}" echo -e "${RED}[-] Please see the logs for troubleshooting. Run: systemctl status ishare2_gui.service ${NO_COLOR}" exit 1 fi } # Function to restart ishare2 GUI ishare2_gui_restart() { systemctl restart ishare2_gui.service if [[ $? -ne 0 ]]; then echo -e "${RED}[-] Error restarting ishare2 GUI service. ${NO_COLOR}" echo -e "${RED}[-] Please see the logs for troubleshooting. Run: systemctl status ishare2_gui.service ${NO_COLOR}" exit 1 fi } # Function to search for images in the JSON index search_images() { TYPE=${1^^} if [[ -n $2 ]]; then # If the user passed a filter FILTER=$2 fi # Download the JSON and store it in a temporary file fetch_json print_separator "Available $TYPE images" # Filter rows with jq and keep the table headers data=$(jq -r '(["ID","NAME","SIZE"] | (., map(length*"-"))), (.["'$TYPE'"][] | select(.name | contains("'$FILTER'")) | [.id, .name, .human_readable_size]) | @tsv' "$TEMP_JSON") # Check if there are matching data rows if [ -n "$data" ]; then echo -e "$data" | column -t -s $'\t' # Calculate and display the total count total_count=$(echo "$data" | wc -l) total_count=$((total_count - 2)) if [[ $total_count -eq 0 ]]; then echo -e "\n${RED}No $TYPE images found for the term: \"$FILTER\"${NO_COLOR}" elif [[ $total_count -eq 1 ]]; then echo -e "\n${GREEN}$total_count $TYPE image found for the term: \"$FILTER\"${NO_COLOR}" elif [[ $FILTER == "" ]]; then echo -e "\n${GREEN}$total_count $TYPE images found${NO_COLOR}" else echo -e "\n${GREEN}$total_count $TYPE images found for the term: \"$FILTER\"${NO_COLOR}" fi fi } # Function to update iptables and add them to startup script block_cisco() { # Remove entries containing "cisco xml" from /etc/hosts sed -i '/cisco xml/d' /etc/hosts || { echo -e "${RED}[-] Failed to remove entries containing 'cisco xml' from /etc/hosts${NO_COLOR}" logger error "Failed to remove entries containing 'cisco xml' from /etc/hosts" exit 1 } # Add the iptables rule to block cisco xml requests to ovfstartup echo 'iptables -I OUTPUT -p udp --dport 53 -m string --hex-string "|03|xml|05|cisco|03|com" --algo bm -j DROP' >>/opt/ovf/ovfstartup || { echo -e "${RED}[-] Failed to add iptables rule to block cisco xml requests${NO_COLOR}" logger "error Failed to add iptables rule to block cisco xml requests" exit 1 } # Restart the ovfstartup service service ovfstartup restart || { echo -e "${RED}[-] Failed to restart ovfstartup service${NO_COLOR}" logger error "Failed to restart ovfstartup service" exit 1 } # If no errors occurred, show success message echo "${GREEN}[+] Successfully updated iptables and added them to the ovfstartup script${NO_COLOR}" } # Function to generate a new iourc license generate_iourc_license() { BIN_PATH="/opt/unetlab/addons/iol/bin/" PYTHON_FILE=$BIN_PATH"CiscoIOUKeygen.py" PERL_FILE=$BIN_PATH"keepalive.pl" # Check if python2 is installed if ! command -v python &>/dev/null; then echo -e "${RED}[-] Python is not installed${NO_COLOR}" logger error "Python is not installed" # Ask the user if they want to install python read -p "[?] Do you want to install python? (y/n): " -r if [[ $REPLY =~ ^[Yy]$ ]]; then install_package python else echo -e "${RED}[-] Python2 is required to generate the iourc license${NO_COLOR}" exit 1 fi fi # Block cisco xml requests block_cisco || { echo "${RED}[-] Failed to block cisco xml requests${NO_COLOR}" logger error "Failed to block cisco xml requests" exit 1 } # Check if the CiscoIOUKeygen.py file exists in the bin directory if ! [[ -e $PYTHON_FILE ]]; then wget -O $PYTHON_FILE "$URL_CISCOIOUKEYGEN_PY" -q || { echo -e "${RED}[-] Error downloading CiscoIOUKeygen.py file${NO_COLOR}" logger error "Error downloading CiscoIOUKeygen.py file" exit 1 } fi echo -e "${YELLOW}[!] Executing CiscoIOUKeygen.py file... ${NO_COLOR}" python $PYTHON_FILE if [[ $? -ne 0 ]]; then echo -e "${RED}[-] Error running CiscoIOUKeygen.py file. ${NO_COLOR}" echo -e "${RED}[-] Please check your internet connection and try again. ${NO_COLOR}" exit 1 fi if ! [[ -e $PERL_FILE ]]; then wget -O $PERL_FILE "$URL_KEEPALIVE_PL" -q if [[ $? -ne 0 ]]; then echo -e "${RED}[-] Error downloading keepalive.pl file. ${NO_COLOR}" echo -e "${RED}[-] Please check your internet connection and try again. ${NO_COLOR}" exit 1 fi fi echo -e "${GREEN}[+] IOL license was generated successfully. ${NO_COLOR}" } # Function to show information about ishare2 show_info() { echo -e "- For documentation about ishare2, visit: https://github.com/ishare2-org/ishare2-cli" echo -e "- If you face any issues with ishare2, please report them at @NetLabHub Telegram Group. Attach screenshots and relevant information." echo -e "- You can use ${YELLOW}ishare2 config${NO_COLOR} to change the chosen downloader." echo -e "- If you cancel a download, use the flag: --overwrite to first delete the existing image and then download it again." echo -e "- To submit new images, please use the following Google Form: https://forms.gle/SotrznrN3CmZdg8M8" echo -e "- Telegram Channel: https://t.me/NetLabHub (For announcements and updates)" echo -e "- Email: contact@labhub.eu.org" } # Function to show help for ishare2 show_ishare2_help() { # For ishare2 ISHARE2_VERSION_INFO=$(cat $ISHARE2_DIR/ishare2_version) UPSTREAM_VERSION=$(curl -s "$URL_VERSION") if ! [[ $ISHARE2_VERSION_INFO == "$UPSTREAM_VERSION" ]]; then ISHARE2_VERSION_INFO="$ISHARE2_VERSION_INFO $(echo -e "${YELLOW}Update available: $UPSTREAM_VERSION. Run: ishare2 upgrade${NO_COLOR}")" else ISHARE2_VERSION_INFO="$ISHARE2_VERSION_INFO $(echo -e "${GREEN}Up to date${NO_COLOR}")" fi # For PNETLab data=$(mysql -uroot -ppnetlab -D pnetlab_db -e "SELECT control_value FROM control WHERE control_value>1;" 2>/dev/null) pnetlab_info=($data) if [[ ${#pnetlab_info[@]} -eq 0 ]]; then PNETLAB_INSTALLED=$(echo -e "${RED}PNETLab is not installed.${NO_COLOR}") else PNETLAB_INSTALLED=$(echo -e "${GREEN}PNETLab v${pnetlab_info[1]} is installed.${NO_COLOR}") fi cat < --overwrite: Used to overwrite an existing image if it already exists on your system. Examples: - ishare2 search - ishare2 search all - ishare2 search bin - ishare2 search qemu - ishare2 search dynamips - ishare2 search Examples: - ishare2 search vios - ishare2 search win- - ishare2 search winserver - ishare2 search kali - ishare2 search mikro - ishare2 search forti - ishare2 search nxos - ishare2 search vmx - ishare2 search esxi - ishare2 search palo - ishare2 search Licensed - More options using ishare2 search all - ishare2 pull bin [--overwrite] - ishare2 pull qemu [--overwrite] - ishare2 pull dynamips [--overwrite] - ishare2 pull bin all - ishare2 pull qemu all (Not available for qemu type due to its large size) - ishare2 pull dynamips all - ishare2 installed all - ishare2 installed bin - ishare2 installed qemu - ishare2 installed dynamips - ishare2 installed docker - ishare2 labs - ishare2 labs - ishare2 labs all - ishare2 mylabs - ishare2 mylabs - ishare2 mylabs all - ishare2 gui install - ishare2 gui start - ishare2 gui stop - ishare2 gui restart - ishare2 relicense - ishare2 upgrade - ishare2 changelog - ishare2 test - ishare2 config - ishare2 help ishare2: $ISHARE2_VERSION_INFO ishare2 Channel: $CHANNEL $PNETLAB_INSTALLED EOF } # Function to show quick help for ishare2 ishare2_quick_help() { cat < --overwrite: Used to overwrite an existing image if it already exists on your system. Try: ishare2 help for more information. EOF } # Function to handle tgz archives handle_tgz_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".tgz" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}" tar -xf "$file" --strip-components 1 -C "$DIR" rm "$file" echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}" fi } # Function to handle tar.gz archives handle_tar_gz_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".tar.gz" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}" tar -xf "$file" --strip-components 1 -C "$DIR" rm "$file" echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}" fi } # Function to handle tar files handle_tar_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".tar" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}" tar -xf "$file" --strip-components 1 -C "$DIR" rm "$file" echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}" fi } # Function to handle zip files handle_zip_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".zip" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}" unzip -a -j "$file" -d "$DIR" rm "$file" echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}" fi } # Function to handle rar files handle_rar_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".rar" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") if ! [[ -e /usr/bin/unrar ]]; then apt -qq update >/dev/null 2>&1 && apt -qq install --assume-yes unrar >/dev/null 2>&1 fi echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}" unrar e "$file" "$DIR" >/dev/null 2>&1 echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}" rm "$file" fi } # Function to handle OVA files handle_ova_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".ova" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then COMPRESSED_FILE=$(ls "${DIR}"/*"$EXTENSION") echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}" tar -xvf "$COMPRESSED_FILE" -C "$DIR" rm "$COMPRESSED_FILE" echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}" fi } # Function to handle ISO files handle_iso_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".iso" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then ISO_FILE=$(ls "${DIR}"/*"$EXTENSION") if ! [[ "${ISO_FILE: -9}" == "cdrom.iso" ]]; then mv "$ISO_FILE" "$DIR"/cdrom.iso # Rename file echo -e "${GREEN}[+] ISO file saved as: $DIR/cdrom.iso ${NO_COLOR}" fi if ! [[ -e /usr/bin/qemu-img ]]; then echo -e "${YELLOW}[!] qemu-img is not installed. Attempting to install it... ${NO_COLOR}" install_packages "qemu-utils" fi if [[ $FILES_COUNTER -eq 1 ]]; then qemu-img create -f qcow2 "$DIR"/virtioa.qcow2 20G fi fi } # Function to handle yaml templates handle_yml_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSIONS=(".yml" ".yaml") #YML_DIR="/opt/unetlab/html/templates/" # Value passed from set_yml_template_folder_location() for ext in "${EXTENSIONS[@]}"; do if ls "${DIR}"/*"$ext" &>/dev/null; then file=$(ls "${DIR}"/*"$ext" | head -1) cp "$file" "$YML_DIR" break fi done } # Function to handle txt and md files handle_txt_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSIONS=(".txt" ".md") for ELEMENT in "$DIR"/*; do for EXTENSION in "${EXTENSIONS[@]}"; do if [[ "${ELEMENT: -${#EXTENSION}}" == "$EXTENSION" ]]; then echo -e "\nReading file: $ELEMENT\n" cat "$ELEMENT" echo -e break fi done done } # Function to handle sh files handle_sh_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".sh" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") chmod +x "$file" echo -e "This image includes a script file: $file" read -p "Do you want to run $file? [y/n]: " -n 1 -r echo "" if [[ $REPLY =~ ^[Yy]$ ]]; then bash "$file" else echo -e "${YELLOW}[!] $file was not executed. ${NO_COLOR}" echo -e "${YELLOW}[!] WARN: This script file might be needed for the image to work properly. ${NO_COLOR}" fi fi } # Function to handle png files (icons) handle_png_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".png" PNG_DIR="/opt/unetlab/html/images/icons/" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") # Change .png filename for a Huawei device: from ne5000e.png to ne.png if [[ $file == "/opt/unetlab/addons/qemu/huaweine5ke-ne5000e/ne5000e.png" ]]; then NEW_PNG_FILENAME="/opt/unetlab/addons/qemu/huaweine5ke-ne5000e/ne.png" mv "$file" $NEW_PNG_FILENAME file=$NEW_PNG_FILENAME fi cp "$file" $PNG_DIR fi } # Function to handle php files handle_php_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".php" PHP_DIR="/opt/unetlab/html/devices/qemu/" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then file=$(ls "${DIR}"/*"$EXTENSION") cp "$file" $PHP_DIR fi } # Function to handle vmdk files handle_vmdk_files() { FOLDERNAME=$1 DIR="/opt/unetlab/addons/qemu/$FOLDERNAME" EXTENSION=".vmdk" if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then VMDK_FILE=$(ls "${DIR}"/*"$EXTENSION") install_qemu_tools echo -e "\nConverting .vmdk to .qcow2. It might take a while..." /opt/qemu/bin/qemu-img convert -p -f vmdk -O qcow2 "$VMDK_FILE" "$DIR"/hda.qcow2 echo "File converted successfully" rm "$VMDK_FILE" fi } # Function to install qemu-utils install_qemu_tools() { apt update apt install -y qemu-utils # check if qemu-img is installed if ! [[ -e /opt/qemu/bin/qemu-img ]]; then echo -e "${RED}[-] qemu-img is not installed. Please install it manually. ${NO_COLOR}" fi } # Function to pull images passed as parameters pull_images() { IMAGE_TYPE=$1 IMAGE_ID=$2 OVERWRITE=$3 # 0. Check parameters # Check if IMAGE_TYPE is not empty if [[ -z "$IMAGE_TYPE" ]]; then echo "Usage: $SCR_NAME pull " echo "Types: qemu, iol, dynamips" echo "Example: $SCR_NAME pull bin " exit 1 fi # Check if IMAGE_ID is not empty and is a number if [[ -z "$IMAGE_ID" || ! "$IMAGE_ID" =~ ^[0-9]+$ ]]; then echo "Usage: $SCR_NAME pull ${IMAGE_TYPE,,} " echo "Example: $SCR_NAME pull ${IMAGE_TYPE,,} 1" exit 1 fi if [ "$IMAGE_TYPE" = "BIN" ]; then IMAGE_TYPE="IOL" else IMAGE_TYPE="$1" fi local download_urls local download_path # Pending: implement corrections_for_qemu_images with new JSON structure local image_name local image_size # Rotate mirrors if enabled rotate_mirrors # 1. Fetch the JSON file fetch_json # Extract the download URLs, path, name, and size for the specified type and ID download_urls=$(jq -r --arg type "$IMAGE_TYPE" --arg id "$IMAGE_ID" '.[$type][] | select(.id == ($id | tonumber)) | .download_links[]' "$TEMP_JSON") download_path=$(jq -r --arg type "$IMAGE_TYPE" --arg id "$IMAGE_ID" '.[$type][] | select(.id == ($id | tonumber)) | .download_path' "$TEMP_JSON") image_name=$(jq -r --arg type "$IMAGE_TYPE" --arg id "$IMAGE_ID" '.[$type][] | select(.id == ($id | tonumber)) | .name' "$TEMP_JSON") image_size=$(jq -r --arg type "$IMAGE_TYPE" --arg id "$IMAGE_ID" '.[$type][] | select(.id == ($id | tonumber)) | .human_readable_size' "$TEMP_JSON") image_size_bytes=$(jq -r --arg type "$IMAGE_TYPE" --arg id "$IMAGE_ID" '.[$type][] | select(.id == ($id | tonumber)) | .size' "$TEMP_JSON") image_file_format=$(jq -r --arg type "$IMAGE_TYPE" --arg id "$IMAGE_ID" '.[$type][] | select(.id == ($id | tonumber)) | .format' "$TEMP_JSON") # 2. Download the image(s) for the specified type and ID # Skip download if the image is already installed FOLDERNAME="$image_name" IMAGE_TYPE_LOWER=$(echo "$IMAGE_TYPE" | tr '[:upper:]' '[:lower:]') case "$IMAGE_TYPE" in "QEMU") IMAGE_PATH="/opt/unetlab/addons/$IMAGE_TYPE_LOWER/$FOLDERNAME" if [ -d "$IMAGE_PATH" ] && [ "$OVERWRITE" != "--overwrite" ]; then echo "The folder '$FOLDERNAME' already exists on the server. Use the --overwrite option to overwrite it" exit 0 fi ;; "BIN" | "IOL") IMAGE_PATH="/opt/unetlab/addons/$IMAGE_TYPE_LOWER/bin/$image_name" if [ -e "$IMAGE_PATH" ] && [ "$OVERWRITE" != "--overwrite" ]; then echo "The file '$image_name' already exists on the server'. Use the --overwrite option to overwrite it" exit 0 fi ;; "DYNAMIPS") IMAGE_PATH="/opt/unetlab/addons/$IMAGE_TYPE_LOWER/$image_name" if [ -e "$IMAGE_PATH" ] && [ "$OVERWRITE" != "--overwrite" ]; then echo "The file '$image_name' already exists on the server'. Use the --overwrite option to overwrite it" exit 0 fi ;; *) echo "Invalid image type: $IMAGE_TYPE" exit 1 ;; esac if [ -n "$download_urls" ]; then # extract one of the download URLs to get the domain, then print the protocol and domain without the path domain=$(echo "$download_urls" | head -n 1 | awk -F/ '{print $1"//"$3}') logger info "Downloading Image: $image_name, Type: $IMAGE_TYPE, ID: $IMAGE_ID" echo -e "${YELLOW}[!] IMAGE INFO ${NO_COLOR}" printf "%-20s: %s\n" " - Image Name" "$image_name" printf "%-20s: %s\n" " - Image Size" "$image_size" printf "%-20s: %s\n" " - Image Type" "$IMAGE_TYPE" printf "%-20s: %s\n" " - Image ID" "$IMAGE_ID" printf "%-20s: %s\n" " - Image path" "$download_path" printf "%-20s: %s\n" " - Using host" "$domain" # Create the directory if it doesn't exist logger info "Creating directory for: $IMAGE_PATH" mkdir -p "$download_path" echo -e "${YELLOW}[!] DOWNLOADING IMAGE ${NO_COLOR}" # Iterate through the download URLs and download each file case $IMAGE_TYPE in "QEMU") for download_url in $download_urls; do download_file "$download_url" "$download_path" # Only check if it's a single file with archive format: tgz, tar.gz, tar, zip, rar, ova, iso local archive_extensions=(".tgz" ".tar.gz" ".tar" ".zip" ".rar" ".ova" ".iso") if [[ $download_path == *"${archive_extensions[@]}" ]]; then check_image_size "$download_path" "$image_name$image_file_format" "$image_size_bytes" fi done ;; "BIN" | "DYNAMIPS" | "IOL") for download_url in $download_urls; do download_file "$download_url" "$download_path" "$image_name" # Check image size against the downloaded file check_image_size "$download_path" "$image_name" "$image_size_bytes" done ;; *) echo "Something went wrong. Invalid image type provided: $IMAGE_TYPE" ;; esac echo -e "${YELLOW}[+] ${BLUE}DOWNLOAD COMPLETED! ${NO_COLOR}" else echo "Image id $IMAGE_ID ($image_name) not found for type $IMAGE_TYPE in the index of images" exit 1 fi # 3. Handle downloaded files if TYPE is QEMU if [ "$IMAGE_TYPE" = "QEMU" ]; then # 3.1. Extract archives handle_tgz_files "$FOLDERNAME" handle_tar_gz_files "$FOLDERNAME" handle_tar_files "$FOLDERNAME" handle_zip_files "$FOLDERNAME" handle_rar_files "$FOLDERNAME" handle_ova_files "$FOLDERNAME" handle_iso_files "$FOLDERNAME" # 3.2. Handle other files handle_yml_files "$FOLDERNAME" handle_txt_files "$FOLDERNAME" handle_sh_files "$FOLDERNAME" handle_png_files "$FOLDERNAME" handle_php_files "$FOLDERNAME" handle_vmdk_files "$FOLDERNAME" # 3.3. Apply fix permissions command fix_permissions fi } # Function to fix permissions of the downloaded images fix_permissions() { UNL_WRAPPER="/opt/unetlab/wrappers/unl_wrapper" echo -e "${YELLOW}[-] Fixing permissions... ${NO_COLOR}" chmod +x $UNL_WRAPPER $UNL_WRAPPER -a fixpermissions if [ $? -ne 0 ]; then echo -e "\n${RED}[-] Failed to fix permissions ${NO_COLOR}" exit 1 fi echo -e "\n${GREEN}[+] Fix permissions command has been executed correctly ${NO_COLOR}" } # Function to run Internet connection tests connection_tests() { # Define an associative array with service names and their respective IP addresses or domains declare -A SERVICES=( ["GitHub"]="github.com" ["GitHub Raw Files"]="raw.githubusercontent.com" ["LabHub (Onedrive backend)"]="labhub.eu.org" ["LabHub (Google Drive backend)"]="drive.labhub.eu.org" ["Google DNS"]="8.8.8.8" ) # Initialize a flag to track if all services are reachable local all_services_reachable=true # Header echo -e "${YELLOW}[-] Running connection tests... ${NO_COLOR}" # Loop through the services and check their reachability for service in "${!SERVICES[@]}"; do echo -e "${YELLOW}[-] Checking if $service is reachable... ${NO_COLOR}" if ping -q -c 5 -W 5 "${SERVICES[$service]}" >/dev/null; then echo -e "${GREEN}[+] $service is reachable. ${NO_COLOR}" else echo -e "${RED}[-] $service is not reachable. ${NO_COLOR}" all_services_reachable=false fi done # Checks completed echo -e "${YELLOW}[-] Connection tests completed. ${NO_COLOR}" # If any service is not reachable, attempt to fix DNS if [ "$all_services_reachable" = false ]; then echo -e "${YELLOW}[?] Some services are not reachable. Do you want to attempt to fix DNS? [y/n] ${NO_COLOR}" read -r -n 1 -p "Choice: " choice echo case $choice in y | Y) fix_dns ;; n | N) echo -e "${YELLOW}[-] DNS was not fixed. ${NO_COLOR}" ;; *) echo -e "${RED}[-] Invalid choice. DNS was not fixed. ${NO_COLOR}" ;; esac fi } # Function to fix DNS (common issue with resolv.conf) fix_dns() { echo -e "${YELLOW}[-] Attempting to fix DNS... ${NO_COLOR}" echo -e "${YELLOW}[-] Running: systemctl restart systemd-resolved.service ${NO_COLOR}" systemctl restart systemd-resolved.service echo -e "${YELLOW}[-] Attempting to fix DNS by setting nameserver to 8.8.8.8... ${NO_COLOR}" echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf >/dev/null if [ $? -eq 0 ]; then echo -e "${GREEN}[+] DNS fix applied successfully. ${NO_COLOR}" else echo -e "${RED}[-] Failed to update DNS settings. ${NO_COLOR}" fi } # Function to handle parameters passed to ishare2 selector() { case "$1" in "search") QUERY="$3" # Switch case to determine the type of the device to be displayed case "$2" in "qemu" | "iol" | "dynamips") TYPE="${2^^}" # Convert to uppercase search_images "$TYPE" "$QUERY" ;; "bin") TYPE="IOL" # Treat "bin" as "iol" in uppercase search_images "$TYPE" "$QUERY" ;; "all") for type in "QEMU" "IOL" "DYNAMIPS"; do TYPE="${type^^}" # Convert to uppercase search_images "$TYPE" echo done ;; *) if [ -n "$2" ]; then FILTER="$2" for type in "QEMU" "IOL" "DYNAMIPS"; do TYPE="${type^^}" search_images "$TYPE" echo done else echo "Usage: $SCR_NAME search [keyword]" echo " $SCR_NAME search all" echo "Types: qemu, iol, dynamips" exit 1 fi ;; esac ;; "pull") case "$2" in "qemu" | "iol" | "dynamips" | "bin") TYPE="${2^^}" # Convert to uppercase if [[ "$3" ]]; then if [[ "$3" = "all" ]]; then pull_all "$TYPE" exit 0 fi fi pull_images "$TYPE" "$3" "$4" ;; *) # If the type is not specified, print the usage echo "Usage: $SCR_NAME pull " echo " $SCR_NAME pull all" echo "Types: qemu, iol, dynamips" ;; esac ;; "installed") if [[ "$2" ]]; then case "$2" in "all") ishare2 installed qemu ishare2 installed dynamips ishare2 installed iol ishare2 installed docker ;; "qemu") echo -e print_separator "Showing installed ${2^^} images" tree -hp /opt/unetlab/addons/qemu/ echo -e ;; "dynamips") echo -e print_separator "Showing installed ${2^^} images" tree -hp /opt/unetlab/addons/dynamips/ echo -e ;; "bin" | "iol") echo -e print_separator "Showing installed ${2^^} images" tree -hp /opt/unetlab/addons/iol/bin/ echo -e ;; "docker") list_dockers count_dockers ;; *) echo "Usage: $SCR_NAME installed " echo " $SCR_NAME installed all" echo "Types: qemu, dynamips, iol, docker" ;; esac else echo "Usage: $SCR_NAME installed " echo " $SCR_NAME installed all" echo "Types: qemu, dynamips, iol, docker" fi ;; "labs") if [[ "$2" ]]; then if [[ "$2" = "all" ]]; then install_images_all_labs "$1" exit 0 else install_images_from_lab "$2" exit 0 fi fi list_labs "$1" ;; "mylabs") if [[ "$2" ]]; then if [[ "$2" = "all" ]]; then install_images_all_labs "$1" exit 0 else install_images_from_lab "$1" "$2" "$3" exit 0 fi fi list_labs "$1" ;; "relicense" | "license") generate_iourc_license ;; "upgrade") menu_ishare2_upgrade ;; "changelog") show_changelog ;; "fix") # if $2 is not passed if [[ -z "$2" ]]; then echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}" echo -e "Usage: $SCR_NAME fix " echo -e "Actions: permissions, dns, block_cisco" exit 1 fi case "$2" in "permissions") fix_permissions ;; "dns") fix_dns ;; "block_cisco") block_cisco ;; *) # If the action is not specified, print the usage echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}" echo -e "Usage: $SCR_NAME fix " echo -e "Actions: permissions, dns" exit 1 ;; esac ;; "gui") if [[ "$2" ]]; then case "$2" in "install") ishare2_gui_install ;; "start") ishare2_gui_start ;; "stop") ishare2_gui_stop ;; "restart") ishare2_gui_restart ;; *) echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}" echo -e "Usage: $SCR_NAME gui " echo -e "Actions: install, start, stop, restart" ;; esac else echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}" echo -e "Usage: $SCR_NAME gui " echo -e "Actions: install, start, stop, restart" fi ;; "help") show_ishare2_help ;; "test") connection_tests ;; "config") create_config ;; "--version") ishare2_version "show" ;; *) ishare2_quick_help ;; esac } main() { check_updates if [[ "$1" = "test" ]]; then # Tests without checking if user is root connection_tests exit 0 fi check_user_is_root check_ishare2_dir ishare2_sources ishare2_version check_config "$1" check_motd set_max_log_lines set_yml_template_folder_location selector "$1" "$2" "$3" "$4" } main "$@"