From af98fc9d5f20a8c5dc0db95121f18355337762f1 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 16 May 2012 23:04:24 +0700 Subject: [PATCH] child_process: new stdio API for .spawn() method --- lib/child_process.js | 236 +++++++++++++++++++++---------- node.gyp | 3 + src/node.js | 5 +- src/process_wrap.cc | 100 +++++++------ src/tcp_wrap.cc | 12 ++ src/tcp_wrap.h | 2 + src/tty_wrap.cc | 184 +++++++++++++----------- src/tty_wrap.h | 58 ++++++++ src/udp_wrap.cc | 52 ++----- src/udp_wrap.h | 60 ++++++++ test/simple/test-process-wrap.js | 6 +- 11 files changed, 472 insertions(+), 246 deletions(-) create mode 100644 src/tty_wrap.h create mode 100644 src/udp_wrap.h diff --git a/lib/child_process.js b/lib/child_process.js index 2e67ebce1c1..f79481dcfe2 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -25,17 +25,38 @@ var Process = process.binding('process_wrap').Process; var util = require('util'); var constants; // if (!constants) constants = process.binding('constants'); -var Pipe; +var handleWraps = {}; +function handleWrapGetter(name, callback) { + var cons; + + Object.defineProperty(handleWraps, name, { + get: function() { + if (cons !== undefined) return cons; + return cons = callback(); + } + }); +} + +handleWrapGetter('Pipe', function() { + return process.binding('pipe_wrap').Pipe; +}); + +handleWrapGetter('TTY', function() { + return process.binding('tty_wrap').TTY; +}); + +handleWrapGetter('TCP', function() { + return process.binding('tcp_wrap').TCP; +}); + +handleWrapGetter('UDP', function() { + return process.binding('udp_wrap').UDP; +}); // constructors for lazy loading function createPipe(ipc) { - // Lazy load - if (!Pipe) { - Pipe = process.binding('pipe_wrap').Pipe; - } - - return new Pipe(ipc); + return new handleWraps.Pipe(ipc); } function createSocket(pipe, readable) { @@ -414,39 +435,18 @@ exports.fork = function(modulePath /*, args, options*/) { execArgv = options.execArgv || process.execArgv; args = execArgv.concat([modulePath], args); - // Don't allow stdinStream and customFds since a stdin channel will be used - if (options.stdinStream) { - throw new Error('stdinStream not allowed for fork()'); - } - - if (options.customFds) { - throw new Error('customFds not allowed for fork()'); - } - // Leave stdin open for the IPC channel. stdout and stderr should be the // same as the parent's if silent isn't set. - options.customFds = (options.silent ? [-1, -1, -1] : [-1, 1, 2]); - - // Just need to set this - child process won't actually use the fd. - // For backwards compat - this can be changed to 'NODE_CHANNEL' before v0.6. - options.env = util._extend({}, options.env || process.env); - options.env.NODE_CHANNEL_FD = 42; + options.stdio = options.silent ? ['ipc', 'pipe', 'pipe'] : ['ipc', 1, 2]; - // stdin is the IPC channel. - options.stdinStream = createPipe(true); - - var child = spawn(process.execPath, args, options); - - setupChannel(child, options.stdinStream); - - return child; + return spawn(process.execPath, args, options); }; -exports._forkChild = function() { +exports._forkChild = function(fd) { // set process.send() var p = createPipe(true); - p.open(0); + p.open(fd); setupChannel(process, p); }; @@ -591,6 +591,11 @@ var spawn = exports.spawn = function(file, args, options) { } var child = new ChildProcess(); + if (options && options.customFds && !options.stdio) { + options.stdio = options.customFds.map(function(fd) { + return fd === -1 ? 'pipe' : fd; + }); + } child.spawn({ file: file, @@ -598,8 +603,7 @@ var spawn = exports.spawn = function(file, args, options) { cwd: options ? options.cwd : null, windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments), envPairs: envPairs, - customFds: options ? options.customFds : null, - stdinStream: options ? options.stdinStream : null, + stdio: options ? options.stdio : null, uid: options ? options.uid : null, gid: options ? options.gid : null }); @@ -658,46 +662,111 @@ function ChildProcess() { util.inherits(ChildProcess, EventEmitter); -function setStreamOption(name, index, options) { - // Skip if we already have options.stdinStream - if (options[name]) return; +function getHandleWrapType(stream) { + if (stream instanceof handleWraps.Pipe) return 'pipe'; + if (stream instanceof handleWraps.TTY) return 'tty'; + if (stream instanceof handleWraps.TCP) return 'tcp'; + if (stream instanceof handleWraps.UDP) return 'udp'; - if (options.customFds && - typeof options.customFds[index] == 'number' && - options.customFds[index] !== -1) { - if (options.customFds[index] === index) { - options[name] = null; - } else { - throw new Error('customFds not yet supported'); - } - } else { - options[name] = createPipe(); - } + return false; } ChildProcess.prototype.spawn = function(options) { - var self = this; - - setStreamOption('stdinStream', 0, options); - setStreamOption('stdoutStream', 1, options); - setStreamOption('stderrStream', 2, options); + var self = this, + ipc, + ipcFd, + // If no `stdio` option was given - use default + stdio = options.stdio || 'pipe'; + + // Replace shortcut with an array + if (typeof stdio === 'string') { + switch (stdio) { + case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break; + case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break; + case 'inherit': stdio = [0, 1, 2]; break; + default: throw new TypeError('Incorrect value of stdio option: ' + stdio); + } + } else if (!Array.isArray(stdio)) { + throw new TypeError('Incorrect value of stdio option: ' + stdio); + } - var r = this._handle.spawn(options); + // At least 3 stdio will be created + if (stdio.length < 3) { + stdio = stdio.concat(new Array(3 - stdio.length)); + } - if (r) { - if (options.stdinStream) { - options.stdinStream.close(); + // Translate stdio into C++-readable form + // (i.e. PipeWraps or fds) + stdio = stdio.reduce(function(acc, stdio, i) { + function cleanup() { + acc.filter(function(stdio) { + return stdio.type === 'pipe' || stdio.type === 'ipc'; + }).forEach(function(stdio) { + stdio.handle.close(); + }); } - if (options.stdoutStream) { - options.stdoutStream.close(); + // Defaults + if (stdio === undefined || stdio === null) { + stdio = i < 3 ? 'pipe' : 'ignore'; } - if (options.stderrStream) { - options.stderrStream.close(); + if (stdio === 'ignore') { + acc.push({type: 'ignore'}); + } else if (stdio === 'pipe' || typeof stdio === 'number' && stdio < 0) { + acc.push({type: 'pipe', handle: createPipe()}); + } else if (stdio === 'ipc') { + if (ipc !== undefined) { + // Cleanup previously created pipes + cleanup(); + throw Error('Child process can have only one IPC pipe'); + } + + ipc = createPipe(true); + ipcFd = i; + + acc.push({ type: 'pipe', handle: ipc }); + } else if (typeof stdio === 'number' || typeof stdio.fd === 'number') { + acc.push({ type: 'fd', fd: stdio.fd || stdio }); + } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) || + getHandleWrapType(stdio._handle)) { + var handle = getHandleWrapType(stdio) ? + stdio : + getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle; + + acc.push({ + type: 'wrap', + wrapType: getHandleWrapType(handle), + handle: handle + }); + } else { + // Cleanup + cleanup(); + throw new TypeError('Incorrect value for stdio stream: ' + stdio); } + return acc; + }, []); + + options.stdio = stdio; + + if (ipc !== undefined) { + // Let child process know about opened IPC channel + options.envPairs = options.envPairs || []; + options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd); + } + + var r = this._handle.spawn(options); + + if (r) { + // Close all opened fds on error + stdio.forEach(function(stdio) { + if (stdio.type === 'pipe') { + stdio.handle.close(); + } + }); + this._handle.close(); this._handle = null; throw errnoException(errno, 'spawn'); @@ -705,25 +774,36 @@ ChildProcess.prototype.spawn = function(options) { this.pid = this._handle.pid; - if (options.stdinStream) { - this.stdin = createSocket(options.stdinStream, false); - } + stdio.forEach(function(stdio, i) { + if (stdio.type === 'ignore') return; - if (options.stdoutStream) { - this.stdout = createSocket(options.stdoutStream, true); - this._closesNeeded++; - this.stdout.on('close', function() { - maybeClose(self); - }); - } + if (stdio.handle) { + // when i === 0 - we're dealing with stdin + // (which is the only one writable pipe) + stdio.socket = createSocket(stdio.handle, i > 0); - if (options.stderrStream) { - this.stderr = createSocket(options.stderrStream, true); - this._closesNeeded++; - this.stderr.on('close', function() { - maybeClose(self); - }); - } + if (i > 0) { + self._closesNeeded++; + stdio.socket.on('close', function() { + maybeClose(self); + }); + } + } + }); + + this.stdin = stdio.length >= 1 && stdio[0].socket !== undefined ? + stdio[0].socket : null; + this.stdout = stdio.length >= 2 && stdio[1].socket !== undefined ? + stdio[1].socket : null; + this.stderr = stdio.length >= 3 && stdio[2].socket !== undefined ? + stdio[2].socket : null; + + this.stdio = stdio.map(function(stdio) { + return stdio.socket === undefined ? null : stdio.socket; + }); + + // Add .send() method and start listening for IPC data + if (ipc !== undefined) setupChannel(this, ipc); return r; }; diff --git a/node.gyp b/node.gyp index 824b61e49fa..0aacc5fb049 100644 --- a/node.gyp +++ b/node.gyp @@ -109,6 +109,9 @@ 'src/node_version.h', 'src/ngx-queue.h', 'src/pipe_wrap.h', + 'src/tty_wrap.h', + 'src/tcp_wrap.h', + 'src/udp_wrap.h', 'src/req_wrap.h', 'src/slab_allocator.h', 'src/stream_wrap.h', diff --git a/src/node.js b/src/node.js index fd5c6760b72..ba0786d6ea2 100644 --- a/src/node.js +++ b/src/node.js @@ -482,7 +482,8 @@ // If we were spawned with env NODE_CHANNEL_FD then load that up and // start parsing data from that stream. if (process.env.NODE_CHANNEL_FD) { - assert(parseInt(process.env.NODE_CHANNEL_FD) >= 0); + var fd = parseInt(process.env.NODE_CHANNEL_FD, 10); + assert(fd >= 0); // Make sure it's not accidentally inherited by child processes. delete process.env.NODE_CHANNEL_FD; @@ -494,7 +495,7 @@ // FIXME is this really necessary? process.binding('tcp_wrap'); - cp._forkChild(); + cp._forkChild(fd); assert(process.send); } } diff --git a/src/process_wrap.cc b/src/process_wrap.cc index ce29853565b..101d89b3fde 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -22,6 +22,10 @@ #include "node.h" #include "handle_wrap.h" #include "pipe_wrap.h" +#include "tty_wrap.h" +#include "tcp_wrap.h" +#include "udp_wrap.h" + #include #include @@ -36,6 +40,7 @@ using v8::HandleScope; using v8::FunctionTemplate; using v8::String; using v8::Array; +using v8::Number; using v8::Function; using v8::TryCatch; using v8::Context; @@ -82,6 +87,56 @@ class ProcessWrap : public HandleWrap { ProcessWrap(Handle object) : HandleWrap(object, NULL) { } ~ProcessWrap() { } + static void ParseStdioOptions(Local js_options, + uv_process_options_t* options) { + Local stdios = js_options + ->Get(String::NewSymbol("stdio")).As(); + int len = stdios->Length(); + options->stdio = new uv_stdio_container_t[len]; + options->stdio_count = len; + + for (int i = 0; i < len; i++) { + Local stdio = stdios + ->Get(Number::New(static_cast(i))).As(); + Local type = stdio->Get(String::NewSymbol("type")); + + if (type->Equals(String::NewSymbol("ignore"))) { + options->stdio[i].flags = UV_IGNORE; + } else if (type->Equals(String::NewSymbol("pipe"))) { + options->stdio[i].flags = UV_CREATE_PIPE; + options->stdio[i].data.stream = reinterpret_cast( + PipeWrap::Unwrap(stdio + ->Get(String::NewSymbol("handle")).As())->UVHandle()); + } else if (type->Equals(String::NewSymbol("wrap"))) { + uv_stream_t* stream = NULL; + Local wrapType = stdio->Get(String::NewSymbol("wrapType")); + if (wrapType->Equals(String::NewSymbol("pipe"))) { + stream = reinterpret_cast(PipeWrap::Unwrap(stdio + ->Get(String::NewSymbol("handle")).As())->UVHandle()); + } else if (wrapType->Equals(String::NewSymbol("tty"))) { + stream = reinterpret_cast(TTYWrap::Unwrap(stdio + ->Get(String::NewSymbol("handle")).As())->UVHandle()); + } else if (wrapType->Equals(String::NewSymbol("tcp"))) { + stream = reinterpret_cast(TCPWrap::Unwrap(stdio + ->Get(String::NewSymbol("handle")).As())->UVHandle()); + } else if (wrapType->Equals(String::NewSymbol("udp"))) { + stream = reinterpret_cast(UDPWrap::Unwrap(stdio + ->Get(String::NewSymbol("handle")).As())->UVHandle()); + } + assert(stream != NULL); + + options->stdio[i].flags = UV_INHERIT_STREAM; + options->stdio[i].data.stream = stream; + } else { + int fd = static_cast( + stdio->Get(String::NewSymbol("fd"))->IntegerValue()); + + options->stdio[i].flags = UV_INHERIT_FD; + options->stdio[i].data.fd = fd; + } + } + } + static Handle Spawn(const Arguments& args) { HandleScope scope; @@ -169,47 +224,8 @@ class ProcessWrap : public HandleWrap { options.env[envc] = NULL; } - uv_stdio_container_t stdio[3]; - memset(stdio, 0, sizeof(stdio)); - - options.stdio = stdio; - options.stdio_count = 3; - options.stdio[0].flags = UV_IGNORE; - options.stdio[1].flags = UV_IGNORE; - options.stdio[2].flags = UV_IGNORE; - - // options.stdin_stream - Local stdin_stream_v = js_options->Get( - String::NewSymbol("stdinStream")); - if (!stdin_stream_v.IsEmpty() && stdin_stream_v->IsObject()) { - PipeWrap* stdin_wrap = PipeWrap::Unwrap(stdin_stream_v->ToObject()); - options.stdio[0].flags = static_cast( - UV_CREATE_PIPE | UV_WRITABLE_PIPE); - options.stdio[0].data.stream = reinterpret_cast( - stdin_wrap->UVHandle()); - } - - // options.stdout_stream - Local stdout_stream_v = js_options->Get( - String::NewSymbol("stdoutStream")); - if (!stdout_stream_v.IsEmpty() && stdout_stream_v->IsObject()) { - PipeWrap* stdout_wrap = PipeWrap::Unwrap(stdout_stream_v->ToObject()); - options.stdio[1].flags = static_cast( - UV_CREATE_PIPE | UV_READABLE_PIPE); - options.stdio[1].data.stream = reinterpret_cast( - stdout_wrap->UVHandle()); - } - - // options.stderr_stream - Local stderr_stream_v = js_options->Get( - String::NewSymbol("stderrStream")); - if (!stderr_stream_v.IsEmpty() && stderr_stream_v->IsObject()) { - PipeWrap* stderr_wrap = PipeWrap::Unwrap(stderr_stream_v->ToObject()); - options.stdio[2].flags = static_cast( - UV_CREATE_PIPE | UV_READABLE_PIPE); - options.stdio[2].data.stream = reinterpret_cast( - stderr_wrap->UVHandle()); - } + // options.stdio + ParseStdioOptions(js_options, &options); // options.windows_verbatim_arguments if (js_options->Get(String::NewSymbol("windowsVerbatimArguments"))-> @@ -238,6 +254,8 @@ class ProcessWrap : public HandleWrap { delete [] options.env; } + delete[] options.stdio; + return scope.Close(Integer::New(r)); } diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 3fc8aa80cb0..5e6f1c2ec18 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -128,6 +128,18 @@ void TCPWrap::Initialize(Handle target) { } +TCPWrap* TCPWrap::Unwrap(Local obj) { + assert(!obj.IsEmpty()); + assert(obj->InternalFieldCount() > 0); + return static_cast(obj->GetPointerFromInternalField(0)); +} + + +uv_tcp_t* TCPWrap::UVHandle() { + return &handle_; +} + + Handle TCPWrap::New(const Arguments& args) { // This constructor should not be exposed to public javascript. // Therefore we assert that we are not trying to call this as a diff --git a/src/tcp_wrap.h b/src/tcp_wrap.h index 26e061f4830..9b8cd291407 100644 --- a/src/tcp_wrap.h +++ b/src/tcp_wrap.h @@ -31,6 +31,8 @@ class TCPWrap : public StreamWrap { static TCPWrap* Unwrap(v8::Local obj); static void Initialize(v8::Handle target); + uv_tcp_t* UVHandle(); + private: TCPWrap(v8::Handle object); ~TCPWrap(); diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index de43a4f23c7..f1a189c91e4 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -24,6 +24,7 @@ #include "req_wrap.h" #include "handle_wrap.h" #include "stream_wrap.h" +#include "tty_wrap.h" namespace node { @@ -42,128 +43,141 @@ using v8::Arguments; using v8::Integer; using v8::Undefined; -class TTYWrap : StreamWrap { - public: - static void Initialize(Handle target) { - StreamWrap::Initialize(target); - HandleScope scope; +void TTYWrap::Initialize(Handle target) { + StreamWrap::Initialize(target); - Local t = FunctionTemplate::New(New); - t->SetClassName(String::NewSymbol("TTY")); + HandleScope scope; - t->InstanceTemplate()->SetInternalFieldCount(1); + Local t = FunctionTemplate::New(New); + t->SetClassName(String::NewSymbol("TTY")); - NODE_SET_PROTOTYPE_METHOD(t, "close", HandleWrap::Close); - NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref); + t->InstanceTemplate()->SetInternalFieldCount(1); - NODE_SET_PROTOTYPE_METHOD(t, "readStart", StreamWrap::ReadStart); - NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop); + NODE_SET_PROTOTYPE_METHOD(t, "close", HandleWrap::Close); + NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref); - NODE_SET_PROTOTYPE_METHOD(t, "writeBuffer", StreamWrap::WriteBuffer); - NODE_SET_PROTOTYPE_METHOD(t, "writeAsciiString", StreamWrap::WriteAsciiString); - NODE_SET_PROTOTYPE_METHOD(t, "writeUtf8String", StreamWrap::WriteUtf8String); - NODE_SET_PROTOTYPE_METHOD(t, "writeUcs2String", StreamWrap::WriteUcs2String); + NODE_SET_PROTOTYPE_METHOD(t, "readStart", StreamWrap::ReadStart); + NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop); - NODE_SET_PROTOTYPE_METHOD(t, "getWindowSize", TTYWrap::GetWindowSize); - NODE_SET_PROTOTYPE_METHOD(t, "setRawMode", SetRawMode); + NODE_SET_PROTOTYPE_METHOD(t, "writeBuffer", StreamWrap::WriteBuffer); + NODE_SET_PROTOTYPE_METHOD(t, "writeAsciiString", StreamWrap::WriteAsciiString); + NODE_SET_PROTOTYPE_METHOD(t, "writeUtf8String", StreamWrap::WriteUtf8String); + NODE_SET_PROTOTYPE_METHOD(t, "writeUcs2String", StreamWrap::WriteUcs2String); - NODE_SET_METHOD(target, "isTTY", IsTTY); - NODE_SET_METHOD(target, "guessHandleType", GuessHandleType); + NODE_SET_PROTOTYPE_METHOD(t, "getWindowSize", TTYWrap::GetWindowSize); + NODE_SET_PROTOTYPE_METHOD(t, "setRawMode", SetRawMode); - target->Set(String::NewSymbol("TTY"), t->GetFunction()); - } + NODE_SET_METHOD(target, "isTTY", IsTTY); + NODE_SET_METHOD(target, "guessHandleType", GuessHandleType); - private: - static Handle GuessHandleType(const Arguments& args) { - HandleScope scope; - int fd = args[0]->Int32Value(); - assert(fd >= 0); + target->Set(String::NewSymbol("TTY"), t->GetFunction()); +} - uv_handle_type t = uv_guess_handle(fd); - switch (t) { - case UV_TTY: - return scope.Close(String::New("TTY")); +TTYWrap* TTYWrap::Unwrap(Local obj) { + assert(!obj.IsEmpty()); + assert(obj->InternalFieldCount() > 0); + return static_cast(obj->GetPointerFromInternalField(0)); +} - case UV_NAMED_PIPE: - return scope.Close(String::New("PIPE")); - case UV_FILE: - return scope.Close(String::New("FILE")); +uv_tty_t* TTYWrap::UVHandle() { + return &handle_; +} - default: - assert(0); - return v8::Undefined(); - } - } - static Handle IsTTY(const Arguments& args) { - HandleScope scope; - int fd = args[0]->Int32Value(); - assert(fd >= 0); - return uv_guess_handle(fd) == UV_TTY ? v8::True() : v8::False(); - } +Handle TTYWrap::GuessHandleType(const Arguments& args) { + HandleScope scope; + int fd = args[0]->Int32Value(); + assert(fd >= 0); + + uv_handle_type t = uv_guess_handle(fd); - static Handle GetWindowSize(const Arguments& args) { - HandleScope scope; + switch (t) { + case UV_TTY: + return scope.Close(String::New("TTY")); - UNWRAP(TTYWrap) + case UV_NAMED_PIPE: + return scope.Close(String::New("PIPE")); - int width, height; - int r = uv_tty_get_winsize(&wrap->handle_, &width, &height); + case UV_FILE: + return scope.Close(String::New("FILE")); - if (r) { - SetErrno(uv_last_error(uv_default_loop())); + default: + assert(0); return v8::Undefined(); - } + } +} - Local a = v8::Array::New(2); - a->Set(0, Integer::New(width)); - a->Set(1, Integer::New(height)); - return scope.Close(a); - } +Handle TTYWrap::IsTTY(const Arguments& args) { + HandleScope scope; + int fd = args[0]->Int32Value(); + assert(fd >= 0); + return uv_guess_handle(fd) == UV_TTY ? v8::True() : v8::False(); +} - static Handle SetRawMode(const Arguments& args) { - HandleScope scope; - UNWRAP(TTYWrap) +Handle TTYWrap::GetWindowSize(const Arguments& args) { + HandleScope scope; - int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue()); + UNWRAP(TTYWrap) - if (r) { - SetErrno(uv_last_error(uv_default_loop())); - } + int width, height; + int r = uv_tty_get_winsize(&wrap->handle_, &width, &height); - return scope.Close(Integer::New(r)); + if (r) { + SetErrno(uv_last_error(uv_default_loop())); + return v8::Undefined(); } - static Handle New(const Arguments& args) { - HandleScope scope; + Local a = v8::Array::New(2); + a->Set(0, Integer::New(width)); + a->Set(1, Integer::New(height)); - // This constructor should not be exposed to public javascript. - // Therefore we assert that we are not trying to call this as a - // normal function. - assert(args.IsConstructCall()); + return scope.Close(a); +} - int fd = args[0]->Int32Value(); - assert(fd >= 0); - TTYWrap* wrap = new TTYWrap(args.This(), fd, args[1]->IsTrue()); - assert(wrap); - wrap->UpdateWriteQueueSize(); +Handle TTYWrap::SetRawMode(const Arguments& args) { + HandleScope scope; - return scope.Close(args.This()); - } + UNWRAP(TTYWrap) + + int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue()); - TTYWrap(Handle object, int fd, bool readable) - : StreamWrap(object, (uv_stream_t*)&handle_) { - uv_tty_init(uv_default_loop(), &handle_, fd, readable); + if (r) { + SetErrno(uv_last_error(uv_default_loop())); } - uv_tty_t handle_; -}; + return scope.Close(Integer::New(r)); +} + + +Handle TTYWrap::New(const Arguments& args) { + HandleScope scope; + + // This constructor should not be exposed to public javascript. + // Therefore we assert that we are not trying to call this as a + // normal function. + assert(args.IsConstructCall()); + + int fd = args[0]->Int32Value(); + assert(fd >= 0); + + TTYWrap* wrap = new TTYWrap(args.This(), fd, args[1]->IsTrue()); + assert(wrap); + wrap->UpdateWriteQueueSize(); + + return scope.Close(args.This()); +} + + +TTYWrap::TTYWrap(Handle object, int fd, bool readable) + : StreamWrap(object, (uv_stream_t*)&handle_) { + uv_tty_init(uv_default_loop(), &handle_, fd, readable); +} } // namespace node diff --git a/src/tty_wrap.h b/src/tty_wrap.h new file mode 100644 index 00000000000..4a3341a6605 --- /dev/null +++ b/src/tty_wrap.h @@ -0,0 +1,58 @@ +// 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. + +#ifndef TTY_WRAP_H_ +#define TTY_WRAP_H_ + +#include "handle_wrap.h" +#include "stream_wrap.h" + +namespace node { + +using v8::Object; +using v8::Handle; +using v8::Local; +using v8::Value; +using v8::Arguments; + + +class TTYWrap : StreamWrap { + public: + static void Initialize(Handle target); + static TTYWrap* Unwrap(Local obj); + + uv_tty_t* UVHandle(); + + private: + TTYWrap(Handle object, int fd, bool readable); + + static Handle GuessHandleType(const Arguments& args); + static Handle IsTTY(const Arguments& args); + static Handle GetWindowSize(const Arguments& args); + static Handle SetRawMode(const Arguments& args); + static Handle New(const Arguments& args); + + uv_tty_t handle_; +}; + +} // namespace node + +#endif // TTY_WRAP_H_ diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 39ab7502155..b0f8bad1f8b 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -24,6 +24,7 @@ #include "slab_allocator.h" #include "req_wrap.h" #include "handle_wrap.h" +#include "udp_wrap.h" #include @@ -60,45 +61,6 @@ static Persistent onmessage_sym; static SlabAllocator slab_allocator(SLAB_SIZE); -class UDPWrap: public HandleWrap { -public: - static void Initialize(Handle target); - static Handle New(const Arguments& args); - static Handle Bind(const Arguments& args); - static Handle Send(const Arguments& args); - static Handle Bind6(const Arguments& args); - static Handle Send6(const Arguments& args); - static Handle RecvStart(const Arguments& args); - static Handle RecvStop(const Arguments& args); - static Handle GetSockName(const Arguments& args); - static Handle AddMembership(const Arguments& args); - static Handle DropMembership(const Arguments& args); - static Handle SetMulticastTTL(const Arguments& args); - static Handle SetMulticastLoopback(const Arguments& args); - static Handle SetBroadcast(const Arguments& args); - static Handle SetTTL(const Arguments& args); - -private: - UDPWrap(Handle object); - virtual ~UDPWrap(); - - static Handle DoBind(const Arguments& args, int family); - static Handle DoSend(const Arguments& args, int family); - static Handle SetMembership(const Arguments& args, - uv_membership membership); - - static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size); - static void OnSend(uv_udp_send_t* req, int status); - static void OnRecv(uv_udp_t* handle, - ssize_t nread, - uv_buf_t buf, - struct sockaddr* addr, - unsigned flags); - - uv_udp_t handle_; -}; - - UDPWrap::UDPWrap(Handle object): HandleWrap(object, (uv_handle_t*)&handle_) { int r = uv_udp_init(uv_default_loop(), &handle_); @@ -426,6 +388,18 @@ void UDPWrap::OnRecv(uv_udp_t* handle, } +UDPWrap* UDPWrap::Unwrap(Local obj) { + assert(!obj.IsEmpty()); + assert(obj->InternalFieldCount() > 0); + return static_cast(obj->GetPointerFromInternalField(0)); +} + + +uv_udp_t* UDPWrap::UVHandle() { + return &handle_; +} + + } // namespace node NODE_MODULE(node_udp_wrap, node::UDPWrap::Initialize) diff --git a/src/udp_wrap.h b/src/udp_wrap.h new file mode 100644 index 00000000000..9ca2eaea904 --- /dev/null +++ b/src/udp_wrap.h @@ -0,0 +1,60 @@ +#ifndef UDP_WRAP_H_ +#define UDP_WRAP_H_ + +#include "node.h" +#include "req_wrap.h" +#include "handle_wrap.h" + +namespace node { + +using v8::Object; +using v8::Handle; +using v8::Local; +using v8::Value; +using v8::String; +using v8::Arguments; + +class UDPWrap: public HandleWrap { + public: + static void Initialize(Handle target); + static Handle New(const Arguments& args); + static Handle Bind(const Arguments& args); + static Handle Send(const Arguments& args); + static Handle Bind6(const Arguments& args); + static Handle Send6(const Arguments& args); + static Handle RecvStart(const Arguments& args); + static Handle RecvStop(const Arguments& args); + static Handle GetSockName(const Arguments& args); + static Handle AddMembership(const Arguments& args); + static Handle DropMembership(const Arguments& args); + static Handle SetMulticastTTL(const Arguments& args); + static Handle SetMulticastLoopback(const Arguments& args); + static Handle SetBroadcast(const Arguments& args); + static Handle SetTTL(const Arguments& args); + static UDPWrap* Unwrap(Local obj); + + uv_udp_t* UVHandle(); + + private: + UDPWrap(Handle object); + virtual ~UDPWrap(); + + static Handle DoBind(const Arguments& args, int family); + static Handle DoSend(const Arguments& args, int family); + static Handle SetMembership(const Arguments& args, + uv_membership membership); + + static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size); + static void OnSend(uv_udp_send_t* req, int status); + static void OnRecv(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags); + + uv_udp_t handle_; +}; + +} // namespace node + +#endif // UDP_WRAP_H_ diff --git a/test/simple/test-process-wrap.js b/test/simple/test-process-wrap.js index 1abf196ee98..655c8281586 100644 --- a/test/simple/test-process-wrap.js +++ b/test/simple/test-process-wrap.js @@ -58,7 +58,11 @@ pipe.onread = function(b, off, len) { p.spawn({ file: process.execPath, args: [process.execPath, '-v'], - stdoutStream: pipe + stdio: [ + { type: 'ignore' }, + { type: 'pipe', handle: pipe }, + { type: 'ignore' } + ] });