v1.9.3: Fix wifi_connect (escaping shell + URL) + log dédié

Bugs corrigés:
- launcher.php passait SSID/PASS au shell sans escapeshellarg(): un
  mot de passe avec $/&/;/espace cassait silencieusement la commande
  avant que nmcli ne soit appelé. Cause probable des retours clients
  "ça bloque au cliquer sur Se connecter".
- wifi.html n'encodait pas SSID/PASS dans l'URL: caractères &/+/=
  corrompaient la requête.

Observabilité:
- Nouveau fichier logs/wifi_connect.log avec timestamps stricts
- launcher.php log la requête entrante (IP, longueurs SSID/PASS)
- connexion.sh: fonction log_wc(), snapshots NM avant/après,
  capture stdout+stderr nmcli, code retour explicite, fallback SSID
  dérivé du serial si deviceName indisponible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
PaulVua
2026-05-20 11:23:10 +02:00
parent 69fa928d56
commit 3109455ea0
5 changed files with 118 additions and 41 deletions

View File

@@ -1 +1 @@
1.9.2 1.9.3

View File

@@ -1,5 +1,23 @@
{ {
"versions": [ "versions": [
{
"version": "1.9.3",
"date": "2026-05-20",
"changes": {
"features": [
"Nouveau log dédié logs/wifi_connect.log (timestamps stricts) tracant chaque étape d'une tentative de connexion WiFi depuis le mode hotspot: réception de la requête PHP, état NetworkManager avant/après, code de retour et sortie complète de nmcli, fallback hotspot si échec"
],
"improvements": [
"connexion.sh: réécrit avec fonction log_wc() horodatée, snapshots NM avant/après, capture stdout+stderr de nmcli, validation des arguments d'entrée, busy timeout SQLite, fallback SSID dérivé du serial RPi"
],
"fixes": [
"Correction critique du flow wifi_connect: launcher.php n'échappait pas les arguments SSID/PASS passés en shell (pas d'escapeshellarg). Les SSID avec espaces ou les mots de passe contenant $, &, ;, espaces, etc. étaient corrompus avant d'atteindre nmcli, faisant échouer silencieusement la connexion (retour client: 'ça bloque au moment de cliquer sur se connecter')",
"wifi.html: ajout de encodeURIComponent() sur SSID et PASS dans l'URL de wifi_connect (caractères &, +, =, # cassaient la requête côté serveur)"
],
"compatibility": []
},
"notes": "Corrige le bug le plus probable derrière les retours clients de connexion WiFi qui échoue sans message d'erreur. Le nouveau log wifi_connect.log permet désormais de diagnostiquer précisément un échec (à fournir lors de tout futur retour client). Log tronqué automatiquement chaque nuit comme les autres logs."
},
{ {
"version": "1.9.2", "version": "1.9.2",
"date": "2026-05-20", "date": "2026-05-20",

View File

@@ -1,51 +1,92 @@
#!/bin/bash #!/bin/bash
echo "-------" # Connect wlan0 to a client WiFi network, replacing the local hotspot.
echo "Start connexion shell script at $(date)" # Called by launcher.php (wifi_connect action) with: $1=SSID $2=PASSWORD
# All output is appended to logs/wifi_connect.log by the caller's redirect,
# but we also write timestamped lines here to make the log self-contained.
# Get deviceName from database for hotspot SSID WIFI_LOG="/var/www/nebuleair_pro_4g/logs/wifi_connect.log"
DEVICE_NAME=$(sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "SELECT value FROM config_table WHERE key='deviceName'") DB="/var/www/nebuleair_pro_4g/sqlite/sensors.db"
echo "Device Name: $DEVICE_NAME"
# Find and disable any active hotspot connection log_wc() {
echo "Disable Hotspot..." local ts
# Get all wireless connections that are currently active (excludes the target WiFi) ts=$(date '+%Y-%m-%d %H:%M:%S')
ACTIVE_HOTSPOT=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active | grep ':802-11-wireless:wlan0' | cut -d: -f1) echo "[$ts] [connexion.sh] $*"
}
if [ -n "$ACTIVE_HOTSPOT" ]; then log_wc "================================================================"
echo "Disabling hotspot connection: $ACTIVE_HOTSPOT" log_wc "=== connexion.sh started ==="
sudo nmcli connection down "$ACTIVE_HOTSPOT" log_wc "PID=$$ user=$(whoami)"
else log_wc "SSID='$1' (len=${#1})"
echo "No active hotspot found" log_wc "PASS=[HIDDEN] (len=${#2})"
if [ -z "$1" ]; then
log_wc "ERROR: empty SSID, aborting"
exit 1
fi
if [ -z "$2" ]; then
log_wc "ERROR: empty PASSWORD, aborting"
exit 1
fi fi
# Get deviceName for the hotspot fallback SSID (with busy timeout against lock)
DEVICE_NAME=$(sqlite3 -cmd ".timeout 5000" "$DB" "SELECT value FROM config_table WHERE key='deviceName'")
log_wc "deviceName from DB: '$DEVICE_NAME'"
# Fallback SSID derived from RPi serial if DB read failed or value is empty
if [ -z "$DEVICE_NAME" ]; then
serial_number=$(cat /proc/cpuinfo | grep Serial | awk '{print substr($3, length($3) - 7)}')
DEVICE_NAME="nebuleair-pro-$serial_number"
log_wc "WARN: deviceName empty in DB, fallback to '$DEVICE_NAME'"
fi
# Snapshot NM state BEFORE we touch anything (helps debug)
log_wc "--- NetworkManager state BEFORE ---"
log_wc "wlan0 state: $(nmcli -g GENERAL.STATE device show wlan0 2>&1)"
log_wc "wlan0 connection: $(nmcli -g GENERAL.CONNECTION device show wlan0 2>&1)"
log_wc "active connections:"
nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>&1 | while read -r line; do log_wc " $line"; done
# Find and bring down any active wireless connection on wlan0 (the hotspot)
ACTIVE_HOTSPOT=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active | grep ':802-11-wireless:wlan0' | cut -d: -f1)
if [ -n "$ACTIVE_HOTSPOT" ]; then
log_wc "Disabling active wireless connection on wlan0: '$ACTIVE_HOTSPOT'"
DOWN_OUT=$(sudo nmcli connection down "$ACTIVE_HOTSPOT" 2>&1)
DOWN_RC=$?
log_wc "nmcli connection down rc=$DOWN_RC out: $DOWN_OUT"
else
log_wc "No active wireless connection on wlan0 (nothing to bring down)"
fi
log_wc "Sleeping 5s to let NM release wlan0..."
sleep 5 sleep 5
echo "Start connection with:" log_wc "--- Attempting nmcli wifi connect ---"
echo "SSID: $1" log_wc "Command: sudo nmcli device wifi connect '$1' password [HIDDEN]"
echo "Password: [HIDDEN]" NMCLI_OUT=$(sudo nmcli device wifi connect "$1" password "$2" 2>&1)
sudo nmcli device wifi connect "$1" password "$2" NMCLI_RC=$?
log_wc "nmcli exit code: $NMCLI_RC"
log_wc "nmcli output: $NMCLI_OUT"
# Check if connection is successful # Snapshot NM state AFTER attempt
if [ $? -eq 0 ]; then log_wc "--- NetworkManager state AFTER ---"
echo "Connection to $1 is successful" log_wc "wlan0 state: $(nmcli -g GENERAL.STATE device show wlan0 2>&1)"
log_wc "wlan0 connection: $(nmcli -g GENERAL.CONNECTION device show wlan0 2>&1)"
# Update SQLite to reflect connected status if [ $NMCLI_RC -eq 0 ]; then
sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='connected' WHERE key='WIFI_status'" log_wc "SUCCESS: connected to '$1'"
echo "Updated database: WIFI_status = connected" sqlite3 -cmd ".timeout 5000" "$DB" "UPDATE config_table SET value='connected' WHERE key='WIFI_status'"
log_wc "DB updated: WIFI_status = connected"
else else
echo "Connection to $1 failed" log_wc "FAILURE: connection to '$1' failed (rc=$NMCLI_RC), restarting hotspot..."
echo "Restarting hotspot..."
# Recreate hotspot with current deviceName as SSID # Recreate hotspot with deviceName (or fallback) as SSID
sudo nmcli device wifi hotspot ifname wlan0 ssid "$DEVICE_NAME" password nebuleaircfg HS_OUT=$(sudo nmcli device wifi hotspot ifname wlan0 ssid "$DEVICE_NAME" password nebuleaircfg 2>&1)
HS_RC=$?
log_wc "nmcli hotspot rc=$HS_RC out: $HS_OUT"
# Update SQLite to reflect hotspot mode sqlite3 -cmd ".timeout 5000" "$DB" "UPDATE config_table SET value='hotspot' WHERE key='WIFI_status'"
sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='hotspot' WHERE key='WIFI_status'" log_wc "DB updated: WIFI_status = hotspot"
echo "Updated database: WIFI_status = hotspot" log_wc "Hotspot restarted with SSID: '$DEVICE_NAME'"
echo "Hotspot restarted with SSID: $DEVICE_NAME"
fi fi
echo "End connexion shell script" log_wc "=== connexion.sh end ==="
echo "-------"

View File

@@ -1268,6 +1268,17 @@ if ($type == "wifi_connect") {
$SSID=$_GET['SSID']; $SSID=$_GET['SSID'];
$PASS=$_GET['pass']; $PASS=$_GET['pass'];
// Dedicated WiFi connect log (separate from app.log for easier debugging)
$wifi_log = '/var/www/nebuleair_pro_4g/logs/wifi_connect.log';
$log_wc = function($msg) use ($wifi_log) {
$ts = date('Y-m-d H:i:s');
@file_put_contents($wifi_log, "[$ts] [launcher.php] $msg\n", FILE_APPEND);
};
$log_wc("=== wifi_connect request received ===");
$log_wc("SSID='" . $SSID . "' (len=" . strlen($SSID) . ")");
$log_wc("PASS=[HIDDEN] (len=" . strlen($PASS) . ")");
$log_wc("client_ip=" . ($_SERVER['REMOTE_ADDR'] ?? '?'));
// Get device name and hostname for instructions // Get device name and hostname for instructions
try { try {
$db = new PDO("sqlite:$database_path"); $db = new PDO("sqlite:$database_path");
@@ -1279,13 +1290,20 @@ if ($type == "wifi_connect") {
$db = null; $db = null;
} catch (PDOException $e) { } catch (PDOException $e) {
$deviceName = 'NebuleAir'; $deviceName = 'NebuleAir';
$log_wc("WARN: PDO error reading deviceName: " . $e->getMessage());
} }
$hostname = trim(shell_exec('hostname 2>/dev/null')) ?: 'aircarto'; $hostname = trim(shell_exec('hostname 2>/dev/null')) ?: 'aircarto';
$log_wc("deviceName='$deviceName' hostname='$hostname'");
// Launch connection script in background // Launch connection script in background.
// CRITICAL: escapeshellarg() each argument so SSIDs with spaces or passwords
// with special characters ($, &, ;, etc.) are passed correctly to bash.
$script_path = '/var/www/nebuleair_pro_4g/connexion.sh'; $script_path = '/var/www/nebuleair_pro_4g/connexion.sh';
$log_file = '/var/www/nebuleair_pro_4g/logs/app.log'; $ssid_arg = escapeshellarg($SSID);
shell_exec("$script_path $SSID $PASS >> $log_file 2>&1 &"); $pass_arg = escapeshellarg($PASS);
$cmd = "$script_path $ssid_arg $pass_arg >> $wifi_log 2>&1 &";
$log_wc("Launching: $script_path <ssid> <pass> (background, output -> wifi_connect.log)");
shell_exec($cmd);
// Return JSON response with instructions // Return JSON response with instructions
header('Content-Type: application/json'); header('Content-Type: application/json');

View File

@@ -261,7 +261,7 @@ function wifi_connect(SSID, PASS){
if (myModal) { myModal.hide(); } if (myModal) { myModal.hide(); }
$.ajax({ $.ajax({
url: 'launcher.php?type=wifi_connect&SSID='+SSID+'&pass='+PASS, url: 'launcher.php?type=wifi_connect&SSID='+encodeURIComponent(SSID)+'&pass='+encodeURIComponent(PASS),
dataType: 'json', dataType: 'json',
method: 'GET', method: 'GET',
success: function(response) { success: function(response) {