6363 property string routingMode: " "
6464 property bool changeSystemDns: false
6565 readonly property string tunnelLogPath: " $HOME/.local/share/adguardvpn-cli/tunnel.log"
66+ readonly property string controlSocketPath: " $HOME/.local/share/adguardvpn-cli/vpn.socket"
6667
6768 property var locations: []
6869 property string lastError: " "
@@ -520,7 +521,9 @@ Item {
520521 function connectFastest () {
521522 const args = buildArgs ([" connect" , " -f" ], true );
522523
523- runAction (" connectFastest" , args, t (" app.title" , " AdGuard VPN" ), t (" toast.fastest_selected" , " Fastest location selected" ));
524+ runAction (" connectFastest" , args, t (" app.title" , " AdGuard VPN" ), t (" toast.fastest_selected" , " Fastest location selected" ), {
525+ prepareDisconnectedRuntime: true
526+ });
524527 }
525528
526529 function resolveLocationTarget (locationText ) {
@@ -561,7 +564,9 @@ Item {
561564
562565 runAction (" connectLocation" , args, t (" app.title" , " AdGuard VPN" ), t (" toast.connecting_to" , " Connecting to {location}" , {
563566 location: rawTarget
564- }));
567+ }), {
568+ prepareDisconnectedRuntime: true
569+ });
565570 }
566571
567572 function disconnect () {
@@ -737,7 +742,64 @@ Item {
737742 }, 100 );
738743 }
739744
740- function runAction (operation , args , toastTitle , toastMessage ) {
745+ function prepareDisconnectedRuntime (callback ) {
746+ const tunPreflightRequired = (currentMode || " " ).toString ().toLowerCase () !== " socks" ;
747+ const prepScript = `
748+ resolve_home() {
749+ if [ -n "$HOME" ]; then
750+ printf '%s' "$HOME"
751+ return
752+ fi
753+ getent passwd "$(id -u)" | cut -d: -f6
754+ }
755+
756+ HOME_DIR="$(resolve_home)"
757+ SOCKET_PATH="${ controlSocketPath} "
758+ case "$SOCKET_PATH" in
759+ '$HOME'/*)
760+ SOCKET_PATH="$HOME_DIR/\$ {SOCKET_PATH#'$HOME'/}"
761+ ;;
762+ esac
763+
764+ if [ "${ tunPreflightRequired ? " 1" : " 0" } " = "1" ] && command -v ip >/dev/null 2>&1; then
765+ DEFAULT_ROUTE_COUNT="$(ip -o route show to default | wc -l | tr -d ' ')"
766+ if [ "\$ {DEFAULT_ROUTE_COUNT:-0}" -gt 1 ]; then
767+ printf 'multi-default'
768+ exit 44
769+ fi
770+ fi
771+
772+ if [ ! -S "$SOCKET_PATH" ]; then
773+ printf 'clean'
774+ exit 0
775+ fi
776+
777+ if command -v adguardvpn-cli >/dev/null 2>&1; then
778+ adguardvpn-cli disconnect >/dev/null 2>&1 || true
779+ sleep 1
780+ fi
781+
782+ if command -v lsof >/dev/null 2>&1 && lsof -nP "$SOCKET_PATH" >/dev/null 2>&1; then
783+ printf 'busy'
784+ exit 42
785+ fi
786+
787+ rm -f "$SOCKET_PATH" >/dev/null 2>&1 || true
788+
789+ if [ -S "$SOCKET_PATH" ]; then
790+ printf 'stale'
791+ exit 43
792+ fi
793+
794+ printf 'cleaned'
795+ ` ;
796+
797+ Proc .runCommand (` ${ pluginId} .prepareRuntime.${ Date .now ()} ` , [" sh" , " -lc" , prepScript], (stdout , exitCode ) => {
798+ callback (cleanOutput (stdout), exitCode);
799+ }, 100 );
800+ }
801+
802+ function runAction (operation , args , toastTitle , toastMessage , options ) {
741803 if (! cliAvailable) {
742804 ToastService .showError (t (" app.title" , " AdGuard VPN" ), t (" toast.cli_unavailable" , " adguardvpn-cli is unavailable" ));
743805 return ;
@@ -753,38 +815,66 @@ Item {
753815 lastError = " " ;
754816 suspendPolling ();
755817
756- runCli (operation, args, (stdout , exitCode ) => {
757- commandRunning = false ;
758- runningCommand = " " ;
759- resumePolling ();
760-
761- const clean = cleanOutput (stdout);
762- recordLastCommand (args, exitCode, clean);
763- if (exitCode === 0 ) {
764- if (toastTitle) {
765- const firstLine = clean .split (" \n " ).map (line => line .trim ()).filter (Boolean )[0 ];
766- ToastService .showInfo (toastTitle, firstLine || toastMessage || t (" toast.done" , " Done" ));
818+ const executeAction = () => {
819+ runningCommand = operation;
820+
821+ runCli (operation, args, (stdout , exitCode ) => {
822+ commandRunning = false ;
823+ runningCommand = " " ;
824+ resumePolling ();
825+
826+ const clean = cleanOutput (stdout);
827+ recordLastCommand (args, exitCode, clean);
828+ if (exitCode === 0 ) {
829+ if (toastTitle) {
830+ const firstLine = clean .split (" \n " ).map (line => line .trim ()).filter (Boolean )[0 ];
831+ ToastService .showInfo (toastTitle, firstLine || toastMessage || t (" toast.done" , " Done" ));
832+ }
833+
834+ Qt .callLater (() => {
835+ refreshStatus ();
836+ refreshConfig ();
837+ refreshLicense ();
838+ });
839+ return ;
767840 }
768841
769- Qt .callLater (() => {
770- refreshStatus ();
771- refreshConfig ();
772- refreshLicense ();
842+ lastError = clean || t (" toast.operation_failed" , " {operation} failed (code {code})" , {
843+ operation: operation,
844+ code: exitCode
773845 });
774- return ;
775- }
846+ const hint = buildLocationHelpHint (lastError);
847+ if (hint) {
848+ lastError = ` ${ lastError} \n ${ hint} ` ;
849+ }
850+ ToastService .showError (t (" app.title" , " AdGuard VPN" ), lastError);
851+ refreshStatus ();
852+ });
853+ };
776854
777- lastError = clean || t (" toast.operation_failed" , " {operation} failed (code {code})" , {
778- operation: operation,
779- code: exitCode
855+ if (options && options .prepareDisconnectedRuntime ) {
856+ runningCommand = ` ${ operation} .prepare` ;
857+ prepareDisconnectedRuntime ((prepStatus , prepExitCode ) => {
858+ if (prepExitCode === 0 ) {
859+ executeAction ();
860+ return ;
861+ }
862+
863+ commandRunning = false ;
864+ runningCommand = " " ;
865+ resumePolling ();
866+ lastError = prepStatus === " multi-default"
867+ ? t (" toast.multiple_default_routes" , " Multiple default routes are active. Disconnect the redundant network interface before connecting in TUN mode." )
868+ : (prepStatus === " busy"
869+ ? t (" toast.runtime_busy" , " AdGuard VPN runtime is still busy after cleanup. Try again in a few seconds." )
870+ : t (" toast.runtime_cleanup_failed" , " Could not recover the AdGuard VPN runtime before connecting." ));
871+ ToastService .showError (t (" app.title" , " AdGuard VPN" ), lastError);
872+ refreshStatus ();
780873 });
781- const hint = buildLocationHelpHint (lastError);
782- if (hint) {
783- lastError = ` ${ lastError} \n ${ hint} ` ;
784- }
785- ToastService .showError (t (" app.title" , " AdGuard VPN" ), lastError);
786- refreshStatus ();
787- });
874+ return ;
875+ }
876+
877+ executeAction ();
788878 }
789879
790880 Timer {
0 commit comments