diff --git a/src/node.cc b/src/node.cc index 07384838b7..6036e158b8 100644 --- a/src/node.cc +++ b/src/node.cc @@ -4,6 +4,7 @@ #include "file.h" #include "http.h" #include "timer.h" +#include "process.h" #include "constants.h" #include "natives.h" @@ -302,6 +303,7 @@ Load (int argc, char *argv[]) NODE_SET_METHOD(node_obj, "reallyExit", node_exit); Timer::Initialize(node_obj); + Process::Initialize(node_obj); DefineConstants(node_obj); diff --git a/src/process.cc b/src/process.cc new file mode 100644 index 0000000000..fddc4ca207 --- /dev/null +++ b/src/process.cc @@ -0,0 +1,225 @@ +#include "node.h" +#include "process.h" + +#include +#include +#include +#include + +using namespace v8; +using namespace node; + +Persistent Process::constructor_template; + +void +Process::Initialize (Handle target) +{ + HandleScope scope; + + Local t = FunctionTemplate::New(Process::New); + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + +#if 0 + NODE_SET_PROTOTYPE_METHOD(constructor_template, "start", Timer::Start); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "stop", Timer::Stop); +#endif + + target->Set(String::NewSymbol("Process"), constructor_template->GetFunction()); +} + +Handle +Process::New (const Arguments& args) +{ + if (args.Length() == 0) return Undefined(); + + HandleScope scope; + + String::Utf8Value command(args[0]->ToString()); + + Process *p = new Process(args.Holder()); + ObjectWrap::InformV8ofAllocation(p); + + int r = p->Spawn(*command); + if (r != 0) { + return ThrowException(String::New("Error spawning")); + } + + return args.This(); +} + +Process::Process (Handle handle) + : ObjectWrap(handle) +{ + ev_init(&stdout_watcher_, Process::OnOutput); + stdout_watcher_.data = this; + + ev_init(&stderr_watcher_, Process::OnError); + stderr_watcher_.data = this; + + ev_init(&stdin_watcher_, Process::OnWritable); + stdin_watcher_.data = this; + + ev_init(&child_watcher_, Process::OnCHLD); + child_watcher_.data = this; + + stdout_pipe_[0] = -1; + stdout_pipe_[1] = -1; + stderr_pipe_[0] = -1; + stderr_pipe_[1] = -1; + stdin_pipe_[0] = -1; + stdin_pipe_[1] = -1; + + pid_ = 0; +} + +Process::~Process () +{ + Shutdown(); +} + +void +Process::Shutdown () +{ + if (stdout_pipe_[0] >= 0) close(stdout_pipe_[0]); + if (stdout_pipe_[1] >= 0) close(stdout_pipe_[1]); + + if (stderr_pipe_[0] >= 0) close(stderr_pipe_[0]); + if (stderr_pipe_[1] >= 0) close(stderr_pipe_[1]); + + if (stdin_pipe_[0] >= 0) close(stdin_pipe_[0]); + if (stdin_pipe_[1] >= 0) close(stdin_pipe_[1]); + + stdout_pipe_[0] = -1; + stdout_pipe_[1] = -1; + stderr_pipe_[0] = -1; + stderr_pipe_[1] = -1; + stdin_pipe_[0] = -1; + stdin_pipe_[1] = -1; + + ev_io_stop(EV_DEFAULT_UC_ &stdout_watcher_); + ev_io_stop(EV_DEFAULT_UC_ &stderr_watcher_); + ev_io_stop(EV_DEFAULT_UC_ &stdin_watcher_); + + ev_child_stop(EV_DEFAULT_UC_ &child_watcher_); + /* XXX Kill the PID? */ + pid_ = 0; + + Detach(); +} + +static int +SetNonBlocking (int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (r != 0) { + perror("SetNonBlocking()"); + } + return r; +} + +int +Process::Spawn (const char *command) +{ + assert(pid_ == 0); + assert(stdout_pipe_[0] == -1); + assert(stdout_pipe_[1] == -1); + assert(stderr_pipe_[0] == -1); + assert(stderr_pipe_[1] == -1); + assert(stdin_pipe_[0] == -1); + assert(stdin_pipe_[1] == -1); + + /* An implementation of popen(), basically */ + if (pipe(stdout_pipe_) < 0) { + perror("pipe()"); + return -1; + } + + if (pipe(stderr_pipe_) < 0) { + perror("pipe()"); + return -2; + } + + if (pipe(stdin_pipe_) < 0) { + perror("pipe()"); + return -3; + } + + switch (pid_ = vfork()) { + case -1: // Error. + Shutdown(); + return -4; + + case 0: // Child. + close(stdout_pipe_[0]); // close read end + dup2(stdout_pipe_[1], STDOUT_FILENO); + + close(stderr_pipe_[0]); // close read end + dup2(stderr_pipe_[1], STDERR_FILENO); + + close(stdin_pipe_[1]); // close write end + dup2(stdin_pipe_[0], STDIN_FILENO); + + execl("/bin/sh", "-c", command, (char *)NULL); + _exit(127); + } + + // Parent. + + ev_child_set(&child_watcher_, pid_, 0); + ev_child_start(EV_DEFAULT_UC_ &child_watcher_); + + SetNonBlocking(stdout_pipe_[0]); + SetNonBlocking(stderr_pipe_[0]); + SetNonBlocking(stdin_pipe_[1]); + + ev_io_set(&stdout_watcher_, stdout_pipe_[0], EV_READ); + ev_io_set(&stderr_watcher_, stderr_pipe_[0], EV_READ); + ev_io_set(&stdin_watcher_, stdin_pipe_[1], EV_WRITE); + + ev_io_start(EV_DEFAULT_UC_ &stdout_watcher_); + ev_io_start(EV_DEFAULT_UC_ &stderr_watcher_); + ev_io_start(EV_DEFAULT_UC_ &stdin_watcher_); + + close(stdout_pipe_[1]); // close write end + close(stderr_pipe_[1]); // close write end + close(stdin_pipe_[0]); // close read end + + stdout_pipe_[1] = -1; + stderr_pipe_[1] = -1; + stdin_pipe_[0] = -1; + + Attach(); + + return 0; +} + +void +Process::OnOutput (EV_P_ ev_io *watcher, int revents) +{ + Process *process = static_cast(watcher->data); + assert(revents == EV_READ); +} + +void +Process::OnError (EV_P_ ev_io *watcher, int revents) +{ + Process *process = static_cast(watcher->data); + assert(revents == EV_READ); +} + +void +Process::OnWritable (EV_P_ ev_io *watcher, int revents) +{ + Process *process = static_cast(watcher->data); + assert(revents == EV_WRITE); +} + +void +Process::OnCHLD (EV_P_ ev_child *watcher, int revents) +{ + ev_child_stop(EV_A_ watcher); + Process *process = static_cast(watcher->data); + assert(revents == EV_CHILD); +} diff --git a/src/process.h b/src/process.h new file mode 100644 index 0000000000..5e2a25eb76 --- /dev/null +++ b/src/process.h @@ -0,0 +1,45 @@ +#ifndef node_process_h +#define node_process_h + +#include "node.h" +#include +#include + +namespace node { + +class Process : ObjectWrap { + public: + static void Initialize (v8::Handle target); + + virtual size_t size (void) { return sizeof(Process); } + + protected: + static v8::Persistent constructor_template; + static v8::Handle New (const v8::Arguments& args); + + Process(v8::Handle handle); + ~Process(); + + void Shutdown (); + int Spawn (const char *command); + + private: + static void OnOutput (EV_P_ ev_io *watcher, int revents); + static void OnError (EV_P_ ev_io *watcher, int revents); + static void OnWritable (EV_P_ ev_io *watcher, int revents); + static void OnCHLD (EV_P_ ev_child *watcher, int revents); + + ev_io stdout_watcher_; + ev_io stderr_watcher_; + ev_io stdin_watcher_; + ev_child child_watcher_; + + int stdout_pipe_[2]; + int stderr_pipe_[2]; + int stdin_pipe_[2]; + + pid_t pid_; +}; + +} // namespace node +#endif // node_process_h diff --git a/wscript b/wscript index 8d49a88118..ce85c400b5 100644 --- a/wscript +++ b/wscript @@ -158,6 +158,7 @@ def build(bld): src/net.cc src/file.cc src/timer.cc + src/process.cc src/constants.cc """ node.includes = """