Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2011-08-02 09:25:57 +0400
committerRyan Dahl <ry@tinyclouds.org>2011-08-02 09:34:16 +0400
commit3ce671284b61c34767994d94548a27836e706735 (patch)
tree40bb55f6532928d019dcd08e04c9b7ca8a3a76ca /src
parent6d5218bc7d344b0f4e0a0cb50fdd25052946cc4f (diff)
Remove node_child_process_win32.cc
Diffstat (limited to 'src')
-rw-r--r--src/node_child_process_win32.cc892
1 files changed, 0 insertions, 892 deletions
diff --git a/src/node_child_process_win32.cc b/src/node_child_process_win32.cc
deleted file mode 100644
index 33eeba4d7e6..00000000000
--- a/src/node_child_process_win32.cc
+++ /dev/null
@@ -1,892 +0,0 @@
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
-#include <node.h>
-#include <node_child_process.h>
-
-#include <v8.h>
-#include <uv.h>
-#include <eio.h>
-
-#include <assert.h>
-#include <signal.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#include <platform_win32.h>
-#include <platform_win32_winsock.h>
-
-namespace node {
-
-using namespace v8;
-
-
-static const WCHAR DEFAULT_PATH[1] = L"";
-static const WCHAR DEFAULT_PATH_EXT[20] = L".COM;.EXE;.BAT;.CMD";
-
-
-static Persistent<String> pid_symbol;
-static Persistent<String> onexit_symbol;
-
-
-static struct watcher_status_struct {
- uv_async_t async_watcher;
- ChildProcess *child;
- HANDLE lock;
- int num_active;
-} watcher_status;
-
-
-/*
- * Path search functions
- */
-
-/*
- * Helper function for search_path
- */
-static inline WCHAR* search_path_join_test(
- const WCHAR* dir, int dir_len, const WCHAR* name, int name_len,
- const WCHAR* ext, int ext_len, const WCHAR* cwd, int cwd_len) {
- WCHAR *result, *result_pos;
-
- if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
- // It's a full path without drive letter, use cwd's drive letter only
- cwd_len = 2;
- } else if (dir_len >= 2 && dir[1] == L':' &&
- (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) {
- // It's a relative path with drive letter (ext.g. D:../some/file)
- // Replace drive letter in dir by full cwd if it points to the same drive,
- // otherwise use the dir only.
- if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) {
- cwd_len = 0;
- } else {
- dir += 2;
- dir_len -= 2;
- }
- } else if (dir_len > 2 && dir[1] == L':') {
- // It's an absolute path with drive letter
- // Don't use the cwd at all
- cwd_len = 0;
- }
-
- // Allocate buffer for output
- result = result_pos =
- new WCHAR[cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1];
-
- // Copy cwd
- wcsncpy(result_pos, cwd, cwd_len);
- result_pos += cwd_len;
-
- // Add a path separator if cwd didn't end with one
- if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
- result_pos[0] = L'\\';
- result_pos++;
- }
-
- // Copy dir
- wcsncpy(result_pos, dir, dir_len);
- result_pos += dir_len;
-
- // Add a separator if the dir didn't end with one
- if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
- result_pos[0] = L'\\';
- result_pos++;
- }
-
- // Copy filename
- wcsncpy(result_pos, name, name_len);
- result_pos += name_len;
-
- // Copy extension
- if (ext_len) {
- result_pos[0] = L'.';
- result_pos++;
- wcsncpy(result_pos, ext, ext_len);
- result_pos += ext_len;
- }
-
- // Null terminator
- result_pos[0] = L'\0';
-
- DWORD attrs = GetFileAttributesW(result);
-
- if (attrs != INVALID_FILE_ATTRIBUTES &&
- !(attrs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) {
- return result;
- }
-
- delete[] result;
- return NULL;
-}
-
-
-/*
- * Helper function for search_path
- */
-static inline WCHAR* path_search_walk_ext(
- const WCHAR *dir, int dir_len, const WCHAR *name, int name_len,
- WCHAR *cwd, int cwd_len, const WCHAR *path_ext, bool name_has_ext) {
- WCHAR* result = NULL;
-
- const WCHAR *ext_start,
- *ext_end = path_ext;
-
- // If the name itself has a nonemtpy extension, try this extension first
- if (name_has_ext) {
- result = search_path_join_test(dir, dir_len,
- name, name_len,
- L"", 0,
- cwd, cwd_len);
- }
-
- // Add path_ext extensions and try to find a name that matches
- while (result == NULL) {
- if (*ext_end == L'\0') {
- break;
- }
-
- // Skip the separator that ext_end now points to
- if (ext_end != path_ext) {
- ext_end++;
- }
-
- // Find the next dot in path_ext
- ext_start = wcschr(ext_end, L'.');
- if (ext_start == NULL) {
- break;
- }
-
- // Skip the dot
- ext_start++;
-
- // Slice until we found a ; or alternatively a \0
- ext_end = wcschr(ext_start, L';');
- if (ext_end == NULL) {
- ext_end = wcschr(ext_start, '\0');
- }
-
- result = search_path_join_test(dir, dir_len,
- name, name_len,
- ext_start, (ext_end - ext_start),
- cwd, cwd_len);
- }
-
- return result;
-}
-
-
-/*
- * search_path searches the system path for an executable filename -
- * the windows API doesn't provide this as a standalone function nor as an
- * option to CreateProcess.
- *
- * It tries to return an absolute filename.
- *
- * Furthermore, it tries to follow the semantics that cmd.exe uses as closely
- * as possible:
- *
- * - Do not search the path if the filename already contains a path (either
- * relative or absolute).
- * (but do use path_ext)
- *
- * - If there's really only a filename, check the current directory for file,
- * then search all path directories.
- *
- * - If filename specifies has *any* extension, search for the file with the
- * specified extension first.
- * (not necessary an executable one or one that appears in path_ext;
- * *but* no extension or just a dot is *not* allowed)
- *
- * - If the literal filename is not found in a directory, try *appending*
- * (not replacing) extensions from path_ext in the specified order.
- * (an extension consisting of just a dot *may* appear in path_ext;
- * unlike what happens if the specified filename ends with a dot,
- * if path_ext specifies a single dot cmd.exe *does* look for an
- * extension-less file)
- *
- * - The path variable may contain relative paths; relative paths are relative
- * to the cwd.
- *
- * - Directories in path may or may not end with a trailing backslash.
- *
- * - Extensions path_ext portions must always start with a dot.
- *
- * - CMD does not trim leading/trailing whitespace from path/pathex entries
- * nor from the environment variables as a whole.
- *
- * - When cmd.exe cannot read a directory, it wil just skip it and go on
- * searching. However, unlike posix-y systems, it will happily try to run a
- * file that is not readable/executable; if the spawn fails it will not
- * continue searching.
- *
- * TODO: correctly interpret UNC paths
- * TODO: check with cmd what should happen when a pathext entry does not start
- * with a dot
- */
-static inline WCHAR* search_path(const WCHAR *file, WCHAR *cwd,
- const WCHAR *path, const WCHAR *path_ext) {
- WCHAR* result = NULL;
-
- int file_len = wcslen(file);
- int cwd_len = wcslen(cwd);
-
- // If the caller supplies an empty filename,
- // we're not gonna return c:\windows\.exe -- GFY!
- if (file_len == 0
- || (file_len == 1 && file[0] == L'.')) {
- return NULL;
- }
-
- // Find the start of the filename so we can split the directory from the name
- WCHAR *file_name_start;
- for (file_name_start = (WCHAR*)file + file_len;
- file_name_start > file
- && file_name_start[-1] != L'\\'
- && file_name_start[-1] != L'/'
- && file_name_start[-1] != L':';
- file_name_start--);
-
- bool file_has_dir = file_name_start != file;
-
- // Check if the filename includes an extension
- WCHAR *dot = wcschr(file_name_start, L'.');
- bool name_has_ext = (dot != NULL && dot[1] != L'\0');
-
- if (file_has_dir) {
- // The file has a path inside, don't use path (but do use path_ex)
- result = path_search_walk_ext(
- file, file_name_start - file,
- file_name_start, file_len - (file_name_start - file),
- cwd, cwd_len,
- path_ext, name_has_ext);
-
- } else {
- const WCHAR *dir_start,
- *dir_end = path;
-
- // The file is really only a name; look in cwd first, then scan path
- result = path_search_walk_ext(L"", 0,
- file, file_len,
- cwd, cwd_len,
- path_ext, name_has_ext);
-
- while (result == NULL) {
- if (*dir_end == L'\0') {
- break;
- }
-
- // Skip the separator that dir_end now points to
- if (dir_end != path) {
- dir_end++;
- }
-
- // Next slice starts just after where the previous one ended
- dir_start = dir_end;
-
- // Slice until the next ; or \0 is found
- dir_end = wcschr(dir_start, L';');
- if (dir_end == NULL) {
- dir_end = wcschr(dir_start, L'\0');
- }
-
- // If the slice is zero-length, don't bother
- if (dir_end - dir_start == 0) {
- continue;
- }
-
- result = path_search_walk_ext(dir_start, dir_end - dir_start,
- file, file_len,
- cwd, cwd_len,
- path_ext, name_has_ext);
- }
- }
-
- return result;
-}
-
-
-/*
- * Process exit "watcher" functions. It's not like a real libev watcher,
- * it's more like a wrapper around ev_async, and RegisterWaitForSingleObject.
- * And its not generalized, it only works with child processes.
- * BTW there is only one exit watcher that watches all childs!
- */
-
-
-// Called from either a eio, a wait thread or a callback thread created by a
-// wait thread
-void ChildProcess::close_stdio_handles(ChildProcess *child) {
- // Before we proceed to synchronize with the main thread, first close
- // the stdio sockets that the child process has used, because it may
- // take some time and would deadlock if done in the main thread.
- for (int i = 0; i < 3; i++) {
- if (!child->got_custom_fds_[i]) {
- shutdown(reinterpret_cast<SOCKET>(child->stdio_handles_[i]), SD_BOTH);
- closesocket(reinterpret_cast<SOCKET>(child->stdio_handles_[i]));
- }
- }
-}
-
-
-// Called from the main thread
-void ChildProcess::notify_exit(uv_async_t* watcher, int status) {
- // Get the child process, then release the lock
- ChildProcess *child = watcher_status.child;
-
- ReleaseSemaphore(watcher_status.lock, 1, NULL);
-
- DWORD exit_code = -127;
-
- EnterCriticalSection(&child->info_lock_);
-
- // Did the process even start anyway?
- if (child->did_start_) {
- // Process launched, then exited
-
- // Drop the wait handle
- UnregisterWait(child->wait_handle_);
-
- // Fetch the process exit code
- if (GetExitCodeProcess(child->process_handle_, &exit_code) == 0) {
- winapi_perror("GetExitCodeProcess");
- }
-
- // Close and unset the process handle
- CloseHandle(child->process_handle_);
- child->process_handle_ = NULL;
- child->pid_ = 0;
- }
-
- LeaveCriticalSection(&child->info_lock_);
-
- child->OnExit(exit_code);
-}
-
-
-// Called from the eio thread
-void ChildProcess::notify_spawn_failure(ChildProcess *child) {
- close_stdio_handles(child);
-
- DWORD result = WaitForSingleObject(watcher_status.lock, INFINITE);
- assert(result == WAIT_OBJECT_0);
-
- watcher_status.child = child;
-
- uv_async_send(&watcher_status.async_watcher);
-}
-
-
-// Called from the windows-managed wait thread
-void CALLBACK ChildProcess::watch_wait_callback(void *data,
- BOOLEAN didTimeout) {
- assert(didTimeout == FALSE);
-
- ChildProcess *child = (ChildProcess*)data;
-
- close_stdio_handles(child);
-
- // If the main thread is blocked, and more than one child process returns,
- // the wait thread will block as well here. It doesn't matter because the
- // main thread can only do one thing at a time anyway.
- DWORD result = WaitForSingleObject(watcher_status.lock, INFINITE);
- assert(result == WAIT_OBJECT_0);
-
- watcher_status.child = child;
- uv_async_send(&watcher_status.async_watcher);
-}
-
-
-// Called from the eio thread
-inline void ChildProcess::watch(ChildProcess *child) {
- DWORD result = WaitForSingleObject(watcher_status.lock, INFINITE);
- assert(result == WAIT_OBJECT_0);
-
- // We must retain the lock here because we don't want the RegisterWait
- // to complete before the wait handle is set to the child process.
- RegisterWaitForSingleObject(&child->wait_handle_, child->process_handle_,
- watch_wait_callback, (void*)child, INFINITE,
- WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
-
- ReleaseSemaphore(watcher_status.lock, 1, NULL);
-}
-
-
-/*
- * Spawn helper functions
- */
-
-
-/*
- * Quotes command line arguments
- * Returns a pointer to the end (next char to be written) of the buffer
- */
-static inline WCHAR* quote_cmd_arg(WCHAR *source, WCHAR *target,
- WCHAR terminator) {
- int len = wcslen(source),
- i;
-
- // Check if the string must be quoted;
- // if unnecessary, don't do it, it may only confuse older programs.
- if (len == 0) {
- goto quote;
- }
- for (i = 0; i < len; i++) {
- if (source[i] == L' ' || source[i] == L'"') {
- goto quote;
- }
- }
-
- // No quotation needed
- wcsncpy(target, source, len);
- target += len;
- *(target++) = terminator;
- return target;
-
-quote:
- // Quote
- *(target++) = L'"';
- for (i = 0; i < len; i++) {
- if (source[i] == L'"' || source[i] == L'\\') {
- *(target++) = '\\';
- }
- *(target++) = source[i];
- }
- *(target++) = L'"';
- *(target++) = terminator;
-
- return target;
-}
-
-
-/*
- * Spawns a child process from a libeio thread
- */
-int ChildProcess::do_spawn(eio_req *req) {
- ChildProcess* child = (ChildProcess*)req->data;
-
- WCHAR* application_path = search_path(child->application_, child->cwd_,
- child->path_, child->path_ext_);
-
- if (application_path) {
- STARTUPINFOW startup;
- PROCESS_INFORMATION info;
-
- startup.cb = sizeof(startup);
- startup.lpReserved = NULL;
- startup.lpDesktop = NULL;
- startup.lpTitle = NULL;
- startup.dwFlags = STARTF_USESTDHANDLES;
- startup.cbReserved2 = 0;
- startup.lpReserved2 = NULL;
- startup.hStdInput = child->stdio_handles_[0];
- startup.hStdOutput = child->stdio_handles_[1];
- startup.hStdError = child->stdio_handles_[2];
-
- EnterCriticalSection(&child->info_lock_);
-
- if (!child->kill_me_) {
- // Try start the process
- BOOL success = CreateProcessW(
- application_path,
- child->arguments_,
- NULL,
- NULL,
- 1,
- CREATE_UNICODE_ENVIRONMENT,
- child->env_win_,
- child->cwd_,
- &startup,
- &info
- );
-
- if (success) {
- child->process_handle_ = info.hProcess;
- child->pid_ = GetProcessId(info.hProcess);
- child->did_start_ = true;
- watch(child);
-
- // Not interesting
- CloseHandle(info.hThread);
-
- LeaveCriticalSection(&child->info_lock_);
- return 0;
- }
- }
-
- LeaveCriticalSection(&child->info_lock_);
- }
-
- // not found, kill_me set or process failed to start
- notify_spawn_failure(child);
- return 0;
-}
-
-
-// Called from the main thread after spawn has finished,
-// there's no need to lock the child because did_start is reliable
-int ChildProcess::after_spawn(eio_req *req) {
- ChildProcess* child = (ChildProcess*)req->data;
-
- if (child->did_start_) {
- child->handle_->Set(pid_symbol, Integer::New(child->pid_));
- } else {
- child->handle_->Set(pid_symbol, Local<Value>::New(Null()));
- }
-
- // Cleanup data structures needed only for spawn() here
- delete [] child->application_;
- delete [] child->arguments_;
- delete [] child->env_win_;
- delete [] child->cwd_;
-
- return 0;
-}
-
-
-/*
- * Kill helper functions
- */
-
-// Called from the main thread while eio/wait threads may still be busy with
-// the process
-int ChildProcess::do_kill(ChildProcess *child, int sig) {
- int rv = 0;
-
- EnterCriticalSection(&child->info_lock_);
-
- child->exit_signal_ = sig;
-
- if (child->did_start_) {
- // On windows killed processes normally return 1
- if (!TerminateProcess(child->process_handle_, 1))
- rv = -1;
- } else {
- child->kill_me_ = true;
- }
-
- LeaveCriticalSection(&child->info_lock_);
-
- return rv;
-}
-
-
-/*
- * ChildProcess non-static Methods
- */
-
-Handle<Value> ChildProcess::New(const Arguments& args) {
- HandleScope scope;
- ChildProcess *p = new ChildProcess();
- p->Wrap(args.Holder());
- return args.This();
-}
-
-
-// This is an internal function. The third argument should be an array
-// of key value pairs seperated with '='.
-Handle<Value> ChildProcess::Spawn(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 3 ||
- !args[0]->IsString() ||
- !args[1]->IsArray() ||
- !args[2]->IsString() ||
- !args[3]->IsArray()) {
- return ThrowException(Exception::Error(String::New("Bad argument.")));
- }
-
- // Get ChildProcess object
- ChildProcess *child = ObjectWrap::Unwrap<ChildProcess>(args.Holder());
-
- // Copy appplication name
- Handle<String> app_handle = args[0]->ToString();
- int app_len = app_handle->Length();
- String::Value app(app_handle);
- child->application_ = new WCHAR[app_len + 1];
- wcsncpy(child->application_, (WCHAR*)*app, app_len + 1);
-
- /*
- * Copy second argument args[1] into a c-string called argv.
- * On windows command line arguments are all quoted and concatenated to
- * one string. The executable name must be prepended. This is not really
- * required by windows but if you don't do this programs that rely on
- * argv[0] being the executable misbehave.
- * Assuming that executable plus all arguments must be wrapped in quotes,
- * every character needs to be quoted with a backslash,
- * and every argument is followed by either a space or a nul char,
- * the maximum required buffer size is Σ[exe and args](2 * length + 3).
- */
- Local<Array> cmd_args_handle = Local<Array>::Cast(args[1]);
- int cmd_argc = cmd_args_handle->Length();
-
- // Compute required buffer
- int max_buf = (1 + cmd_argc) * 3 + app_len * 2,
- i;
- for (i = 0; i < cmd_argc; i++) {
- Local<String> arg_handle =
- cmd_args_handle->Get(Integer::New(i))->ToString();
- max_buf += arg_handle->Length() * 2;
- }
-
- child->arguments_ = new WCHAR[max_buf];
- WCHAR *pos = child->arguments_;
- pos = quote_cmd_arg((WCHAR*)*app, pos, cmd_argc ? L' ' : L'\0');
- for (i = 0; i < cmd_argc; i++) {
- String::Value arg(cmd_args_handle->Get(Integer::New(i))->ToString());
- pos = quote_cmd_arg((WCHAR*)*arg, pos, (i < cmd_argc - 1) ? L' ' : L'\0');
- }
-
- // Current working directory
- Local<String>cwd_handle = Local<String>::Cast(args[2]);
- int cwd_len = cwd_handle->Length();
- if (cwd_len > 0) {
- // Cwd was specified
- String::Value cwd(cwd_handle);
- child->cwd_ = new WCHAR[cwd_len + 1];
- wcsncpy(child->cwd_, (WCHAR*)*cwd, cwd_len + 1);
- } else {
- // Cwd not specified
- int chars = GetCurrentDirectoryW(0, NULL);
- if (!chars) {
- winapi_perror("GetCurrentDirectoryW");
- child->cwd_ = new WCHAR[0];
- child->cwd_[0] = '\0';
- } else {
- child->cwd_ = new WCHAR[chars];
- GetCurrentDirectoryW(chars, child->cwd_);
- }
- }
-
- /*
- * args[3] holds the environment as a js array containing key=value pairs.
- * The way windows takes environment variables is different than what C does;
- * Windows wants a contiguous block of null-terminated strings, terminated
- * with an additional null.
- * Get a pointer to the pathext and path environment variables as well,
- * because do_spawn needs it. These are just pointers into env_win.
- */
- Local<Array> env_list_handle = Local<Array>::Cast(args[3]);
- int envc = env_list_handle->Length();
- Local<String> env_val_handle[envc];
-
- int env_win_len = envc + 1; // room for \0 terminators plus closing null
- for (int i = 0; i < envc; i++) {
- env_val_handle[i] = env_list_handle->Get(Integer::New(i))->ToString();
- env_win_len += env_val_handle[i]->Length();
- }
-
- WCHAR *env_win = new WCHAR[env_win_len],
- *env_win_pos = env_win;
- WCHAR *path = NULL, *path_ext = NULL;
-
- for (int i = 0; i < envc; i++) {
- int len = env_val_handle[i]->Length() + 1; // including \0
- String::Value pair(env_val_handle[i]);
- wcsncpy(env_win_pos, (WCHAR*)*pair, (size_t)len);
-
- // Try to get a pointer to PATH and PATHEXT
- if (_wcsnicmp(L"PATH=", env_win_pos, 5) == 0) {
- path = env_win_pos + 5;
- }
- if (_wcsnicmp(L"PATHEXT=", env_win_pos, 8) == 0) {
- path_ext = env_win_pos + 8;
- }
-
- env_win_pos += len;
- }
-
- *env_win_pos = L'\0';
-
- child->env_win_ = env_win;
-
- if (path != NULL) {
- child->path_ = path;
- } else {
- child->path_ = DEFAULT_PATH;
- }
-
- if (path_ext != NULL) {
- child->path_ext_ = path_ext;
- } else {
- child->path_ext_ = DEFAULT_PATH_EXT;
- }
-
- // Open pipes or re-use custom_fds to talk to child
- Local<Array> custom_fds_handle = Local<Array>::Cast(args[4]);
- int custom_fds_len = custom_fds_handle->Length();
-
- HANDLE *child_handles = (HANDLE*)&child->stdio_handles_;
- bool *has_custom_fds = (bool*)&child->got_custom_fds_;
- int parent_fds[3];
-
- for (int i = 0; i < 3; i++) {
- int custom_fd = -1;
- if (i < custom_fds_len && !custom_fds_handle->Get(i)->IsUndefined())
- custom_fd = custom_fds_handle->Get(i)->ToInteger()->Value();
-
- if (custom_fd == -1) {
- // Create a new pipe
- HANDLE parent_handle, child_handle;
- if (wsa_sync_async_socketpair(AF_INET, SOCK_STREAM, IPPROTO_IP,
- (SOCKET*)&child_handle, (SOCKET*)&parent_handle) == SOCKET_ERROR)
- wsa_perror("wsa_sync_async_socketpair");
-
- // Make parent handle nonblocking
- unsigned long ioctl_value = 1;
- if (ioctlsocket((SOCKET)parent_handle, FIONBIO, &ioctl_value) ==
- SOCKET_ERROR)
- wsa_perror("ioctlsocket");
-
- // Make parent handle non-inheritable
- if (!SetHandleInformation(parent_handle, HANDLE_FLAG_INHERIT, 0))
- winapi_perror("SetHandleInformation");
-
- // Make child handle inheritable
- if (!SetHandleInformation(child_handle, HANDLE_FLAG_INHERIT,
- HANDLE_FLAG_INHERIT))
- winapi_perror("SetHandleInformation");
-
- // Enable linger on socket so all written data gets through
- BOOL opt_value = 0;
- if (setsockopt((SOCKET)child_handle, SOL_SOCKET, SO_DONTLINGER,
- (char*)&opt_value, sizeof(opt_value)) == SOCKET_ERROR)
- wsa_perror("setsockopt");
-
- has_custom_fds[i] = false;
- child_handles[i] = child_handle;
- parent_fds[i] = (int)_open_osfhandle((intptr_t)parent_handle, 0);
-
- } else {
- // Use this custom fd
- HANDLE custom_handle = (HANDLE)_get_osfhandle(custom_fd);
-
- // Make handle inheritable, don't care it it fails
- // It may fail for certain types of handles - but always try to
- // spawn; it'll still work for e.g. console handles
- SetHandleInformation(custom_handle, HANDLE_FLAG_INHERIT,
- HANDLE_FLAG_INHERIT);
-
- has_custom_fds[i] = true;
- child_handles[i] = custom_handle;
- parent_fds[i] = custom_fd;
- }
- }
-
- // Return the opened fds
- Local<Array> result = Array::New(3);
- assert(parent_fds[0] >= 0);
- result->Set(0, Integer::New(parent_fds[0]));
- assert(parent_fds[1] >= 0);
- result->Set(1, Integer::New(parent_fds[1]));
- assert(parent_fds[2] >= 0);
- result->Set(2, Integer::New(parent_fds[2]));
-
- // Grab a reference so it doesn't get GC'ed
- child->Ref();
-
- eio_custom(do_spawn, EIO_PRI_DEFAULT, after_spawn, (void*)child);
-
- return scope.Close(result);
-}
-
-
-Handle<Value> ChildProcess::Kill(const Arguments& args) {
- HandleScope scope;
- ChildProcess *child = ObjectWrap::Unwrap<ChildProcess>(args.Holder());
- assert(child);
-
- int sig = SIGTERM;
-
- if (args.Length() > 0) {
- if (args[0]->IsNumber()) {
- sig = args[0]->Int32Value();
- } else {
- return ThrowException(Exception::TypeError(String::New("Bad argument.")));
- }
- }
-
- if (do_kill(child, sig) != 0) {
- return ThrowException(ErrnoException(GetLastError()));
- }
-
- return True();
-}
-
-
-// Called from the main thread _after_ all eio/wait threads are done with the
-// process, so there's no need to lock here.
-void ChildProcess::OnExit(int status) {
- HandleScope scope;
-
- // Unref() the child, as it's no longer used by threads
- Unref();
-
- handle_->Set(pid_symbol, Null());
-
- Local<Value> onexit_v = handle_->Get(onexit_symbol);
- assert(onexit_v->IsFunction());
- Local<Function> onexit = Local<Function>::Cast(onexit_v);
-
- TryCatch try_catch;
- Local<Value> argv[2];
-
- argv[0] = Integer::New(status);
-
- if (exit_signal_ != 0) {
- argv[1] = Integer::New(exit_signal_);
- } else {
- argv[1] = Local<Value>::New(Null());
- }
-
- onexit->Call(handle_, 2, argv);
-
- if (try_catch.HasCaught()) {
- FatalException(try_catch);
- }
-}
-
-
-void ChildProcess::Initialize(Handle<Object> target) {
- HandleScope scope;
-
- Local<FunctionTemplate> t = FunctionTemplate::New(ChildProcess::New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
- t->SetClassName(String::NewSymbol("ChildProcess"));
-
- pid_symbol = NODE_PSYMBOL("pid");
- onexit_symbol = NODE_PSYMBOL("onexit");
-
- NODE_SET_PROTOTYPE_METHOD(t, "spawn", ChildProcess::Spawn);
- NODE_SET_PROTOTYPE_METHOD(t, "kill", ChildProcess::Kill);
-
- target->Set(String::NewSymbol("ChildProcess"), t->GetFunction());
-
- uv_async_init(&watcher_status.async_watcher, notify_exit);
- watcher_status.lock = CreateSemaphore(NULL, 1, 1, NULL);
-}
-
-} // namespace node
-
-NODE_MODULE(node_child_process, node::ChildProcess::Initialize);