/* 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'; const { Ci, Cu } = require("chrome"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Subprocess.jsm"); Cu.import("resource://gre/modules/Task.jsm"); const Runtime = require("sdk/system/runtime"); const Environment = require("sdk/system/environment").env; const DEFAULT_ENVIRONMENT = []; if (Runtime.OS == "Linux" && "DISPLAY" in Environment) { DEFAULT_ENVIRONMENT.push("DISPLAY=" + Environment.DISPLAY); } function awaitPromise(promise) { let value; let resolved = null; promise.then(val => { resolved = true; value = val; }, val => { resolved = false; value = val; }); while (resolved === null) Services.tm.mainThread.processNextEvent(true); if (resolved === true) return value; throw value; } let readAllData = Task.async(function* (pipe, read, callback) { let string; while (string = yield read(pipe)) callback(string); }); let write = (pipe, data) => { let buffer = new Uint8Array(Array.from(data, c => c.charCodeAt(0))); return pipe.write(data); }; var subprocess = { call: function(options) { var result; let procPromise = Task.spawn(function*() { let opts = {}; if (options.mergeStderr) { opts.stderr = "stdout" } else if (options.stderr) { opts.stderr = "pipe"; } if (options.command instanceof Ci.nsIFile) { opts.command = options.command.path; } else { opts.command = yield Subprocess.pathSearch(options.command); } if (options.workdir) { opts.workdir = options.workdir; } opts.arguments = options.arguments || []; // Set up environment let envVars = options.environment || DEFAULT_ENVIRONMENT; if (envVars.length) { let environment = {}; for (let val of envVars) { let idx = val.indexOf("="); if (idx >= 0) environment[val.slice(0, idx)] = val.slice(idx + 1); } opts.environment = environment; } let proc = yield Subprocess.call(opts); Object.defineProperty(result, "pid", { value: proc.pid, enumerable: true, configurable: true, }); let promises = []; // Set up IO handlers. let read = pipe => pipe.readString(); if (options.charset === null) { read = pipe => { return pipe.read().then(buffer => { return String.fromCharCode(...buffer); }); }; } if (options.stdout) promises.push(readAllData(proc.stdout, read, options.stdout)); if (options.stderr && proc.stderr) promises.push(readAllData(proc.stderr, read, options.stderr)); // Process stdin if (typeof options.stdin === "string") { write(proc.stdin, options.stdin); proc.stdin.close(); } // Handle process completion if (options.done) Promise.all(promises) .then(() => proc.wait()) .then(options.done); return proc; }); procPromise.catch(e => { if (options.done) options.done({exitCode: -1}, e); else Cu.reportError(e instanceof Error ? e : e.message || e); }); if (typeof options.stdin === "function") { // Unfortunately, some callers (child_process.js) depend on this // being called synchronously. options.stdin({ write(val) { procPromise.then(proc => { write(proc.stdin, val); }); }, close() { procPromise.then(proc => { proc.stdin.close(); }); }, }); } result = { get pid() { return awaitPromise(procPromise.then(proc => { return proc.pid; })); }, wait() { return awaitPromise(procPromise.then(proc => { return proc.wait().then(({exitCode}) => exitCode); })); }, kill(hard = false) { procPromise.then(proc => { proc.kill(hard ? 0 : undefined); }); }, }; return result; }, }; module.exports = subprocess;