runParity.js 3.95 KiB
Newer Older
Amaury Martiny's avatar
Amaury Martiny committed
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
//
// SPDX-License-Identifier: MIT

const { app } = require('electron');
Amaury Martiny's avatar
Amaury Martiny committed
const fs = require('fs');
const { spawn } = require('child_process');
Amaury Martiny's avatar
Amaury Martiny committed
const { promisify } = require('util');
Amaury Martiny's avatar
Amaury Martiny committed
const { cli, parityArgv } = require('../cli');
const isParityRunning = require('./isParityRunning');
Amaury Martiny's avatar
Amaury Martiny committed
const handleError = require('./handleError');
const { parityPath } = require('./doesParityExist');
Amaury Martiny's avatar
Amaury Martiny committed
const pino = require('../utils/pino')();
Amaury Martiny's avatar
Amaury Martiny committed
const pinoParity = require('../utils/pino')({ name: 'parity' });
Amaury Martiny's avatar
Amaury Martiny committed
const fsChmod = promisify(fs.chmod);
Amaury Martiny's avatar
Amaury Martiny committed

let parity = null; // Will hold the running parity instance

Amaury Martiny's avatar
Amaury Martiny committed
// These are errors output by parity, which Parity UI ignores (i.e. doesn't
// panic). They happen when an instance of parity is already running, and
// parity-ui tries to launch another one.
const catchableErrors = [
Amaury Martiny's avatar
Amaury Martiny committed
  'is already in use, make sure that another instance of an Ethereum client is not running',
Amaury Martiny's avatar
Amaury Martiny committed
  'IO error: While lock file:'
Amaury Martiny's avatar
Amaury Martiny committed
module.exports = {
  async runParity (mainWindow) {
Amaury Martiny's avatar
Amaury Martiny committed
    try {
      // Do not run parity with --no-run-parity
      if (cli.runParity === false) {
        return;
      }

      // Do not run parity if there is already another instance running
Amaury Martiny's avatar
Amaury Martiny committed
      const isRunning = await isParityRunning(mainWindow);
      if (isRunning) {
        return;
      }

Amaury Martiny's avatar
Amaury Martiny committed
      // Do not run parity if parityPath has not been calculated. Shouldn't
      // happen as we always run runParity after doesParityExist resolves.
      if (!parityPath) {
        throw new Error('Attempting to run Parity before parityPath is set.');
      }

Amaury Martiny's avatar
Amaury Martiny committed
      // Some users somehow had no +x on the parity binary after downloading
      // it. We try to set it here (no guarantee it will work, we might not
      // have rights to do it).
      try {
        await fsChmod(parityPath(), '755');
      } catch (e) {}

Amaury Martiny's avatar
Amaury Martiny committed
      let logLastLine; // Always contains last line of the Parity logs
Amaury Martiny's avatar
Amaury Martiny committed

      // Run an instance of parity with the correct args
      parity = spawn(parityPath(), parityArgv);
Amaury Martiny's avatar
Amaury Martiny committed
      pino.info(
Amaury Martiny's avatar
Amaury Martiny committed
        `Running command "${parityPath().replace(' ', '\\ ')} ${parityArgv.join(
          ' '
Amaury Martiny's avatar
Amaury Martiny committed
        )}".`
Amaury Martiny's avatar
Amaury Martiny committed
      );

      // Save in memory the last line of the log file, for handling error
      const callback = data => {
        if (data && data.length) {
          logLastLine = data.toString();
        }
Amaury Martiny's avatar
Amaury Martiny committed
        pinoParity.info(data.toString());
Amaury Martiny's avatar
Amaury Martiny committed
      };
      parity.stdout.on('data', callback);
      parity.stderr.on('data', callback);

      parity.on('error', err => {
Amaury Martiny's avatar
Amaury Martiny committed
        handleError(err, 'An error occured while running parity.');
      });
Amaury Martiny's avatar
Amaury Martiny committed
      parity.on('close', (exitCode, signal) => {
        if (exitCode === 0) {
          return;
        }

        // When there's already an instance of parity running, then the log
        // is logging a particular line, see below. In this case, we just
        // silently ignore our local instance, and let the 1st parity
        // instance be the main one.
Amaury Martiny's avatar
Amaury Martiny committed
        if (
          logLastLine &&
          catchableErrors.some(error => logLastLine.includes(error))
        ) {
          pino.warn(
Amaury Martiny's avatar
Amaury Martiny committed
            'Another instance of parity is running, closing local instance.'
          );
          return;
        }

        // If the exit code is not 0, then we show some error message
        if (Object.keys(parityArgv).length > 0) {
          app.exit(1);
        } else {
          handleError(
            new Error(`Exit code ${exitCode}, with signal ${signal}.`),
            'An error occured while running parity.'
          );
        }
      });

      // Notify the renderers
      mainWindow.webContents.send('parity-running', true);
      global.isParityRunning = true; // Send this variable to renderes via IPC

      return Promise.resolve();
    } catch (err) {
      handleError(err, 'An error occured while running parity.');
    }
Amaury Martiny's avatar
Amaury Martiny committed
    if (parity) {
Amaury Martiny's avatar
Amaury Martiny committed
      pino.info('Stopping parity.');
Amaury Martiny's avatar
Amaury Martiny committed
      parity.kill();
      parity = null;
    }
  }
};