GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
// Copyright Joyent, Inc. and other Node contributors. |
||
2 |
// |
||
3 |
// Permission is hereby granted, free of charge, to any person obtaining a |
||
4 |
// copy of this software and associated documentation files (the |
||
5 |
// "Software"), to deal in the Software without restriction, including |
||
6 |
// without limitation the rights to use, copy, modify, merge, publish, |
||
7 |
// distribute, sublicense, and/or sell copies of the Software, and to permit |
||
8 |
// persons to whom the Software is furnished to do so, subject to the |
||
9 |
// following conditions: |
||
10 |
// |
||
11 |
// The above copyright notice and this permission notice shall be included |
||
12 |
// in all copies or substantial portions of the Software. |
||
13 |
// |
||
14 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||
15 |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
16 |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
||
17 |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||
18 |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||
19 |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
||
20 |
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
21 |
|||
22 |
#include "env-inl.h" |
||
23 |
#include "stream_base-inl.h" |
||
24 |
#include "stream_wrap.h" |
||
25 |
#include "util-inl.h" |
||
26 |
|||
27 |
#include <cstring> |
||
28 |
#include <cstdlib> |
||
29 |
|||
30 |
namespace node { |
||
31 |
|||
32 |
using v8::Array; |
||
33 |
using v8::Context; |
||
34 |
using v8::FunctionCallbackInfo; |
||
35 |
using v8::FunctionTemplate; |
||
36 |
using v8::HandleScope; |
||
37 |
using v8::Int32; |
||
38 |
using v8::Integer; |
||
39 |
using v8::Local; |
||
40 |
using v8::Number; |
||
41 |
using v8::Object; |
||
42 |
using v8::String; |
||
43 |
using v8::Value; |
||
44 |
|||
45 |
namespace { |
||
46 |
|||
47 |
class ProcessWrap : public HandleWrap { |
||
48 |
public: |
||
49 |
4752 |
static void Initialize(Local<Object> target, |
|
50 |
Local<Value> unused, |
||
51 |
Local<Context> context, |
||
52 |
void* priv) { |
||
53 |
4752 |
Environment* env = Environment::GetCurrent(context); |
|
54 |
4752 |
Local<FunctionTemplate> constructor = env->NewFunctionTemplate(New); |
|
55 |
9504 |
constructor->InstanceTemplate()->SetInternalFieldCount( |
|
56 |
ProcessWrap::kInternalFieldCount); |
||
57 |
|||
58 |
4752 |
constructor->Inherit(HandleWrap::GetConstructorTemplate(env)); |
|
59 |
|||
60 |
4752 |
env->SetProtoMethod(constructor, "spawn", Spawn); |
|
61 |
4752 |
env->SetProtoMethod(constructor, "kill", Kill); |
|
62 |
|||
63 |
4752 |
env->SetConstructorFunction(target, "Process", constructor); |
|
64 |
4752 |
} |
|
65 |
|||
66 |
1 |
SET_NO_MEMORY_INFO() |
|
67 |
9 |
SET_MEMORY_INFO_NAME(ProcessWrap) |
|
68 |
1 |
SET_SELF_SIZE(ProcessWrap) |
|
69 |
|||
70 |
private: |
||
71 |
1869 |
static void New(const FunctionCallbackInfo<Value>& args) { |
|
72 |
// This constructor should not be exposed to public javascript. |
||
73 |
// Therefore we assert that we are not trying to call this as a |
||
74 |
// normal function. |
||
75 |
✗✓ | 1869 |
CHECK(args.IsConstructCall()); |
76 |
1869 |
Environment* env = Environment::GetCurrent(args); |
|
77 |
1869 |
new ProcessWrap(env, args.This()); |
|
78 |
1869 |
} |
|
79 |
|||
80 |
1869 |
ProcessWrap(Environment* env, Local<Object> object) |
|
81 |
1869 |
: HandleWrap(env, |
|
82 |
object, |
||
83 |
1869 |
reinterpret_cast<uv_handle_t*>(&process_), |
|
84 |
1869 |
AsyncWrap::PROVIDER_PROCESSWRAP) { |
|
85 |
1869 |
MarkAsUninitialized(); |
|
86 |
1869 |
} |
|
87 |
|||
88 |
3912 |
static uv_stream_t* StreamForWrap(Environment* env, Local<Object> stdio) { |
|
89 |
3912 |
Local<String> handle_key = env->handle_string(); |
|
90 |
// This property has always been set by JS land if we are in this code path. |
||
91 |
Local<Object> handle = |
||
92 |
7824 |
stdio->Get(env->context(), handle_key).ToLocalChecked().As<Object>(); |
|
93 |
|||
94 |
3912 |
uv_stream_t* stream = LibuvStreamWrap::From(env, handle)->stream(); |
|
95 |
✗✓ | 3912 |
CHECK_NOT_NULL(stream); |
96 |
3912 |
return stream; |
|
97 |
} |
||
98 |
|||
99 |
1859 |
static void ParseStdioOptions(Environment* env, |
|
100 |
Local<Object> js_options, |
||
101 |
uv_process_options_t* options) { |
||
102 |
1859 |
Local<Context> context = env->context(); |
|
103 |
1859 |
Local<String> stdio_key = env->stdio_string(); |
|
104 |
Local<Array> stdios = |
||
105 |
5577 |
js_options->Get(context, stdio_key).ToLocalChecked().As<Array>(); |
|
106 |
|||
107 |
1859 |
uint32_t len = stdios->Length(); |
|
108 |
✓✗ | 1859 |
options->stdio = new uv_stdio_container_t[len]; |
109 |
1859 |
options->stdio_count = len; |
|
110 |
|||
111 |
✓✓ | 7943 |
for (uint32_t i = 0; i < len; i++) { |
112 |
Local<Object> stdio = |
||
113 |
18252 |
stdios->Get(context, i).ToLocalChecked().As<Object>(); |
|
114 |
Local<Value> type = |
||
115 |
18252 |
stdio->Get(context, env->type_string()).ToLocalChecked(); |
|
116 |
|||
117 |
✓✓ | 12168 |
if (type->StrictEquals(env->ignore_string())) { |
118 |
54 |
options->stdio[i].flags = UV_IGNORE; |
|
119 |
✓✓ | 12060 |
} else if (type->StrictEquals(env->pipe_string())) { |
120 |
3904 |
options->stdio[i].flags = static_cast<uv_stdio_flags>( |
|
121 |
UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE); |
||
122 |
3904 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
123 |
✓✓ | 4252 |
} else if (type->StrictEquals(env->overlapped_string())) { |
124 |
1 |
options->stdio[i].flags = static_cast<uv_stdio_flags>( |
|
125 |
UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE | |
||
126 |
UV_OVERLAPPED_PIPE); |
||
127 |
1 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
128 |
✓✓ | 4250 |
} else if (type->StrictEquals(env->wrap_string())) { |
129 |
7 |
options->stdio[i].flags = UV_INHERIT_STREAM; |
|
130 |
7 |
options->stdio[i].data.stream = StreamForWrap(env, stdio); |
|
131 |
} else { |
||
132 |
2118 |
Local<String> fd_key = env->fd_string(); |
|
133 |
4236 |
Local<Value> fd_value = stdio->Get(context, fd_key).ToLocalChecked(); |
|
134 |
✗✓ | 2118 |
CHECK(fd_value->IsNumber()); |
135 |
2118 |
int fd = static_cast<int>(fd_value.As<Integer>()->Value()); |
|
136 |
2118 |
options->stdio[i].flags = UV_INHERIT_FD; |
|
137 |
2118 |
options->stdio[i].data.fd = fd; |
|
138 |
} |
||
139 |
} |
||
140 |
1859 |
} |
|
141 |
|||
142 |
1859 |
static void Spawn(const FunctionCallbackInfo<Value>& args) { |
|
143 |
1859 |
Environment* env = Environment::GetCurrent(args); |
|
144 |
1859 |
Local<Context> context = env->context(); |
|
145 |
ProcessWrap* wrap; |
||
146 |
✗✓ | 1859 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
147 |
|||
148 |
Local<Object> js_options = |
||
149 |
1859 |
args[0]->ToObject(env->context()).ToLocalChecked(); |
|
150 |
|||
151 |
uv_process_options_t options; |
||
152 |
1859 |
memset(&options, 0, sizeof(uv_process_options_t)); |
|
153 |
|||
154 |
1859 |
options.exit_cb = OnExit; |
|
155 |
|||
156 |
// options.uid |
||
157 |
Local<Value> uid_v = |
||
158 |
5577 |
js_options->Get(context, env->uid_string()).ToLocalChecked(); |
|
159 |
✓✓✓✗ ✓✓ |
3720 |
if (!uid_v->IsUndefined() && !uid_v->IsNull()) { |
160 |
✗✓ | 1 |
CHECK(uid_v->IsInt32()); |
161 |
1 |
const int32_t uid = uid_v.As<Int32>()->Value(); |
|
162 |
1 |
options.flags |= UV_PROCESS_SETUID; |
|
163 |
1 |
options.uid = static_cast<uv_uid_t>(uid); |
|
164 |
} |
||
165 |
|||
166 |
// options.gid |
||
167 |
Local<Value> gid_v = |
||
168 |
5577 |
js_options->Get(context, env->gid_string()).ToLocalChecked(); |
|
169 |
✓✓✓✗ ✓✓ |
3720 |
if (!gid_v->IsUndefined() && !gid_v->IsNull()) { |
170 |
✗✓ | 1 |
CHECK(gid_v->IsInt32()); |
171 |
1 |
const int32_t gid = gid_v.As<Int32>()->Value(); |
|
172 |
1 |
options.flags |= UV_PROCESS_SETGID; |
|
173 |
1 |
options.gid = static_cast<uv_gid_t>(gid); |
|
174 |
} |
||
175 |
|||
176 |
// TODO(bnoordhuis) is this possible to do without mallocing ? |
||
177 |
|||
178 |
// options.file |
||
179 |
Local<Value> file_v = |
||
180 |
5577 |
js_options->Get(context, env->file_string()).ToLocalChecked(); |
|
181 |
✗✓ | 3718 |
CHECK(file_v->IsString()); |
182 |
3718 |
node::Utf8Value file(env->isolate(), file_v); |
|
183 |
1859 |
options.file = *file; |
|
184 |
|||
185 |
// options.args |
||
186 |
Local<Value> argv_v = |
||
187 |
5577 |
js_options->Get(context, env->args_string()).ToLocalChecked(); |
|
188 |
✓✗✓✗ ✓✗ |
3718 |
if (!argv_v.IsEmpty() && argv_v->IsArray()) { |
189 |
1859 |
Local<Array> js_argv = argv_v.As<Array>(); |
|
190 |
1859 |
int argc = js_argv->Length(); |
|
191 |
✗✓ | 1859 |
CHECK_GT(argc + 1, 0); // Check for overflow. |
192 |
|||
193 |
// Heap allocate to detect errors. +1 is for nullptr. |
||
194 |
✓✗ | 1859 |
options.args = new char*[argc + 1]; |
195 |
✓✓ | 9739 |
for (int i = 0; i < argc; i++) { |
196 |
node::Utf8Value arg(env->isolate(), |
||
197 |
23640 |
js_argv->Get(context, i).ToLocalChecked()); |
|
198 |
7880 |
options.args[i] = strdup(*arg); |
|
199 |
✗✓ | 7880 |
CHECK_NOT_NULL(options.args[i]); |
200 |
} |
||
201 |
1859 |
options.args[argc] = nullptr; |
|
202 |
} |
||
203 |
|||
204 |
// options.cwd |
||
205 |
Local<Value> cwd_v = |
||
206 |
5577 |
js_options->Get(context, env->cwd_string()).ToLocalChecked(); |
|
207 |
node::Utf8Value cwd(env->isolate(), |
||
208 |
✓✓ | 3718 |
cwd_v->IsString() ? cwd_v : Local<Value>()); |
209 |
✓✓ | 1859 |
if (cwd.length() > 0) { |
210 |
94 |
options.cwd = *cwd; |
|
211 |
} |
||
212 |
|||
213 |
// options.env |
||
214 |
Local<Value> env_v = |
||
215 |
5577 |
js_options->Get(context, env->env_pairs_string()).ToLocalChecked(); |
|
216 |
✓✗✓✓ ✓✓ |
3718 |
if (!env_v.IsEmpty() && env_v->IsArray()) { |
217 |
1857 |
Local<Array> env_opt = env_v.As<Array>(); |
|
218 |
1857 |
int envc = env_opt->Length(); |
|
219 |
✗✓ | 1857 |
CHECK_GT(envc + 1, 0); // Check for overflow. |
220 |
✓✗ | 1857 |
options.env = new char*[envc + 1]; // Heap allocated to detect errors. |
221 |
✓✓ | 136911 |
for (int i = 0; i < envc; i++) { |
222 |
node::Utf8Value pair(env->isolate(), |
||
223 |
405162 |
env_opt->Get(context, i).ToLocalChecked()); |
|
224 |
135054 |
options.env[i] = strdup(*pair); |
|
225 |
✗✓ | 135054 |
CHECK_NOT_NULL(options.env[i]); |
226 |
} |
||
227 |
1857 |
options.env[envc] = nullptr; |
|
228 |
} |
||
229 |
|||
230 |
// options.stdio |
||
231 |
1859 |
ParseStdioOptions(env, js_options, &options); |
|
232 |
|||
233 |
// options.windowsHide |
||
234 |
Local<Value> hide_v = |
||
235 |
5577 |
js_options->Get(context, env->windows_hide_string()).ToLocalChecked(); |
|
236 |
|||
237 |
✓✓ | 1859 |
if (hide_v->IsTrue()) { |
238 |
5 |
options.flags |= UV_PROCESS_WINDOWS_HIDE; |
|
239 |
} |
||
240 |
|||
241 |
✗✓ | 1859 |
if (env->hide_console_windows()) { |
242 |
options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE; |
||
243 |
} |
||
244 |
|||
245 |
// options.windows_verbatim_arguments |
||
246 |
Local<Value> wva_v = |
||
247 |
3718 |
js_options->Get(context, env->windows_verbatim_arguments_string()) |
|
248 |
1859 |
.ToLocalChecked(); |
|
249 |
|||
250 |
✗✓ | 1859 |
if (wva_v->IsTrue()) { |
251 |
options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; |
||
252 |
} |
||
253 |
|||
254 |
// options.detached |
||
255 |
Local<Value> detached_v = |
||
256 |
5577 |
js_options->Get(context, env->detached_string()).ToLocalChecked(); |
|
257 |
|||
258 |
✓✓ | 1859 |
if (detached_v->IsTrue()) { |
259 |
5 |
options.flags |= UV_PROCESS_DETACHED; |
|
260 |
} |
||
261 |
|||
262 |
1859 |
int err = uv_spawn(env->event_loop(), &wrap->process_, &options); |
|
263 |
1859 |
wrap->MarkAsInitialized(); |
|
264 |
|||
265 |
✓✓ | 1859 |
if (err == 0) { |
266 |
✗✓ | 1852 |
CHECK_EQ(wrap->process_.data, wrap); |
267 |
1852 |
wrap->object()->Set(context, env->pid_string(), |
|
268 |
Integer::New(env->isolate(), |
||
269 |
7408 |
wrap->process_.pid)).Check(); |
|
270 |
} |
||
271 |
|||
272 |
✓✗ | 1859 |
if (options.args) { |
273 |
✓✓ | 9739 |
for (int i = 0; options.args[i]; i++) free(options.args[i]); |
274 |
✓✗ | 1859 |
delete [] options.args; |
275 |
} |
||
276 |
|||
277 |
✓✓ | 1859 |
if (options.env) { |
278 |
✓✓ | 136911 |
for (int i = 0; options.env[i]; i++) free(options.env[i]); |
279 |
✓✗ | 1857 |
delete [] options.env; |
280 |
} |
||
281 |
|||
282 |
✓✗ | 1859 |
delete[] options.stdio; |
283 |
|||
284 |
3718 |
args.GetReturnValue().Set(err); |
|
285 |
} |
||
286 |
|||
287 |
236 |
static void Kill(const FunctionCallbackInfo<Value>& args) { |
|
288 |
236 |
Environment* env = Environment::GetCurrent(args); |
|
289 |
ProcessWrap* wrap; |
||
290 |
✗✓ | 236 |
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); |
291 |
236 |
int signal = args[0]->Int32Value(env->context()).FromJust(); |
|
292 |
236 |
int err = uv_process_kill(&wrap->process_, signal); |
|
293 |
472 |
args.GetReturnValue().Set(err); |
|
294 |
} |
||
295 |
|||
296 |
1812 |
static void OnExit(uv_process_t* handle, |
|
297 |
int64_t exit_status, |
||
298 |
int term_signal) { |
||
299 |
1812 |
ProcessWrap* wrap = ContainerOf(&ProcessWrap::process_, handle); |
|
300 |
✗✓ | 1812 |
CHECK_EQ(&wrap->process_, handle); |
301 |
|||
302 |
1812 |
Environment* env = wrap->env(); |
|
303 |
3613 |
HandleScope handle_scope(env->isolate()); |
|
304 |
1812 |
Context::Scope context_scope(env->context()); |
|
305 |
|||
306 |
Local<Value> argv[] = { |
||
307 |
Number::New(env->isolate(), static_cast<double>(exit_status)), |
||
308 |
OneByteString(env->isolate(), signo_string(term_signal)) |
||
309 |
3624 |
}; |
|
310 |
|||
311 |
1812 |
wrap->MakeCallback(env->onexit_string(), arraysize(argv), argv); |
|
312 |
1801 |
} |
|
313 |
|||
314 |
uv_process_t process_; |
||
315 |
}; |
||
316 |
|||
317 |
|||
318 |
} // anonymous namespace |
||
319 |
} // namespace node |
||
320 |
|||
321 |
5274 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_wrap, node::ProcessWrap::Initialize) |
Generated by: GCOVR (Version 4.2) |