diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/README_WiFi_Connectivity.md b/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/README_WiFi_Connectivity.md new file mode 100644 index 00000000..96c53a5b --- /dev/null +++ b/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/README_WiFi_Connectivity.md @@ -0,0 +1,75 @@ +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause-Clear + +# WiFi Connectivity Validation + +## ๐Ÿ“‹ Overview + +This test validates WiFi functionality by: + +- Connecting to an access point (AP) using either `nmcli` or `wpa_supplicant`. +- Verifying IP acquisition via DHCP. +- Checking internet connectivity with a `ping` test. +- Handling systemd network service status. +- Supporting flexible SSID/password input via arguments, environment, or file. + +## โœ… SSID/PASSWORD Input Priority (Hybrid Approach) + +1. **Command-line arguments**: + ```sh + ./run.sh "MySSID" "MyPassword" + ``` + +2. **Environment variables**: + ```sh + SSID_ENV=MySSID PASSWORD_ENV=MyPassword ./run.sh + ``` + +3. **Fallback to `ssid_list.txt` file** (if above not set): + ```txt + MySSID MyPassword + ``` + +## โš™๏ธ Supported Tools + +- Primary: `nmcli` +- Fallback: `wpa_supplicant`, `udhcpc`, `ifconfig` + +Ensure these tools are available in the system before running the test. Missing tools are detected and logged as skipped/failure. + +## ๐Ÿงช Test Flow + +1. **Dependency check** โ€“ verifies necessary binaries are present. +2. **Systemd services check** โ€“ attempts to start network services if inactive. +3. **WiFi connect (nmcli or wpa_supplicant)** โ€“ based on tool availability. +4. **IP assignment check** โ€“ validates `ifconfig wlan0` output. +5. **Internet test** โ€“ pings `8.8.8.8` to confirm outbound reachability. +6. **Result logging** โ€“ writes `.res` file and logs all actions. + +## ๐Ÿงพ Output + +- `WiFi_Connectivity.res`: Contains `WiFi_Connectivity PASS` or `FAIL`. +- Logs are printed using `log_info`, `log_pass`, and `log_fail` from `functestlib.sh`. + +## ๐Ÿ“‚ Directory Structure + +``` +WiFi/ +โ”œโ”€โ”€ run.sh +โ”œโ”€โ”€ ssid_list.txt (optional) +โ”œโ”€โ”€ README.md +``` + +## ๐ŸŒ Integration (meta-qcom_PreMerge.yaml) + +Add this test with SSID parameters as follows: + +```yaml +- name: WiFi_Connectivity + path: Runner/suites/Connectivity/WiFi + timeout: + minutes: 5 + params: + SSID_ENV: "xxxx" + PASSWORD_ENV: "xxxx" +``` diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/run.sh b/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/run.sh new file mode 100755 index 00000000..77517a31 --- /dev/null +++ b/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/run.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# shellcheck disable=SC1090 +if [ -z "$__INIT_ENV_LOADED" ]; then + . "$INIT_ENV" +fi + +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +TESTNAME="WiFi_Dynamic_IP" +#res_file="./$TESTNAME.res" +test_path=$(find_test_case_by_name "$TESTNAME") +cd "$test_path" || exit 1 + +log_info "-------------------------------------------------------------" +log_info "------------------- Starting $TESTNAME Test -----------------" + +# Credential extraction +creds=$(get_wifi_credentials "$1" "$2") || log_skip_exit "$TESTNAME" "WiFi: SSID and/or password missing. Skipping test." wifi_cleanup "" +SSID=$(echo "$creds" | awk '{print $1}') +PASSWORD=$(echo "$creds" | awk '{print $2}') +log_info "Using SSID='$SSID' and PASSWORD='[hidden]'" + +check_dependencies iw ping + +# If not a kernel-only/minimal build, systemd is checked, else skipped automatically +check_systemd_services systemd-networkd.service || log_fail_exit "$TESTNAME" "Network services check failed" wifi_cleanup "" + +WIFI_IFACE=$(get_wifi_interface) || log_fail_exit "$TESTNAME" "No WiFi interface found" wifi_cleanup "" +log_info "Using WiFi interface: $WIFI_IFACE" + +# nmcli with retry +if wifi_connect_nmcli "$WIFI_IFACE" "$SSID" "$PASSWORD"; then + IP=$(wifi_get_ip "$WIFI_IFACE") + [ -z "$IP" ] && log_fail_exit "$TESTNAME" "No IP after nmcli" wifi_cleanup "$WIFI_IFACE" + if retry_command "ping -I \"$WIFI_IFACE\" -c 3 -W 2 8.8.8.8 >/dev/null 2>&1" 3 3; then + log_pass_exit "$TESTNAME" "Internet connectivity verified via ping" wifi_cleanup "$WIFI_IFACE" + else + log_fail_exit "$TESTNAME" "Ping test failed after nmcli connection" wifi_cleanup "$WIFI_IFACE" + fi +fi + +# wpa_supplicant+udhcpc with retry +if wifi_connect_wpa_supplicant "$WIFI_IFACE" "$SSID" "$PASSWORD"; then + IP=$(wifi_get_ip "$WIFI_IFACE") + [ -z "$IP" ] && log_fail_exit "$TESTNAME" "No IP after wpa_supplicant" wifi_cleanup "$WIFI_IFACE" + if retry_command "ping -I \"$WIFI_IFACE\" -c 3 -W 2 8.8.8.8 >/dev/null 2>&1" 3 3; then + log_pass_exit "$TESTNAME" "Internet connectivity verified via ping" wifi_cleanup "$WIFI_IFACE" + else + log_fail_exit "$TESTNAME" "Ping test failed after wpa_supplicant connection" wifi_cleanup "$WIFI_IFACE" + fi +fi + +log_fail_exit "$TESTNAME" "All WiFi connection methods failed for $WIFI_IFACE (SSID: $SSID)" wifi_cleanup "$WIFI_IFACE" diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/ssid_list.txt b/Runner/suites/Connectivity/WiFi/WiFi_Dynamic_IP/ssid_list.txt new file mode 100755 index 00000000..e69de29b diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/run.sh b/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/run.sh new file mode 100755 index 00000000..d4a92a02 --- /dev/null +++ b/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/run.sh @@ -0,0 +1,97 @@ +#!/bin/sh + +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source if not already loaded (idempotent) +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi +# Always source functestlib.sh, using $TOOLS exported by init_env +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +TESTNAME="WiFi_Firmware_Driver" +test_path=$(find_test_case_by_name "$TESTNAME") +cd "$test_path" || exit 1 + +log_info "--------------------------------------------------------------------------" +log_info "-------------------Starting $TESTNAME Testcase----------------------------" +log_info "=== Test Initialization ===" + +check_dependencies find grep modprobe lsmod cat + +# Detect SoC from /proc/device-tree/model +if [ -f /proc/device-tree/model ]; then + read -r soc_model < /proc/device-tree/model +else + soc_model="Unknown" +fi +log_info "Detected SoC model: $soc_model" + +# Scan firmware +log_info "Scanning for WiFi firmware under /lib/firmware/ath11k/..." +fwfile="" +if find /lib/firmware/ath11k/ -type f -name "amss.bin" -print -quit 2>/dev/null | grep -q .; then + fwfile=$(find /lib/firmware/ath11k/ -type f -name "amss.bin" -print -quit 2>/dev/null) +elif find /lib/firmware/ath11k/ -type f -name "wpss.mbn" -print -quit 2>/dev/null | grep -q .; then + fwfile=$(find /lib/firmware/ath11k/ -type f -name "wpss.mbn" -print -quit 2>/dev/null) +fi + +if [ -z "$fwfile" ]; then + log_skip_exit "$TESTNAME" "No WiFi firmware (amss.bin or wpss.mbn) found under /lib/firmware/ath11k/" +fi + +size=$(stat -c%s "$fwfile" 2>/dev/null) +basename=$(basename "$fwfile") +log_info "Detected firmware [$basename]: $fwfile (size: $size bytes)" + +case "$basename" in + wpss.mbn) + log_info "Platform using wpss.mbn firmware (e.g., Kodiak)" + if validate_remoteproc_running "wpss"; then + log_info "Remoteproc 'wpss' is active and validated." + else + log_fail_exit "$TESTNAME" "Remoteproc 'wpss' validation failed." + fi + log_info "No module load needed for wpss-based platform (e.g., Kodiak)." + ;; + amss.bin) + log_info "amss.bin firmware detected (e.g., WCN6855 - Lemans/Monaco)" + if ! modprobe ath11k_pci 2>/dev/null; then + log_fail_exit "$TESTNAME" "Failed to load ath11k_pci module." + fi + ;; + *) + log_skip_exit "$TESTNAME" "Unsupported firmware type: $basename" + ;; +esac + +log_info "Checking active ath11k-related kernel modules via lsmod..." +if lsmod | grep -Eq '^ath11k(_.*)?\s'; then + lsmod | grep -E '^ath11k(_.*)?\s' | while read -r mod_line; do + log_info " Module loaded: $mod_line" + done +else + log_fail_exit "$TESTNAME" "No ath11k-related kernel module detected via lsmod" +fi + +log_pass_exit "$TESTNAME" "WiFi firmware and driver validation successful." diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Manual_IP/run.sh b/Runner/suites/Connectivity/WiFi/WiFi_Manual_IP/run.sh new file mode 100755 index 00000000..c45101ab --- /dev/null +++ b/Runner/suites/Connectivity/WiFi/WiFi_Manual_IP/run.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source if not already loaded (idempotent) +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi +# Always source functestlib.sh, using $TOOLS exported by init_env +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +TESTNAME="WiFi_Manual_IP" +test_path=$(find_test_case_by_name "$TESTNAME") +cd "$test_path" || exit 1 + +log_info "--------------------------------------------------------------------------" +log_info "-------------------Starting $TESTNAME Testcase----------------------------" +log_info "=== Test Initialization ===" + +# Trap to always restore udhcpc script +trap 'restore_udhcpc_script' EXIT + +# Credential extraction (from arguments, env, or ssid_list.txt) +if ! CRED=$(get_wifi_credentials "$1" "$2") || [ -z "$CRED" ]; then + log_skip_exit "$TESTNAME" "WiFi: SSID and/or password missing. Skipping test." +fi + +SSID=$(echo "$CRED" | awk '{print $1}') +PASSWORD=$(echo "$CRED" | awk '{print $2}') +log_info "Using SSID='$SSID' and PASSWORD='[hidden]'" + +check_dependencies iw wpa_supplicant udhcpc ip + +WIFI_IF=$(get_wifi_interface) +[ -z "$WIFI_IF" ] && log_fail_exit "$TESTNAME" "No WiFi interface detected." + +UDHCPC_SCRIPT=$(ensure_udhcpc_script) +[ ! -x "$UDHCPC_SCRIPT" ] && log_fail_exit "$TESTNAME" "Failed to create udhcpc script." + +wifi_cleanup() { + killall -q wpa_supplicant 2>/dev/null + rm -f /tmp/wpa_supplicant.conf wpa.log + ip link set "$WIFI_IF" down 2>/dev/null +} + +# Generate WPA config using helper (no duplicate code!) +WPA_CONF="$(wifi_write_wpa_conf "$WIFI_IF" "$SSID" "$PASSWORD")" +if [ ! -f "$WPA_CONF" ]; then + log_fail_exit "$TESTNAME" "Failed to create WPA config" wifi_cleanup +fi + +killall -q wpa_supplicant 2>/dev/null +wpa_supplicant -B -i "$WIFI_IF" -c "$WPA_CONF" 2>&1 | tee wpa.log +sleep 4 + +# Run udhcpc with the script +udhcpc -i "$WIFI_IF" -s "$UDHCPC_SCRIPT" -n -q & +sleep 8 + +IP=$(ip addr show "$WIFI_IF" | awk '/inet / {print $2}' | cut -d/ -f1) +if [ -n "$IP" ]; then + log_info "WiFi got IP: $IP (manual DHCP via udhcpc)" + if ping -I "$WIFI_IF" -c 3 -W 2 8.8.8.8 >/dev/null 2>&1; then + log_pass_exit "$TESTNAME" "WiFi: Internet connectivity verified via ping" wifi_cleanup + else + log_fail_exit "$TESTNAME" "WiFi: Ping test failed after DHCP/manual IP" wifi_cleanup + fi +else + log_fail_exit "$TESTNAME" "Failed to acquire IP via udhcpc" wifi_cleanup +fi diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Manual_IP/ssid_list.txt b/Runner/suites/Connectivity/WiFi/WiFi_Manual_IP/ssid_list.txt new file mode 100755 index 00000000..e69de29b diff --git a/Runner/suites/Connectivity/WiFi/WiFi_OnOff/run.sh b/Runner/suites/Connectivity/WiFi/WiFi_OnOff/run.sh new file mode 100755 index 00000000..84110348 --- /dev/null +++ b/Runner/suites/Connectivity/WiFi/WiFi_OnOff/run.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source if not already loaded (idempotent) +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi +# Always source functestlib.sh, using $TOOLS exported by init_env +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +TESTNAME="WiFi_OnOff" +test_path=$(find_test_case_by_name "$TESTNAME") +cd "$test_path" || exit 1 + +log_info "-----------------------------------------------------------------------------------------" +log_info "-------------------Starting $TESTNAME Testcase----------------------------" +log_info "=== Test Initialization ===" + +check_dependencies ip iw + +wifi_iface="$(get_wifi_interface)" +if [ -z "$wifi_iface" ]; then + log_skip_exit "$TESTNAME" "No WiFi interface found. Skipping." "" +fi + +# Bring WiFi down +if bring_interface_up_down "$wifi_iface" down; then + log_info "Brought $wifi_iface down successfully." +else + log_fail_exit "$TESTNAME" "Failed to bring $wifi_iface down." "" +fi + +sleep 2 + +# Bring WiFi up +if bring_interface_up_down "$wifi_iface" up; then + log_info "Brought $wifi_iface up successfully." + log_pass_exit "$TESTNAME" "$wifi_iface toggled up/down successfully." "" +else + log_fail_exit "$TESTNAME" "Failed to bring $wifi_iface up after down." "" +fi diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index ae5b69eb..c87968ee 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -1023,13 +1023,13 @@ get_remoteproc_path_by_firmware() { [ -d "$path" ] && echo "$path" && return 0 return 1 } - + # Get remoteproc state get_remoteproc_state() { rproc_path="$1" [ -f "$rproc_path/state" ] && cat "$rproc_path/state" } - + # Wait for a remoteproc to reach a specific state wait_remoteproc_state() { rproc_path="$1" @@ -1044,28 +1044,28 @@ wait_remoteproc_state() { done return 1 } - + # Stop remoteproc stop_remoteproc() { rproc_path="$1" echo stop > "$rproc_path/state" wait_remoteproc_state "$rproc_path" "offline" 6 } - + # Start remoteproc start_remoteproc() { rproc_path="$1" echo start > "$rproc_path/state" wait_remoteproc_state "$rproc_path" "running" 6 } - + # Validate remoteproc running state with retries and logging validate_remoteproc_running() { fw_name="$1" log_file="${2:-/dev/null}" max_wait_secs="${3:-10}" delay_per_try_secs="${4:-1}" - + rproc_path=$(get_remoteproc_path_by_firmware "$fw_name") if [ -z "$rproc_path" ]; then echo "[ERROR] Remoteproc for '$fw_name' not found" >> "$log_file" @@ -1076,7 +1076,7 @@ validate_remoteproc_running() { } >> "$log_file" return 1 fi - + total_waited=0 while [ "$total_waited" -lt "$max_wait_secs" ]; do state=$(get_remoteproc_state "$rproc_path") @@ -1086,7 +1086,7 @@ validate_remoteproc_running() { sleep "$delay_per_try_secs" total_waited=$((total_waited + delay_per_try_secs)) done - + echo "[ERROR] $fw_name remoteproc did not reach 'running' state within ${max_wait_secs}s (last state: $state)" >> "$log_file" { echo "---- Last 20 remoteproc dmesg logs ----" @@ -1095,3 +1095,301 @@ validate_remoteproc_running() { } >> "$log_file" return 1 } + +# Clean up WiFi test environment (reusable for other tests) +wifi_cleanup() { + iface="$1" + log_info "Cleaning up WiFi test environment..." + killall -q wpa_supplicant 2>/dev/null + rm -f /tmp/wpa_supplicant.conf nmcli.log wpa.log + if [ -n "$iface" ]; then + ip link set "$iface" down 2>/dev/null || ifconfig "$iface" down 2>/dev/null + fi +} + +# Extract credentials from args/env/file +get_wifi_credentials() { + ssid="$1" + pass="$2" + if [ -z "$ssid" ] || [ -z "$pass" ]; then + ssid="${SSID:-$ssid}" + pass="${PASSWORD:-$pass}" + fi + if [ -z "$ssid" ] || [ -z "$pass" ]; then + if [ -f "./ssid_list.txt" ]; then + read -r ssid pass _ < ./ssid_list.txt + fi + fi + ssid=$(echo "$ssid" | xargs) + pass=$(echo "$pass" | xargs) + if [ -z "$ssid" ] || [ -z "$pass" ]; then + return 1 + fi + printf '%s %s\n' "$ssid" "$pass" + return 0 +} + +# POSIX-compliant: Retry a shell command up to N times with delay +retry_command() { + cmd="$1" + retries="$2" + delay="$3" + i=1 + while [ "$i" -le "$retries" ]; do + if eval "$cmd"; then + return 0 + fi + log_warn "Attempt $i/$retries failed: $cmd" + i=$((i + 1)) + sleep "$delay" + done + return 1 +} + +# Connect using nmcli with retries (returns 0 on success) +wifi_connect_nmcli() { + iface="$1" + ssid="$2" + pass="$3" + if command -v nmcli >/dev/null 2>&1; then + log_info "Trying to connect using nmcli..." + retry_command "nmcli dev wifi connect \"$ssid\" password \"$pass\" ifname \"$iface\" 2>&1 | tee nmcli.log" 3 3 + return $? + fi + return 1 +} + +# Connect using wpa_supplicant+udhcpc with retries (returns 0 on success) +wifi_connect_wpa_supplicant() { + iface="$1" + ssid="$2" + pass="$3" + if command -v wpa_supplicant >/dev/null 2>&1 && command -v udhcpc >/dev/null 2>&1; then + log_info "Falling back to wpa_supplicant + udhcpc" + WPA_CONF="/tmp/wpa_supplicant.conf" + { + echo "ctrl_interface=/var/run/wpa_supplicant" + echo "network={" + echo " ssid=\"$ssid\"" + echo " key_mgmt=WPA-PSK" + echo " pairwise=CCMP TKIP" + echo " group=CCMP TKIP" + echo " psk=\"$pass\"" + echo "}" + } > "$WPA_CONF" + killall -q wpa_supplicant 2>/dev/null + retry_command "wpa_supplicant -B -i \"$iface\" -D nl80211 -c \"$WPA_CONF\" 2>&1 | tee wpa.log" 3 2 + sleep 4 + udhcpc -i "$iface" >/dev/null 2>&1 + sleep 2 + return 0 + fi + log_error "Neither nmcli nor wpa_supplicant+udhcpc available" + return 1 +} + +# Get IPv4 address (returns IP or empty) +wifi_get_ip() { + iface="$1" + ip="" + if command -v ifconfig >/dev/null 2>&1; then + ip=$(ifconfig "$iface" 2>/dev/null | awk '/inet / {print $2; exit}') + fi + if [ -z "$ip" ] && command -v ip >/dev/null 2>&1; then + ip=$(ip addr show "$iface" 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1) + fi + echo "$ip" +} + +# Log+exit helpers with optional cleanup +log_pass_exit() { + # Usage: log_pass_exit "$TESTNAME" "Message" cleanup_func arg + TESTNAME="$1" + MSG="$2" + CLEANUP_FUNC="$3" + CLEANUP_ARG="$4" + log_pass "$MSG" + echo "$TESTNAME PASS" > "./$TESTNAME.res" + if [ -n "$CLEANUP_FUNC" ] && command -v "$CLEANUP_FUNC" >/dev/null 2>&1; then + "$CLEANUP_FUNC" "$CLEANUP_ARG" + fi + exit 0 +} + +log_fail_exit() { + # Usage: log_fail_exit "$TESTNAME" "Message" cleanup_func arg + TESTNAME="$1" + MSG="$2" + CLEANUP_FUNC="$3" + CLEANUP_ARG="$4" + log_fail "$MSG" + echo "$TESTNAME FAIL" > "./$TESTNAME.res" + if [ -n "$CLEANUP_FUNC" ] && command -v "$CLEANUP_FUNC" >/dev/null 2>&1; then + "$CLEANUP_FUNC" "$CLEANUP_ARG" + fi + exit 1 +} + +log_skip_exit() { + # Usage: log_skip_exit "$TESTNAME" "Message" cleanup_func arg + TESTNAME="$1" + MSG="$2" + CLEANUP_FUNC="$3" + CLEANUP_ARG="$4" + log_skip "$MSG" + echo "$TESTNAME SKIP" > "./$TESTNAME.res" + if [ -n "$CLEANUP_FUNC" ] && command -v "$CLEANUP_FUNC" >/dev/null 2>&1; then + "$CLEANUP_FUNC" "$CLEANUP_ARG" + fi + exit 0 +} + +# Robust systemd service check: returns 0 if networkd up, 0 if systemd missing, 1 if error +check_systemd_services() { + # If systemd not present, pass for minimal or kernel-only builds + if ! command -v systemctl >/dev/null 2>&1; then + log_info "systemd/systemctl not found (kernel/minimal build). Skipping service checks." + return 0 + fi + for service in "$@"; do + if systemctl is-enabled "$service" >/dev/null 2>&1; then + if ! systemctl is-active --quiet "$service"; then + log_warn "$service not running. Retrying start..." + retry_command "systemctl start $service" 3 2 + if ! systemctl is-active --quiet "$service"; then + log_fail "$service failed to start after 3 retries." + return 1 + else + log_pass "$service started after retry." + fi + fi + else + log_warn "$service not enabled or not found." + fi + done + return 0 +} + +# Ensure udhcpc default.script exists, create if missing +ensure_udhcpc_script() { + udhcpc_dir="/usr/share/udhcpc" + udhcpc_script="$udhcpc_dir/default.script" + udhcpc_backup="$udhcpc_script.bak" + + if [ ! -d "$udhcpc_dir" ]; then + mkdir -p "$udhcpc_dir" || return 1 + fi + + # Backup if script already exists and is not a backup yet + if [ -f "$udhcpc_script" ] && [ ! -f "$udhcpc_backup" ]; then + cp "$udhcpc_script" "$udhcpc_backup" + fi + + if [ ! -x "$udhcpc_script" ]; then + cat > "$udhcpc_script" <<'EOF' +#!/bin/sh +case "$1" in + deconfig) + ip addr flush dev "$interface" + ;; + renew|bound) + echo "[INFO] Configuring $interface with IP: $ip/$subnet" + ip addr flush dev "$interface" + ip addr add "$ip/$subnet" dev "$interface" + ip link set "$interface" up + if [ -n "$router" ]; then + ip route del default dev "$interface" 2>/dev/null + ip route add default via "$router" dev "$interface" + fi +echo "[INFO] Setting DNS to 8.8.8.8" +echo "nameserver 8.8.8.8" > /etc/resolv.conf + ;; +esac +exit 0 +EOF + chmod +x "$udhcpc_script" + fi + + echo "$udhcpc_script" +} + +# Resotre back the default.script +restore_udhcpc_script() { + udhcpc_dir="/usr/share/udhcpc" + udhcpc_script="$udhcpc_dir/default.script" + udhcpc_backup="$udhcpc_script.bak" + + if [ -f "$udhcpc_backup" ]; then + mv -f "$udhcpc_backup" "$udhcpc_script" + echo "[INFO] Restored original udhcpc default.script" + fi +} + +# Bring an interface up or down using available tools +# Usage: bring_interface_up_down +bring_interface_up_down() { + iface="$1" + state="$2" + if command -v ip >/dev/null 2>&1; then + ip link set "$iface" "$state" + elif command -v ifconfig >/dev/null 2>&1; then + if [ "$state" = "up" ]; then + ifconfig "$iface" up + else + ifconfig "$iface" down + fi + else + log_error "No ip or ifconfig tools found to bring $iface $state" + return 1 + fi +} + +wifi_write_wpa_conf() { + iface="$1" + ssid="$2" + pass="$3" + conf_file="/tmp/wpa_supplicant_${iface}.conf" + { + echo "ctrl_interface=/var/run/wpa_supplicant" + echo "network={" + echo " ssid=\"$ssid\"" + echo " key_mgmt=WPA-PSK" + echo " pairwise=CCMP TKIP" + echo " group=CCMP TKIP" + echo " psk=\"$pass\"" + echo "}" + } > "$conf_file" + echo "$conf_file" +} + +# Find the first available WiFi interface (wl* or wlan0), using 'ip' or 'ifconfig'. +# Prints the interface name, or returns non-zero if not found. +get_wifi_interface() { + WIFI_IF="" + + # Prefer 'ip' if available. + if command -v ip >/dev/null 2>&1; then + WIFI_IF=$(ip link | awk -F: '/ wl/ {print $2}' | tr -d ' ' | head -n1) + if [ -z "$WIFI_IF" ]; then + WIFI_IF=$(ip link | awk -F: '/^[0-9]+: wl/ {print $2}' | tr -d ' ' | head -n1) + fi + if [ -z "$WIFI_IF" ] && ip link show wlan0 >/dev/null 2>&1; then + WIFI_IF="wlan0" + fi + else + # Fallback to 'ifconfig' if 'ip' is missing. + if command -v ifconfig >/dev/null 2>&1; then + WIFI_IF=$(ifconfig -a 2>/dev/null | grep -o '^wl[^:]*' | head -n1) + if [ -z "$WIFI_IF" ] && ifconfig wlan0 >/dev/null 2>&1; then + WIFI_IF="wlan0" + fi + fi + fi + + if [ -n "$WIFI_IF" ]; then + echo "$WIFI_IF" + return 0 + else + return 1 + fi +}