diff options
Diffstat (limited to 'addon-sdk/source/lib/sdk/system/child_process.js')
-rw-r--r-- | addon-sdk/source/lib/sdk/system/child_process.js | 332 |
1 files changed, 0 insertions, 332 deletions
diff --git a/addon-sdk/source/lib/sdk/system/child_process.js b/addon-sdk/source/lib/sdk/system/child_process.js deleted file mode 100644 index 8ea1f4f80..000000000 --- a/addon-sdk/source/lib/sdk/system/child_process.js +++ /dev/null @@ -1,332 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -module.metadata = { - 'stability': 'experimental' -}; - -var { Ci } = require('chrome'); -var subprocess = require('./child_process/subprocess'); -var { EventTarget } = require('../event/target'); -var { Stream } = require('../io/stream'); -var { on, emit, off } = require('../event/core'); -var { Class } = require('../core/heritage'); -var { platform } = require('../system'); -var { isFunction, isArray } = require('../lang/type'); -var { delay } = require('../lang/functional'); -var { merge } = require('../util/object'); -var { setTimeout, clearTimeout } = require('../timers'); -var isWindows = platform.indexOf('win') === 0; - -var processes = new WeakMap(); - - -/** - * The `Child` class wraps a subprocess command, exposes - * the stdio streams, and methods to manipulate the subprocess - */ -var Child = Class({ - implements: [EventTarget], - initialize: function initialize (options) { - let child = this; - let proc; - - this.killed = false; - this.exitCode = undefined; - this.signalCode = undefined; - - this.stdin = Stream(); - this.stdout = Stream(); - this.stderr = Stream(); - - try { - proc = subprocess.call({ - command: options.file, - arguments: options.cmdArgs, - environment: serializeEnv(options.env), - workdir: options.cwd, - charset: options.encoding, - stdout: data => emit(child.stdout, 'data', data), - stderr: data => emit(child.stderr, 'data', data), - stdin: stream => { - child.stdin.on('data', pumpStdin); - child.stdin.on('end', function closeStdin () { - child.stdin.off('data', pumpStdin); - child.stdin.off('end', closeStdin); - stream.close(); - }); - function pumpStdin (data) { - stream.write(data); - } - }, - done: function (result, error) { - if (error) - return handleError(error); - - // Only emit if child is not killed; otherwise, - // the `kill` method will handle this - if (!child.killed) { - child.exitCode = result.exitCode; - child.signalCode = null; - - // If process exits with < 0, there was an error - if (child.exitCode < 0) { - handleError(new Error('Process exited with exit code ' + child.exitCode)); - } - else { - // Also do 'exit' event as there's not much of - // a difference in our implementation as we're not using - // node streams - emit(child, 'exit', child.exitCode, child.signalCode); - } - - // Emit 'close' event with exit code and signal, - // which is `null`, as it was not a killed process - emit(child, 'close', child.exitCode, child.signalCode); - } - } - }); - processes.set(child, proc); - } catch (e) { - // Delay the error handling so an error handler can be set - // during the same tick that the Child was created - delay(() => handleError(e)); - } - - // `handleError` is called when process could not even - // be spawned - function handleError (e) { - // If error is an nsIObject, make a fresh error object - // so we're not exposing nsIObjects, and we can modify it - // with additional process information, like node - let error = e; - if (e instanceof Ci.nsISupports) { - error = new Error(e.message, e.filename, e.lineNumber); - } - emit(child, 'error', error); - child.exitCode = -1; - child.signalCode = null; - emit(child, 'close', child.exitCode, child.signalCode); - } - }, - kill: function kill (signal) { - let proc = processes.get(this); - proc.kill(signal); - this.killed = true; - this.exitCode = null; - this.signalCode = signal; - emit(this, 'exit', this.exitCode, this.signalCode); - emit(this, 'close', this.exitCode, this.signalCode); - }, - get pid() { return processes.get(this, {}).pid || -1; } -}); - -function spawn (file, ...args) { - let cmdArgs = []; - // Default options - let options = { - cwd: null, - env: null, - encoding: 'UTF-8' - }; - - if (args[1]) { - merge(options, args[1]); - cmdArgs = args[0]; - } - else { - if (isArray(args[0])) - cmdArgs = args[0]; - else - merge(options, args[0]); - } - - if ('gid' in options) - console.warn('`gid` option is not yet supported for `child_process`'); - if ('uid' in options) - console.warn('`uid` option is not yet supported for `child_process`'); - if ('detached' in options) - console.warn('`detached` option is not yet supported for `child_process`'); - - options.file = file; - options.cmdArgs = cmdArgs; - - return Child(options); -} - -exports.spawn = spawn; - -/** - * exec(command, options, callback) - */ -function exec (cmd, ...args) { - let file, cmdArgs, callback, options = {}; - - if (isFunction(args[0])) - callback = args[0]; - else { - merge(options, args[0]); - callback = args[1]; - } - - if (isWindows) { - file = 'C:\\Windows\\System32\\cmd.exe'; - cmdArgs = ['/S/C', cmd || '']; - } - else { - file = '/bin/sh'; - cmdArgs = ['-c', cmd]; - } - - // Undocumented option from node being able to specify shell - if (options && options.shell) - file = options.shell; - - return execFile(file, cmdArgs, options, callback); -} -exports.exec = exec; -/** - * execFile (file, args, options, callback) - */ -function execFile (file, ...args) { - let cmdArgs = [], callback; - // Default options - let options = { - cwd: null, - env: null, - encoding: 'utf8', - timeout: 0, - maxBuffer: 204800, //200 KB (200*1024 bytes) - killSignal: 'SIGTERM' - }; - - if (isFunction(args[args.length - 1])) - callback = args[args.length - 1]; - - if (isArray(args[0])) { - cmdArgs = args[0]; - merge(options, args[1]); - } else if (!isFunction(args[0])) - merge(options, args[0]); - - let child = spawn(file, cmdArgs, options); - let exited = false; - let stdout = ''; - let stderr = ''; - let error = null; - let timeoutId = null; - - child.stdout.setEncoding(options.encoding); - child.stderr.setEncoding(options.encoding); - - on(child.stdout, 'data', pumpStdout); - on(child.stderr, 'data', pumpStderr); - on(child, 'close', exitHandler); - on(child, 'error', errorHandler); - - if (options.timeout > 0) { - setTimeout(() => { - kill(); - timeoutId = null; - }, options.timeout); - } - - function exitHandler (code, signal) { - - // Return if exitHandler called previously, occurs - // when multiple maxBuffer errors thrown and attempt to kill multiple - // times - if (exited) return; - exited = true; - - if (!isFunction(callback)) return; - - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = null; - } - - if (!error && (code !== 0 || signal !== null)) - error = createProcessError(new Error('Command failed: ' + stderr), { - code: code, - signal: signal, - killed: !!child.killed - }); - - callback(error, stdout, stderr); - - off(child.stdout, 'data', pumpStdout); - off(child.stderr, 'data', pumpStderr); - off(child, 'close', exitHandler); - off(child, 'error', errorHandler); - } - - function errorHandler (e) { - error = e; - exitHandler(); - } - - function kill () { - try { - child.kill(options.killSignal); - } catch (e) { - // In the scenario where the kill signal happens when - // the process is already closing, just abort the kill fail - if (/library is not open/.test(e)) - return; - error = e; - exitHandler(-1, options.killSignal); - } - } - - function pumpStdout (data) { - stdout += data; - if (stdout.length > options.maxBuffer) { - error = new Error('stdout maxBuffer exceeded'); - kill(); - } - } - - function pumpStderr (data) { - stderr += data; - if (stderr.length > options.maxBuffer) { - error = new Error('stderr maxBuffer exceeded'); - kill(); - } - } - - return child; -} -exports.execFile = execFile; - -exports.fork = function fork () { - throw new Error("child_process#fork is not currently supported"); -}; - -function serializeEnv (obj) { - return Object.keys(obj || {}).map(prop => prop + '=' + obj[prop]); -} - -function createProcessError (err, options = {}) { - // If code and signal look OK, this was probably a failure - // attempting to spawn the process (like ENOENT in node) -- use - // the code from the error message - if (!options.code && !options.signal) { - let match = err.message.match(/(NS_ERROR_\w*)/); - if (match && match.length > 1) - err.code = match[1]; - else { - // If no good error message found, use the passed in exit code; - // this occurs when killing a process that's already closing, - // where we want both a valid exit code (0) and the error - err.code = options.code != null ? options.code : null; - } - } - else - err.code = options.code != null ? options.code : null; - err.signal = options.signal || null; - err.killed = options.killed || false; - return err; -} |