src: replace naive search in Buffer::IndexOf
[platform/upstream/nodejs.git] / src / process_wrap.cc
1 #include "env.h"
2 #include "env-inl.h"
3 #include "handle_wrap.h"
4 #include "node_wrap.h"
5 #include "util.h"
6 #include "util-inl.h"
7
8 #include <string.h>
9 #include <stdlib.h>
10
11 namespace node {
12
13 using v8::Array;
14 using v8::Context;
15 using v8::Function;
16 using v8::FunctionCallbackInfo;
17 using v8::FunctionTemplate;
18 using v8::HandleScope;
19 using v8::Integer;
20 using v8::Local;
21 using v8::Number;
22 using v8::Object;
23 using v8::String;
24 using v8::Value;
25
26 class ProcessWrap : public HandleWrap {
27  public:
28   static void Initialize(Local<Object> target,
29                          Local<Value> unused,
30                          Local<Context> context) {
31     Environment* env = Environment::GetCurrent(context);
32     Local<FunctionTemplate> constructor = env->NewFunctionTemplate(New);
33     constructor->InstanceTemplate()->SetInternalFieldCount(1);
34     constructor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Process"));
35
36     env->SetProtoMethod(constructor, "close", HandleWrap::Close);
37
38     env->SetProtoMethod(constructor, "spawn", Spawn);
39     env->SetProtoMethod(constructor, "kill", Kill);
40
41     env->SetProtoMethod(constructor, "ref", HandleWrap::Ref);
42     env->SetProtoMethod(constructor, "unref", HandleWrap::Unref);
43
44     target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Process"),
45                 constructor->GetFunction());
46   }
47
48   size_t self_size() const override { return sizeof(*this); }
49
50  private:
51   static void New(const FunctionCallbackInfo<Value>& args) {
52     // This constructor should not be exposed to public javascript.
53     // Therefore we assert that we are not trying to call this as a
54     // normal function.
55     CHECK(args.IsConstructCall());
56     Environment* env = Environment::GetCurrent(args);
57     new ProcessWrap(env, args.This());
58   }
59
60   ProcessWrap(Environment* env, Local<Object> object)
61       : HandleWrap(env,
62                    object,
63                    reinterpret_cast<uv_handle_t*>(&process_),
64                    AsyncWrap::PROVIDER_PROCESSWRAP) {
65   }
66
67   static void ParseStdioOptions(Environment* env,
68                                 Local<Object> js_options,
69                                 uv_process_options_t* options) {
70     Local<String> stdio_key = env->stdio_string();
71     Local<Array> stdios = js_options->Get(stdio_key).As<Array>();
72
73     uint32_t len = stdios->Length();
74     options->stdio = new uv_stdio_container_t[len];
75     options->stdio_count = len;
76
77     for (uint32_t i = 0; i < len; i++) {
78       Local<Object> stdio = stdios->Get(i).As<Object>();
79       Local<Value> type = stdio->Get(env->type_string());
80
81       if (type->Equals(env->ignore_string())) {
82         options->stdio[i].flags = UV_IGNORE;
83       } else if (type->Equals(env->pipe_string())) {
84         options->stdio[i].flags = static_cast<uv_stdio_flags>(
85             UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE);
86         Local<String> handle_key = env->handle_string();
87         Local<Object> handle = stdio->Get(handle_key).As<Object>();
88         options->stdio[i].data.stream =
89             reinterpret_cast<uv_stream_t*>(
90                 Unwrap<PipeWrap>(handle)->UVHandle());
91       } else if (type->Equals(env->wrap_string())) {
92         Local<String> handle_key = env->handle_string();
93         Local<Object> handle = stdio->Get(handle_key).As<Object>();
94         uv_stream_t* stream = HandleToStream(env, handle);
95         CHECK_NE(stream, nullptr);
96
97         options->stdio[i].flags = UV_INHERIT_STREAM;
98         options->stdio[i].data.stream = stream;
99       } else {
100         Local<String> fd_key = env->fd_string();
101         int fd = static_cast<int>(stdio->Get(fd_key)->IntegerValue());
102         options->stdio[i].flags = UV_INHERIT_FD;
103         options->stdio[i].data.fd = fd;
104       }
105     }
106   }
107
108   static void Spawn(const FunctionCallbackInfo<Value>& args) {
109     Environment* env = Environment::GetCurrent(args);
110
111     ProcessWrap* wrap = Unwrap<ProcessWrap>(args.Holder());
112
113     Local<Object> js_options = args[0]->ToObject(env->isolate());
114
115     uv_process_options_t options;
116     memset(&options, 0, sizeof(uv_process_options_t));
117
118     options.exit_cb = OnExit;
119
120     // options.uid
121     Local<Value> uid_v = js_options->Get(env->uid_string());
122     if (uid_v->IsInt32()) {
123       int32_t uid = uid_v->Int32Value();
124       if (uid & ~((uv_uid_t) ~0)) {
125         return env->ThrowRangeError("options.uid is out of range");
126       }
127       options.flags |= UV_PROCESS_SETUID;
128       options.uid = (uv_uid_t) uid;
129     } else if (!uid_v->IsUndefined() && !uid_v->IsNull()) {
130       return env->ThrowTypeError("options.uid should be a number");
131     }
132
133     // options.gid
134     Local<Value> gid_v = js_options->Get(env->gid_string());
135     if (gid_v->IsInt32()) {
136       int32_t gid = gid_v->Int32Value();
137       if (gid & ~((uv_gid_t) ~0)) {
138         return env->ThrowRangeError("options.gid is out of range");
139       }
140       options.flags |= UV_PROCESS_SETGID;
141       options.gid = (uv_gid_t) gid;
142     } else if (!gid_v->IsUndefined() && !gid_v->IsNull()) {
143       return env->ThrowTypeError("options.gid should be a number");
144     }
145
146     // TODO(bnoordhuis) is this possible to do without mallocing ?
147
148     // options.file
149     Local<Value> file_v = js_options->Get(env->file_string());
150     node::Utf8Value file(env->isolate(),
151                          file_v->IsString() ? file_v : Local<Value>());
152     if (file.length() > 0) {
153       options.file = *file;
154     } else {
155       return env->ThrowTypeError("Bad argument");
156     }
157
158     // options.args
159     Local<Value> argv_v = js_options->Get(env->args_string());
160     if (!argv_v.IsEmpty() && argv_v->IsArray()) {
161       Local<Array> js_argv = Local<Array>::Cast(argv_v);
162       int argc = js_argv->Length();
163       // Heap allocate to detect errors. +1 is for nullptr.
164       options.args = new char*[argc + 1];
165       for (int i = 0; i < argc; i++) {
166         node::Utf8Value arg(env->isolate(), js_argv->Get(i));
167         options.args[i] = strdup(*arg);
168       }
169       options.args[argc] = nullptr;
170     }
171
172     // options.cwd
173     Local<Value> cwd_v = js_options->Get(env->cwd_string());
174     node::Utf8Value cwd(env->isolate(),
175                         cwd_v->IsString() ? cwd_v : Local<Value>());
176     if (cwd.length() > 0) {
177       options.cwd = *cwd;
178     }
179
180     // options.env
181     Local<Value> env_v = js_options->Get(env->env_pairs_string());
182     if (!env_v.IsEmpty() && env_v->IsArray()) {
183       Local<Array> env_opt = Local<Array>::Cast(env_v);
184       int envc = env_opt->Length();
185       options.env = new char*[envc + 1];  // Heap allocated to detect errors.
186       for (int i = 0; i < envc; i++) {
187         node::Utf8Value pair(env->isolate(), env_opt->Get(i));
188         options.env[i] = strdup(*pair);
189       }
190       options.env[envc] = nullptr;
191     }
192
193     // options.stdio
194     ParseStdioOptions(env, js_options, &options);
195
196     // options.windows_verbatim_arguments
197     Local<String> windows_verbatim_arguments_key =
198         env->windows_verbatim_arguments_string();
199     if (js_options->Get(windows_verbatim_arguments_key)->IsTrue()) {
200       options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
201     }
202
203     // options.detached
204     Local<String> detached_key = env->detached_string();
205     if (js_options->Get(detached_key)->IsTrue()) {
206       options.flags |= UV_PROCESS_DETACHED;
207     }
208
209     int err = uv_spawn(env->event_loop(), &wrap->process_, &options);
210
211     if (err == 0) {
212       CHECK_EQ(wrap->process_.data, wrap);
213       wrap->object()->Set(env->pid_string(),
214                           Integer::New(env->isolate(), wrap->process_.pid));
215     }
216
217     if (options.args) {
218       for (int i = 0; options.args[i]; i++) free(options.args[i]);
219       delete [] options.args;
220     }
221
222     if (options.env) {
223       for (int i = 0; options.env[i]; i++) free(options.env[i]);
224       delete [] options.env;
225     }
226
227     delete[] options.stdio;
228
229     args.GetReturnValue().Set(err);
230   }
231
232   static void Kill(const FunctionCallbackInfo<Value>& args) {
233     ProcessWrap* wrap = Unwrap<ProcessWrap>(args.Holder());
234     int signal = args[0]->Int32Value();
235     int err = uv_process_kill(&wrap->process_, signal);
236     args.GetReturnValue().Set(err);
237   }
238
239   static void OnExit(uv_process_t* handle,
240                      int64_t exit_status,
241                      int term_signal) {
242     ProcessWrap* wrap = static_cast<ProcessWrap*>(handle->data);
243     CHECK_NE(wrap, nullptr);
244     CHECK_EQ(&wrap->process_, handle);
245
246     Environment* env = wrap->env();
247     HandleScope handle_scope(env->isolate());
248     Context::Scope context_scope(env->context());
249
250     Local<Value> argv[] = {
251       Number::New(env->isolate(), static_cast<double>(exit_status)),
252       OneByteString(env->isolate(), signo_string(term_signal))
253     };
254
255     wrap->MakeCallback(env->onexit_string(), ARRAY_SIZE(argv), argv);
256   }
257
258   uv_process_t process_;
259 };
260
261
262 }  // namespace node
263
264 NODE_MODULE_CONTEXT_AWARE_BUILTIN(process_wrap, node::ProcessWrap::Initialize)