runParity.js 3.92 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 noop = require('lodash/noop');
Amaury Martiny's avatar
Amaury Martiny committed
const { spawn } = require('child_process');
const util = require('util');

Amaury Martiny's avatar
Amaury Martiny committed
const { cli, parityArgv } = require('../cli');
Amaury Martiny's avatar
Amaury Martiny committed
const handleError = require('./handleError');
const parityPath = require('../utils/parityPath');

const fsChmod = util.promisify(fs.chmod);
Amaury Martiny's avatar
Amaury Martiny committed
const fsExists = util.promisify(fs.stat);
const fsUnlink = util.promisify(fs.unlink);

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 = [
  'is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.',
  'Error(Msg("IO error: While lock file:'
];

Amaury Martiny's avatar
Amaury Martiny committed
module.exports = {
  runParity (mainWindow) {
Amaury Martiny's avatar
Amaury Martiny committed
    // Do not run parity with --no-run-parity
    if (cli.runParity === false) {
      return;
    }

Amaury Martiny's avatar
Amaury Martiny committed
    // Create a logStream to save logs
    const logFile = `${parityPath()}.log`;

    fsExists(logFile)
      .then(() => fsUnlink(logFile)) // Delete logFile and create a fresh one on each launch
      .catch(noop)
      .then(() => fsChmod(parityPath(), '755')) // Should already be 755 after download, just to be sure
Amaury Martiny's avatar
Amaury Martiny committed
      .then(() => {
        const logStream = fs.createWriteStream(logFile, { flags: 'a' });
        let logLastLine; // Always contains last line of the logFile
        // Run an instance of parity with the correct args
Amaury Martiny's avatar
Amaury Martiny committed
        parity = spawn(parityPath(), parityArgv);
        // Pipe all parity command output into the logFile
Amaury Martiny's avatar
Amaury Martiny committed
        parity.stdout.pipe(logStream);
        parity.stderr.pipe(logStream);
Amaury Martiny's avatar
Amaury Martiny committed

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

Amaury Martiny's avatar
Amaury Martiny committed
        parity.on('error', err => {
          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 (catchableErrors.some(error => logLastLine.includes(error))) {
            console.log(
              'Another instance of parity is running, closing local instance.'
            );
            return;
          }

Amaury Martiny's avatar
Amaury Martiny committed
          // If the exit code is not 0, then we show some error message
Amaury Martiny's avatar
Amaury Martiny committed
          if (Object.keys(parityArgv).length > 0) {
Amaury Martiny's avatar
Amaury Martiny committed
            // If parity has been launched with some args, then most likely the
            // args are wrong, so we show the output of parity.
            const log = fs.readFileSync(logFile);
            console.log(log.toString());
            app.exit(1);
Amaury Martiny's avatar
Amaury Martiny committed
          } else {
            handleError(
              new Error(`Exit code ${exitCode}, with signal ${signal}.`),
              'An error occured while running parity.'
            );
      .then(() => {
        // Notify the renderers
        mainWindow.webContents.send('parity-running', true);
        global.isParityRunning = true; // Send this variable to renderes via IPC
      })
Amaury Martiny's avatar
Amaury Martiny committed
      .catch(err => {
        handleError(err, 'An error occured while running parity.');
      });
  },
Amaury Martiny's avatar
Amaury Martiny committed
    if (parity) {
      console.log('Stopping parity.');
      parity.kill();
      parity = null;
    }
  }
};