Support the Synchronous Process Execution of Node v0.12(v0.11).
This commit is contained in:
parent
995f22b4bf
commit
ab94a1ea2b
3 changed files with 76 additions and 60 deletions
|
@ -191,8 +191,7 @@ grunt.initConfig({
|
||||||
|
|
||||||
### Platforms
|
### Platforms
|
||||||
|
|
||||||
The your Node and OS may not support interactively reading from stdin. The stdin interfaces are different by platforms.
|
The stdin interfaces are different by platforms. If the platform doesn't support interactively reading from stdin, an error is thrown.
|
||||||
If in those platforms, an error is thrown.
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
try {
|
try {
|
||||||
|
@ -205,18 +204,16 @@ try {
|
||||||
|
|
||||||
### Reading by shell
|
### Reading by shell
|
||||||
|
|
||||||
readlineSync tries reading from stdin by shell if it is needed. And, it use "piping via files" for synchronous running.
|
readlineSync tries reading from stdin by shell if it is needed. And if the running Node doesn't support the [Synchronous Process Execution](http://nodejs.org/api/child_process.html#child_process_synchronous_process_creation) (i.e. Node v0.10-), it use "piping via files" for synchronous running.
|
||||||
As everyone knows, "piping via files" is no good. It blocks event loop and a process. It may make your script be slow.
|
As everyone knows, "piping via files" is no good. It blocks event loop and a process. It may make your script be slow.
|
||||||
|
|
||||||
Why did I choose it? :
|
Why did I choose it? :
|
||||||
|
|
||||||
+ The best solution is [child_process.execSync](https://github.com/joyent/node/blob/master/doc/api/child_process.markdown#child_processexecsynccommand-options) in core modules of Node. But it is not supported by current version.
|
|
||||||
+ The good modules (native addon) for synchronous execution exist. But node-gyp can't compile those in some platforms or Node versions.
|
+ The good modules (native addon) for synchronous execution exist. But node-gyp can't compile those in some platforms or Node versions.
|
||||||
+ I think that the security is important more than the speed. Some modules have problem about security. (Those don't protect data.) I think that the speed is not needed usually, because readlineSync is used while user types keys.
|
+ I think that the security is important more than the speed. Some modules have problem about security. (Those don't protect data.) I think that the speed is not needed usually, because readlineSync is used while user types keys.
|
||||||
|
|
||||||
Someday, I may rewrite readlineSync to use child_process.execSync, or safety module.
|
|
||||||
|
|
||||||
## Release History
|
## Release History
|
||||||
|
* 2015-02-12 v0.5.5 Support the Synchronous Process Execution of Node v0.12(v0.11).
|
||||||
* 2015-01-27 v0.5.0 Add `options.noTrim`.
|
* 2015-01-27 v0.5.0 Add `options.noTrim`.
|
||||||
* 2014-07-12 v0.4.0 Add `options.noEchoBack`.
|
* 2014-07-12 v0.4.0 Add `options.noEchoBack`.
|
||||||
* 2014-07-12 v0.3.0 Add `setPrint()`.
|
* 2014-07-12 v0.3.0 Add `setPrint()`.
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
var
|
var
|
||||||
BUF_SIZE = 256,
|
BUF_SIZE = 256,
|
||||||
|
IS_WIN = process.platform === 'win32',
|
||||||
|
SHELL_PATH = IS_WIN ? 'cmd.exe' : '/bin/sh',
|
||||||
|
|
||||||
ALGORITHM_CIPHER = 'aes-256-cbc',
|
ALGORITHM_CIPHER = 'aes-256-cbc',
|
||||||
ALGORITHM_HASH = 'sha256',
|
ALGORITHM_HASH = 'sha256',
|
||||||
|
|
||||||
|
@ -79,62 +82,37 @@ function _readlineSync(display, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _readlineShell(noEchoBack) {
|
function _readlineShell(noEchoBack) {
|
||||||
// piping via files instead of execSync (node v0.12+)
|
var shellStdout, command,
|
||||||
// https://github.com/joyent/node/blob/master/doc/api/child_process.markdown#child_processexecsynccommand-options
|
options = {
|
||||||
// see README > Note
|
env: process.env,
|
||||||
var shellPath, args, shellStdout,
|
stdio: [stdin], // ScriptPW needs piped stdin
|
||||||
pathStdout = getTempfile('readline-sync.stdout'),
|
encoding: encoding
|
||||||
pathStatus = getTempfile('readline-sync.status'),
|
},
|
||||||
pathDone = getTempfile('readline-sync.done'),
|
optEchoBack = noEchoBack ? ' noechoback' : '';
|
||||||
optEchoBack = noEchoBack ? ' noechoback' : '',
|
|
||||||
crypto = require('crypto'), shasum, decipher, password;
|
|
||||||
|
|
||||||
shasum = crypto.createHash(ALGORITHM_HASH);
|
if (IS_WIN) {
|
||||||
shasum.update('' + process.pid + (salt++) + Math.random());
|
|
||||||
password = shasum.digest('hex');
|
|
||||||
decipher = crypto.createDecipher(ALGORITHM_CIPHER, password);
|
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
// The quote (") is escaped by node before parsed by shell. Then use ENV{Q}.
|
// The quote (") is escaped by node before parsed by shell. Then use ENV{Q}.
|
||||||
shellPath = 'cmd.exe';
|
|
||||||
args = ['/V:ON', '/S', '/C',
|
|
||||||
'%Q%' + __dirname + '\\read.bat%Q%' + optEchoBack +
|
|
||||||
' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q%' +
|
|
||||||
' %Q%' + ALGORITHM_CIPHER + '%Q% %Q%' + password + '%Q%' +
|
|
||||||
' >%Q%' + pathStdout + '%Q%' +
|
|
||||||
' & (echo !ERRORLEVEL!)>%Q%' + pathStatus + '%Q% & (echo 1)>%Q%' + pathDone + '%Q%'];
|
|
||||||
process.env.Q = '"';
|
process.env.Q = '"';
|
||||||
|
command = '%Q%' + __dirname + '\\read.bat%Q%' + optEchoBack;
|
||||||
} else {
|
} else {
|
||||||
shellPath = '/bin/sh';
|
command = '"' + __dirname + '/read.sh"' + optEchoBack;
|
||||||
args = ['-c',
|
|
||||||
'DATA=`(' + shellPath + ' "' + __dirname + '/read.sh"' + optEchoBack + ')`; RTN=$?;' +
|
|
||||||
' if [ $RTN -eq 0 ]; then (printf \'%s\' "$DATA" |' +
|
|
||||||
'"' + process.execPath + '" "' + __dirname + '/encrypt.js"' +
|
|
||||||
' "' + ALGORITHM_CIPHER + '" "' + password + '"' +
|
|
||||||
' >"' + pathStdout + '") fi;' +
|
|
||||||
' expr $RTN + $? >"' + pathStatus + '"; echo 1 >"' + pathDone + '"'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stdin.pause(); // re-start in child process
|
stdin.pause(); // re-start in child process
|
||||||
childProc.spawn(shellPath, args, {
|
if (childProc.execFileSync) {
|
||||||
env: process.env,
|
shellStdout = childProc.execFileSync(SHELL_PATH,
|
||||||
stdio: [stdin] // ScriptPW needs piped stdin
|
IS_WIN ? ['/S', '/C', command] : ['-c', command], options);
|
||||||
});
|
|
||||||
|
|
||||||
while (fs.readFileSync(pathDone, {encoding: encoding}).trim() !== '1') {}
|
|
||||||
if (fs.readFileSync(pathStatus, {encoding: encoding}).trim() === '0') {
|
|
||||||
shellStdout =
|
|
||||||
decipher.update(fs.readFileSync(pathStdout, {encoding: 'binary'}), 'hex', encoding) +
|
|
||||||
decipher.final(encoding);
|
|
||||||
shellStdout = shellStdout.replace(/^'|'$/g, '');
|
shellStdout = shellStdout.replace(/^'|'$/g, '');
|
||||||
|
} else {
|
||||||
|
shellStdout = _execSyncByFile(command, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.unlinkSync(pathStdout);
|
|
||||||
fs.unlinkSync(pathStatus);
|
|
||||||
fs.unlinkSync(pathDone);
|
|
||||||
return shellStdout;
|
return shellStdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// piping via files (node v0.10-)
|
||||||
|
function _execSyncByFile(command, options) {
|
||||||
|
|
||||||
function getTempfile(name) {
|
function getTempfile(name) {
|
||||||
var path = require('path'), filepath, suffix = '', fd;
|
var path = require('path'), filepath, suffix = '', fd;
|
||||||
tempdir = tempdir || require('os').tmpdir();
|
tempdir = tempdir || require('os').tmpdir();
|
||||||
|
@ -157,6 +135,47 @@ function getTempfile(name) {
|
||||||
return filepath;
|
return filepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var shellStdout,
|
||||||
|
pathStdout = getTempfile('readline-sync.stdout'),
|
||||||
|
pathStatus = getTempfile('readline-sync.status'),
|
||||||
|
pathDone = getTempfile('readline-sync.done'),
|
||||||
|
crypto = require('crypto'), shasum, decipher, password;
|
||||||
|
|
||||||
|
shasum = crypto.createHash(ALGORITHM_HASH);
|
||||||
|
shasum.update('' + process.pid + (salt++) + Math.random());
|
||||||
|
password = shasum.digest('hex');
|
||||||
|
decipher = crypto.createDecipher(ALGORITHM_CIPHER, password);
|
||||||
|
|
||||||
|
childProc.spawn(SHELL_PATH,
|
||||||
|
IS_WIN ? ['/V:ON', '/S', '/C',
|
||||||
|
command + ' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q%' +
|
||||||
|
' %Q%' + ALGORITHM_CIPHER + '%Q% %Q%' + password + '%Q%' +
|
||||||
|
' >%Q%' + pathStdout + '%Q%' +
|
||||||
|
' & (echo !ERRORLEVEL!)>%Q%' + pathStatus + '%Q% & (echo 1)>%Q%' + pathDone + '%Q%'] :
|
||||||
|
['-c',
|
||||||
|
'DATA=`(' + SHELL_PATH + ' ' + command + ')`; RTN=$?;' +
|
||||||
|
' if [ $RTN -eq 0 ]; then (printf \'%s\' "$DATA" |' +
|
||||||
|
'"' + process.execPath + '" "' + __dirname + '/encrypt.js"' +
|
||||||
|
' "' + ALGORITHM_CIPHER + '" "' + password + '"' +
|
||||||
|
' >"' + pathStdout + '") fi;' +
|
||||||
|
' expr $RTN + $? >"' + pathStatus + '"; echo 1 >"' + pathDone + '"'],
|
||||||
|
options);
|
||||||
|
|
||||||
|
while (fs.readFileSync(pathDone, {encoding: encoding}).trim() !== '1') {}
|
||||||
|
if (fs.readFileSync(pathStatus, {encoding: encoding}).trim() === '0') {
|
||||||
|
shellStdout =
|
||||||
|
decipher.update(fs.readFileSync(pathStdout, {encoding: 'binary'}), 'hex', encoding) +
|
||||||
|
decipher.final(encoding);
|
||||||
|
shellStdout = shellStdout.replace(/^'|'$/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.unlinkSync(pathStdout);
|
||||||
|
fs.unlinkSync(pathStatus);
|
||||||
|
fs.unlinkSync(pathDone);
|
||||||
|
|
||||||
|
return shellStdout;
|
||||||
|
}
|
||||||
|
|
||||||
// for dev
|
// for dev
|
||||||
exports.useShellSet = function(use) { useShell = use; };
|
exports.useShellSet = function(use) { useShell = use; };
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "readline-sync",
|
"name": "readline-sync",
|
||||||
"version": "0.5.4",
|
"version": "0.5.5",
|
||||||
"title": "readlineSync",
|
"title": "readlineSync",
|
||||||
"description": "Synchronous Readline",
|
"description": "Synchronous Readline",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
Loading…
Reference in a new issue