summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--app/lib/backend.js6
-rw-r--r--app/lib/ipc-facade.js5
-rw-r--r--app/lib/jsonrpc-ws-ipc.js4
-rw-r--r--app/main.js116
-rw-r--r--flow-libs/electron.js.flow11
-rw-r--r--package.json1
-rw-r--r--test/mocks/ipc.js1
-rw-r--r--yarn.lock9
8 files changed, 144 insertions, 9 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);
diff --git a/flow-libs/electron.js.flow b/flow-libs/electron.js.flow
index 98358003aa..c417abc907 100644
--- a/flow-libs/electron.js.flow
+++ b/flow-libs/electron.js.flow
@@ -2,6 +2,15 @@
import EventEmitter from 'events';
+declare var process: Process & {
+ resourcesPath: string;
+ type: "browser" | "renderer";
+ versions: {
+ electron: string;
+ chrome: string;
+ };
+};
+
declare module 'electron' {
// common types
@@ -176,4 +185,4 @@ declare module 'electron' {
declare var remote: Remote;
declare var shell: Shell;
declare var clipboard: Clipboard;
-} \ No newline at end of file
+}
diff --git a/package.json b/package.json
index 486e29d169..0d5b3a5044 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"babel-runtime": "^6.22.0",
"cheap-ruler": "^2.4.1",
"electron-log": "^2.2.0",
+ "electron-sudo": "https://github.com/eriklarko/electron-sudo.git",
"history": "^4.6.1",
"jsonrpc-lite": "^1.2.3",
"mapbox-gl": "^0.37.0",
diff --git a/test/mocks/ipc.js b/test/mocks/ipc.js
index 25c8cdf7fa..bd99b8d680 100644
--- a/test/mocks/ipc.js
+++ b/test/mocks/ipc.js
@@ -14,6 +14,7 @@ export function newMockIpc() {
const mockIpc: IpcFacade & MockIpc = {
+ setConnectionString: (_str: string) => {},
getAccountData: () => {
return new Promise(r => r({
paid_until: '',
diff --git a/yarn.lock b/yarn.lock
index d8b8ba47d5..ecdc8a87e1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1140,7 +1140,7 @@ bluebird-lst@^1.0.2:
dependencies:
bluebird "^3.5.0"
-bluebird@^3.4.7, bluebird@^3.5.0:
+bluebird@^3.4.6, bluebird@^3.4.7, bluebird@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
@@ -2089,6 +2089,13 @@ electron-publish@19.12.0:
fs-extra-p "^4.3.0"
mime "^1.3.6"
+"electron-sudo@https://github.com/eriklarko/electron-sudo.git":
+ version "4.0.12"
+ resolved "https://github.com/eriklarko/electron-sudo.git#d351ea8c9ce13f7e69955168d00acfdf66c1ead2"
+ dependencies:
+ babel-runtime "^6.18.0"
+ bluebird "^3.4.6"
+
electron-window@^0.8.0:
version "0.8.1"
resolved "https://registry.yarnpkg.com/electron-window/-/electron-window-0.8.1.tgz#16ca187eb4870b0679274fc8299c5960e6ab2c5e"