diff options
Diffstat (limited to 'gui/src/main')
| -rw-r--r-- | gui/src/main/linux-split-tunneling.ts | 74 |
1 files changed, 63 insertions, 11 deletions
diff --git a/gui/src/main/linux-split-tunneling.ts b/gui/src/main/linux-split-tunneling.ts index acaa674746..9c41740776 100644 --- a/gui/src/main/linux-split-tunneling.ts +++ b/gui/src/main/linux-split-tunneling.ts @@ -2,6 +2,9 @@ import argvSplit from 'argv-split'; import child_process from 'child_process'; import path from 'path'; import { ILinuxSplitTunnelingApplication } from '../shared/application-types'; +import { messages } from '../shared/gettext'; +import { LaunchApplicationResult } from '../shared/ipc-schema'; +import { Scheduler } from '../shared/scheduler'; import { getDesktopEntries, readDesktopEntry, @@ -25,25 +28,74 @@ const PROBLEMATIC_APPLICATIONS = { launchingElsewhere: ['gnome-terminal'], }; +// Launches an application. The application parameter could be a path the an executable or .desktop +// file or an object representing an application export async function launchApplication( app: ILinuxSplitTunnelingApplication | string, -): Promise<void> { - let excludeArguments: string[] | undefined; +): Promise<LaunchApplicationResult> { + let excludeArguments: string[]; + try { + excludeArguments = await getLaunchCommand(app); + } catch (e) { + return { error: e.message }; + } + + return new Promise((resolve, _reject) => { + const scheduler = new Scheduler(); + const proc = child_process.spawn('mullvad-exclude', excludeArguments, { detached: true }); + + // If the process exits within 200 milliseconds the user is notified that it failed to launch. + scheduler.schedule(() => { + proc.removeAllListeners(); + resolve({ success: true }); + }, 200); + + proc.stderr.on('data', (data) => { + if (data.includes('Failed to launch the process') && data.includes('ENOENT')) { + scheduler.cancel(); + proc.removeAllListeners(); + resolve({ + error: + // TRANSLATORS: This error message is shown if the user tries to launch an app that + // TRANSLATORS: doesn't exist. + messages.pgettext('split-tunneling-view', 'Please try again or contact support.'), + }); + } + }); + proc.once('exit', (code) => { + scheduler.cancel(); + proc.removeAllListeners(); + + if (code === 1) { + resolve({ + error: + // TRANSLATORS: This error message is shown if an application failes during startup. + messages.pgettext('split-tunneling-view', 'Please try again or contact support.'), + }); + } else { + resolve({ success: true }); + } + }); + }); +} + +// Takes the same argument as launchApplication and returns the command to run +async function getLaunchCommand(app: ILinuxSplitTunnelingApplication | string): Promise<string[]> { if (typeof app === 'object') { - excludeArguments = formatExec(app.exec); + return formatExec(app.exec); } else if (path.extname(app) === '.desktop') { const entry = await readDesktopEntry(app); if (entry.exec !== undefined) { - excludeArguments = formatExec(entry.exec); + return formatExec(entry.exec); + } else { + throw new Error( + // TRANSLATORS: This error message is shown if the user tries to launch a Linux desktop + // TRANSLATORS: entry file that doesn't contain the required 'Exec' value. + messages.pgettext('split-tunneling-view', 'Please contact support.'), + ); } } else { - excludeArguments = [app]; - } - - if (excludeArguments !== undefined && excludeArguments.length > 0) { - child_process.spawn('mullvad-exclude', excludeArguments, { detached: true }); - } else { - throw new Error('Invalid application'); + return [app]; } } |
