addon-sdk/source/lib/sdk/system/child_process/subprocess.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/system/child_process/subprocess.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1687 @@
     1.4 +// -*- coding: utf-8 -*-
     1.5 +// vim: et:ts=4:sw=4:sts=4:ft=javascript
     1.6 +/* ***** BEGIN LICENSE BLOCK *****
     1.7 + * Version: MPL 1.1/GPL 2.0/LGPL 2.1
     1.8 + *
     1.9 + * The contents of this file are subject to the Mozilla Public
    1.10 + * License Version 1.1 (the "MPL"); you may not use this file
    1.11 + * except in compliance with the MPL. You may obtain a copy of
    1.12 + * the MPL at http://www.mozilla.org/MPL/
    1.13 + *
    1.14 + * Software distributed under the MPL is distributed on an "AS
    1.15 + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
    1.16 + * implied. See the MPL for the specific language governing
    1.17 + * rights and limitations under the MPL.
    1.18 + *
    1.19 + * The Original Code is subprocess.jsm.
    1.20 + *
    1.21 + * The Initial Developer of this code is Jan Gerber.
    1.22 + * Portions created by Jan Gerber <j@mailb.org>
    1.23 + * are Copyright (C) 2011 Jan Gerber.
    1.24 + * All Rights Reserved.
    1.25 + *
    1.26 + * Contributor(s):
    1.27 + * Patrick Brunschwig <patrick@enigmail.net>
    1.28 + *
    1.29 + * Alternatively, the contents of this file may be used under the terms of
    1.30 + * either the GNU General Public License Version 2 or later (the "GPL"), or
    1.31 + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    1.32 + * in which case the provisions of the GPL or the LGPL are applicable instead
    1.33 + * of those above. If you wish to allow use of your version of this file only
    1.34 + * under the terms of either the GPL or the LGPL, and not to allow others to
    1.35 + * use your version of this file under the terms of the MPL, indicate your
    1.36 + * decision by deleting the provisions above and replace them with the notice
    1.37 + * and other provisions required by the GPL or the LGPL. If you do not delete
    1.38 + * the provisions above, a recipient may use your version of this file under
    1.39 + * the terms of any one of the MPL, the GPL or the LGPL.
    1.40 + * ***** END LICENSE BLOCK ***** */
    1.41 +
    1.42 +/*
    1.43 + * This object allows to start a process, and read/write data to/from it
    1.44 + * using stdin/stdout/stderr streams.
    1.45 + * Usage example:
    1.46 + *
    1.47 + *  var p = subprocess.call({
    1.48 + *    command:     '/bin/foo',
    1.49 + *    arguments:   ['-v', 'foo'],
    1.50 + *    environment: [ "XYZ=abc", "MYVAR=def" ],
    1.51 + *    charset: 'UTF-8',
    1.52 + *    workdir: '/home/foo',
    1.53 + *    //stdin: "some value to write to stdin\nfoobar",
    1.54 + *    stdin: function(stdin) {
    1.55 + *      stdin.write("some value to write to stdin\nfoobar");
    1.56 + *      stdin.close();
    1.57 + *    },
    1.58 + *    stdout: function(data) {
    1.59 + *      dump("got data on stdout:" + data + "\n");
    1.60 + *    },
    1.61 + *    stderr: function(data) {
    1.62 + *      dump("got data on stderr:" + data + "\n");
    1.63 + *    },
    1.64 + *    done: function(result) {
    1.65 + *      dump("process terminated with " + result.exitCode + "\n");
    1.66 + *    },
    1.67 + *    mergeStderr: false
    1.68 + *  });
    1.69 + *  p.wait(); // wait for the subprocess to terminate
    1.70 + *            // this will block the main thread,
    1.71 + *            // only do if you can wait that long
    1.72 + *
    1.73 + *
    1.74 + * Description of parameters:
    1.75 + * --------------------------
    1.76 + * Apart from <command>, all arguments are optional.
    1.77 + *
    1.78 + * command:     either a |nsIFile| object pointing to an executable file or a
    1.79 + *              String containing the platform-dependent path to an executable
    1.80 + *              file.
    1.81 + *
    1.82 + * arguments:   optional string array containing the arguments to the command.
    1.83 + *
    1.84 + * environment: optional string array containing environment variables to pass
    1.85 + *              to the command. The array elements must have the form
    1.86 + *              "VAR=data". Please note that if environment is defined, it
    1.87 + *              replaces any existing environment variables for the subprocess.
    1.88 + *
    1.89 + * charset:     Output is decoded with given charset and a string is returned.
    1.90 + *              If charset is undefined, "UTF-8" is used as default.
    1.91 + *              To get binary data, set this to null and the returned string
    1.92 + *              is not decoded in any way.
    1.93 + *
    1.94 + * workdir:     optional; String containing the platform-dependent path to a
    1.95 + *              directory to become the current working directory of the subprocess.
    1.96 + *
    1.97 + * stdin:       optional input data for the process to be passed on standard
    1.98 + *              input. stdin can either be a string or a function.
    1.99 + *              A |string| gets written to stdin and stdin gets closed;
   1.100 + *              A |function| gets passed an object with write and close function.
   1.101 + *              Please note that the write() function will return almost immediately;
   1.102 + *              data is always written asynchronously on a separate thread.
   1.103 + *
   1.104 + * stdout:      an optional function that can receive output data from the
   1.105 + *              process. The stdout-function is called asynchronously; it can be
   1.106 + *              called mutliple times during the execution of a process.
   1.107 + *              At a minimum at each occurance of \n or \r.
   1.108 + *              Please note that null-characters might need to be escaped
   1.109 + *              with something like 'data.replace(/\0/g, "\\0");'.
   1.110 + *
   1.111 + * stderr:      an optional function that can receive stderr data from the
   1.112 + *              process. The stderr-function is called asynchronously; it can be
   1.113 + *              called mutliple times during the execution of a process. Please
   1.114 + *              note that null-characters might need to be escaped with
   1.115 + *              something like 'data.replace(/\0/g, "\\0");'.
   1.116 + *              (on windows it only gets called once right now)
   1.117 + *
   1.118 + * done:        optional function that is called when the process has terminated.
   1.119 + *              The exit code from the process available via result.exitCode. If
   1.120 + *              stdout is not defined, then the output from stdout is available
   1.121 + *              via result.stdout. stderr data is in result.stderr
   1.122 + *
   1.123 + * mergeStderr: optional boolean value. If true, stderr is merged with stdout;
   1.124 + *              no data will be provided to stderr.
   1.125 + *
   1.126 + *
   1.127 + * Description of object returned by subprocess.call(...)
   1.128 + * ------------------------------------------------------
   1.129 + * The object returned by subprocess.call offers a few methods that can be
   1.130 + * executed:
   1.131 + *
   1.132 + * wait():         waits for the subprocess to terminate. It is not required to use
   1.133 + *                 wait; done will be called in any case when the subprocess terminated.
   1.134 + *
   1.135 + * kill(hardKill): kill the subprocess. Any open pipes will be closed and
   1.136 + *                 done will be called.
   1.137 + *                 hardKill [ignored on Windows]:
   1.138 + *                  - false: signal the process terminate (SIGTERM)
   1.139 + *                  - true:  kill the process (SIGKILL)
   1.140 + *
   1.141 + *
   1.142 + * Other methods in subprocess
   1.143 + * ---------------------------
   1.144 + *
   1.145 + * registerDebugHandler(functionRef):   register a handler that is called to get
   1.146 + *                                      debugging information
   1.147 + * registerLogHandler(functionRef):     register a handler that is called to get error
   1.148 + *                                      messages
   1.149 + *
   1.150 + * example:
   1.151 + *    subprocess.registerLogHandler( function(s) { dump(s); } );
   1.152 + */
   1.153 +
   1.154 +'use strict';
   1.155 +
   1.156 +const { Cc, Ci, Cu, ChromeWorker } = require("chrome");
   1.157 +
   1.158 +Cu.import("resource://gre/modules/ctypes.jsm");
   1.159 +
   1.160 +const NS_LOCAL_FILE = "@mozilla.org/file/local;1";
   1.161 +
   1.162 +const Runtime = require("sdk/system/runtime");
   1.163 +const Environment = require("sdk/system/environment").env;
   1.164 +const DEFAULT_ENVIRONMENT = [];
   1.165 +if (Runtime.OS == "Linux" && "DISPLAY" in Environment) {
   1.166 +  DEFAULT_ENVIRONMENT.push("DISPLAY=" + Environment.DISPLAY);
   1.167 +}
   1.168 +
   1.169 +/*
   1.170 +Fake require statements to ensure worker scripts are packaged:
   1.171 +require("./subprocess_worker_win.js");
   1.172 +require("./subprocess_worker_unix.js");
   1.173 +*/
   1.174 +const URL_PREFIX = module.uri.replace(/subprocess\.js/, "");
   1.175 +const WORKER_URL_WIN = URL_PREFIX + "subprocess_worker_win.js";
   1.176 +const WORKER_URL_UNIX = URL_PREFIX + "subprocess_worker_unix.js";
   1.177 +
   1.178 +//Windows API definitions
   1.179 +if (ctypes.size_t.size == 8) {
   1.180 +    var WinABI = ctypes.default_abi;
   1.181 +} else {
   1.182 +    var WinABI = ctypes.winapi_abi;
   1.183 +}
   1.184 +const WORD = ctypes.uint16_t;
   1.185 +const DWORD = ctypes.uint32_t;
   1.186 +const LPDWORD = DWORD.ptr;
   1.187 +
   1.188 +const UINT = ctypes.unsigned_int;
   1.189 +const BOOL = ctypes.bool;
   1.190 +const HANDLE = ctypes.size_t;
   1.191 +const HWND = HANDLE;
   1.192 +const HMODULE = HANDLE;
   1.193 +const WPARAM = ctypes.size_t;
   1.194 +const LPARAM = ctypes.size_t;
   1.195 +const LRESULT = ctypes.size_t;
   1.196 +const ULONG_PTR = ctypes.uintptr_t;
   1.197 +const PVOID = ctypes.voidptr_t;
   1.198 +const LPVOID = PVOID;
   1.199 +const LPCTSTR = ctypes.jschar.ptr;
   1.200 +const LPCWSTR = ctypes.jschar.ptr;
   1.201 +const LPTSTR = ctypes.jschar.ptr;
   1.202 +const LPSTR = ctypes.char.ptr;
   1.203 +const LPCSTR = ctypes.char.ptr;
   1.204 +const LPBYTE = ctypes.char.ptr;
   1.205 +
   1.206 +const CREATE_NEW_CONSOLE = 0x00000010;
   1.207 +const CREATE_NO_WINDOW = 0x08000000;
   1.208 +const CREATE_UNICODE_ENVIRONMENT = 0x00000400;
   1.209 +const STARTF_USESHOWWINDOW = 0x00000001;
   1.210 +const STARTF_USESTDHANDLES = 0x00000100;
   1.211 +const SW_HIDE = 0;
   1.212 +const DUPLICATE_SAME_ACCESS = 0x00000002;
   1.213 +const STILL_ACTIVE = 259;
   1.214 +const INFINITE = DWORD(0xFFFFFFFF);
   1.215 +const WAIT_TIMEOUT = 0x00000102;
   1.216 +
   1.217 +/*
   1.218 +typedef struct _SECURITY_ATTRIBUTES {
   1.219 + DWORD  nLength;
   1.220 + LPVOID lpSecurityDescriptor;
   1.221 + BOOL   bInheritHandle;
   1.222 +} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
   1.223 +*/
   1.224 +const SECURITY_ATTRIBUTES = new ctypes.StructType("SECURITY_ATTRIBUTES", [
   1.225 +    {"nLength": DWORD},
   1.226 +    {"lpSecurityDescriptor": LPVOID},
   1.227 +    {"bInheritHandle": BOOL},
   1.228 +]);
   1.229 +
   1.230 +/*
   1.231 +typedef struct _STARTUPINFO {
   1.232 +  DWORD  cb;
   1.233 +  LPTSTR lpReserved;
   1.234 +  LPTSTR lpDesktop;
   1.235 +  LPTSTR lpTitle;
   1.236 +  DWORD  dwX;
   1.237 +  DWORD  dwY;
   1.238 +  DWORD  dwXSize;
   1.239 +  DWORD  dwYSize;
   1.240 +  DWORD  dwXCountChars;
   1.241 +  DWORD  dwYCountChars;
   1.242 +  DWORD  dwFillAttribute;
   1.243 +  DWORD  dwFlags;
   1.244 +  WORD   wShowWindow;
   1.245 +  WORD   cbReserved2;
   1.246 +  LPBYTE lpReserved2;
   1.247 +  HANDLE hStdInput;
   1.248 +  HANDLE hStdOutput;
   1.249 +  HANDLE hStdError;
   1.250 +} STARTUPINFO, *LPSTARTUPINFO;
   1.251 +*/
   1.252 +const STARTUPINFO = new ctypes.StructType("STARTUPINFO", [
   1.253 +    {"cb": DWORD},
   1.254 +    {"lpReserved": LPTSTR},
   1.255 +    {"lpDesktop": LPTSTR},
   1.256 +    {"lpTitle": LPTSTR},
   1.257 +    {"dwX": DWORD},
   1.258 +    {"dwY": DWORD},
   1.259 +    {"dwXSize": DWORD},
   1.260 +    {"dwYSize": DWORD},
   1.261 +    {"dwXCountChars": DWORD},
   1.262 +    {"dwYCountChars": DWORD},
   1.263 +    {"dwFillAttribute": DWORD},
   1.264 +    {"dwFlags": DWORD},
   1.265 +    {"wShowWindow": WORD},
   1.266 +    {"cbReserved2": WORD},
   1.267 +    {"lpReserved2": LPBYTE},
   1.268 +    {"hStdInput": HANDLE},
   1.269 +    {"hStdOutput": HANDLE},
   1.270 +    {"hStdError": HANDLE},
   1.271 +]);
   1.272 +
   1.273 +/*
   1.274 +typedef struct _PROCESS_INFORMATION {
   1.275 +  HANDLE hProcess;
   1.276 +  HANDLE hThread;
   1.277 +  DWORD  dwProcessId;
   1.278 +  DWORD  dwThreadId;
   1.279 +} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
   1.280 +*/
   1.281 +const PROCESS_INFORMATION = new ctypes.StructType("PROCESS_INFORMATION", [
   1.282 +    {"hProcess": HANDLE},
   1.283 +    {"hThread": HANDLE},
   1.284 +    {"dwProcessId": DWORD},
   1.285 +    {"dwThreadId": DWORD},
   1.286 +]);
   1.287 +
   1.288 +/*
   1.289 +typedef struct _OVERLAPPED {
   1.290 +  ULONG_PTR Internal;
   1.291 +  ULONG_PTR InternalHigh;
   1.292 +  union {
   1.293 +    struct {
   1.294 +      DWORD Offset;
   1.295 +      DWORD OffsetHigh;
   1.296 +    };
   1.297 +    PVOID  Pointer;
   1.298 +  };
   1.299 +  HANDLE    hEvent;
   1.300 +} OVERLAPPED, *LPOVERLAPPED;
   1.301 +*/
   1.302 +const OVERLAPPED = new ctypes.StructType("OVERLAPPED");
   1.303 +
   1.304 +//UNIX definitions
   1.305 +const pid_t = ctypes.int32_t;
   1.306 +const WNOHANG = 1;
   1.307 +const F_GETFD = 1;
   1.308 +const F_SETFL = 4;
   1.309 +
   1.310 +const LIBNAME       = 0;
   1.311 +const O_NONBLOCK    = 1;
   1.312 +const RLIM_T        = 2;
   1.313 +const RLIMIT_NOFILE = 3;
   1.314 +
   1.315 +function getPlatformValue(valueType) {
   1.316 +
   1.317 +    if (! gXulRuntime)
   1.318 +        gXulRuntime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
   1.319 +
   1.320 +    const platformDefaults = {
   1.321 +        // Windows API:
   1.322 +        'winnt':   [ 'kernel32.dll' ],
   1.323 +
   1.324 +        // Unix API:
   1.325 +        //            library name   O_NONBLOCK RLIM_T                RLIMIT_NOFILE
   1.326 +        'darwin':  [ 'libc.dylib',   0x04     , ctypes.uint64_t     , 8 ],
   1.327 +        'linux':   [ 'libc.so.6',    2024     , ctypes.unsigned_long, 7 ],
   1.328 +        'freebsd': [ 'libc.so.7',    0x04     , ctypes.int64_t      , 8 ],
   1.329 +        'openbsd': [ 'libc.so.61.0', 0x04     , ctypes.int64_t      , 8 ],
   1.330 +        'sunos':   [ 'libc.so',      0x80     , ctypes.unsigned_long, 5 ]
   1.331 +    }
   1.332 +
   1.333 +    return platformDefaults[gXulRuntime.OS.toLowerCase()][valueType];
   1.334 +}
   1.335 +
   1.336 +
   1.337 +var gDebugFunc = null,
   1.338 +    gLogFunc = null,
   1.339 +    gXulRuntime = null;
   1.340 +
   1.341 +function LogError(s) {
   1.342 +    if (gLogFunc)
   1.343 +        gLogFunc(s);
   1.344 +    else
   1.345 +        dump(s);
   1.346 +}
   1.347 +
   1.348 +function debugLog(s) {
   1.349 +    if (gDebugFunc)
   1.350 +        gDebugFunc(s);
   1.351 +}
   1.352 +
   1.353 +function setTimeout(callback, timeout) {
   1.354 +    var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   1.355 +    timer.initWithCallback(callback, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
   1.356 +};
   1.357 +
   1.358 +function getBytes(data) {
   1.359 +  var string = '';
   1.360 +  data.forEach(function(x) { string += String.fromCharCode(x < 0 ? x + 256 : x) });
   1.361 +  return string;
   1.362 +}
   1.363 +
   1.364 +function readString(data, length, charset) {
   1.365 +    var string = '', bytes = [];
   1.366 +    for(var i = 0;i < length; i++) {
   1.367 +        if(data[i] == 0 && charset !== null) // stop on NULL character for non-binary data
   1.368 +           break
   1.369 +        bytes.push(data[i]);
   1.370 +    }
   1.371 +    if (!bytes || bytes.length == 0)
   1.372 +        return string;
   1.373 +    if(charset === null) {
   1.374 +        return bytes;
   1.375 +    }
   1.376 +    return convertBytes(bytes, charset);
   1.377 +}
   1.378 +
   1.379 +function convertBytes(bytes, charset) {
   1.380 +    var string = '';
   1.381 +    charset = charset || 'UTF-8';
   1.382 +    var unicodeConv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
   1.383 +                        .getService(Ci.nsIScriptableUnicodeConverter);
   1.384 +    try {
   1.385 +        unicodeConv.charset = charset;
   1.386 +        string = unicodeConv.convertFromByteArray(bytes, bytes.length);
   1.387 +    } catch (ex) {
   1.388 +        LogError("String conversion failed: "+ex.toString()+"\n")
   1.389 +        string = '';
   1.390 +    }
   1.391 +    string += unicodeConv.Finish();
   1.392 +    return string;
   1.393 +}
   1.394 +
   1.395 +
   1.396 +// temporary solution for removal of nsILocalFile
   1.397 +function getLocalFileApi() {
   1.398 +  if ("nsILocalFile" in Ci) {
   1.399 +    return Ci.nsILocalFile;
   1.400 +  }
   1.401 +  else
   1.402 +    return Ci.nsIFile;
   1.403 +}
   1.404 +
   1.405 +function getCommandStr(command) {
   1.406 +    let commandStr = null;
   1.407 +    if (typeof(command) == "string") {
   1.408 +        let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
   1.409 +        file.initWithPath(command);
   1.410 +        if (! (file.isExecutable() && file.isFile()))
   1.411 +            throw("File '"+command+"' is not an executable file");
   1.412 +        commandStr = command;
   1.413 +    }
   1.414 +    else {
   1.415 +        if (! (command.isExecutable() && command.isFile()))
   1.416 +            throw("File '"+command.path+"' is not an executable file");
   1.417 +        commandStr = command.path;
   1.418 +    }
   1.419 +
   1.420 +    return commandStr;
   1.421 +}
   1.422 +
   1.423 +function getWorkDir(workdir) {
   1.424 +    let workdirStr = null;
   1.425 +    if (typeof(workdir) == "string") {
   1.426 +        let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
   1.427 +        file.initWithPath(workdir);
   1.428 +        if (! (file.isDirectory()))
   1.429 +            throw("Directory '"+workdir+"' does not exist");
   1.430 +        workdirStr = workdir;
   1.431 +    }
   1.432 +    else if (workdir) {
   1.433 +        if (! workdir.isDirectory())
   1.434 +            throw("Directory '"+workdir.path+"' does not exist");
   1.435 +        workdirStr = workdir.path;
   1.436 +    }
   1.437 +    return workdirStr;
   1.438 +}
   1.439 +
   1.440 +
   1.441 +var subprocess = {
   1.442 +    call: function(options) {
   1.443 +        options.mergeStderr = options.mergeStderr || false;
   1.444 +        options.workdir = options.workdir ||  null;
   1.445 +        options.environment = options.environment || DEFAULT_ENVIRONMENT;
   1.446 +        if (options.arguments) {
   1.447 +            var args = options.arguments;
   1.448 +            options.arguments = [];
   1.449 +            args.forEach(function(argument) {
   1.450 +                options.arguments.push(argument);
   1.451 +            });
   1.452 +        } else {
   1.453 +            options.arguments = [];
   1.454 +        }
   1.455 +
   1.456 +        options.libc = getPlatformValue(LIBNAME);
   1.457 +
   1.458 +        if (gXulRuntime.OS.substring(0, 3) == "WIN") {
   1.459 +            return subprocess_win32(options);
   1.460 +        } else {
   1.461 +            return subprocess_unix(options);
   1.462 +        }
   1.463 +
   1.464 +    },
   1.465 +    registerDebugHandler: function(func) {
   1.466 +        gDebugFunc = func;
   1.467 +    },
   1.468 +    registerLogHandler: function(func) {
   1.469 +        gLogFunc = func;
   1.470 +    }
   1.471 +};
   1.472 +
   1.473 +
   1.474 +
   1.475 +function subprocess_win32(options) {
   1.476 +    var kernel32dll = ctypes.open(options.libc),
   1.477 +        hChildProcess,
   1.478 +        active = true,
   1.479 +        done = false,
   1.480 +        exitCode = -1,
   1.481 +        child = {},
   1.482 +        stdinWorker = null,
   1.483 +        stdoutWorker = null,
   1.484 +        stderrWorker = null,
   1.485 +        pendingWriteCount = 0,
   1.486 +        readers = options.mergeStderr ? 1 : 2,
   1.487 +        stdinOpenState = 2,
   1.488 +        error = '',
   1.489 +        output = '';
   1.490 +
   1.491 +    // stdin pipe states
   1.492 +    const OPEN = 2;
   1.493 +    const CLOSEABLE = 1;
   1.494 +    const CLOSED = 0;
   1.495 +
   1.496 +    //api declarations
   1.497 +    /*
   1.498 +    BOOL WINAPI CloseHandle(
   1.499 +      __in  HANDLE hObject
   1.500 +    );
   1.501 +    */
   1.502 +    var CloseHandle = kernel32dll.declare("CloseHandle",
   1.503 +                                            WinABI,
   1.504 +                                            BOOL,
   1.505 +                                            HANDLE
   1.506 +    );
   1.507 +
   1.508 +    /*
   1.509 +    BOOL WINAPI CreateProcess(
   1.510 +      __in_opt     LPCTSTR lpApplicationName,
   1.511 +      __inout_opt  LPTSTR lpCommandLine,
   1.512 +      __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
   1.513 +      __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
   1.514 +      __in         BOOL bInheritHandles,
   1.515 +      __in         DWORD dwCreationFlags,
   1.516 +      __in_opt     LPVOID lpEnvironment,
   1.517 +      __in_opt     LPCTSTR lpCurrentDirectory,
   1.518 +      __in         LPSTARTUPINFO lpStartupInfo,
   1.519 +      __out        LPPROCESS_INFORMATION lpProcessInformation
   1.520 +    );
   1.521 +     */
   1.522 +    var CreateProcessW = kernel32dll.declare("CreateProcessW",
   1.523 +                                            WinABI,
   1.524 +                                            BOOL,
   1.525 +                                            LPCTSTR,
   1.526 +                                            LPTSTR,
   1.527 +                                            SECURITY_ATTRIBUTES.ptr,
   1.528 +                                            SECURITY_ATTRIBUTES.ptr,
   1.529 +                                            BOOL,
   1.530 +                                            DWORD,
   1.531 +                                            LPVOID,
   1.532 +                                            LPCTSTR,
   1.533 +                                            STARTUPINFO.ptr,
   1.534 +                                            PROCESS_INFORMATION.ptr
   1.535 +                                         );
   1.536 +
   1.537 +//     /*
   1.538 +//     BOOL WINAPI ReadFile(
   1.539 +//       __in         HANDLE hFile,
   1.540 +//       __out        LPVOID ReadFileBuffer,
   1.541 +//       __in         DWORD nNumberOfBytesToRead,
   1.542 +//       __out_opt    LPDWORD lpNumberOfBytesRead,
   1.543 +//       __inout_opt  LPOVERLAPPED lpOverlapped
   1.544 +//     );
   1.545 +//     */
   1.546 +//     var ReadFileBufferSize = 1024,
   1.547 +//         ReadFileBuffer = ctypes.char.array(ReadFileBufferSize),
   1.548 +//         ReadFile = kernel32dll.declare("ReadFile",
   1.549 +//                                         WinABI,
   1.550 +//                                         BOOL,
   1.551 +//                                         HANDLE,
   1.552 +//                                         ReadFileBuffer,
   1.553 +//                                         DWORD,
   1.554 +//                                         LPDWORD,
   1.555 +//                                         OVERLAPPED.ptr
   1.556 +//     );
   1.557 +//
   1.558 +//     /*
   1.559 +//     BOOL WINAPI PeekNamedPipe(
   1.560 +//       __in       HANDLE hNamedPipe,
   1.561 +//       __out_opt  LPVOID lpBuffer,
   1.562 +//       __in       DWORD nBufferSize,
   1.563 +//       __out_opt  LPDWORD lpBytesRead,
   1.564 +//       __out_opt  LPDWORD lpTotalBytesAvail,
   1.565 +//       __out_opt  LPDWORD lpBytesLeftThisMessage
   1.566 +//     );
   1.567 +//     */
   1.568 +//     var PeekNamedPipe = kernel32dll.declare("PeekNamedPipe",
   1.569 +//                                         WinABI,
   1.570 +//                                         BOOL,
   1.571 +//                                         HANDLE,
   1.572 +//                                         ReadFileBuffer,
   1.573 +//                                         DWORD,
   1.574 +//                                         LPDWORD,
   1.575 +//                                         LPDWORD,
   1.576 +//                                         LPDWORD
   1.577 +//     );
   1.578 +//
   1.579 +//     /*
   1.580 +//     BOOL WINAPI WriteFile(
   1.581 +//       __in         HANDLE hFile,
   1.582 +//       __in         LPCVOID lpBuffer,
   1.583 +//       __in         DWORD nNumberOfBytesToWrite,
   1.584 +//       __out_opt    LPDWORD lpNumberOfBytesWritten,
   1.585 +//       __inout_opt  LPOVERLAPPED lpOverlapped
   1.586 +//     );
   1.587 +//     */
   1.588 +//     var WriteFile = kernel32dll.declare("WriteFile",
   1.589 +//                                         WinABI,
   1.590 +//                                         BOOL,
   1.591 +//                                         HANDLE,
   1.592 +//                                         ctypes.char.ptr,
   1.593 +//                                         DWORD,
   1.594 +//                                         LPDWORD,
   1.595 +//                                         OVERLAPPED.ptr
   1.596 +//     );
   1.597 +
   1.598 +    /*
   1.599 +    BOOL WINAPI CreatePipe(
   1.600 +      __out     PHANDLE hReadPipe,
   1.601 +      __out     PHANDLE hWritePipe,
   1.602 +      __in_opt  LPSECURITY_ATTRIBUTES lpPipeAttributes,
   1.603 +      __in      DWORD nSize
   1.604 +    );
   1.605 +    */
   1.606 +    var CreatePipe = kernel32dll.declare("CreatePipe",
   1.607 +                                        WinABI,
   1.608 +                                        BOOL,
   1.609 +                                        HANDLE.ptr,
   1.610 +                                        HANDLE.ptr,
   1.611 +                                        SECURITY_ATTRIBUTES.ptr,
   1.612 +                                        DWORD
   1.613 +    );
   1.614 +
   1.615 +    /*
   1.616 +    HANDLE WINAPI GetCurrentProcess(void);
   1.617 +    */
   1.618 +    var GetCurrentProcess = kernel32dll.declare("GetCurrentProcess",
   1.619 +                                        WinABI,
   1.620 +                                        HANDLE
   1.621 +    );
   1.622 +
   1.623 +    /*
   1.624 +    DWORD WINAPI GetLastError(void);
   1.625 +    */
   1.626 +    var GetLastError = kernel32dll.declare("GetLastError",
   1.627 +                                        WinABI,
   1.628 +                                        DWORD
   1.629 +    );
   1.630 +
   1.631 +    /*
   1.632 +    BOOL WINAPI DuplicateHandle(
   1.633 +      __in   HANDLE hSourceProcessHandle,
   1.634 +      __in   HANDLE hSourceHandle,
   1.635 +      __in   HANDLE hTargetProcessHandle,
   1.636 +      __out  LPHANDLE lpTargetHandle,
   1.637 +      __in   DWORD dwDesiredAccess,
   1.638 +      __in   BOOL bInheritHandle,
   1.639 +      __in   DWORD dwOptions
   1.640 +    );
   1.641 +    */
   1.642 +    var DuplicateHandle = kernel32dll.declare("DuplicateHandle",
   1.643 +                                        WinABI,
   1.644 +                                        BOOL,
   1.645 +                                        HANDLE,
   1.646 +                                        HANDLE,
   1.647 +                                        HANDLE,
   1.648 +                                        HANDLE.ptr,
   1.649 +                                        DWORD,
   1.650 +                                        BOOL,
   1.651 +                                        DWORD
   1.652 +    );
   1.653 +
   1.654 +
   1.655 +    /*
   1.656 +    BOOL WINAPI GetExitCodeProcess(
   1.657 +      __in   HANDLE hProcess,
   1.658 +      __out  LPDWORD lpExitCode
   1.659 +    );
   1.660 +    */
   1.661 +    var GetExitCodeProcess = kernel32dll.declare("GetExitCodeProcess",
   1.662 +                                        WinABI,
   1.663 +                                        BOOL,
   1.664 +                                        HANDLE,
   1.665 +                                        LPDWORD
   1.666 +    );
   1.667 +
   1.668 +    /*
   1.669 +    DWORD WINAPI WaitForSingleObject(
   1.670 +      __in  HANDLE hHandle,
   1.671 +      __in  DWORD dwMilliseconds
   1.672 +    );
   1.673 +    */
   1.674 +    var WaitForSingleObject = kernel32dll.declare("WaitForSingleObject",
   1.675 +                                        WinABI,
   1.676 +                                        DWORD,
   1.677 +                                        HANDLE,
   1.678 +                                        DWORD
   1.679 +    );
   1.680 +
   1.681 +    /*
   1.682 +    BOOL WINAPI TerminateProcess(
   1.683 +      __in  HANDLE hProcess,
   1.684 +      __in  UINT uExitCode
   1.685 +    );
   1.686 +    */
   1.687 +    var TerminateProcess = kernel32dll.declare("TerminateProcess",
   1.688 +                                        WinABI,
   1.689 +                                        BOOL,
   1.690 +                                        HANDLE,
   1.691 +                                        UINT
   1.692 +    );
   1.693 +
   1.694 +    //functions
   1.695 +    function popen(command, workdir, args, environment, child) {
   1.696 +        //escape arguments
   1.697 +        args.unshift(command);
   1.698 +        for (var i = 0; i < args.length; i++) {
   1.699 +          if (typeof args[i] != "string") { args[i] = args[i].toString(); }
   1.700 +          /* quote arguments with spaces */
   1.701 +          if (args[i].match(/\s/)) {
   1.702 +            args[i] = "\"" + args[i] + "\"";
   1.703 +          }
   1.704 +          /* If backslash is followed by a quote, double it */
   1.705 +          args[i] = args[i].replace(/\\\"/g, "\\\\\"");
   1.706 +        }
   1.707 +        command = args.join(' ');
   1.708 +
   1.709 +        environment = environment || [];
   1.710 +        if(environment.length) {
   1.711 +            //An environment block consists of
   1.712 +            //a null-terminated block of null-terminated strings.
   1.713 +            //Using CREATE_UNICODE_ENVIRONMENT so needs to be jschar
   1.714 +            environment = ctypes.jschar.array()(environment.join('\0') + '\0');
   1.715 +        } else {
   1.716 +            environment = null;
   1.717 +        }
   1.718 +
   1.719 +        var hOutputReadTmp = new HANDLE(),
   1.720 +            hOutputRead = new HANDLE(),
   1.721 +            hOutputWrite = new HANDLE();
   1.722 +
   1.723 +        var hErrorRead = new HANDLE(),
   1.724 +            hErrorReadTmp = new HANDLE(),
   1.725 +            hErrorWrite = new HANDLE();
   1.726 +
   1.727 +        var hInputRead = new HANDLE(),
   1.728 +            hInputWriteTmp = new HANDLE(),
   1.729 +            hInputWrite = new HANDLE();
   1.730 +
   1.731 +        // Set up the security attributes struct.
   1.732 +        var sa = new SECURITY_ATTRIBUTES();
   1.733 +        sa.nLength = SECURITY_ATTRIBUTES.size;
   1.734 +        sa.lpSecurityDescriptor = null;
   1.735 +        sa.bInheritHandle = true;
   1.736 +
   1.737 +        // Create output pipe.
   1.738 +
   1.739 +        if(!CreatePipe(hOutputReadTmp.address(), hOutputWrite.address(), sa.address(), 0))
   1.740 +            LogError('CreatePipe hOutputReadTmp failed');
   1.741 +
   1.742 +        if(options.mergeStderr) {
   1.743 +          // Create a duplicate of the output write handle for the std error
   1.744 +          // write handle. This is necessary in case the child application
   1.745 +          // closes one of its std output handles.
   1.746 +          if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
   1.747 +                               GetCurrentProcess(), hErrorWrite.address(), 0,
   1.748 +                               true, DUPLICATE_SAME_ACCESS))
   1.749 +             LogError("DuplicateHandle hOutputWrite failed");
   1.750 +        } else {
   1.751 +            // Create error pipe.
   1.752 +            if(!CreatePipe(hErrorReadTmp.address(), hErrorWrite.address(), sa.address(), 0))
   1.753 +                LogError('CreatePipe hErrorReadTmp failed');
   1.754 +        }
   1.755 +
   1.756 +        // Create input pipe.
   1.757 +        if (!CreatePipe(hInputRead.address(),hInputWriteTmp.address(),sa.address(), 0))
   1.758 +            LogError("CreatePipe hInputRead failed");
   1.759 +
   1.760 +        // Create new output/error read handle and the input write handles. Set
   1.761 +        // the Properties to FALSE. Otherwise, the child inherits the
   1.762 +        // properties and, as a result, non-closeable handles to the pipes
   1.763 +        // are created.
   1.764 +        if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
   1.765 +                             GetCurrentProcess(),
   1.766 +                             hOutputRead.address(), // Address of new handle.
   1.767 +                             0, false, // Make it uninheritable.
   1.768 +                             DUPLICATE_SAME_ACCESS))
   1.769 +             LogError("DupliateHandle hOutputReadTmp failed");
   1.770 +
   1.771 +        if(!options.mergeStderr) {
   1.772 +            if (!DuplicateHandle(GetCurrentProcess(), hErrorReadTmp,
   1.773 +                             GetCurrentProcess(),
   1.774 +                             hErrorRead.address(), // Address of new handle.
   1.775 +                             0, false, // Make it uninheritable.
   1.776 +                             DUPLICATE_SAME_ACCESS))
   1.777 +             LogError("DupliateHandle hErrorReadTmp failed");
   1.778 +        }
   1.779 +        if (!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp,
   1.780 +                             GetCurrentProcess(),
   1.781 +                             hInputWrite.address(), // Address of new handle.
   1.782 +                             0, false, // Make it uninheritable.
   1.783 +                             DUPLICATE_SAME_ACCESS))
   1.784 +          LogError("DupliateHandle hInputWriteTmp failed");
   1.785 +
   1.786 +        // Close inheritable copies of the handles.
   1.787 +        if (!CloseHandle(hOutputReadTmp)) LogError("CloseHandle hOutputReadTmp failed");
   1.788 +        if(!options.mergeStderr)
   1.789 +            if (!CloseHandle(hErrorReadTmp)) LogError("CloseHandle hErrorReadTmp failed");
   1.790 +        if (!CloseHandle(hInputWriteTmp)) LogError("CloseHandle failed");
   1.791 +
   1.792 +        var pi = new PROCESS_INFORMATION();
   1.793 +        var si = new STARTUPINFO();
   1.794 +
   1.795 +        si.cb = STARTUPINFO.size;
   1.796 +        si.dwFlags = STARTF_USESTDHANDLES;
   1.797 +        si.hStdInput  = hInputRead;
   1.798 +        si.hStdOutput = hOutputWrite;
   1.799 +        si.hStdError  = hErrorWrite;
   1.800 +
   1.801 +        // Launch the process
   1.802 +        if(!CreateProcessW(null,            // executable name
   1.803 +                           command,         // command buffer
   1.804 +                           null,            // process security attribute
   1.805 +                           null,            // thread security attribute
   1.806 +                           true,            // inherits system handles
   1.807 +                           CREATE_UNICODE_ENVIRONMENT|CREATE_NO_WINDOW, // process flags
   1.808 +                           environment,     // envrionment block
   1.809 +                           workdir,          // set as current directory
   1.810 +                           si.address(),    // (in) startup information
   1.811 +                           pi.address()     // (out) process information
   1.812 +        ))
   1.813 +            throw("Fatal - Could not launch subprocess '"+command+"'");
   1.814 +
   1.815 +        // Close any unnecessary handles.
   1.816 +        if (!CloseHandle(pi.hThread))
   1.817 +            LogError("CloseHandle pi.hThread failed");
   1.818 +
   1.819 +        // Close pipe handles (do not continue to modify the parent).
   1.820 +        // You need to make sure that no handles to the write end of the
   1.821 +        // output pipe are maintained in this process or else the pipe will
   1.822 +        // not close when the child process exits and the ReadFile will hang.
   1.823 +        if (!CloseHandle(hInputRead)) LogError("CloseHandle hInputRead failed");
   1.824 +        if (!CloseHandle(hOutputWrite)) LogError("CloseHandle hOutputWrite failed");
   1.825 +        if (!CloseHandle(hErrorWrite)) LogError("CloseHandle hErrorWrite failed");
   1.826 +
   1.827 +        //return values
   1.828 +        child.stdin = hInputWrite;
   1.829 +        child.stdout = hOutputRead;
   1.830 +        child.stderr = options.mergeStderr ? undefined : hErrorRead;
   1.831 +        child.process = pi.hProcess;
   1.832 +        return pi.hProcess;
   1.833 +    }
   1.834 +
   1.835 +    /*
   1.836 +     * createStdinWriter ()
   1.837 +     *
   1.838 +     * Create a ChromeWorker object for writing data to the subprocess' stdin
   1.839 +     * pipe. The ChromeWorker object lives on a separate thread; this avoids
   1.840 +     * internal deadlocks.
   1.841 +     */
   1.842 +    function createStdinWriter() {
   1.843 +        debugLog("Creating new stdin worker\n");
   1.844 +        stdinWorker = new ChromeWorker(WORKER_URL_WIN);
   1.845 +        stdinWorker.onmessage = function(event) {
   1.846 +            switch(event.data) {
   1.847 +            case "WriteOK":
   1.848 +                pendingWriteCount--;
   1.849 +                debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
   1.850 +                break;
   1.851 +            case "ClosedOK":
   1.852 +                stdinOpenState = CLOSED;
   1.853 +                debugLog("Stdin pipe closed\n");
   1.854 +                break;
   1.855 +            default:
   1.856 +                debugLog("got msg from stdinWorker: "+event.data+"\n");
   1.857 +            }
   1.858 +        }
   1.859 +        stdinWorker.onerror = function(error) {
   1.860 +            pendingWriteCount--;
   1.861 +            LogError("got error from stdinWorker: "+error.message+"\n");
   1.862 +        }
   1.863 +
   1.864 +        stdinWorker.postMessage({msg: "init", libc: options.libc});
   1.865 +    }
   1.866 +
   1.867 +    /*
   1.868 +     * writeStdin()
   1.869 +     * @data: String containing the data to write
   1.870 +     *
   1.871 +     * Write data to the subprocess' stdin (equals to sending a request to the
   1.872 +     * ChromeWorker object to write the data).
   1.873 +     */
   1.874 +    function writeStdin(data) {
   1.875 +        ++pendingWriteCount;
   1.876 +        debugLog("sending "+data.length+" bytes to stdinWorker\n");
   1.877 +        var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
   1.878 +
   1.879 +        stdinWorker.postMessage({
   1.880 +                msg: 'write',
   1.881 +                pipe: pipePtr,
   1.882 +                data: data
   1.883 +            });
   1.884 +    }
   1.885 +
   1.886 +    /*
   1.887 +     * closeStdinHandle()
   1.888 +     *
   1.889 +     * Close the stdin pipe, either directly or by requesting the ChromeWorker to
   1.890 +     * close the pipe. The ChromeWorker will only close the pipe after the last write
   1.891 +     * request process is done.
   1.892 +     */
   1.893 +
   1.894 +    function closeStdinHandle() {
   1.895 +        debugLog("trying to close stdin\n");
   1.896 +        if (stdinOpenState != OPEN) return;
   1.897 +        stdinOpenState = CLOSEABLE;
   1.898 +
   1.899 +        if (stdinWorker) {
   1.900 +            debugLog("sending close stdin to worker\n");
   1.901 +            var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
   1.902 +            stdinWorker.postMessage({
   1.903 +                msg: 'close',
   1.904 +                pipe: pipePtr
   1.905 +            });
   1.906 +        }
   1.907 +        else {
   1.908 +            stdinOpenState = CLOSED;
   1.909 +            debugLog("Closing Stdin\n");
   1.910 +            CloseHandle(child.stdin) || LogError("CloseHandle hInputWrite failed");
   1.911 +        }
   1.912 +    }
   1.913 +
   1.914 +
   1.915 +    /*
   1.916 +     * createReader(pipe, name)
   1.917 +     *
   1.918 +     * @pipe: handle to the pipe
   1.919 +     * @name: String containing the pipe name (stdout or stderr)
   1.920 +     *
   1.921 +     * Create a ChromeWorker object for reading data asynchronously from
   1.922 +     * the pipe (i.e. on a separate thread), and passing the result back to
   1.923 +     * the caller.
   1.924 +     */
   1.925 +    function createReader(pipe, name, callbackFunc) {
   1.926 +        var worker = new ChromeWorker(WORKER_URL_WIN);
   1.927 +        worker.onmessage = function(event) {
   1.928 +            switch(event.data.msg) {
   1.929 +            case "data":
   1.930 +                debugLog("got "+event.data.count+" bytes from "+name+"\n");
   1.931 +                var data = '';
   1.932 +                if (options.charset === null) {
   1.933 +                    data = getBytes(event.data.data);
   1.934 +                }
   1.935 +                else {
   1.936 +                    try {
   1.937 +                        data = convertBytes(event.data.data, options.charset);
   1.938 +                    }
   1.939 +                    catch(ex) {
   1.940 +                        console.warn("error decoding output: " + ex);
   1.941 +                        data = getBytes(event.data.data);
   1.942 +                    }
   1.943 +                }
   1.944 +
   1.945 +                callbackFunc(data);
   1.946 +                break;
   1.947 +            case "done":
   1.948 +                debugLog("Pipe "+name+" closed\n");
   1.949 +                --readers;
   1.950 +                if (readers == 0) cleanup();
   1.951 +                break;
   1.952 +            default:
   1.953 +                debugLog("Got msg from "+name+": "+event.data.data+"\n");
   1.954 +            }
   1.955 +        }
   1.956 +
   1.957 +        worker.onerror = function(errorMsg) {
   1.958 +            LogError("Got error from "+name+": "+errorMsg.message);
   1.959 +        }
   1.960 +
   1.961 +        var pipePtr = parseInt(ctypes.cast(pipe.address(), ctypes.uintptr_t).value);
   1.962 +
   1.963 +        worker.postMessage({
   1.964 +                msg: 'read',
   1.965 +                pipe: pipePtr,
   1.966 +                libc: options.libc,
   1.967 +                charset: options.charset === null ? "null" : options.charset,
   1.968 +                name: name
   1.969 +            });
   1.970 +
   1.971 +        return worker;
   1.972 +    }
   1.973 +
   1.974 +    /*
   1.975 +     * readPipes()
   1.976 +     *
   1.977 +     * Open the pipes for reading from stdout and stderr
   1.978 +     */
   1.979 +    function readPipes() {
   1.980 +        stdoutWorker = createReader(child.stdout, "stdout", function (data) {
   1.981 +            if(options.stdout) {
   1.982 +                setTimeout(function() {
   1.983 +                    options.stdout(data);
   1.984 +                }, 0);
   1.985 +            } else {
   1.986 +                output += data;
   1.987 +            }
   1.988 +        });
   1.989 +
   1.990 +
   1.991 +        if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
   1.992 +            if(options.stderr) {
   1.993 +                setTimeout(function() {
   1.994 +                    options.stderr(data);
   1.995 +                }, 0);
   1.996 +            } else {
   1.997 +                error += data;
   1.998 +            }
   1.999 +        });
  1.1000 +    }
  1.1001 +
  1.1002 +    /*
  1.1003 +     * cleanup()
  1.1004 +     *
  1.1005 +     * close stdin if needed, get the exit code from the subprocess and invoke
  1.1006 +     * the caller's done() function.
  1.1007 +     *
  1.1008 +     * Note: because stdout() and stderr() are called using setTimeout, we need to
  1.1009 +     * do the same here in order to guarantee the message sequence.
  1.1010 +     */
  1.1011 +    function cleanup() {
  1.1012 +        debugLog("Cleanup called\n");
  1.1013 +        if(active) {
  1.1014 +            active = false;
  1.1015 +
  1.1016 +            closeStdinHandle(); // should only be required in case of errors
  1.1017 +
  1.1018 +            var exit = new DWORD();
  1.1019 +            GetExitCodeProcess(child.process, exit.address());
  1.1020 +            exitCode = exit.value;
  1.1021 +
  1.1022 +            if (stdinWorker)
  1.1023 +                stdinWorker.postMessage({msg: 'stop'})
  1.1024 +
  1.1025 +            setTimeout(function _done() {
  1.1026 +                if (options.done) {
  1.1027 +                    try {
  1.1028 +                        options.done({
  1.1029 +                            exitCode: exitCode,
  1.1030 +                            stdout: output,
  1.1031 +                            stderr: error,
  1.1032 +                        });
  1.1033 +                    }
  1.1034 +                    catch (ex) {
  1.1035 +                        // prevent from blocking if options.done() throws an error
  1.1036 +                        done = true;
  1.1037 +                        throw ex;
  1.1038 +                    }
  1.1039 +                }
  1.1040 +                done = true;
  1.1041 +            }, 0);
  1.1042 +            kernel32dll.close();
  1.1043 +        }
  1.1044 +    }
  1.1045 +
  1.1046 +    var cmdStr = getCommandStr(options.command);
  1.1047 +    var workDir = getWorkDir(options.workdir);
  1.1048 +
  1.1049 +    //main
  1.1050 +    hChildProcess = popen(cmdStr, workDir, options.arguments, options.environment, child);
  1.1051 +
  1.1052 +    readPipes();
  1.1053 +
  1.1054 +    if (options.stdin) {
  1.1055 +       createStdinWriter();
  1.1056 +
  1.1057 +        if(typeof(options.stdin) == 'function') {
  1.1058 +            try {
  1.1059 +                options.stdin({
  1.1060 +                    write: function(data) {
  1.1061 +                        writeStdin(data);
  1.1062 +                    },
  1.1063 +                    close: function() {
  1.1064 +                        closeStdinHandle();
  1.1065 +                    }
  1.1066 +                });
  1.1067 +            }
  1.1068 +            catch (ex) {
  1.1069 +                // prevent from failing if options.stdin() throws an exception
  1.1070 +                closeStdinHandle();
  1.1071 +                throw ex;
  1.1072 +            }
  1.1073 +        } else {
  1.1074 +            writeStdin(options.stdin);
  1.1075 +            closeStdinHandle();
  1.1076 +        }
  1.1077 +    }
  1.1078 +    else
  1.1079 +        closeStdinHandle();
  1.1080 +
  1.1081 +    return {
  1.1082 +        kill: function(hardKill) {
  1.1083 +            // hardKill is currently ignored on Windows
  1.1084 +            var r = !!TerminateProcess(child.process, 255);
  1.1085 +            cleanup(-1);
  1.1086 +            return r;
  1.1087 +        },
  1.1088 +        wait: function() {
  1.1089 +            // wait for async operations to complete
  1.1090 +            var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
  1.1091 +            while (!done) thread.processNextEvent(true);
  1.1092 +
  1.1093 +            return exitCode;
  1.1094 +        }
  1.1095 +    }
  1.1096 +}
  1.1097 +
  1.1098 +
  1.1099 +function subprocess_unix(options) {
  1.1100 +    // stdin pipe states
  1.1101 +    const OPEN = 2;
  1.1102 +    const CLOSEABLE = 1;
  1.1103 +    const CLOSED = 0;
  1.1104 +
  1.1105 +    var libc = ctypes.open(options.libc),
  1.1106 +        active = true,
  1.1107 +        done = false,
  1.1108 +        exitCode = -1,
  1.1109 +        workerExitCode = 0,
  1.1110 +        child = {},
  1.1111 +        pid = -1,
  1.1112 +        stdinWorker = null,
  1.1113 +        stdoutWorker = null,
  1.1114 +        stderrWorker = null,
  1.1115 +        pendingWriteCount = 0,
  1.1116 +        readers = options.mergeStderr ? 1 : 2,
  1.1117 +        stdinOpenState = OPEN,
  1.1118 +        error = '',
  1.1119 +        output = '';
  1.1120 +
  1.1121 +    //api declarations
  1.1122 +
  1.1123 +    //pid_t fork(void);
  1.1124 +    var fork = libc.declare("fork",
  1.1125 +                         ctypes.default_abi,
  1.1126 +                         pid_t
  1.1127 +    );
  1.1128 +
  1.1129 +    //NULL terminated array of strings, argv[0] will be command >> + 2
  1.1130 +    var argv = ctypes.char.ptr.array(options.arguments.length + 2);
  1.1131 +    var envp = ctypes.char.ptr.array(options.environment.length + 1);
  1.1132 +
  1.1133 +    // posix_spawn_file_actions_t is a complex struct that may be different on
  1.1134 +    // each platform. We do not care about its attributes, we don't need to
  1.1135 +    // get access to them, but we do need to allocate the right amount
  1.1136 +    // of memory for it.
  1.1137 +    // At 2013/10/28, its size was 80 on linux, but better be safe (and larger),
  1.1138 +    // than crash when posix_spawn_file_actions_init fill `action` with zeros.
  1.1139 +    // Use `gcc sizeof_fileaction.c && ./a.out` to check that size.
  1.1140 +    var posix_spawn_file_actions_t = ctypes.uint8_t.array(100);
  1.1141 +
  1.1142 +    //int posix_spawn(pid_t *restrict pid, const char *restrict path,
  1.1143 +    //   const posix_spawn_file_actions_t *file_actions,
  1.1144 +    //   const posix_spawnattr_t *restrict attrp,
  1.1145 +    //   char *const argv[restrict], char *const envp[restrict]);
  1.1146 +    var posix_spawn = libc.declare("posix_spawn",
  1.1147 +                         ctypes.default_abi,
  1.1148 +                         ctypes.int,
  1.1149 +                         pid_t.ptr,
  1.1150 +                         ctypes.char.ptr,
  1.1151 +                         posix_spawn_file_actions_t.ptr,
  1.1152 +                         ctypes.voidptr_t,
  1.1153 +                         argv,
  1.1154 +                         envp
  1.1155 +    );
  1.1156 +
  1.1157 +    //int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
  1.1158 +    var posix_spawn_file_actions_init = libc.declare("posix_spawn_file_actions_init",
  1.1159 +                         ctypes.default_abi,
  1.1160 +                         ctypes.int,
  1.1161 +                         posix_spawn_file_actions_t.ptr
  1.1162 +    );
  1.1163 +
  1.1164 +    //int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
  1.1165 +    var posix_spawn_file_actions_destroy = libc.declare("posix_spawn_file_actions_destroy",
  1.1166 +                         ctypes.default_abi,
  1.1167 +                         ctypes.int,
  1.1168 +                         posix_spawn_file_actions_t.ptr
  1.1169 +    );
  1.1170 +
  1.1171 +    // int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *
  1.1172 +    //                                      file_actions, int fildes, int newfildes);
  1.1173 +    var posix_spawn_file_actions_adddup2 = libc.declare("posix_spawn_file_actions_adddup2",
  1.1174 +                         ctypes.default_abi,
  1.1175 +                         ctypes.int,
  1.1176 +                         posix_spawn_file_actions_t.ptr,
  1.1177 +                         ctypes.int,
  1.1178 +                         ctypes.int
  1.1179 +    );
  1.1180 +
  1.1181 +    // int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *
  1.1182 +    //                                       file_actions, int fildes);
  1.1183 +    var posix_spawn_file_actions_addclose = libc.declare("posix_spawn_file_actions_addclose",
  1.1184 +                         ctypes.default_abi,
  1.1185 +                         ctypes.int,
  1.1186 +                         posix_spawn_file_actions_t.ptr,
  1.1187 +                         ctypes.int
  1.1188 +    );
  1.1189 +
  1.1190 +    //int pipe(int pipefd[2]);
  1.1191 +    var pipefd = ctypes.int.array(2);
  1.1192 +    var pipe = libc.declare("pipe",
  1.1193 +                         ctypes.default_abi,
  1.1194 +                         ctypes.int,
  1.1195 +                         pipefd
  1.1196 +    );
  1.1197 +
  1.1198 +    //int close(int fd);
  1.1199 +    var close = libc.declare("close",
  1.1200 +                          ctypes.default_abi,
  1.1201 +                          ctypes.int,
  1.1202 +                          ctypes.int
  1.1203 +    );
  1.1204 +
  1.1205 +    //pid_t waitpid(pid_t pid, int *status, int options);
  1.1206 +    var waitpid = libc.declare("waitpid",
  1.1207 +                          ctypes.default_abi,
  1.1208 +                          pid_t,
  1.1209 +                          pid_t,
  1.1210 +                          ctypes.int.ptr,
  1.1211 +                          ctypes.int
  1.1212 +    );
  1.1213 +
  1.1214 +    //int kill(pid_t pid, int sig);
  1.1215 +    var kill = libc.declare("kill",
  1.1216 +                          ctypes.default_abi,
  1.1217 +                          ctypes.int,
  1.1218 +                          pid_t,
  1.1219 +                          ctypes.int
  1.1220 +    );
  1.1221 +
  1.1222 +    //int read(int fd, void *buf, size_t count);
  1.1223 +    var bufferSize = 1024;
  1.1224 +    var buffer = ctypes.char.array(bufferSize);
  1.1225 +    var read = libc.declare("read",
  1.1226 +                          ctypes.default_abi,
  1.1227 +                          ctypes.int,
  1.1228 +                          ctypes.int,
  1.1229 +                          buffer,
  1.1230 +                          ctypes.int
  1.1231 +    );
  1.1232 +
  1.1233 +    //ssize_t write(int fd, const void *buf, size_t count);
  1.1234 +    var write = libc.declare("write",
  1.1235 +                          ctypes.default_abi,
  1.1236 +                          ctypes.int,
  1.1237 +                          ctypes.int,
  1.1238 +                          ctypes.char.ptr,
  1.1239 +                          ctypes.int
  1.1240 +    );
  1.1241 +
  1.1242 +    //int chdir(const char *path);
  1.1243 +    var chdir = libc.declare("chdir",
  1.1244 +                          ctypes.default_abi,
  1.1245 +                          ctypes.int,
  1.1246 +                          ctypes.char.ptr
  1.1247 +    );
  1.1248 +
  1.1249 +    //int fcntl(int fd, int cmd, ... /* arg */ );
  1.1250 +    var fcntl = libc.declare("fcntl",
  1.1251 +                          ctypes.default_abi,
  1.1252 +                          ctypes.int,
  1.1253 +                          ctypes.int,
  1.1254 +                          ctypes.int,
  1.1255 +                          ctypes.int
  1.1256 +    );
  1.1257 +
  1.1258 +    function popen(command, workdir, args, environment, child) {
  1.1259 +        var _in,
  1.1260 +            _out,
  1.1261 +            _err,
  1.1262 +            pid,
  1.1263 +            rc;
  1.1264 +        _in = new pipefd();
  1.1265 +        _out = new pipefd();
  1.1266 +        if(!options.mergeStderr)
  1.1267 +            _err = new pipefd();
  1.1268 +
  1.1269 +        var _args = argv();
  1.1270 +        args.unshift(command);
  1.1271 +        for(var i=0;i<args.length;i++) {
  1.1272 +            _args[i] = ctypes.char.array()(args[i]);
  1.1273 +        }
  1.1274 +        var _envp = envp();
  1.1275 +        for(var i=0;i<environment.length;i++) {
  1.1276 +            _envp[i] = ctypes.char.array()(environment[i]);
  1.1277 +            // LogError(_envp);
  1.1278 +        }
  1.1279 +
  1.1280 +        rc = pipe(_in);
  1.1281 +        if (rc < 0) {
  1.1282 +            return -1;
  1.1283 +        }
  1.1284 +        rc = pipe(_out);
  1.1285 +        fcntl(_out[0], F_SETFL, getPlatformValue(O_NONBLOCK));
  1.1286 +        if (rc < 0) {
  1.1287 +            close(_in[0]);
  1.1288 +            close(_in[1]);
  1.1289 +            return -1
  1.1290 +        }
  1.1291 +        if(!options.mergeStderr) {
  1.1292 +            rc = pipe(_err);
  1.1293 +            fcntl(_err[0], F_SETFL, getPlatformValue(O_NONBLOCK));
  1.1294 +            if (rc < 0) {
  1.1295 +                close(_in[0]);
  1.1296 +                close(_in[1]);
  1.1297 +                close(_out[0]);
  1.1298 +                close(_out[1]);
  1.1299 +                return -1
  1.1300 +            }
  1.1301 +        }
  1.1302 +
  1.1303 +        let STDIN_FILENO = 0;
  1.1304 +        let STDOUT_FILENO = 1;
  1.1305 +        let STDERR_FILENO = 2;
  1.1306 +
  1.1307 +        let action = posix_spawn_file_actions_t();
  1.1308 +        posix_spawn_file_actions_init(action.address());
  1.1309 +
  1.1310 +        posix_spawn_file_actions_adddup2(action.address(), _in[0], STDIN_FILENO);
  1.1311 +        posix_spawn_file_actions_addclose(action.address(), _in[1]);
  1.1312 +        posix_spawn_file_actions_addclose(action.address(), _in[0]);
  1.1313 +
  1.1314 +        posix_spawn_file_actions_adddup2(action.address(), _out[1], STDOUT_FILENO);
  1.1315 +        posix_spawn_file_actions_addclose(action.address(), _out[1]);
  1.1316 +        posix_spawn_file_actions_addclose(action.address(), _out[0]);
  1.1317 +
  1.1318 +        if (!options.mergeStderr) {
  1.1319 +          posix_spawn_file_actions_adddup2(action.address(), _err[1], STDERR_FILENO);
  1.1320 +          posix_spawn_file_actions_addclose(action.address(), _err[1]);
  1.1321 +          posix_spawn_file_actions_addclose(action.address(), _err[0]);
  1.1322 +        }
  1.1323 +
  1.1324 +        // posix_spawn doesn't support setting a custom workdir for the child,
  1.1325 +        // so change the cwd in the parent process before launching the child process.
  1.1326 +        if (workdir) {
  1.1327 +          if (chdir(workdir) < 0) {
  1.1328 +            throw new Error("Unable to change workdir before launching child process");
  1.1329 +          }
  1.1330 +        }
  1.1331 +
  1.1332 +        closeOtherFds(action, _in[1], _out[0], options.mergeStderr ? undefined : _err[0]);
  1.1333 +
  1.1334 +        let id = pid_t(0);
  1.1335 +        let rv = posix_spawn(id.address(), command, action.address(), null, _args, _envp);
  1.1336 +        posix_spawn_file_actions_destroy(action.address());
  1.1337 +        if (rv != 0) {
  1.1338 +          // we should not really end up here
  1.1339 +          if(!options.mergeStderr) {
  1.1340 +            close(_err[0]);
  1.1341 +            close(_err[1]);
  1.1342 +          }
  1.1343 +          close(_out[0]);
  1.1344 +          close(_out[1]);
  1.1345 +          close(_in[0]);
  1.1346 +          close(_in[1]);
  1.1347 +          throw new Error("Fatal - failed to create subprocess '"+command+"'");
  1.1348 +        }
  1.1349 +        pid = id.value;
  1.1350 +
  1.1351 +        close(_in[0]);
  1.1352 +        close(_out[1]);
  1.1353 +        if (!options.mergeStderr)
  1.1354 +          close(_err[1]);
  1.1355 +        child.stdin  = _in[1];
  1.1356 +        child.stdout = _out[0];
  1.1357 +        child.stderr = options.mergeStderr ? undefined : _err[0];
  1.1358 +        child.pid = pid;
  1.1359 +
  1.1360 +        return pid;
  1.1361 +    }
  1.1362 +
  1.1363 +
  1.1364 +    // close any file descriptors that are not required for the process
  1.1365 +    function closeOtherFds(action, fdIn, fdOut, fdErr) {
  1.1366 +        // Unfortunately on mac, any fd registered in posix_spawn_file_actions_addclose
  1.1367 +        // that can't be closed correctly will make posix_spawn fail...
  1.1368 +        // Even if we ensure registering only still opened fds.
  1.1369 +        if (gXulRuntime.OS == "Darwin")
  1.1370 +            return;
  1.1371 +
  1.1372 +        var maxFD = 256; // arbitrary max
  1.1373 +
  1.1374 +
  1.1375 +        var rlim_t = getPlatformValue(RLIM_T);
  1.1376 +
  1.1377 +        const RLIMITS = new ctypes.StructType("RLIMITS", [
  1.1378 +            {"rlim_cur": rlim_t},
  1.1379 +            {"rlim_max": rlim_t}
  1.1380 +        ]);
  1.1381 +
  1.1382 +        try {
  1.1383 +            var getrlimit = libc.declare("getrlimit",
  1.1384 +                                  ctypes.default_abi,
  1.1385 +                                  ctypes.int,
  1.1386 +                                  ctypes.int,
  1.1387 +                                  RLIMITS.ptr
  1.1388 +            );
  1.1389 +
  1.1390 +            var rl = new RLIMITS();
  1.1391 +            if (getrlimit(getPlatformValue(RLIMIT_NOFILE), rl.address()) == 0) {
  1.1392 +                maxFD = rl.rlim_cur;
  1.1393 +            }
  1.1394 +            debugLog("getlimit: maxFD="+maxFD+"\n");
  1.1395 +
  1.1396 +        }
  1.1397 +        catch(ex) {
  1.1398 +            debugLog("getrlimit: no such function on this OS\n");
  1.1399 +            debugLog(ex.toString());
  1.1400 +        }
  1.1401 +
  1.1402 +        // close any file descriptors
  1.1403 +        // fd's 0-2 are already closed
  1.1404 +        for (var i = 3; i < maxFD; i++) {
  1.1405 +            if (i != fdIn && i != fdOut && i != fdErr && fcntl(i, F_GETFD, -1) >= 0) {
  1.1406 +                posix_spawn_file_actions_addclose(action.address(), i);
  1.1407 +            }
  1.1408 +        }
  1.1409 +    }
  1.1410 +
  1.1411 +    /*
  1.1412 +     * createStdinWriter ()
  1.1413 +     *
  1.1414 +     * Create a ChromeWorker object for writing data to the subprocess' stdin
  1.1415 +     * pipe. The ChromeWorker object lives on a separate thread; this avoids
  1.1416 +     * internal deadlocks.
  1.1417 +     */
  1.1418 +    function createStdinWriter() {
  1.1419 +        debugLog("Creating new stdin worker\n");
  1.1420 +        stdinWorker = new ChromeWorker(WORKER_URL_UNIX);
  1.1421 +        stdinWorker.onmessage = function(event) {
  1.1422 +            switch (event.data.msg) {
  1.1423 +            case "info":
  1.1424 +                switch(event.data.data) {
  1.1425 +                case "WriteOK":
  1.1426 +                    pendingWriteCount--;
  1.1427 +                    debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
  1.1428 +                    break;
  1.1429 +                case "ClosedOK":
  1.1430 +                    stdinOpenState = CLOSED;
  1.1431 +                    debugLog("Stdin pipe closed\n");
  1.1432 +                    break;
  1.1433 +                default:
  1.1434 +                    debugLog("got msg from stdinWorker: "+event.data.data+"\n");
  1.1435 +                }
  1.1436 +                break;
  1.1437 +            case "debug":
  1.1438 +                debugLog("stdinWorker: "+event.data.data+"\n");
  1.1439 +                break;
  1.1440 +            case "error":
  1.1441 +                LogError("got error from stdinWorker: "+event.data.data+"\n");
  1.1442 +                pendingWriteCount = 0;
  1.1443 +                stdinOpenState = CLOSED;
  1.1444 +            }
  1.1445 +        }
  1.1446 +        stdinWorker.onerror = function(error) {
  1.1447 +            pendingWriteCount = 0;
  1.1448 +            closeStdinHandle();
  1.1449 +            LogError("got error from stdinWorker: "+error.message+"\n");
  1.1450 +        }
  1.1451 +        stdinWorker.postMessage({msg: "init", libc: options.libc});
  1.1452 +    }
  1.1453 +
  1.1454 +    /*
  1.1455 +     * writeStdin()
  1.1456 +     * @data: String containing the data to write
  1.1457 +     *
  1.1458 +     * Write data to the subprocess' stdin (equals to sending a request to the
  1.1459 +     * ChromeWorker object to write the data).
  1.1460 +     */
  1.1461 +    function writeStdin(data) {
  1.1462 +        if (stdinOpenState == CLOSED) return; // do not write to closed pipes
  1.1463 +
  1.1464 +        ++pendingWriteCount;
  1.1465 +        debugLog("sending "+data.length+" bytes to stdinWorker\n");
  1.1466 +        var pipe = parseInt(child.stdin);
  1.1467 +
  1.1468 +        stdinWorker.postMessage({
  1.1469 +            msg: 'write',
  1.1470 +            pipe: pipe,
  1.1471 +            data: data
  1.1472 +        });
  1.1473 +    }
  1.1474 +
  1.1475 +
  1.1476 +    /*
  1.1477 +     * closeStdinHandle()
  1.1478 +     *
  1.1479 +     * Close the stdin pipe, either directly or by requesting the ChromeWorker to
  1.1480 +     * close the pipe. The ChromeWorker will only close the pipe after the last write
  1.1481 +     * request process is done.
  1.1482 +     */
  1.1483 +
  1.1484 +    function closeStdinHandle() {
  1.1485 +        debugLog("trying to close stdin\n");
  1.1486 +        if (stdinOpenState != OPEN) return;
  1.1487 +        stdinOpenState = CLOSEABLE;
  1.1488 +
  1.1489 +        if (stdinWorker) {
  1.1490 +            debugLog("sending close stdin to worker\n");
  1.1491 +            var pipePtr = parseInt(child.stdin);
  1.1492 +
  1.1493 +            stdinWorker.postMessage({
  1.1494 +                msg: 'close',
  1.1495 +                pipe: pipePtr
  1.1496 +            });
  1.1497 +        }
  1.1498 +        else {
  1.1499 +            stdinOpenState = CLOSED;
  1.1500 +            debugLog("Closing Stdin\n");
  1.1501 +            close(child.stdin) && LogError("CloseHandle stdin failed");
  1.1502 +        }
  1.1503 +    }
  1.1504 +
  1.1505 +
  1.1506 +    /*
  1.1507 +     * createReader(pipe, name)
  1.1508 +     *
  1.1509 +     * @pipe: handle to the pipe
  1.1510 +     * @name: String containing the pipe name (stdout or stderr)
  1.1511 +     * @callbackFunc: function to be called with the read data
  1.1512 +     *
  1.1513 +     * Create a ChromeWorker object for reading data asynchronously from
  1.1514 +     * the pipe (i.e. on a separate thread), and passing the result back to
  1.1515 +     * the caller.
  1.1516 +     *
  1.1517 +     */
  1.1518 +    function createReader(pipe, name, callbackFunc) {
  1.1519 +        var worker = new ChromeWorker(WORKER_URL_UNIX);
  1.1520 +        worker.onmessage = function(event) {
  1.1521 +            switch(event.data.msg) {
  1.1522 +            case "data":
  1.1523 +                debugLog("got "+event.data.count+" bytes from "+name+"\n");
  1.1524 +                var data = '';
  1.1525 +                if (options.charset === null) {
  1.1526 +                    data = getBytes(event.data.data);
  1.1527 +                }
  1.1528 +                else {
  1.1529 +                    try {
  1.1530 +                        data = convertBytes(event.data.data, options.charset);
  1.1531 +                    }
  1.1532 +                    catch(ex) {
  1.1533 +                        console.warn("error decoding output: " + ex);
  1.1534 +                        data = getBytes(event.data.data);
  1.1535 +                    }
  1.1536 +                }
  1.1537 +
  1.1538 +                callbackFunc(data);
  1.1539 +                break;
  1.1540 +            case "done":
  1.1541 +                debugLog("Pipe "+name+" closed\n");
  1.1542 +                if (event.data.data > 0) workerExitCode = event.data.data;
  1.1543 +                --readers;
  1.1544 +                if (readers == 0) cleanup();
  1.1545 +                break;
  1.1546 +            default:
  1.1547 +                debugLog("Got msg from "+name+": "+event.data.data+"\n");
  1.1548 +            }
  1.1549 +        }
  1.1550 +        worker.onerror = function(error) {
  1.1551 +            LogError("Got error from "+name+": "+error.message);
  1.1552 +        }
  1.1553 +
  1.1554 +        worker.postMessage({
  1.1555 +                msg: 'read',
  1.1556 +                pipe: pipe,
  1.1557 +                pid: pid,
  1.1558 +                libc: options.libc,
  1.1559 +                charset: options.charset === null ? "null" : options.charset,
  1.1560 +                name: name
  1.1561 +            });
  1.1562 +
  1.1563 +        return worker;
  1.1564 +    }
  1.1565 +
  1.1566 +    /*
  1.1567 +     * readPipes()
  1.1568 +     *
  1.1569 +     * Open the pipes for reading from stdout and stderr
  1.1570 +     */
  1.1571 +    function readPipes() {
  1.1572 +
  1.1573 +        stdoutWorker = createReader(child.stdout, "stdout", function (data) {
  1.1574 +            if(options.stdout) {
  1.1575 +                setTimeout(function() {
  1.1576 +                    options.stdout(data);
  1.1577 +                }, 0);
  1.1578 +            } else {
  1.1579 +                output += data;
  1.1580 +            }
  1.1581 +        });
  1.1582 +
  1.1583 +        if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
  1.1584 +            if(options.stderr) {
  1.1585 +                setTimeout(function() {
  1.1586 +                    options.stderr(data);
  1.1587 +                 }, 0);
  1.1588 +            } else {
  1.1589 +                error += data;
  1.1590 +            }
  1.1591 +        });
  1.1592 +    }
  1.1593 +
  1.1594 +    function cleanup() {
  1.1595 +        debugLog("Cleanup called\n");
  1.1596 +        if(active) {
  1.1597 +            active = false;
  1.1598 +
  1.1599 +            closeStdinHandle(); // should only be required in case of errors
  1.1600 +
  1.1601 +            var result, status = ctypes.int();
  1.1602 +            result = waitpid(child.pid, status.address(), 0);
  1.1603 +            if (result > 0)
  1.1604 +                exitCode = status.value
  1.1605 +            else
  1.1606 +                if (workerExitCode >= 0)
  1.1607 +                    exitCode = workerExitCode
  1.1608 +                else
  1.1609 +                    exitCode = status.value;
  1.1610 +
  1.1611 +            if (stdinWorker)
  1.1612 +                stdinWorker.postMessage({msg: 'stop'})
  1.1613 +
  1.1614 +            setTimeout(function _done() {
  1.1615 +                if (options.done) {
  1.1616 +                    try {
  1.1617 +                        options.done({
  1.1618 +                            exitCode: exitCode,
  1.1619 +                            stdout: output,
  1.1620 +                            stderr: error,
  1.1621 +                        });
  1.1622 +                    }
  1.1623 +                    catch(ex) {
  1.1624 +                        // prevent from blocking if options.done() throws an error
  1.1625 +                        done = true;
  1.1626 +                        throw ex;
  1.1627 +                    }
  1.1628 +
  1.1629 +                }
  1.1630 +                done = true;
  1.1631 +            }, 0);
  1.1632 +
  1.1633 +            libc.close();
  1.1634 +        }
  1.1635 +    }
  1.1636 +
  1.1637 +    //main
  1.1638 +
  1.1639 +    var cmdStr = getCommandStr(options.command);
  1.1640 +    var workDir = getWorkDir(options.workdir);
  1.1641 +
  1.1642 +    child = {};
  1.1643 +    pid = popen(cmdStr, workDir, options.arguments, options.environment, child);
  1.1644 +
  1.1645 +    debugLog("subprocess started; got PID "+pid+"\n");
  1.1646 +
  1.1647 +    readPipes();
  1.1648 +
  1.1649 +    if (options.stdin) {
  1.1650 +        createStdinWriter();
  1.1651 +        if(typeof(options.stdin) == 'function') {
  1.1652 +            try {
  1.1653 +                options.stdin({
  1.1654 +                    write: function(data) {
  1.1655 +                        writeStdin(data);
  1.1656 +                    },
  1.1657 +                    close: function() {
  1.1658 +                        closeStdinHandle();
  1.1659 +                    }
  1.1660 +                });
  1.1661 +            }
  1.1662 +            catch(ex) {
  1.1663 +                // prevent from failing if options.stdin() throws an exception
  1.1664 +                closeStdinHandle();
  1.1665 +                throw ex;
  1.1666 +            }
  1.1667 +        } else {
  1.1668 +            writeStdin(options.stdin);
  1.1669 +            closeStdinHandle();
  1.1670 +        }
  1.1671 +    }
  1.1672 +
  1.1673 +    return {
  1.1674 +        wait: function() {
  1.1675 +            // wait for async operations to complete
  1.1676 +            var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
  1.1677 +            while (! done) thread.processNextEvent(true)
  1.1678 +            return exitCode;
  1.1679 +        },
  1.1680 +        kill: function(hardKill) {
  1.1681 +            var rv = kill(pid, (hardKill ? 9: 15));
  1.1682 +            cleanup(-1);
  1.1683 +            return rv;
  1.1684 +        },
  1.1685 +        pid: pid
  1.1686 +    }
  1.1687 +}
  1.1688 +
  1.1689 +
  1.1690 +module.exports = subprocess;

mercurial