diff options
Diffstat (limited to 'app')
| -rw-r--r-- | app/lib/backend.js | 6 | ||||
| -rw-r--r-- | app/lib/ipc-facade.js | 5 | ||||
| -rw-r--r-- | app/lib/jsonrpc-ws-ipc.js | 4 | ||||
| -rw-r--r-- | app/main.js | 116 |
4 files changed, 124 insertions, 7 deletions
diff --git a/app/lib/backend.js b/app/lib/backend.js index 48ede8ff62..c8bea1600b 100644 --- a/app/lib/backend.js +++ b/app/lib/backend.js @@ -84,7 +84,11 @@ export class Backend { setLocation(loc: string) { log.info('Got connection info to backend', loc); - this._ipc = new RealIpc(loc); + if (this._ipc) { + this._ipc.setConnectionString(loc); + } else { + this._ipc = new RealIpc(loc); + } this._registerIpcListeners(); } diff --git a/app/lib/ipc-facade.js b/app/lib/ipc-facade.js index 3dac5d188a..2620f7958c 100644 --- a/app/lib/ipc-facade.js +++ b/app/lib/ipc-facade.js @@ -27,6 +27,7 @@ export type BackendState = { }; export interface IpcFacade { + setConnectionString(string): void, getAccountData(AccountNumber): Promise<AccountData>, getAccount(): Promise<?AccountNumber>, setAccount(accountNumber: AccountNumber): Promise<void>, @@ -47,6 +48,10 @@ export class RealIpc implements IpcFacade { this._ipc = new JsonRpcWs(connectionString); } + setConnectionString(str: string) { + this._ipc.setConnectionString(str); + } + getAccountData(accountNumber: AccountNumber): Promise<AccountData> { return this._ipc.send('get_account_data', accountNumber) .then(raw => { diff --git a/app/lib/jsonrpc-ws-ipc.js b/app/lib/jsonrpc-ws-ipc.js index 44caf12d5c..597d6830fe 100644 --- a/app/lib/jsonrpc-ws-ipc.js +++ b/app/lib/jsonrpc-ws-ipc.js @@ -89,6 +89,10 @@ export default class Ipc { this._reconnect(); } + setConnectionString(str: string) { + this._connectionString = str; + } + setSendTimeout(millis: number) { this._sendTimeoutMillis = millis; } diff --git a/app/main.js b/app/main.js index 4f8d9e23be..a643e2cb8a 100644 --- a/app/main.js +++ b/app/main.js @@ -4,15 +4,22 @@ import fs from 'fs'; import log from 'electron-log'; import { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage } from 'electron'; import TrayIconManager from './lib/tray-icon-manager'; +import ElectronSudo from 'electron-sudo'; import type { TrayIconType } from './lib/tray-icon-manager'; const isDevelopment = (process.env.NODE_ENV === 'development'); const isMacOS = (process.platform === 'darwin'); +const isLinux = (process.platform === 'linux'); +const isWindows = (process.platform === 'win32'); + +const rpcAddressFile = path.join(app.getPath('temp'), '.mullvad_rpc_address'); +let browserWindowReady = false; const appDelegate = { _window: (null: ?BrowserWindow), _tray: (null: ?Tray), + connectionFilePollInterval: (null: ?number), setup: () => { // Override appData path to avoid collisions with old client @@ -29,6 +36,8 @@ const appDelegate = { log.transports.file.level = 'info'; } + appDelegate._startBackend(); + app.on('window-all-closed', () => appDelegate.onAllWindowsClosed()); app.on('ready', () => appDelegate.onReady()); }, @@ -36,7 +45,7 @@ const appDelegate = { onReady: async () => { const window = appDelegate._window = appDelegate._createWindow(); - ipcMain.on('on-browser-window-ready', () => appDelegate._sendBackendInfo(window)); + ipcMain.on('on-browser-window-ready', () => browserWindowReady = true); window.loadURL('file://' + path.join(__dirname, 'index.html')); @@ -58,13 +67,108 @@ const appDelegate = { app.quit(); }, - _sendBackendInfo: (window: BrowserWindow) => { - const file = path.join(app.getPath('temp'), '.mullvad_rpc_address'); - log.info('reading the ipc connection info from', file); + _startBackend: () => { + + const backendIsRunning = appDelegate._rpcAddressFileExists(); + if (backendIsRunning) { + log.info('Not starting the backend as it appears to already be running'); + return; + } + + const pathToBackend = appDelegate._findPathToBackend(); + log.info('Starting the mullvad backend at', pathToBackend); + + const options = { + name: 'Mullvad', + }; + const sudo = new ElectronSudo(options); + sudo.spawn( pathToBackend ) + .then( p => { + appDelegate._setupBackendProcessListeners(p); + return p; + }) + .then( p => { + appDelegate._pollForConnectionInfoFile(); + return p; + }); + }, + _rpcAddressFileExists: () => { + return fs.existsSync(rpcAddressFile); + }, + _findPathToBackend: () => { + if (isDevelopment) { + return path.resolve(process.env.MULLVAD_BACKEND || '../talpid_core/target/debug/mullvadd'); + + } else if (isMacOS) { + return path.join(process.resourcesPath, 'mullvadd'); + + } else if (isLinux) { + // TODO: Decide + return ''; + + } else if (isWindows) { + // TODO: Decide + return ''; + } + }, + _setupBackendProcessListeners: (p) => { + // electron-sudo writes all output to some buffers in memory. + // For long-running processes such as this one that would + // cause a memory leak. + p.stdout.removeAllListeners('data'); + p.stderr.removeAllListeners('data'); + + p.stdout.on('data', (data) => { + log.info('BACKEND stdout:', data.toString()); + }); + p.stderr.on('data', (data) => { + log.warn('BACKEND stderr:', data.toString()); + }); + + p.on('error', (err) => { + log.error('Failed to start or kill the backend', err); + }); + + p.on('exit', (code) => { + const timeoutMs = 500; + log.info('The backend exited with code', code + '. Attempting to restart it in', timeoutMs, 'milliseconds...'); + setTimeout( () => appDelegate._startBackend(), timeoutMs); + }); + }, + _pollForConnectionInfoFile: () => { + + if (appDelegate.connectionFilePollInterval) { + log.warn('Attempted to start polling for the RPC connection info file while another polling was already running'); + return; + } + + const pollIntervalMs = 200; + appDelegate.connectionFilePollInterval = setInterval(() => { + + if (browserWindowReady && appDelegate._rpcAddressFileExists()) { + + if (appDelegate.connectionFilePollInterval) { + clearInterval(appDelegate.connectionFilePollInterval); + appDelegate.connectionFilePollInterval = null; + } + + appDelegate._sendBackendInfo(); + } + + }, pollIntervalMs); + }, + _sendBackendInfo: () => { + const window = appDelegate._window; + if (!window) { + log.error('Attempted to send backend rpc address before the window was ready'); + return; + } + + log.debug('Reading the ipc connection info from', rpcAddressFile); - fs.readFile(file, 'utf8', function (err,data) { + fs.readFile(rpcAddressFile, 'utf8', function (err,data) { if (err) { - return log.info('Could not find backend connection info', err); + return log.error('Could not find backend connection info', err); } log.info('Read IPC connection info', data); |
