fix: DNS coupée après arrêt du tunnel, gel UI, dialogues sudo à répétition
- wg-quick down échoue silencieusement si l'interface a déjà disparu (crash, kill, veille) et ne retire donc jamais l'entrée DNS posée par wg-quick up : la résolution reste pointée sur un résolveur mort. Ajout d'un nettoyage DNS inconditionnel (app/core/dns.py) déclenché à la connexion, la déconnexion, la fermeture et au démarrage suivant. - is_connected() se basait sur `wg show`, qui rend un code 0 même en cas d'échec de permission pour un utilisateur non-root : l'app se croyait déconnectée en permanence et ne démontait donc jamais le tunnel à la fermeture. Détection réécrite via sysfs (sans privilèges). - Connexion/déconnexion et ping tournaient sur le thread Qt : gel de l'UI pendant les élévations de privilèges. Déportés dans des QThread (app/ui/worker.py). - Restauration réseau garantie sur toutes les sorties (croix, tray, SIGINT/SIGTERM, atexit, exception) via app/core/shutdown.py. - run_privileged() : la détection "sudo veut un mot de passe" ratait en session non-anglophone et sur les comptes ALL=(ALL) ALL ; réécrite pour se fier au message d'erreur réel plutôt qu'à `sudo -l`. - Écriture de la config WireGuard dans /etc/wireguard (root:root 0700) passe maintenant par install -D via la chaîne de privilèges complète au lieu d'un tee non autorisé par les sudoers. - Ajout d'un diagnostic des droits (make check-privileges, bouton Admin) et mise à jour de setup-sudoers pour couvrir toutes les commandes désormais utilisées.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""Exécution de tâches bloquantes hors du thread graphique.
|
||||
|
||||
`wg-quick up/down` passe par sudo ou pkexec : entre l'attente du dialogue
|
||||
d'authentification et le montage du tunnel, l'appel peut durer plusieurs
|
||||
dizaines de secondes. Lancé depuis le thread Qt, il gèle toute l'interface
|
||||
(fenêtre grise, « ne répond pas »). Ces workers déportent l'appel.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
|
||||
|
||||
class TaskWorker(QThread):
|
||||
"""Exécute `fn(*args)` dans un thread et émet son résultat.
|
||||
|
||||
`done` porte (ok, message, tag) ; `tag` permet à l'appelant de savoir de
|
||||
quelle tâche il s'agit lorsqu'un même slot en traite plusieurs.
|
||||
"""
|
||||
|
||||
done = pyqtSignal(bool, str, str)
|
||||
|
||||
def __init__(self, fn: Callable[..., tuple[bool, str]], *args: Any,
|
||||
tag: str = "", parent=None):
|
||||
super().__init__(parent)
|
||||
self._fn = fn
|
||||
self._args = args
|
||||
self._tag = tag
|
||||
|
||||
def run(self) -> None:
|
||||
try:
|
||||
ok, msg = self._fn(*self._args)
|
||||
except Exception as e: # noqa: BLE001 — remonté à l'UI
|
||||
ok, msg = False, str(e)
|
||||
self.done.emit(bool(ok), str(msg), self._tag)
|
||||
|
||||
|
||||
class ValueWorker(QThread):
|
||||
"""Variante pour une fonction qui retourne une valeur quelconque."""
|
||||
|
||||
done = pyqtSignal(object)
|
||||
|
||||
def __init__(self, fn: Callable[..., Any], *args: Any, parent=None):
|
||||
super().__init__(parent)
|
||||
self._fn = fn
|
||||
self._args = args
|
||||
|
||||
def run(self) -> None:
|
||||
try:
|
||||
result = self._fn(*self._args)
|
||||
except Exception: # noqa: BLE001
|
||||
result = None
|
||||
self.done.emit(result)
|
||||
Reference in New Issue
Block a user