wallet-core/node_modules/spawn-wrap/index.js
2017-05-28 00:40:43 +02:00

327 lines
8.8 KiB
JavaScript

module.exports = wrap
wrap.runMain = runMain
var Module = require('module')
var fs = require('fs')
var cp = require('child_process')
var ChildProcess = cp.ChildProcess
var assert = require('assert')
var crypto = require('crypto')
var mkdirp = require('mkdirp')
var rimraf = require('rimraf')
var path = require('path')
var signalExit = require('signal-exit')
var homedir = require('os-homedir')() + '/.node-spawn-wrap-'
var winRebase = require('./lib/win-rebase')
var which = require('which')
var cmdname = path.basename(process.execPath, '.exe')
var shim = '#!' + process.execPath + '\n' +
fs.readFileSync(__dirname + '/shim.js')
var isWindows = require('./lib/is-windows')()
var pathRe = /^PATH=/
if (isWindows) pathRe = /^PATH=/i
var colon = isWindows ? ';' : ':'
function wrap (argv, env, workingDir) {
if (!ChildProcess) {
var child = cp.spawn(process.execPath, [])
ChildProcess = child.constructor
child.kill('SIGKILL')
}
// spawn_sync available since Node v0.11
var spawnSyncBinding, spawnSync
try {
spawnSyncBinding = process.binding('spawn_sync')
} catch (e) {}
// if we're passed in the working dir, then it means that setup
// was already done, so no need.
var doSetup = !workingDir
if (doSetup) {
workingDir = setup(argv, env)
}
var spawn = ChildProcess.prototype.spawn
if (spawnSyncBinding) {
spawnSync = spawnSyncBinding.spawn
}
function unwrap () {
if (doSetup) {
rimraf.sync(workingDir)
}
ChildProcess.prototype.spawn = spawn
if (spawnSyncBinding) {
spawnSync = spawnSyncBinding.spawn
}
}
if (spawnSyncBinding) {
spawnSyncBinding.spawn = wrappedSpawnFunction(spawnSync, workingDir)
}
ChildProcess.prototype.spawn = wrappedSpawnFunction(spawn, workingDir)
return unwrap
}
function wrappedSpawnFunction (fn, workingDir) {
return wrappedSpawn
function wrappedSpawn (options) {
var pathEnv
var cmdi, c, re, match, exe
// handle case where node/iojs is exec'd
// this doesn't handle EVERYTHING, but just the most common
// case of doing `exec(process.execPath + ' file.js')
var file = path.basename(options.file, '.exe')
if (file === 'dash' ||
file === 'sh' ||
file === 'bash' ||
file === 'zsh') {
cmdi = options.args.indexOf('-c')
if (cmdi !== -1) {
c = options.args[cmdi + 1]
re = /^\s*((?:[^\= ]*\=[^\=\s]*\s*)*)([^\s]+|"[^"]+"|'[^']+')/
match = c.match(re)
if (match) {
exe = path.basename(match[2])
// Strip a trailing quote from the basename if it matches a
// leading quote.
var quote = match[2].charAt(0)
if ((quote === '"' || quote === '\'') && quote === exe.slice(-1)) {
exe = exe.slice(0, -1)
}
if (exe === 'iojs' ||
exe === 'node' ||
exe === cmdname) {
c = c.replace(re, '$1 ' + workingDir + '/node')
options.args[cmdi + 1] = c
} else if (exe === 'npm' && !isWindows) {
var npmPath = whichOrUndefined('npm')
if (npmPath) {
c = c.replace(re, '$1 ' + workingDir + '/node ' + npmPath)
options.args[cmdi + 1] = c
}
}
}
}
} else if (isWindows && (
file === path.basename(process.env.comspec || '') ||
file === 'cmd'
)) {
cmdi = options.args.indexOf('/c')
if (cmdi !== -1) {
options.args[cmdi + 1] = winRebase(options.args[cmdi + 1],
workingDir + '/node.cmd',
whichOrUndefined)
}
} else if (file === 'node' || file === 'iojs' || cmdname === file) {
// make sure it has a main script.
// otherwise, just let it through.
var a = 0
var hasMain = false
for (var a = 1; !hasMain && a < options.args.length; a++) {
switch (options.args[a]) {
case '-i':
case '--interactive':
case '--eval':
case '-e':
case '-pe':
hasMain = false
a = options.args.length
continue
case '-r':
case '--require':
a += 1
continue
default:
if (options.args[a].match(/^-/)) {
continue
} else {
hasMain = true
a = options.args.length
break
}
}
}
if (hasMain) {
options.file = workingDir + '/' + file
options.args[0] = workingDir + '/' + file
}
} else {
try {
var resolved = which.sync(options.file)
} catch (er) {}
if (resolved) {
var shebang = fs.readFileSync(resolved, 'utf8')
var match = shebang.match(/^#!([^\r\n]+)/)
if (match) {
var shebangbin = match[1].split(' ')[0]
var maybeNode = path.basename(shebangbin)
if (maybeNode === 'node' || maybeNode === 'iojs' || cmdname === maybeNode) {
file = options.file = shebangbin
options.args = [shebangbin, workingDir + '/' + maybeNode]
.concat(resolved)
.concat(match[1].split(' ').slice(1))
.concat(options.args.slice(1))
}
}
}
}
for (var i = 0; i < options.envPairs.length; i++) {
var ep = options.envPairs[i]
if (ep.match(pathRe)) {
pathEnv = ep.substr(5)
var k = ep.substr(0, 5)
options.envPairs[i] = k + workingDir + colon + pathEnv
}
}
if (!pathEnv) {
options.envPairs.push((isWindows ? 'Path=' : 'PATH=') + workingDir)
}
if (file === 'npm' && !isWindows) {
var npmPath = whichOrUndefined('npm')
if (npmPath) {
options.args[0] = npmPath
options.file = workingDir + '/node'
options.args.unshift(workingDir + '/node')
}
}
if (isWindows) fixWindowsBins(workingDir, options)
return fn.call(this, options)
}
}
function whichOrUndefined (executable) {
var path
try {
path = which.sync(executable)
} catch (er) {}
return path
}
// by default Windows will reference the full
// path to the node.exe or iojs.exe as the bin,
// we should instead point spawn() at our .cmd shim.
function fixWindowsBins (workingDir, options) {
var re = /.*\b(node|iojs)(\.exe)?$/
if (options.file.match(re)) {
options.file = process.execPath
var shim = workingDir + '/node'
options.args.splice(0, 1, options.file, workingDir + '/node')
}
}
function setup (argv, env) {
if (argv && typeof argv === 'object' && !env && !Array.isArray(argv)) {
env = argv
argv = []
}
if (!argv && !env) {
throw new Error('at least one of "argv" and "env" required')
}
if (argv) {
assert(Array.isArray(argv), 'argv must be array')
} else {
argv = []
}
if (env) {
assert(typeof env === 'object', 'env must be an object')
} else {
env = {}
}
// For stuff like --use_strict or --harmony, we need to inject
// the argument *before* the wrap-main.
var execArgv = []
for (var i = 0; i < argv.length; i++) {
if (argv[i].match(/^-/)) {
execArgv.push(argv[i])
if (argv[i] === '-r' || argv[i] === '--require') {
execArgv.push(argv[++i])
}
} else {
break
}
}
if (execArgv.length) {
if (execArgv.length === argv.length) {
argv.length = 0
} else {
argv = argv.slice(execArgv.length)
}
}
var settings = JSON.stringify({
module: __filename,
deps: {
foregroundChild: require.resolve('foreground-child'),
signalExit: require.resolve('signal-exit'),
},
argv: argv,
execArgv: execArgv,
env: env,
root: process.pid
}, null, 2) + '\n'
var workingDir = homedir + process.pid + '-' +
crypto.randomBytes(6).toString('hex')
signalExit(function () {
rimraf.sync(workingDir)
})
mkdirp.sync(workingDir)
workingDir = fs.realpathSync(workingDir)
if (isWindows) {
var cmdShim =
'@echo off\r\n' +
'SETLOCAL\r\n' +
'SET PATHEXT=%PATHEXT:;.JS;=;%\r\n' +
'"' + process.execPath + '"' + ' "%~dp0\\.\\node" %*\r\n'
fs.writeFileSync(workingDir + '/node.cmd', cmdShim)
fs.chmodSync(workingDir + '/node.cmd', '0755')
fs.writeFileSync(workingDir + '/iojs.cmd', cmdShim)
fs.chmodSync(workingDir + '/iojs.cmd', '0755')
}
fs.writeFileSync(workingDir + '/node', shim)
fs.chmodSync(workingDir + '/node', '0755')
fs.writeFileSync(workingDir + '/iojs', shim)
fs.chmodSync(workingDir + '/iojs', '0755')
if (cmdname !== 'iojs' && cmdname !== 'node') {
fs.writeFileSync(workingDir + '/' + cmdname, shim)
fs.chmodSync(workingDir + '/' + cmdname, '0755')
}
fs.writeFileSync(workingDir + '/settings.json', settings)
return workingDir
}
function runMain () {
process.argv.splice(1, 1)
process.argv[1] = path.resolve(process.argv[1])
Module.runMain()
}