b2g/simulator/packages/subprocess/lib/subprocess.js

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     4  */
     6 'use strict';
     8 const { Cc, Ci, Cu, ChromeWorker } = require("chrome");
    10 Cu.import("resource://gre/modules/ctypes.jsm");
    12 const NS_LOCAL_FILE = "@mozilla.org/file/local;1";
    14 const Runtime = require("sdk/system/runtime");
    15 const Environment = require("sdk/system/environment").env;
    16 const DEFAULT_ENVIRONMENT = [];
    17 if (Runtime.OS == "Linux" && "DISPLAY" in Environment) {
    18   DEFAULT_ENVIRONMENT.push("DISPLAY=" + Environment.DISPLAY);
    19 }
    21 /*
    22 Fake require statements to ensure worker scripts are packaged:
    23 require("subprocess_worker_win.js");
    24 require("subprocess_worker_unix.js");
    25 */
    26 const URL_PREFIX = module.uri.replace(/subprocess\.js/, "");
    27 const WORKER_URL_WIN = URL_PREFIX + "subprocess_worker_win.js";
    28 const WORKER_URL_UNIX = URL_PREFIX + "subprocess_worker_unix.js";
    30 //Windows API definitions
    31 if (ctypes.size_t.size == 8) {
    32     var WinABI = ctypes.default_abi;
    33 } else {
    34     var WinABI = ctypes.winapi_abi;
    35 }
    36 const WORD = ctypes.uint16_t;
    37 const DWORD = ctypes.uint32_t;
    38 const LPDWORD = DWORD.ptr;
    40 const UINT = ctypes.unsigned_int;
    41 const BOOL = ctypes.bool;
    42 const HANDLE = ctypes.size_t;
    43 const HWND = HANDLE;
    44 const HMODULE = HANDLE;
    45 const WPARAM = ctypes.size_t;
    46 const LPARAM = ctypes.size_t;
    47 const LRESULT = ctypes.size_t;
    48 const ULONG_PTR = ctypes.uintptr_t;
    49 const PVOID = ctypes.voidptr_t;
    50 const LPVOID = PVOID;
    51 const LPCTSTR = ctypes.jschar.ptr;
    52 const LPCWSTR = ctypes.jschar.ptr;
    53 const LPTSTR = ctypes.jschar.ptr;
    54 const LPSTR = ctypes.char.ptr;
    55 const LPCSTR = ctypes.char.ptr;
    56 const LPBYTE = ctypes.char.ptr;
    58 const CREATE_NEW_CONSOLE = 0x00000010;
    59 const CREATE_NO_WINDOW = 0x08000000;
    60 const CREATE_UNICODE_ENVIRONMENT = 0x00000400;
    61 const STARTF_USESHOWWINDOW = 0x00000001;
    62 const STARTF_USESTDHANDLES = 0x00000100;
    63 const SW_HIDE = 0;
    64 const DUPLICATE_SAME_ACCESS = 0x00000002;
    65 const STILL_ACTIVE = 259;
    66 const INFINITE = DWORD(0xFFFFFFFF);
    67 const WAIT_TIMEOUT = 0x00000102;
    69 /*
    70 typedef struct _SECURITY_ATTRIBUTES {
    71  DWORD  nLength;
    72  LPVOID lpSecurityDescriptor;
    73  BOOL   bInheritHandle;
    74 } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
    75 */
    76 const SECURITY_ATTRIBUTES = new ctypes.StructType("SECURITY_ATTRIBUTES", [
    77     {"nLength": DWORD},
    78     {"lpSecurityDescriptor": LPVOID},
    79     {"bInheritHandle": BOOL},
    80 ]);
    82 /*
    83 typedef struct _STARTUPINFO {
    84   DWORD  cb;
    85   LPTSTR lpReserved;
    86   LPTSTR lpDesktop;
    87   LPTSTR lpTitle;
    88   DWORD  dwX;
    89   DWORD  dwY;
    90   DWORD  dwXSize;
    91   DWORD  dwYSize;
    92   DWORD  dwXCountChars;
    93   DWORD  dwYCountChars;
    94   DWORD  dwFillAttribute;
    95   DWORD  dwFlags;
    96   WORD   wShowWindow;
    97   WORD   cbReserved2;
    98   LPBYTE lpReserved2;
    99   HANDLE hStdInput;
   100   HANDLE hStdOutput;
   101   HANDLE hStdError;
   102 } STARTUPINFO, *LPSTARTUPINFO;
   103 */
   104 const STARTUPINFO = new ctypes.StructType("STARTUPINFO", [
   105     {"cb": DWORD},
   106     {"lpReserved": LPTSTR},
   107     {"lpDesktop": LPTSTR},
   108     {"lpTitle": LPTSTR},
   109     {"dwX": DWORD},
   110     {"dwY": DWORD},
   111     {"dwXSize": DWORD},
   112     {"dwYSize": DWORD},
   113     {"dwXCountChars": DWORD},
   114     {"dwYCountChars": DWORD},
   115     {"dwFillAttribute": DWORD},
   116     {"dwFlags": DWORD},
   117     {"wShowWindow": WORD},
   118     {"cbReserved2": WORD},
   119     {"lpReserved2": LPBYTE},
   120     {"hStdInput": HANDLE},
   121     {"hStdOutput": HANDLE},
   122     {"hStdError": HANDLE},
   123 ]);
   125 /*
   126 typedef struct _PROCESS_INFORMATION {
   127   HANDLE hProcess;
   128   HANDLE hThread;
   129   DWORD  dwProcessId;
   130   DWORD  dwThreadId;
   131 } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
   132 */
   133 const PROCESS_INFORMATION = new ctypes.StructType("PROCESS_INFORMATION", [
   134     {"hProcess": HANDLE},
   135     {"hThread": HANDLE},
   136     {"dwProcessId": DWORD},
   137     {"dwThreadId": DWORD},
   138 ]);
   140 /*
   141 typedef struct _OVERLAPPED {
   142   ULONG_PTR Internal;
   143   ULONG_PTR InternalHigh;
   144   union {
   145     struct {
   146       DWORD Offset;
   147       DWORD OffsetHigh;
   148     };
   149     PVOID  Pointer;
   150   };
   151   HANDLE    hEvent;
   152 } OVERLAPPED, *LPOVERLAPPED;
   153 */
   154 const OVERLAPPED = new ctypes.StructType("OVERLAPPED");
   156 //UNIX definitions
   157 const pid_t = ctypes.int32_t;
   158 const WNOHANG = 1;
   159 const F_GETFD = 1;
   160 const F_SETFL = 4;
   162 const LIBNAME       = 0;
   163 const O_NONBLOCK    = 1;
   164 const RLIM_T        = 2;
   165 const RLIMIT_NOFILE = 3;
   167 function getPlatformValue(valueType) {
   169     if (! gXulRuntime)
   170         gXulRuntime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
   172     const platformDefaults = {
   173         // Windows API:
   174         'winnt':   [ 'kernel32.dll' ],
   176         // Unix API:
   177         //            library name   O_NONBLOCK RLIM_T                RLIMIT_NOFILE
   178         'darwin':  [ 'libc.dylib',   0x04     , ctypes.uint64_t     , 8 ],
   179         'linux':   [ 'libc.so.6',    2024     , ctypes.unsigned_long, 7 ],
   180         'freebsd': [ 'libc.so.7',    0x04     , ctypes.int64_t      , 8 ],
   181         'openbsd': [ 'libc.so.61.0', 0x04     , ctypes.int64_t      , 8 ],
   182         'sunos':   [ 'libc.so',      0x80     , ctypes.unsigned_long, 5 ]
   183     }
   185     return platformDefaults[gXulRuntime.OS.toLowerCase()][valueType];
   186 }
   189 var gDebugFunc = null,
   190     gLogFunc = null,
   191     gXulRuntime = null;
   193 function LogError(s) {
   194     if (gLogFunc)
   195         gLogFunc(s);
   196     else
   197         dump(s);
   198 }
   200 function debugLog(s) {
   201     if (gDebugFunc)
   202         gDebugFunc(s);
   203 }
   205 function setTimeout(callback, timeout) {
   206     var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   207     timer.initWithCallback(callback, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
   208 };
   210 function getBytes(data) {
   211   var string = '';
   212   data.forEach(function(x) { string += String.fromCharCode(x < 0 ? x + 256 : x) });
   213   return string;
   214 }
   216 function readString(data, length, charset) {
   217     var string = '', bytes = [];
   218     for(var i = 0;i < length; i++) {
   219         if(data[i] == 0 && charset !== null) // stop on NULL character for non-binary data
   220            break
   221         bytes.push(data[i]);
   222     }
   223     if (!bytes || bytes.length == 0)
   224         return string;
   225     if(charset === null) {
   226         return bytes;
   227     }
   228     return convertBytes(bytes, charset);
   229 }
   231 function convertBytes(bytes, charset) {
   232     var string = '';
   233     charset = charset || 'UTF-8';
   234     var unicodeConv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
   235                         .getService(Ci.nsIScriptableUnicodeConverter);
   236     try {
   237         unicodeConv.charset = charset;
   238         string = unicodeConv.convertFromByteArray(bytes, bytes.length);
   239     } catch (ex) {
   240         LogError("String conversion failed: "+ex.toString()+"\n")
   241         string = '';
   242     }
   243     string += unicodeConv.Finish();
   244     return string;
   245 }
   248 // temporary solution for removal of nsILocalFile
   249 function getLocalFileApi() {
   250   if ("nsILocalFile" in Ci) {
   251     return Ci.nsILocalFile;
   252   }
   253   else
   254     return Ci.nsIFile;
   255 }
   257 function getCommandStr(command) {
   258     let commandStr = null;
   259     if (typeof(command) == "string") {
   260         let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
   261         file.initWithPath(command);
   262         if (! (file.isExecutable() && file.isFile()))
   263             throw("File '"+command+"' is not an executable file");
   264         commandStr = command;
   265     }
   266     else {
   267         if (! (command.isExecutable() && command.isFile()))
   268             throw("File '"+command.path+"' is not an executable file");
   269         commandStr = command.path;
   270     }
   272     return commandStr;
   273 }
   275 function getWorkDir(workdir) {
   276     let workdirStr = null;
   277     if (typeof(workdir) == "string") {
   278         let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
   279         file.initWithPath(workdir);
   280         if (! (file.isDirectory()))
   281             throw("Directory '"+workdir+"' does not exist");
   282         workdirStr = workdir;
   283     }
   284     else if (workdir) {
   285         if (! workdir.isDirectory())
   286             throw("Directory '"+workdir.path+"' does not exist");
   287         workdirStr = workdir.path;
   288     }
   289     return workdirStr;
   290 }
   293 var subprocess = {
   294     call: function(options) {
   295         options.mergeStderr = options.mergeStderr || false;
   296         options.workdir = options.workdir ||  null;
   297         options.environment = options.environment || DEFAULT_ENVIRONMENT;
   298         if (options.arguments) {
   299             var args = options.arguments;
   300             options.arguments = [];
   301             args.forEach(function(argument) {
   302                 options.arguments.push(argument);
   303             });
   304         } else {
   305             options.arguments = [];
   306         }
   308         options.libc = getPlatformValue(LIBNAME);
   310         if (gXulRuntime.OS.substring(0, 3) == "WIN") {
   311             return subprocess_win32(options);
   312         } else {
   313             return subprocess_unix(options);
   314         }
   316     },
   317     registerDebugHandler: function(func) {
   318         gDebugFunc = func;
   319     },
   320     registerLogHandler: function(func) {
   321         gLogFunc = func;
   322     }
   323 };
   327 function subprocess_win32(options) {
   328     var kernel32dll = ctypes.open(options.libc),
   329         hChildProcess,
   330         active = true,
   331         done = false,
   332         exitCode = -1,
   333         child = {},
   334         stdinWorker = null,
   335         stdoutWorker = null,
   336         stderrWorker = null,
   337         pendingWriteCount = 0,
   338         readers = options.mergeStderr ? 1 : 2,
   339         stdinOpenState = 2,
   340         error = '',
   341         output = '';
   343     // stdin pipe states
   344     const OPEN = 2;
   345     const CLOSEABLE = 1;
   346     const CLOSED = 0;
   348     //api declarations
   349     /*
   350     BOOL WINAPI CloseHandle(
   351       __in  HANDLE hObject
   352     );
   353     */
   354     var CloseHandle = kernel32dll.declare("CloseHandle",
   355                                             WinABI,
   356                                             BOOL,
   357                                             HANDLE
   358     );
   360     /*
   361     BOOL WINAPI CreateProcess(
   362       __in_opt     LPCTSTR lpApplicationName,
   363       __inout_opt  LPTSTR lpCommandLine,
   364       __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
   365       __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
   366       __in         BOOL bInheritHandles,
   367       __in         DWORD dwCreationFlags,
   368       __in_opt     LPVOID lpEnvironment,
   369       __in_opt     LPCTSTR lpCurrentDirectory,
   370       __in         LPSTARTUPINFO lpStartupInfo,
   371       __out        LPPROCESS_INFORMATION lpProcessInformation
   372     );
   373      */
   374     var CreateProcessW = kernel32dll.declare("CreateProcessW",
   375                                             WinABI,
   376                                             BOOL,
   377                                             LPCTSTR,
   378                                             LPTSTR,
   379                                             SECURITY_ATTRIBUTES.ptr,
   380                                             SECURITY_ATTRIBUTES.ptr,
   381                                             BOOL,
   382                                             DWORD,
   383                                             LPVOID,
   384                                             LPCTSTR,
   385                                             STARTUPINFO.ptr,
   386                                             PROCESS_INFORMATION.ptr
   387                                          );
   389 //     /*
   390 //     BOOL WINAPI ReadFile(
   391 //       __in         HANDLE hFile,
   392 //       __out        LPVOID ReadFileBuffer,
   393 //       __in         DWORD nNumberOfBytesToRead,
   394 //       __out_opt    LPDWORD lpNumberOfBytesRead,
   395 //       __inout_opt  LPOVERLAPPED lpOverlapped
   396 //     );
   397 //     */
   398 //     var ReadFileBufferSize = 1024,
   399 //         ReadFileBuffer = ctypes.char.array(ReadFileBufferSize),
   400 //         ReadFile = kernel32dll.declare("ReadFile",
   401 //                                         WinABI,
   402 //                                         BOOL,
   403 //                                         HANDLE,
   404 //                                         ReadFileBuffer,
   405 //                                         DWORD,
   406 //                                         LPDWORD,
   407 //                                         OVERLAPPED.ptr
   408 //     );
   409 //
   410 //     /*
   411 //     BOOL WINAPI PeekNamedPipe(
   412 //       __in       HANDLE hNamedPipe,
   413 //       __out_opt  LPVOID lpBuffer,
   414 //       __in       DWORD nBufferSize,
   415 //       __out_opt  LPDWORD lpBytesRead,
   416 //       __out_opt  LPDWORD lpTotalBytesAvail,
   417 //       __out_opt  LPDWORD lpBytesLeftThisMessage
   418 //     );
   419 //     */
   420 //     var PeekNamedPipe = kernel32dll.declare("PeekNamedPipe",
   421 //                                         WinABI,
   422 //                                         BOOL,
   423 //                                         HANDLE,
   424 //                                         ReadFileBuffer,
   425 //                                         DWORD,
   426 //                                         LPDWORD,
   427 //                                         LPDWORD,
   428 //                                         LPDWORD
   429 //     );
   430 //
   431 //     /*
   432 //     BOOL WINAPI WriteFile(
   433 //       __in         HANDLE hFile,
   434 //       __in         LPCVOID lpBuffer,
   435 //       __in         DWORD nNumberOfBytesToWrite,
   436 //       __out_opt    LPDWORD lpNumberOfBytesWritten,
   437 //       __inout_opt  LPOVERLAPPED lpOverlapped
   438 //     );
   439 //     */
   440 //     var WriteFile = kernel32dll.declare("WriteFile",
   441 //                                         WinABI,
   442 //                                         BOOL,
   443 //                                         HANDLE,
   444 //                                         ctypes.char.ptr,
   445 //                                         DWORD,
   446 //                                         LPDWORD,
   447 //                                         OVERLAPPED.ptr
   448 //     );
   450     /*
   451     BOOL WINAPI CreatePipe(
   452       __out     PHANDLE hReadPipe,
   453       __out     PHANDLE hWritePipe,
   454       __in_opt  LPSECURITY_ATTRIBUTES lpPipeAttributes,
   455       __in      DWORD nSize
   456     );
   457     */
   458     var CreatePipe = kernel32dll.declare("CreatePipe",
   459                                         WinABI,
   460                                         BOOL,
   461                                         HANDLE.ptr,
   462                                         HANDLE.ptr,
   463                                         SECURITY_ATTRIBUTES.ptr,
   464                                         DWORD
   465     );
   467     /*
   468     HANDLE WINAPI GetCurrentProcess(void);
   469     */
   470     var GetCurrentProcess = kernel32dll.declare("GetCurrentProcess",
   471                                         WinABI,
   472                                         HANDLE
   473     );
   475     /*
   476     DWORD WINAPI GetLastError(void);
   477     */
   478     var GetLastError = kernel32dll.declare("GetLastError",
   479                                         WinABI,
   480                                         DWORD
   481     );
   483     /*
   484     BOOL WINAPI DuplicateHandle(
   485       __in   HANDLE hSourceProcessHandle,
   486       __in   HANDLE hSourceHandle,
   487       __in   HANDLE hTargetProcessHandle,
   488       __out  LPHANDLE lpTargetHandle,
   489       __in   DWORD dwDesiredAccess,
   490       __in   BOOL bInheritHandle,
   491       __in   DWORD dwOptions
   492     );
   493     */
   494     var DuplicateHandle = kernel32dll.declare("DuplicateHandle",
   495                                         WinABI,
   496                                         BOOL,
   497                                         HANDLE,
   498                                         HANDLE,
   499                                         HANDLE,
   500                                         HANDLE.ptr,
   501                                         DWORD,
   502                                         BOOL,
   503                                         DWORD
   504     );
   507     /*
   508     BOOL WINAPI GetExitCodeProcess(
   509       __in   HANDLE hProcess,
   510       __out  LPDWORD lpExitCode
   511     );
   512     */
   513     var GetExitCodeProcess = kernel32dll.declare("GetExitCodeProcess",
   514                                         WinABI,
   515                                         BOOL,
   516                                         HANDLE,
   517                                         LPDWORD
   518     );
   520     /*
   521     DWORD WINAPI WaitForSingleObject(
   522       __in  HANDLE hHandle,
   523       __in  DWORD dwMilliseconds
   524     );
   525     */
   526     var WaitForSingleObject = kernel32dll.declare("WaitForSingleObject",
   527                                         WinABI,
   528                                         DWORD,
   529                                         HANDLE,
   530                                         DWORD
   531     );
   533     /*
   534     BOOL WINAPI TerminateProcess(
   535       __in  HANDLE hProcess,
   536       __in  UINT uExitCode
   537     );
   538     */
   539     var TerminateProcess = kernel32dll.declare("TerminateProcess",
   540                                         WinABI,
   541                                         BOOL,
   542                                         HANDLE,
   543                                         UINT
   544     );
   546     //functions
   547     function popen(command, workdir, args, environment, child) {
   548         //escape arguments
   549         args.unshift(command);
   550         for (var i = 0; i < args.length; i++) {
   551           if (typeof args[i] != "string") { args[i] = args[i].toString(); }
   552           /* quote arguments with spaces */
   553           if (args[i].match(/\s/)) {
   554             args[i] = "\"" + args[i] + "\"";
   555           }
   556           /* If backslash is followed by a quote, double it */
   557           args[i] = args[i].replace(/\\\"/g, "\\\\\"");
   558         }
   559         command = args.join(' ');
   561         environment = environment || [];
   562         if(environment.length) {
   563             //An environment block consists of
   564             //a null-terminated block of null-terminated strings.
   565             //Using CREATE_UNICODE_ENVIRONMENT so needs to be jschar
   566             environment = ctypes.jschar.array()(environment.join('\0') + '\0');
   567         } else {
   568             environment = null;
   569         }
   571         var hOutputReadTmp = new HANDLE(),
   572             hOutputRead = new HANDLE(),
   573             hOutputWrite = new HANDLE();
   575         var hErrorRead = new HANDLE(),
   576             hErrorReadTmp = new HANDLE(),
   577             hErrorWrite = new HANDLE();
   579         var hInputRead = new HANDLE(),
   580             hInputWriteTmp = new HANDLE(),
   581             hInputWrite = new HANDLE();
   583         // Set up the security attributes struct.
   584         var sa = new SECURITY_ATTRIBUTES();
   585         sa.nLength = SECURITY_ATTRIBUTES.size;
   586         sa.lpSecurityDescriptor = null;
   587         sa.bInheritHandle = true;
   589         // Create output pipe.
   591         if(!CreatePipe(hOutputReadTmp.address(), hOutputWrite.address(), sa.address(), 0))
   592             LogError('CreatePipe hOutputReadTmp failed');
   594         if(options.mergeStderr) {
   595           // Create a duplicate of the output write handle for the std error
   596           // write handle. This is necessary in case the child application
   597           // closes one of its std output handles.
   598           if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
   599                                GetCurrentProcess(), hErrorWrite.address(), 0,
   600                                true, DUPLICATE_SAME_ACCESS))
   601              LogError("DuplicateHandle hOutputWrite failed");
   602         } else {
   603             // Create error pipe.
   604             if(!CreatePipe(hErrorReadTmp.address(), hErrorWrite.address(), sa.address(), 0))
   605                 LogError('CreatePipe hErrorReadTmp failed');
   606         }
   608         // Create input pipe.
   609         if (!CreatePipe(hInputRead.address(),hInputWriteTmp.address(),sa.address(), 0))
   610             LogError("CreatePipe hInputRead failed");
   612         // Create new output/error read handle and the input write handles. Set
   613         // the Properties to FALSE. Otherwise, the child inherits the
   614         // properties and, as a result, non-closeable handles to the pipes
   615         // are created.
   616         if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
   617                              GetCurrentProcess(),
   618                              hOutputRead.address(), // Address of new handle.
   619                              0, false, // Make it uninheritable.
   620                              DUPLICATE_SAME_ACCESS))
   621              LogError("DupliateHandle hOutputReadTmp failed");
   623         if(!options.mergeStderr) {
   624             if (!DuplicateHandle(GetCurrentProcess(), hErrorReadTmp,
   625                              GetCurrentProcess(),
   626                              hErrorRead.address(), // Address of new handle.
   627                              0, false, // Make it uninheritable.
   628                              DUPLICATE_SAME_ACCESS))
   629              LogError("DupliateHandle hErrorReadTmp failed");
   630         }
   631         if (!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp,
   632                              GetCurrentProcess(),
   633                              hInputWrite.address(), // Address of new handle.
   634                              0, false, // Make it uninheritable.
   635                              DUPLICATE_SAME_ACCESS))
   636           LogError("DupliateHandle hInputWriteTmp failed");
   638         // Close inheritable copies of the handles.
   639         if (!CloseHandle(hOutputReadTmp)) LogError("CloseHandle hOutputReadTmp failed");
   640         if(!options.mergeStderr)
   641             if (!CloseHandle(hErrorReadTmp)) LogError("CloseHandle hErrorReadTmp failed");
   642         if (!CloseHandle(hInputWriteTmp)) LogError("CloseHandle failed");
   644         var pi = new PROCESS_INFORMATION();
   645         var si = new STARTUPINFO();
   647         si.cb = STARTUPINFO.size;
   648         si.dwFlags = STARTF_USESTDHANDLES;
   649         si.hStdInput  = hInputRead;
   650         si.hStdOutput = hOutputWrite;
   651         si.hStdError  = hErrorWrite;
   653         // Launch the process
   654         if(!CreateProcessW(null,            // executable name
   655                            command,         // command buffer
   656                            null,            // process security attribute
   657                            null,            // thread security attribute
   658                            true,            // inherits system handles
   659                            CREATE_UNICODE_ENVIRONMENT|CREATE_NO_WINDOW, // process flags
   660                            environment,     // envrionment block
   661                            workdir,          // set as current directory
   662                            si.address(),    // (in) startup information
   663                            pi.address()     // (out) process information
   664         ))
   665             throw("Fatal - Could not launch subprocess '"+command+"'");
   667         // Close any unnecessary handles.
   668         if (!CloseHandle(pi.hThread))
   669             LogError("CloseHandle pi.hThread failed");
   671         // Close pipe handles (do not continue to modify the parent).
   672         // You need to make sure that no handles to the write end of the
   673         // output pipe are maintained in this process or else the pipe will
   674         // not close when the child process exits and the ReadFile will hang.
   675         if (!CloseHandle(hInputRead)) LogError("CloseHandle hInputRead failed");
   676         if (!CloseHandle(hOutputWrite)) LogError("CloseHandle hOutputWrite failed");
   677         if (!CloseHandle(hErrorWrite)) LogError("CloseHandle hErrorWrite failed");
   679         //return values
   680         child.stdin = hInputWrite;
   681         child.stdout = hOutputRead;
   682         child.stderr = options.mergeStderr ? undefined : hErrorRead;
   683         child.process = pi.hProcess;
   684         return pi.hProcess;
   685     }
   687     /*
   688      * createStdinWriter ()
   689      *
   690      * Create a ChromeWorker object for writing data to the subprocess' stdin
   691      * pipe. The ChromeWorker object lives on a separate thread; this avoids
   692      * internal deadlocks.
   693      */
   694     function createStdinWriter() {
   695         debugLog("Creating new stdin worker\n");
   696         stdinWorker = new ChromeWorker(WORKER_URL_WIN);
   697         stdinWorker.onmessage = function(event) {
   698             switch(event.data) {
   699             case "WriteOK":
   700                 pendingWriteCount--;
   701                 debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
   702                 break;
   703             case "ClosedOK":
   704                 stdinOpenState = CLOSED;
   705                 debugLog("Stdin pipe closed\n");
   706                 break;
   707             default:
   708                 debugLog("got msg from stdinWorker: "+event.data+"\n");
   709             }
   710         }
   711         stdinWorker.onerror = function(error) {
   712             pendingWriteCount--;
   713             LogError("got error from stdinWorker: "+error.message+"\n");
   714         }
   716         stdinWorker.postMessage({msg: "init", libc: options.libc});
   717     }
   719     /*
   720      * writeStdin()
   721      * @data: String containing the data to write
   722      *
   723      * Write data to the subprocess' stdin (equals to sending a request to the
   724      * ChromeWorker object to write the data).
   725      */
   726     function writeStdin(data) {
   727         ++pendingWriteCount;
   728         debugLog("sending "+data.length+" bytes to stdinWorker\n");
   729         var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
   731         stdinWorker.postMessage({
   732                 msg: 'write',
   733                 pipe: pipePtr,
   734                 data: data
   735             });
   736     }
   738     /*
   739      * closeStdinHandle()
   740      *
   741      * Close the stdin pipe, either directly or by requesting the ChromeWorker to
   742      * close the pipe. The ChromeWorker will only close the pipe after the last write
   743      * request process is done.
   744      */
   746     function closeStdinHandle() {
   747         debugLog("trying to close stdin\n");
   748         if (stdinOpenState != OPEN) return;
   749         stdinOpenState = CLOSEABLE;
   751         if (stdinWorker) {
   752             debugLog("sending close stdin to worker\n");
   753             var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
   754             stdinWorker.postMessage({
   755                 msg: 'close',
   756                 pipe: pipePtr
   757             });
   758         }
   759         else {
   760             stdinOpenState = CLOSED;
   761             debugLog("Closing Stdin\n");
   762             CloseHandle(child.stdin) || LogError("CloseHandle hInputWrite failed");
   763         }
   764     }
   767     /*
   768      * createReader(pipe, name)
   769      *
   770      * @pipe: handle to the pipe
   771      * @name: String containing the pipe name (stdout or stderr)
   772      *
   773      * Create a ChromeWorker object for reading data asynchronously from
   774      * the pipe (i.e. on a separate thread), and passing the result back to
   775      * the caller.
   776      */
   777     function createReader(pipe, name, callbackFunc) {
   778         var worker = new ChromeWorker(WORKER_URL_WIN);
   779         worker.onmessage = function(event) {
   780             switch(event.data.msg) {
   781             case "data":
   782                 debugLog("got "+event.data.count+" bytes from "+name+"\n");
   783                 var data = '';
   784                 if (options.charset === null) {
   785                     data = getBytes(event.data.data);
   786                 }
   787                 else {
   788                     try {
   789                         data = convertBytes(event.data.data, options.charset);
   790                     }
   791                     catch(ex) {
   792                         console.warn("error decoding output: " + ex);
   793                         data = getBytes(event.data.data);
   794                     }
   795                 }
   797                 callbackFunc(data);
   798                 break;
   799             case "done":
   800                 debugLog("Pipe "+name+" closed\n");
   801                 --readers;
   802                 if (readers == 0) cleanup();
   803                 break;
   804             default:
   805                 debugLog("Got msg from "+name+": "+event.data.data+"\n");
   806             }
   807         }
   809         worker.onerror = function(errorMsg) {
   810             LogError("Got error from "+name+": "+errorMsg.message);
   811         }
   813         var pipePtr = parseInt(ctypes.cast(pipe.address(), ctypes.uintptr_t).value);
   815         worker.postMessage({
   816                 msg: 'read',
   817                 pipe: pipePtr,
   818                 libc: options.libc,
   819                 charset: options.charset === null ? "null" : options.charset,
   820                 name: name
   821             });
   823         return worker;
   824     }
   826     /*
   827      * readPipes()
   828      *
   829      * Open the pipes for reading from stdout and stderr
   830      */
   831     function readPipes() {
   833         stdoutWorker = createReader(child.stdout, "stdout", function (data) {
   834             if(options.stdout) {
   835                 setTimeout(function() {
   836                     options.stdout(data);
   837                 }, 0);
   838             } else {
   839                 output += data;
   840             }
   841         });
   844         if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
   845             if(options.stderr) {
   846                 setTimeout(function() {
   847                     options.stderr(data);
   848                 }, 0);
   849             } else {
   850                 error += data;
   851             }
   852         });
   853     }
   855     /*
   856      * cleanup()
   857      *
   858      * close stdin if needed, get the exit code from the subprocess and invoke
   859      * the caller's done() function.
   860      *
   861      * Note: because stdout() and stderr() are called using setTimeout, we need to
   862      * do the same here in order to guarantee the message sequence.
   863      */
   864     function cleanup() {
   865         debugLog("Cleanup called\n");
   866         if(active) {
   867             active = false;
   869             closeStdinHandle(); // should only be required in case of errors
   871             var exit = new DWORD();
   872             GetExitCodeProcess(child.process, exit.address());
   873             exitCode = exit.value;
   875             if (stdinWorker)
   876                 stdinWorker.postMessage({msg: 'stop'})
   878             setTimeout(function _done() {
   879                 if (options.done) {
   880                     try {
   881                         options.done({
   882                             exitCode: exitCode,
   883                             stdout: output,
   884                             stderr: error,
   885                         });
   886                     }
   887                     catch (ex) {
   888                         // prevent from blocking if options.done() throws an error
   889                         done = true;
   890                         throw ex;
   891                     }
   892                 }
   893                 done = true;
   894             }, 0);
   895             kernel32dll.close();
   896         }
   897     }
   899     var cmdStr = getCommandStr(options.command);
   900     var workDir = getWorkDir(options.workdir);
   902     //main
   903     hChildProcess = popen(cmdStr, workDir, options.arguments, options.environment, child);
   905     readPipes();
   907     if (options.stdin) {
   908        createStdinWriter();
   910         if(typeof(options.stdin) == 'function') {
   911             try {
   912                 options.stdin({
   913                     write: function(data) {
   914                         writeStdin(data);
   915                     },
   916                     close: function() {
   917                         closeStdinHandle();
   918                     }
   919                 });
   920             }
   921             catch (ex) {
   922                 // prevent from failing if options.stdin() throws an exception
   923                 closeStdinHandle();
   924                 throw ex;
   925             }
   926         } else {
   927             writeStdin(options.stdin);
   928             closeStdinHandle();
   929         }
   930     }
   931     else
   932         closeStdinHandle();
   934     return {
   935         kill: function(hardKill) {
   936             // hardKill is currently ignored on Windows
   937             var r = !!TerminateProcess(child.process, 255);
   938             cleanup(-1);
   939             return r;
   940         },
   941         wait: function() {
   942             // wait for async operations to complete
   943             var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
   944             while (!done) thread.processNextEvent(true);
   946             return exitCode;
   947         }
   948     }
   949 }
   952 function subprocess_unix(options) {
   953     // stdin pipe states
   954     const OPEN = 2;
   955     const CLOSEABLE = 1;
   956     const CLOSED = 0;
   958     var libc = ctypes.open(options.libc),
   959         active = true,
   960         done = false,
   961         exitCode = -1,
   962         workerExitCode = 0,
   963         child = {},
   964         pid = -1,
   965         stdinWorker = null,
   966         stdoutWorker = null,
   967         stderrWorker = null,
   968         pendingWriteCount = 0,
   969         readers = options.mergeStderr ? 1 : 2,
   970         stdinOpenState = OPEN,
   971         error = '',
   972         output = '';
   974     //api declarations
   976     //pid_t fork(void);
   977     var fork = libc.declare("fork",
   978                          ctypes.default_abi,
   979                          pid_t
   980     );
   982     //NULL terminated array of strings, argv[0] will be command >> + 2
   983     var argv = ctypes.char.ptr.array(options.arguments.length + 2);
   984     var envp = ctypes.char.ptr.array(options.environment.length + 1);
   986     // posix_spawn_file_actions_t is a complex struct that may be different on
   987     // each platform. We do not care about its attributes, we don't need to
   988     // get access to them, but we do need to allocate the right amount
   989     // of memory for it.
   990     // Bug 936297 - Use OS.File internals to fetch
   991     // sizeof(posix_spawn_file_actions_t)
   992     var { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
   993     var sizeof_file_actions_t = OS.Constants.libc.OSFILE_SIZEOF_DIRENT;
   994     var posix_spawn_file_actions_t = ctypes.uint8_t.array(sizeof_file_actions_t);
   996     //int posix_spawn(pid_t *restrict pid, const char *restrict path,
   997     //   const posix_spawn_file_actions_t *file_actions,
   998     //   const posix_spawnattr_t *restrict attrp,
   999     //   char *const argv[restrict], char *const envp[restrict]);
  1000     var posix_spawn = libc.declare("posix_spawn",
  1001                          ctypes.default_abi,
  1002                          ctypes.int,
  1003                          pid_t.ptr,
  1004                          ctypes.char.ptr,
  1005                          posix_spawn_file_actions_t.ptr,
  1006                          ctypes.voidptr_t,
  1007                          argv,
  1008                          envp
  1009     );
  1011     //int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
  1012     var posix_spawn_file_actions_init = libc.declare("posix_spawn_file_actions_init",
  1013                          ctypes.default_abi,
  1014                          ctypes.int,
  1015                          posix_spawn_file_actions_t.ptr
  1016     );
  1018     //int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
  1019     var posix_spawn_file_actions_destroy = libc.declare("posix_spawn_file_actions_destroy",
  1020                          ctypes.default_abi,
  1021                          ctypes.int,
  1022                          posix_spawn_file_actions_t.ptr
  1023     );
  1025     // int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *
  1026     //                                      file_actions, int fildes, int newfildes);
  1027     var posix_spawn_file_actions_adddup2 = libc.declare("posix_spawn_file_actions_adddup2",
  1028                          ctypes.default_abi,
  1029                          ctypes.int,
  1030                          posix_spawn_file_actions_t.ptr,
  1031                          ctypes.int,
  1032                          ctypes.int
  1033     );
  1035     // int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *
  1036     //                                       file_actions, int fildes);
  1037     var posix_spawn_file_actions_addclose = libc.declare("posix_spawn_file_actions_addclose",
  1038                          ctypes.default_abi,
  1039                          ctypes.int,
  1040                          posix_spawn_file_actions_t.ptr,
  1041                          ctypes.int
  1042     );
  1044     //int pipe(int pipefd[2]);
  1045     var pipefd = ctypes.int.array(2);
  1046     var pipe = libc.declare("pipe",
  1047                          ctypes.default_abi,
  1048                          ctypes.int,
  1049                          pipefd
  1050     );
  1052     //int close(int fd);
  1053     var close = libc.declare("close",
  1054                           ctypes.default_abi,
  1055                           ctypes.int,
  1056                           ctypes.int
  1057     );
  1059     //pid_t waitpid(pid_t pid, int *status, int options);
  1060     var waitpid = libc.declare("waitpid",
  1061                           ctypes.default_abi,
  1062                           pid_t,
  1063                           pid_t,
  1064                           ctypes.int.ptr,
  1065                           ctypes.int
  1066     );
  1068     //int kill(pid_t pid, int sig);
  1069     var kill = libc.declare("kill",
  1070                           ctypes.default_abi,
  1071                           ctypes.int,
  1072                           pid_t,
  1073                           ctypes.int
  1074     );
  1076     //int read(int fd, void *buf, size_t count);
  1077     var bufferSize = 1024;
  1078     var buffer = ctypes.char.array(bufferSize);
  1079     var read = libc.declare("read",
  1080                           ctypes.default_abi,
  1081                           ctypes.int,
  1082                           ctypes.int,
  1083                           buffer,
  1084                           ctypes.int
  1085     );
  1087     //ssize_t write(int fd, const void *buf, size_t count);
  1088     var write = libc.declare("write",
  1089                           ctypes.default_abi,
  1090                           ctypes.int,
  1091                           ctypes.int,
  1092                           ctypes.char.ptr,
  1093                           ctypes.int
  1094     );
  1096     //int chdir(const char *path);
  1097     var chdir = libc.declare("chdir",
  1098                           ctypes.default_abi,
  1099                           ctypes.int,
  1100                           ctypes.char.ptr
  1101     );
  1103     //int fcntl(int fd, int cmd, ... /* arg */ );
  1104     var fcntl = libc.declare("fcntl",
  1105                           ctypes.default_abi,
  1106                           ctypes.int,
  1107                           ctypes.int,
  1108                           ctypes.int,
  1109                           ctypes.int
  1110     );
  1112     function popen(command, workdir, args, environment, child) {
  1113         var _in,
  1114             _out,
  1115             _err,
  1116             pid,
  1117             rc;
  1118         _in = new pipefd();
  1119         _out = new pipefd();
  1120         if(!options.mergeStderr)
  1121             _err = new pipefd();
  1123         var _args = argv();
  1124         args.unshift(command);
  1125         for(var i=0;i<args.length;i++) {
  1126             _args[i] = ctypes.char.array()(args[i]);
  1128         var _envp = envp();
  1129         for(var i=0;i<environment.length;i++) {
  1130             _envp[i] = ctypes.char.array()(environment[i]);
  1131             // LogError(_envp);
  1134         rc = pipe(_in);
  1135         if (rc < 0) {
  1136             return -1;
  1138         rc = pipe(_out);
  1139         fcntl(_out[0], F_SETFL, getPlatformValue(O_NONBLOCK));
  1140         if (rc < 0) {
  1141             close(_in[0]);
  1142             close(_in[1]);
  1143             return -1
  1145         if(!options.mergeStderr) {
  1146             rc = pipe(_err);
  1147             fcntl(_err[0], F_SETFL, getPlatformValue(O_NONBLOCK));
  1148             if (rc < 0) {
  1149                 close(_in[0]);
  1150                 close(_in[1]);
  1151                 close(_out[0]);
  1152                 close(_out[1]);
  1153                 return -1
  1157         let STDIN_FILENO = 0;
  1158         let STDOUT_FILENO = 1;
  1159         let STDERR_FILENO = 2;
  1161         let action = posix_spawn_file_actions_t();
  1162         posix_spawn_file_actions_init(action.address());
  1164         posix_spawn_file_actions_adddup2(action.address(), _in[0], STDIN_FILENO);
  1165         posix_spawn_file_actions_addclose(action.address(), _in[1]);
  1166         posix_spawn_file_actions_addclose(action.address(), _in[0]);
  1168         posix_spawn_file_actions_adddup2(action.address(), _out[1], STDOUT_FILENO);
  1169         posix_spawn_file_actions_addclose(action.address(), _out[1]);
  1170         posix_spawn_file_actions_addclose(action.address(), _out[0]);
  1172         if (!options.mergeStderr) {
  1173           posix_spawn_file_actions_adddup2(action.address(), _err[1], STDERR_FILENO);
  1174           posix_spawn_file_actions_addclose(action.address(), _err[1]);
  1175           posix_spawn_file_actions_addclose(action.address(), _err[0]);
  1178         // posix_spawn doesn't support setting a custom workdir for the child,
  1179         // so change the cwd in the parent process before launching the child process.
  1180         if (workdir) {
  1181           if (chdir(workdir) < 0) {
  1182             throw new Error("Unable to change workdir before launching child process");
  1186         closeOtherFds(action, _in[1], _out[0], options.mergeStderr ? undefined : _err[0]);
  1188         let id = pid_t(0);
  1189         let rv = posix_spawn(id.address(), command, action.address(), null, _args, _envp);
  1190         posix_spawn_file_actions_destroy(action.address());
  1191         if (rv != 0) {
  1192           // we should not really end up here
  1193           if(!options.mergeStderr) {
  1194             close(_err[0]);
  1195             close(_err[1]);
  1197           close(_out[0]);
  1198           close(_out[1]);
  1199           close(_in[0]);
  1200           close(_in[1]);
  1201           throw new Error("Fatal - failed to create subprocess '"+command+"'");
  1203         pid = id.value;
  1205         close(_in[0]);
  1206         close(_out[1]);
  1207         if (!options.mergeStderr)
  1208           close(_err[1]);
  1209         child.stdin  = _in[1];
  1210         child.stdout = _out[0];
  1211         child.stderr = options.mergeStderr ? undefined : _err[0];
  1212         child.pid = pid;
  1214         return pid;
  1218     // close any file descriptors that are not required for the process
  1219     function closeOtherFds(action, fdIn, fdOut, fdErr) {
  1220         // Unfortunately on mac, any fd registered in posix_spawn_file_actions_addclose
  1221         // that can't be closed correctly will make posix_spawn fail...
  1222         // Even if we ensure registering only still opened fds.
  1223         if (gXulRuntime.OS == "Darwin")
  1224             return;
  1226         var maxFD = 256; // arbitrary max
  1229         var rlim_t = getPlatformValue(RLIM_T);
  1231         const RLIMITS = new ctypes.StructType("RLIMITS", [
  1232             {"rlim_cur": rlim_t},
  1233             {"rlim_max": rlim_t}
  1234         ]);
  1236         try {
  1237             var getrlimit = libc.declare("getrlimit",
  1238                                   ctypes.default_abi,
  1239                                   ctypes.int,
  1240                                   ctypes.int,
  1241                                   RLIMITS.ptr
  1242             );
  1244             var rl = new RLIMITS();
  1245             if (getrlimit(getPlatformValue(RLIMIT_NOFILE), rl.address()) == 0) {
  1246                 maxFD = rl.rlim_cur;
  1248             debugLog("getlimit: maxFD="+maxFD+"\n");
  1251         catch(ex) {
  1252             debugLog("getrlimit: no such function on this OS\n");
  1253             debugLog(ex.toString());
  1256         // close any file descriptors
  1257         // fd's 0-2 are already closed
  1258         for (var i = 3; i < maxFD; i++) {
  1259             if (i != fdIn && i != fdOut && i != fdErr && fcntl(i, F_GETFD, -1) >= 0) {
  1260                 posix_spawn_file_actions_addclose(action.address(), i);
  1265     /*
  1266      * createStdinWriter ()
  1268      * Create a ChromeWorker object for writing data to the subprocess' stdin
  1269      * pipe. The ChromeWorker object lives on a separate thread; this avoids
  1270      * internal deadlocks.
  1271      */
  1272     function createStdinWriter() {
  1273         debugLog("Creating new stdin worker\n");
  1274         stdinWorker = new ChromeWorker(WORKER_URL_UNIX);
  1275         stdinWorker.onmessage = function(event) {
  1276             switch (event.data.msg) {
  1277             case "info":
  1278                 switch(event.data.data) {
  1279                 case "WriteOK":
  1280                     pendingWriteCount--;
  1281                     debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
  1282                     break;
  1283                 case "ClosedOK":
  1284                     stdinOpenState = CLOSED;
  1285                     debugLog("Stdin pipe closed\n");
  1286                     break;
  1287                 default:
  1288                     debugLog("got msg from stdinWorker: "+event.data.data+"\n");
  1290                 break;
  1291             case "debug":
  1292                 debugLog("stdinWorker: "+event.data.data+"\n");
  1293                 break;
  1294             case "error":
  1295                 LogError("got error from stdinWorker: "+event.data.data+"\n");
  1296                 pendingWriteCount = 0;
  1297                 stdinOpenState = CLOSED;
  1300         stdinWorker.onerror = function(error) {
  1301             pendingWriteCount = 0;
  1302             closeStdinHandle();
  1303             LogError("got error from stdinWorker: "+error.message+"\n");
  1305         stdinWorker.postMessage({msg: "init", libc: options.libc});
  1308     /*
  1309      * writeStdin()
  1310      * @data: String containing the data to write
  1312      * Write data to the subprocess' stdin (equals to sending a request to the
  1313      * ChromeWorker object to write the data).
  1314      */
  1315     function writeStdin(data) {
  1316         if (stdinOpenState == CLOSED) return; // do not write to closed pipes
  1318         ++pendingWriteCount;
  1319         debugLog("sending "+data.length+" bytes to stdinWorker\n");
  1320         var pipe = parseInt(child.stdin);
  1322         stdinWorker.postMessage({
  1323             msg: 'write',
  1324             pipe: pipe,
  1325             data: data
  1326         });
  1330     /*
  1331      * closeStdinHandle()
  1333      * Close the stdin pipe, either directly or by requesting the ChromeWorker to
  1334      * close the pipe. The ChromeWorker will only close the pipe after the last write
  1335      * request process is done.
  1336      */
  1338     function closeStdinHandle() {
  1339         debugLog("trying to close stdin\n");
  1340         if (stdinOpenState != OPEN) return;
  1341         stdinOpenState = CLOSEABLE;
  1343         if (stdinWorker) {
  1344             debugLog("sending close stdin to worker\n");
  1345             var pipePtr = parseInt(child.stdin);
  1347             stdinWorker.postMessage({
  1348                 msg: 'close',
  1349                 pipe: pipePtr
  1350             });
  1352         else {
  1353             stdinOpenState = CLOSED;
  1354             debugLog("Closing Stdin\n");
  1355             close(child.stdin) && LogError("CloseHandle stdin failed");
  1360     /*
  1361      * createReader(pipe, name)
  1363      * @pipe: handle to the pipe
  1364      * @name: String containing the pipe name (stdout or stderr)
  1365      * @callbackFunc: function to be called with the read data
  1367      * Create a ChromeWorker object for reading data asynchronously from
  1368      * the pipe (i.e. on a separate thread), and passing the result back to
  1369      * the caller.
  1371      */
  1372     function createReader(pipe, name, callbackFunc) {
  1373         var worker = new ChromeWorker(WORKER_URL_UNIX);
  1374         worker.onmessage = function(event) {
  1375             switch(event.data.msg) {
  1376             case "data":
  1377                 debugLog("got "+event.data.count+" bytes from "+name+"\n");
  1378                 var data = '';
  1379                 if (options.charset === null) {
  1380                     data = getBytes(event.data.data);
  1382                 else {
  1383                     try {
  1384                         data = convertBytes(event.data.data, options.charset);
  1386                     catch(ex) {
  1387                         console.warn("error decoding output: " + ex);
  1388                         data = getBytes(event.data.data);
  1392                 callbackFunc(data);
  1393                 break;
  1394             case "done":
  1395                 debugLog("Pipe "+name+" closed\n");
  1396                 if (event.data.data != 0) workerExitCode = event.data.data;
  1397                 --readers;
  1398                 if (readers == 0) cleanup();
  1399                 break;
  1400             default:
  1401                 debugLog("Got msg from "+name+": "+event.data.data+"\n");
  1404         worker.onerror = function(error) {
  1405             LogError("Got error from "+name+": "+error.message);
  1408         worker.postMessage({
  1409                 msg: 'read',
  1410                 pipe: pipe,
  1411                 pid: pid,
  1412                 libc: options.libc,
  1413                 charset: options.charset === null ? "null" : options.charset,
  1414                 name: name
  1415             });
  1417         return worker;
  1420     /*
  1421      * readPipes()
  1423      * Open the pipes for reading from stdout and stderr
  1424      */
  1425     function readPipes() {
  1427         stdoutWorker = createReader(child.stdout, "stdout", function (data) {
  1428             if(options.stdout) {
  1429                 setTimeout(function() {
  1430                     options.stdout(data);
  1431                 }, 0);
  1432             } else {
  1433                 output += data;
  1435         });
  1437         if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
  1438             if(options.stderr) {
  1439                 setTimeout(function() {
  1440                     options.stderr(data);
  1441                  }, 0);
  1442             } else {
  1443                 error += data;
  1445         });
  1448     function cleanup() {
  1449         debugLog("Cleanup called\n");
  1450         if(active) {
  1451             active = false;
  1453             closeStdinHandle(); // should only be required in case of errors
  1455             var result, status = ctypes.int();
  1456             result = waitpid(child.pid, status.address(), 0);
  1457             if (result > 0)
  1458                 exitCode = status.value
  1459             else
  1460                 if (workerExitCode >= 0)
  1461                     exitCode = workerExitCode
  1462                 else
  1463                     exitCode = status.value;
  1465             if (stdinWorker)
  1466                 stdinWorker.postMessage({msg: 'stop'})
  1468             setTimeout(function _done() {
  1469                 if (options.done) {
  1470                     try {
  1471                         options.done({
  1472                             exitCode: exitCode,
  1473                             stdout: output,
  1474                             stderr: error,
  1475                         });
  1477                     catch(ex) {
  1478                         // prevent from blocking if options.done() throws an error
  1479                         done = true;
  1480                         throw ex;
  1484                 done = true;
  1485             }, 0);
  1487             libc.close();
  1491     //main
  1493     var cmdStr = getCommandStr(options.command);
  1494     var workDir = getWorkDir(options.workdir);
  1496     child = {};
  1497     pid = popen(cmdStr, workDir, options.arguments, options.environment, child);
  1499     debugLog("subprocess started; got PID "+pid+"\n");
  1501     readPipes();
  1503     if (options.stdin) {
  1504         createStdinWriter();
  1505         if(typeof(options.stdin) == 'function') {
  1506             try {
  1507                 options.stdin({
  1508                     write: function(data) {
  1509                         writeStdin(data);
  1510                     },
  1511                     close: function() {
  1512                         closeStdinHandle();
  1514                 });
  1516             catch(ex) {
  1517                 // prevent from failing if options.stdin() throws an exception
  1518                 closeStdinHandle();
  1519                 throw ex;
  1521         } else {
  1522             writeStdin(options.stdin);
  1523             closeStdinHandle();
  1527     return {
  1528         wait: function() {
  1529             // wait for async operations to complete
  1530             var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
  1531             while (! done) thread.processNextEvent(true)
  1532             return exitCode;
  1533         },
  1534         kill: function(hardKill) {
  1535             var rv = kill(pid, (hardKill ? 9: 15));
  1536             cleanup(-1);
  1537             return rv;
  1543 module.exports = subprocess;

mercurial