mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 15:27:17 +00:00
fix(GUI): emit progress back to parent using node-ipc (#774)
This PR makes use of `node-ipc` to emit progress information from the child CLI to the GUI process rather than the tailing approach we've been doing until now. This change was motivated by the following Electron fix which landed in v1.4.4: https://github.com/electron/electron/pull/7578. Before such fix, Electron would not output anything to stdout/stderr if the Electron process was running with `ELECTRON_RUN_AS_NODE` under Windows, which forced us to implement `--log` option in the Etcher CLI to output state information to a file. Since this issue is fixed, we can consume the Etcher CLI output from within `child_process.spawn`, which opens more interesting possibilities for sharing information between both processes. This coindentally fixes a Windows issue where the tailing module would receive malformed JSON, causing Etcher to crash at `JSON.parse`. The reason of this problem was a bug in the tailing module we were using. Fixes: https://github.com/resin-io/etcher/issues/642 Change-Type: patch Changelog-Entry: Fix "Unexpected end of JSON" error in Windows. Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
parent
4d3eab4915
commit
d9822faf2f
@ -38,7 +38,6 @@ Options
|
||||
--drive, -d drive
|
||||
--check, -c validate write
|
||||
--robot, -r parse-able output without interactivity
|
||||
--log, -l output log file
|
||||
--yes, -y confirm non-interactively
|
||||
--unmount, -u unmount on success
|
||||
```
|
||||
|
@ -80,14 +80,6 @@ module.exports = yargs
|
||||
return true;
|
||||
})
|
||||
|
||||
.check((argv) => {
|
||||
if (argv.log && !argv.robot) {
|
||||
throw new Error('The `--log` option requires `--robot`');
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
.options({
|
||||
help: {
|
||||
describe: 'show help',
|
||||
@ -115,11 +107,6 @@ module.exports = yargs
|
||||
boolean: true,
|
||||
alias: 'r'
|
||||
},
|
||||
log: {
|
||||
describe: 'output log file',
|
||||
string: true,
|
||||
alias: 'l'
|
||||
},
|
||||
yes: {
|
||||
describe: 'confirm non-interactively',
|
||||
boolean: true,
|
||||
|
@ -24,7 +24,6 @@ const drivelist = Bluebird.promisifyAll(require('drivelist'));
|
||||
const writer = require('./writer');
|
||||
const utils = require('./utils');
|
||||
const options = require('./cli');
|
||||
const log = require('./log');
|
||||
const EXIT_CODES = require('../src/exit-codes');
|
||||
|
||||
form.run([
|
||||
@ -74,7 +73,7 @@ form.run([
|
||||
}, (state) => {
|
||||
|
||||
if (options.robot) {
|
||||
log.toStdout(JSON.stringify({
|
||||
console.log(JSON.stringify({
|
||||
command: 'progress',
|
||||
data: {
|
||||
type: state.type,
|
||||
@ -93,7 +92,7 @@ form.run([
|
||||
|
||||
return Bluebird.try(() => {
|
||||
if (options.robot) {
|
||||
return log.toStdout(JSON.stringify({
|
||||
return console.log(JSON.stringify({
|
||||
command: 'done',
|
||||
data: {
|
||||
sourceChecksum: results.sourceChecksum
|
||||
@ -115,11 +114,12 @@ form.run([
|
||||
|
||||
return Bluebird.try(() => {
|
||||
if (options.robot) {
|
||||
return log.toStderr(JSON.stringify({
|
||||
return console.error(JSON.stringify({
|
||||
command: 'error',
|
||||
data: {
|
||||
message: error.message,
|
||||
description: error.description,
|
||||
stacktrace: error.stack,
|
||||
code: error.code
|
||||
}
|
||||
}));
|
||||
@ -134,4 +134,4 @@ form.run([
|
||||
process.exit(EXIT_CODES.GENERAL_ERROR);
|
||||
});
|
||||
|
||||
}).finally(log.close);
|
||||
});
|
||||
|
106
lib/cli/log.js
106
lib/cli/log.js
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const Bluebird = require('bluebird');
|
||||
const fs = require('fs');
|
||||
const options = require('./cli');
|
||||
const logStream = options.log ? fs.createWriteStream(options.log) : null;
|
||||
const STDOUT_STREAM = logStream || process.stdout;
|
||||
const STDERR_STREAM = logStream || process.stderr;
|
||||
|
||||
/**
|
||||
* The purpose of this module is to workaround an Electron Windows issue
|
||||
* where an Electron process with `ELECTRON_RUN_AS_NODE` enabled will
|
||||
* not attach `stdout`/`stderr` to the process, therefore not allowing
|
||||
* us to redirect output to a file from `child_process` or console pipes.
|
||||
*
|
||||
* A temporary solution is to implement logic that redirects output
|
||||
* to a log file in the Etcher CLI.
|
||||
*
|
||||
* TODO: Delete this file, and the corresponding `--log` option once
|
||||
* this issue is fixed in Electron.
|
||||
* See: https://github.com/electron/electron/issues/5715
|
||||
*/
|
||||
|
||||
/**
|
||||
* @summary Write a line to stdout
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @description
|
||||
* If the `--log` option was passed, this function writes the line
|
||||
* to it, otherwise to `process.stdout`.
|
||||
*
|
||||
* @param {String} line - line
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* log.toStdout('Hello world!');
|
||||
*/
|
||||
exports.toStdout = (line) => {
|
||||
return new Bluebird((resolve) => {
|
||||
STDOUT_STREAM.write(line + '\n', () => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Write a line to stderr
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @description
|
||||
* If the `--log` option was passed, this function writes the line
|
||||
* to it, otherwise to `process.stderr`.
|
||||
*
|
||||
* @param {String} line - line
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* log.toStderr('Hello world!');
|
||||
*/
|
||||
exports.toStderr = (line) => {
|
||||
return new Bluebird((resolve) => {
|
||||
STDERR_STREAM.write(line + '\n', () => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Close any used streams, if needed
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* log.close();
|
||||
*/
|
||||
exports.close = () => {
|
||||
return new Bluebird((resolve) => {
|
||||
if (!logStream) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
logStream.close(() => {
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
};
|
@ -63,6 +63,20 @@ const app = angular.module('Etcher', [
|
||||
require('./utils/manifest-bind/manifest-bind')
|
||||
]);
|
||||
|
||||
app.run(() => {
|
||||
console.log([
|
||||
' _____ _ _',
|
||||
'| ___| | | |',
|
||||
'| |__ | |_ ___| |__ ___ _ __',
|
||||
'| __|| __/ __| \'_ \\ / _ \\ \'__|',
|
||||
'| |___| || (__| | | | __/ |',
|
||||
'\\____/ \\__\\___|_| |_|\\___|_|',
|
||||
'',
|
||||
'Interested in joining the Etcher team?',
|
||||
'Drop us a line at jobs@resin.io'
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
app.run((AnalyticsService, UpdateNotifierService, SelectionStateModel) => {
|
||||
AnalyticsService.logEvent('Application start');
|
||||
|
||||
|
@ -1,35 +1,69 @@
|
||||
# Etcher Child Writer
|
||||
Etcher Child Writer
|
||||
===================
|
||||
|
||||
This module is in charge of dealing with the gory details of elevating and managing the child writer process. As a word of warning, it contains tons of workarounds and "hacks" to deal with platform differences, packaging, and inter-process communication. This empowers us to write this small guide to explain how it works in a more high level manner, hoping to make it easier to grok for contributors.
|
||||
This module is in charge of dealing with the gory details of elevating and
|
||||
managing the child writer process. As a word of warning, it contains tons of
|
||||
workarounds and "hacks" to deal with platform differences, packaging, and
|
||||
inter-process communication. This empowers us to write this small guide to
|
||||
explain how it works in a more high level manner, hoping to make it easier to
|
||||
grok for contributors.
|
||||
|
||||
## The problem
|
||||
The problem
|
||||
-----------
|
||||
|
||||
Elevating a forked process is an easy task. Thanks to the widely available NPM modules to display nice GUI prompt dialogs, elevation is just a matter of executing the process with one of those modules instead of with `child_process` directly.
|
||||
Elevating a forked process is an easy task. Thanks to the widely available NPM
|
||||
modules to display nice GUI prompt dialogs, elevation is just a matter of
|
||||
executing the process with one of those modules instead of with `child_process`
|
||||
directly.
|
||||
|
||||
The main problems we faced are:
|
||||
|
||||
- The modules that implement elevation provide "execution" support, but don't allow us to fork/spawn the process and consume its `stdout` and `stderr` in a stream fashion. This also means that we can't use the nice `process.send` IPC communication channel directly that `child_process.fork` gives us to send messages back to the parent.
|
||||
- The modules that implement elevation provide "execution" support, but don't
|
||||
allow us to fork/spawn the process and consume its `stdout` and `stderr` in a
|
||||
stream fashion. This also means that we can't use the nice `process.send` IPC
|
||||
communication channel directly that `child_process.fork` gives us to send
|
||||
messages back to the parent.
|
||||
|
||||
- Since we can't assume anything from the environment Etcher is running on, we must make use of the same application entry point to execute both the GUI and the CLI code, which starts to get messy once we throw `asar` packaging into the mix.
|
||||
- Since we can't assume anything from the environment Etcher is running on, we
|
||||
must make use of the same application entry point to execute both the GUI and
|
||||
the CLI code, which starts to get messy once we throw `asar` packaging into
|
||||
the mix.
|
||||
|
||||
- Each elevation mechanism has its quirks, mainly on GNU/Linux. Making sure that the forked process was elevated correctly and could work without issues required various workarounds targeting `pkexec` or `kdesudo`.
|
||||
- Each elevation mechanism has its quirks, mainly on GNU/Linux. Making sure
|
||||
that the forked process was elevated correctly and could work without issues
|
||||
required various workarounds targeting `pkexec` or `kdesudo`.
|
||||
|
||||
## How it works
|
||||
How it works
|
||||
------------
|
||||
|
||||
The Etcher binary runs in CLI or GUI mode depending on an environment variable called `ELECTRON_RUN_AS_NODE`. When this variable is set, it instructs Electron to run as a normal NodeJS process (without Chromium, etc), but still keep any patches applied by Electron, like `asar` support.
|
||||
The Etcher binary runs in CLI or GUI mode depending on an environment variable
|
||||
called `ELECTRON_RUN_AS_NODE`. When this variable is set, it instructs Electron
|
||||
to run as a normal NodeJS process (without Chromium, etc), but still keep any
|
||||
patches applied by Electron, like `asar` support.
|
||||
|
||||
When the Etcher GUI is ran, and the user presses the "Flash!" button, it passes all the required information, like the image path, the device path, current settings, etc; to the child writer module, which based on this information computes an array of arguments, including an option called `--robot`, which can be passed to the Etcher CLI to perform the writing as the user instructed. The `--robot` option basically tells the Etcher CLI to output state information in a way that can be very easily parsed by the parent process.
|
||||
When the Etcher GUI is ran, and the user presses the "Flash!" button, the GUI
|
||||
creates an IPC server, and forks a process called the "writer proxy", passing
|
||||
it all the required information to perform the flashing, such as the image
|
||||
path, the device path, the current settings, etc.
|
||||
|
||||
Afterwards, the child writer entry point *forks* yet another script passing all the command line arguments collected so far and blindly proxies all IPC messages from its child to the parent, the GUI process.
|
||||
The writer proxy then checks if its currently elevated, and if not, prompts the
|
||||
user for elevation and re-spawns itself.
|
||||
|
||||
Once the new script starts, it creates a temporary log file, starts tailing it, parses what it gets, and sends any data to its parent using IPC messages (which are proxied to the GUI). After all is set, the script checks if its currently elevated, and if not, prompts the user for his password and re-executes itself, while keeping the original process alive and tailing.
|
||||
Once the writer proxy has enough permissions to directly access devices, it
|
||||
spawns the Etcher CLI passing the `--robot` option along with all the
|
||||
information gathered before. The `--robot` option basically tells the Etcher
|
||||
CLI to output state information in a way that can be very easily parsed by the
|
||||
parent process.
|
||||
|
||||
Since elevation is done by executing, not forking, we lose IPC communication at this point. Since the non-elevated version of the script is alive, we can go ahead and spawn the Etcher CLI with all the arguments computed before, and redirect `stdout` to the temporary log file.
|
||||
The output of the Etcher CLI is then sent to the IPC server that was opened by
|
||||
the GUI, which nicely displays them in the progress bar the user sees.
|
||||
|
||||
The non-elevated process receives the `--robot` output by tailing the temporary file, parses each line and sends back the results to the GUI, which nicely displays them in the progress bar the user sees.
|
||||
Summary
|
||||
-------
|
||||
|
||||
## Summary
|
||||
There are lots of details we're omitting for the sake of clarity. Feel free to
|
||||
dive in inside the child writer code, which is heavily commented to explain the
|
||||
reasons behind each decision or workaround.
|
||||
|
||||
There are lots of details we're omitting for the sake of clarity. Feel free to dive in inside the child writer code, which is heavily commented to explain the reasons behind each decision or workaround.
|
||||
|
||||
Don't hesitate in getting in touch if you have any suggestion, or just want to know more!
|
||||
Don't hesitate in getting in touch if you have any suggestion, or just want to
|
||||
know more!
|
||||
|
@ -35,12 +35,6 @@ module.exports = {
|
||||
* @property {String} WRITER_PROXY_SCRIPT
|
||||
* @memberof CONSTANTS
|
||||
*/
|
||||
WRITER_PROXY_SCRIPT: path.join(__dirname, 'writer-proxy.js'),
|
||||
|
||||
/**
|
||||
* @property {String} TEMPORARY_LOG_FILE_ENVIRONMENT_VARIABLE
|
||||
* @memberof CONSTANTS
|
||||
*/
|
||||
TEMPORARY_LOG_FILE_ENVIRONMENT_VARIABLE: 'ETCHER_INTERNAL_LOG_FILE'
|
||||
WRITER_PROXY_SCRIPT: path.join(__dirname, 'writer-proxy.js')
|
||||
|
||||
};
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const childProcess = require('child_process');
|
||||
const ipc = require('node-ipc');
|
||||
const rendererUtils = require('./renderer-utils');
|
||||
const utils = require('./utils');
|
||||
const CONSTANTS = require('./constants');
|
||||
@ -56,20 +57,55 @@ const EXIT_CODES = require('../exit-codes');
|
||||
exports.write = (image, drive, options) => {
|
||||
const emitter = new EventEmitter();
|
||||
|
||||
utils.getTemporaryLogFilePath().then((logFile) => {
|
||||
const argv = utils.getCLIWriterArguments({
|
||||
entryPoint: rendererUtils.getApplicationEntryPoint(),
|
||||
logFile: logFile,
|
||||
image: image,
|
||||
device: drive.device,
|
||||
validateWriteOnSuccess: options.validateWriteOnSuccess,
|
||||
unmountOnSuccess: options.unmountOnSuccess
|
||||
});
|
||||
const argv = utils.getCLIWriterArguments({
|
||||
entryPoint: rendererUtils.getApplicationEntryPoint(),
|
||||
image: image,
|
||||
device: drive.device,
|
||||
validateWriteOnSuccess: options.validateWriteOnSuccess,
|
||||
unmountOnSuccess: options.unmountOnSuccess
|
||||
});
|
||||
|
||||
// Make writer proxy inherit the temporary log file location
|
||||
// while keeping current environment variables intact.
|
||||
process.env[CONSTANTS.TEMPORARY_LOG_FILE_ENVIRONMENT_VARIABLE] = logFile;
|
||||
// There might be multiple Etcher instances running at
|
||||
// the same time, therefore we must ensure each IPC
|
||||
// server/client has a different name.
|
||||
process.env.IPC_SERVER_ID = `etcher-server-${process.pid}`;
|
||||
process.env.IPC_CLIENT_ID = `etcher-client-${process.pid}`;
|
||||
|
||||
ipc.config.id = process.env.IPC_SERVER_ID;
|
||||
ipc.config.silent = true;
|
||||
ipc.serve();
|
||||
|
||||
ipc.server.on('error', (error) => {
|
||||
emitter.emit('error', error);
|
||||
});
|
||||
|
||||
ipc.server.on('message', (data) => {
|
||||
let message;
|
||||
try {
|
||||
message = JSON.parse(data);
|
||||
} catch (error) {
|
||||
return emitter.emit('error', new Error(`Invalid message: ${data}`));
|
||||
}
|
||||
|
||||
if (!message.command || !message.data) {
|
||||
return emitter.emit('error', new Error(`Invalid message: ${data}`));
|
||||
}
|
||||
|
||||
// The error object is decomposed by the CLI for serialisation
|
||||
// purposes. We compose it back to an `Error` here in order
|
||||
// to provide better encapsulation.
|
||||
if (message.command === 'error') {
|
||||
const error = new Error(message.data.message);
|
||||
error.code = message.data.code;
|
||||
error.description = message.data.description;
|
||||
error.stack = message.data.stacktrace;
|
||||
return emitter.emit('error', error);
|
||||
}
|
||||
|
||||
emitter.emit(message.command, message.data);
|
||||
});
|
||||
|
||||
ipc.server.on('start', () => {
|
||||
const child = childProcess.fork(CONSTANTS.WRITER_PROXY_SCRIPT, argv, {
|
||||
silent: true,
|
||||
env: process.env
|
||||
@ -83,21 +119,6 @@ exports.write = (image, drive, options) => {
|
||||
emitter.emit('error', new Error(data.toString()));
|
||||
});
|
||||
|
||||
child.on('message', (message) => {
|
||||
|
||||
// The error object is decomposed by the CLI for serialisation
|
||||
// purposes. We compose it back to an `Error` here in order
|
||||
// to provide better encapsulation.
|
||||
if (message.command === 'error') {
|
||||
const error = new Error(message.data.message);
|
||||
error.code = message.data.code;
|
||||
error.description = message.data.description;
|
||||
return emitter.emit('error', error);
|
||||
}
|
||||
|
||||
emitter.emit(message.command, message.data);
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
emitter.emit('error', error);
|
||||
});
|
||||
@ -115,5 +136,7 @@ exports.write = (image, drive, options) => {
|
||||
});
|
||||
});
|
||||
|
||||
ipc.server.start();
|
||||
|
||||
return emitter;
|
||||
};
|
||||
|
@ -18,9 +18,6 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const os = require('os');
|
||||
const Bluebird = require('bluebird');
|
||||
const tmp = Bluebird.promisifyAll(require('tmp'));
|
||||
const packageJSON = require('../../../package.json');
|
||||
|
||||
/**
|
||||
* @summary Get the explicit boolean form of an argument
|
||||
@ -59,7 +56,6 @@ exports.getBooleanArgumentForm = (argumentName, value) => {
|
||||
* @param {String} options.image - image
|
||||
* @param {String} options.device - device
|
||||
* @param {String} options.entryPoint - entry point
|
||||
* @param {String} [options.logFile] - log file
|
||||
* @param {Boolean} [options.validateWriteOnSuccess] - validate write on success
|
||||
* @param {Boolean} [options.unmountOnSuccess] - unmount on success
|
||||
* @returns {String[]} arguments
|
||||
@ -100,13 +96,6 @@ exports.getCLIWriterArguments = (options) => {
|
||||
|
||||
];
|
||||
|
||||
// This temporarily workarounds a Windows Electron issue where
|
||||
// we can't pipe `stdout`/`stderr` with system pipes.
|
||||
// See: https://github.com/electron/electron/issues/5715
|
||||
if (options.logFile) {
|
||||
argv.push('--log', options.logFile);
|
||||
}
|
||||
|
||||
return argv;
|
||||
};
|
||||
|
||||
@ -126,23 +115,3 @@ exports.escapeWhiteSpacesFromArguments = (argv) => {
|
||||
return argument.replace(/\s/g, '\\ ');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Get a temporary log file path
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @fulfil {String} - file path
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* utils.getTemporaryLogFilePath().then((filePath) => {
|
||||
* console.log(filePath);
|
||||
* });
|
||||
*/
|
||||
exports.getTemporaryLogFilePath = () => {
|
||||
return tmp.fileAsync({
|
||||
prefix: `${packageJSON.name}-`,
|
||||
postfix: '.log'
|
||||
});
|
||||
};
|
||||
|
@ -19,14 +19,12 @@
|
||||
const Bluebird = require('bluebird');
|
||||
const childProcess = require('child_process');
|
||||
const isElevated = Bluebird.promisify(require('is-elevated'));
|
||||
const ipc = require('node-ipc');
|
||||
const _ = require('lodash');
|
||||
const os = require('os');
|
||||
const Tail = require('tail').Tail;
|
||||
const fileTail = require('file-tail');
|
||||
const sudoPrompt = Bluebird.promisifyAll(require('sudo-prompt'));
|
||||
const EXIT_CODES = require('../exit-codes');
|
||||
const packageJSON = require('../../../package.json');
|
||||
const CONSTANTS = require('./constants');
|
||||
const utils = require('./utils');
|
||||
|
||||
// This script is in charge of spawning the writer process and
|
||||
@ -35,47 +33,13 @@ const utils = require('./utils');
|
||||
// modules don't work in a spawn/fork fashion.
|
||||
//
|
||||
// This script spawns the writer process and redirects its `stdout`
|
||||
// to a temporary 'log file', which is tailed by this same script.
|
||||
// The output is then parsed, and sent as IPC messages to the
|
||||
// parent process, taking care of the writer elevation as needed.
|
||||
// and `stderr` to the parent process using IPC communication,
|
||||
// taking care of the writer elevation as needed.
|
||||
|
||||
const EXECUTABLE = process.argv[0];
|
||||
const ETCHER_ARGUMENTS = process.argv.slice(2);
|
||||
|
||||
return isElevated().then((elevated) => {
|
||||
const logFile = process.env[CONSTANTS.TEMPORARY_LOG_FILE_ENVIRONMENT_VARIABLE];
|
||||
|
||||
if (process.send) {
|
||||
console.log(`Tailing ${logFile}`);
|
||||
|
||||
// Sadly, `fs.createReadStream()` won't work since
|
||||
// the stream that function returns gets closed
|
||||
// when it initially reaches EOF, instead of waiting
|
||||
// for the file to receive more data.
|
||||
const tail = _.attempt(() => {
|
||||
|
||||
if (os.platform() === 'win32') {
|
||||
|
||||
// This tail module overcomes various Windows
|
||||
// issues by not using `fs.watch()`, but doesn't
|
||||
// work 100% as expected on other operating systems,
|
||||
// therefore we only use it on Windows.
|
||||
return fileTail.startTailing(logFile);
|
||||
|
||||
}
|
||||
|
||||
return new Tail(logFile);
|
||||
});
|
||||
|
||||
tail.on('error', (error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
tail.on('line', (line) => {
|
||||
process.send(JSON.parse(line));
|
||||
});
|
||||
}
|
||||
|
||||
if (!elevated) {
|
||||
console.log('Attempting to elevate');
|
||||
@ -88,7 +52,10 @@ return isElevated().then((elevated) => {
|
||||
'ELECTRON_RUN_AS_NODE=1',
|
||||
'&&',
|
||||
'set',
|
||||
`${CONSTANTS.TEMPORARY_LOG_FILE_ENVIRONMENT_VARIABLE}=${logFile}`,
|
||||
`IPC_SERVER_ID=${process.env.IPC_SERVER_ID}`,
|
||||
'&&',
|
||||
'set',
|
||||
`IPC_CLIENT_ID=${process.env.IPC_CLIENT_ID}`,
|
||||
'&&',
|
||||
|
||||
// This is a trick to make the binary afterwards catch
|
||||
@ -116,7 +83,8 @@ return isElevated().then((elevated) => {
|
||||
// in are manually inherited.
|
||||
'env',
|
||||
'ELECTRON_RUN_AS_NODE=1',
|
||||
`${CONSTANTS.TEMPORARY_LOG_FILE_ENVIRONMENT_VARIABLE}=${logFile}`
|
||||
`IPC_SERVER_ID=${process.env.IPC_SERVER_ID}`,
|
||||
`IPC_CLIENT_ID=${process.env.IPC_CLIENT_ID}`
|
||||
|
||||
];
|
||||
|
||||
@ -197,18 +165,23 @@ return isElevated().then((elevated) => {
|
||||
console.log('Re-spawning with elevation');
|
||||
|
||||
return new Bluebird((resolve, reject) => {
|
||||
const child = childProcess.spawn(EXECUTABLE, ETCHER_ARGUMENTS);
|
||||
ipc.config.id = process.env.IPC_CLIENT_ID;
|
||||
ipc.config.silent = true;
|
||||
ipc.connectTo(process.env.IPC_SERVER_ID, () => {
|
||||
ipc.of[process.env.IPC_SERVER_ID].on('error', reject);
|
||||
ipc.of[process.env.IPC_SERVER_ID].on('connect', () => {
|
||||
const child = childProcess.spawn(EXECUTABLE, ETCHER_ARGUMENTS);
|
||||
child.on('error', reject);
|
||||
child.on('close', resolve);
|
||||
|
||||
child.stdout.on('data', (data) => {
|
||||
console.log(data.toString());
|
||||
const emitMessage = (data) => {
|
||||
ipc.of[process.env.IPC_SERVER_ID].emit('message', data.toString());
|
||||
};
|
||||
|
||||
child.stdout.on('data', emitMessage);
|
||||
child.stderr.on('data', emitMessage);
|
||||
});
|
||||
});
|
||||
|
||||
child.stderr.on('data', (data) => {
|
||||
console.error(data.toString());
|
||||
});
|
||||
|
||||
child.on('error', reject);
|
||||
child.on('close', resolve);
|
||||
}).then((exitCode) => {
|
||||
process.exit(exitCode);
|
||||
});
|
||||
|
79
npm-shrinkwrap.json
generated
79
npm-shrinkwrap.json
generated
@ -579,6 +579,11 @@
|
||||
"from": "builtin-status-codes@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz"
|
||||
},
|
||||
"cached-path-relative": {
|
||||
"version": "1.0.0",
|
||||
"from": "cached-path-relative@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.0.tgz"
|
||||
},
|
||||
"caller-path": {
|
||||
"version": "0.1.0",
|
||||
"from": "caller-path@>=0.1.0 <0.2.0",
|
||||
@ -1338,6 +1343,11 @@
|
||||
"from": "es6-map@>=0.1.3 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.4.tgz"
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "3.3.1",
|
||||
"from": "es6-promise@>=3.2.1 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz"
|
||||
},
|
||||
"es6-set": {
|
||||
"version": "0.1.4",
|
||||
"from": "es6-set@>=0.1.3 <0.2.0",
|
||||
@ -1458,6 +1468,11 @@
|
||||
"from": "event-emitter@>=0.3.4 <0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz"
|
||||
},
|
||||
"event-pubsub": {
|
||||
"version": "4.2.3",
|
||||
"from": "event-pubsub@4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.2.3.tgz"
|
||||
},
|
||||
"events": {
|
||||
"version": "1.1.1",
|
||||
"from": "events@>=1.1.0 <1.2.0",
|
||||
@ -2517,6 +2532,16 @@
|
||||
"from": "jodid25519@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz"
|
||||
},
|
||||
"js-message": {
|
||||
"version": "1.0.5",
|
||||
"from": "js-message@>=1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz"
|
||||
},
|
||||
"js-queue": {
|
||||
"version": "1.0.0",
|
||||
"from": "js-queue@>=1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-queue/-/js-queue-1.0.0.tgz"
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "1.0.3",
|
||||
"from": "js-tokens@>=1.0.1 <2.0.0",
|
||||
@ -3543,40 +3568,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"module-deps": {
|
||||
"version": "4.0.7",
|
||||
"from": "module-deps@>=4.0.2 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.0.7.tgz",
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"from": "isarray@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.1.4",
|
||||
"from": "readable-stream@>=2.0.2 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz"
|
||||
},
|
||||
"through2": {
|
||||
"version": "2.0.1",
|
||||
"from": "through2@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz",
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "2.0.6",
|
||||
"from": "readable-stream@>=2.0.0 <2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.1",
|
||||
"from": "xtend@>=4.0.0 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.13.0",
|
||||
"from": "moment@>=2.8.0 <3.0.0",
|
||||
@ -3629,6 +3620,11 @@
|
||||
"from": "nested-error-stacks@>=1.0.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz"
|
||||
},
|
||||
"node-cmd": {
|
||||
"version": "1.1.1",
|
||||
"from": "node-cmd@>=1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-cmd/-/node-cmd-1.1.1.tgz"
|
||||
},
|
||||
"node-gyp": {
|
||||
"version": "3.4.0",
|
||||
"from": "node-gyp@>=3.3.1 <4.0.0",
|
||||
@ -3646,6 +3642,11 @@
|
||||
"from": "node-int64@>=0.4.0 <0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz"
|
||||
},
|
||||
"node-ipc": {
|
||||
"version": "8.9.2",
|
||||
"from": "node-ipc@latest",
|
||||
"resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-8.9.2.tgz"
|
||||
},
|
||||
"node-stream-zip": {
|
||||
"version": "1.3.4",
|
||||
"from": "node-stream-zip@>=1.3.4 <2.0.0",
|
||||
@ -4782,6 +4783,11 @@
|
||||
"from": "sudo-prompt@6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-6.1.0.tgz"
|
||||
},
|
||||
"sumchecker": {
|
||||
"version": "1.2.0",
|
||||
"from": "sumchecker@>=1.2.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.2.0.tgz"
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"from": "supports-color@>=2.0.0 <3.0.0",
|
||||
@ -4900,11 +4906,6 @@
|
||||
"from": "timers-browserify@>=1.0.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz"
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.28",
|
||||
"from": "tmp@0.0.28",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz"
|
||||
},
|
||||
"tn1150": {
|
||||
"version": "0.1.0",
|
||||
"from": "tn1150@>=0.1.0 <0.2.0",
|
||||
|
@ -76,6 +76,7 @@
|
||||
"immutable": "^3.8.1",
|
||||
"is-elevated": "^1.0.0",
|
||||
"lodash": "^4.5.1",
|
||||
"node-ipc": "^8.9.2",
|
||||
"redux": "^3.5.2",
|
||||
"redux-localstorage": "^0.4.1",
|
||||
"removedrive": "^1.1.1",
|
||||
@ -86,7 +87,6 @@
|
||||
"semver": "^5.1.0",
|
||||
"sudo-prompt": "^6.1.0",
|
||||
"tail": "^1.1.0",
|
||||
"tmp": "0.0.28",
|
||||
"trackjs": "^2.1.16",
|
||||
"umount": "^1.1.3",
|
||||
"username": "^2.1.0",
|
||||
@ -100,7 +100,7 @@
|
||||
"electron-mocha": "^1.2.2",
|
||||
"electron-osx-sign": "^0.3.0",
|
||||
"electron-packager": "^7.0.1",
|
||||
"electron-prebuilt": "1.2.6",
|
||||
"electron-prebuilt": "1.4.4",
|
||||
"eslint": "^2.13.1",
|
||||
"jsonfile": "^2.3.1",
|
||||
"mochainon": "^1.0.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user