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: Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)49 static void Initialize(Local<Object> target, 50 Local<Value> unused, 51 Local<Context> context, 52 void* priv) { 53 Environment* env = Environment::GetCurrent(context); 54 Local<FunctionTemplate> constructor = env->NewFunctionTemplate(New); 55 constructor->InstanceTemplate()->SetInternalFieldCount( 56 ProcessWrap::kInternalFieldCount); 57 58 constructor->Inherit(HandleWrap::GetConstructorTemplate(env)); 59 60 env->SetProtoMethod(constructor, "spawn", Spawn); 61 env->SetProtoMethod(constructor, "kill", Kill); 62 63 env->SetConstructorFunction(target, "Process", constructor); 64 } 65 66 SET_NO_MEMORY_INFO() 67 SET_MEMORY_INFO_NAME(ProcessWrap) 68 SET_SELF_SIZE(ProcessWrap) 69 70 private: New(const FunctionCallbackInfo<Value> & args)71 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 CHECK(args.IsConstructCall()); 76 Environment* env = Environment::GetCurrent(args); 77 new ProcessWrap(env, args.This()); 78 } 79 ProcessWrap(Environment * env,Local<Object> object)80 ProcessWrap(Environment* env, Local<Object> object) 81 : HandleWrap(env, 82 object, 83 reinterpret_cast<uv_handle_t*>(&process_), 84 AsyncWrap::PROVIDER_PROCESSWRAP) { 85 MarkAsUninitialized(); 86 } 87 StreamForWrap(Environment * env,Local<Object> stdio)88 static uv_stream_t* StreamForWrap(Environment* env, Local<Object> stdio) { 89 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 stdio->Get(env->context(), handle_key).ToLocalChecked().As<Object>(); 93 94 uv_stream_t* stream = LibuvStreamWrap::From(env, handle)->stream(); 95 CHECK_NOT_NULL(stream); 96 return stream; 97 } 98 ParseStdioOptions(Environment * env,Local<Object> js_options,uv_process_options_t * options)99 static void ParseStdioOptions(Environment* env, 100 Local<Object> js_options, 101 uv_process_options_t* options) { 102 Local<Context> context = env->context(); 103 Local<String> stdio_key = env->stdio_string(); 104 Local<Array> stdios = 105 js_options->Get(context, stdio_key).ToLocalChecked().As<Array>(); 106 107 uint32_t len = stdios->Length(); 108 options->stdio = new uv_stdio_container_t[len]; 109 options->stdio_count = len; 110 111 for (uint32_t i = 0; i < len; i++) { 112 Local<Object> stdio = 113 stdios->Get(context, i).ToLocalChecked().As<Object>(); 114 Local<Value> type = 115 stdio->Get(context, env->type_string()).ToLocalChecked(); 116 117 if (type->StrictEquals(env->ignore_string())) { 118 options->stdio[i].flags = UV_IGNORE; 119 } else if (type->StrictEquals(env->pipe_string())) { 120 options->stdio[i].flags = static_cast<uv_stdio_flags>( 121 UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE); 122 options->stdio[i].data.stream = StreamForWrap(env, stdio); 123 } else if (type->StrictEquals(env->overlapped_string())) { 124 options->stdio[i].flags = static_cast<uv_stdio_flags>( 125 UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE | 126 UV_OVERLAPPED_PIPE); 127 options->stdio[i].data.stream = StreamForWrap(env, stdio); 128 } else if (type->StrictEquals(env->wrap_string())) { 129 options->stdio[i].flags = UV_INHERIT_STREAM; 130 options->stdio[i].data.stream = StreamForWrap(env, stdio); 131 } else { 132 Local<String> fd_key = env->fd_string(); 133 Local<Value> fd_value = stdio->Get(context, fd_key).ToLocalChecked(); 134 CHECK(fd_value->IsNumber()); 135 int fd = static_cast<int>(fd_value.As<Integer>()->Value()); 136 options->stdio[i].flags = UV_INHERIT_FD; 137 options->stdio[i].data.fd = fd; 138 } 139 } 140 } 141 Spawn(const FunctionCallbackInfo<Value> & args)142 static void Spawn(const FunctionCallbackInfo<Value>& args) { 143 Environment* env = Environment::GetCurrent(args); 144 Local<Context> context = env->context(); 145 ProcessWrap* wrap; 146 ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); 147 148 Local<Object> js_options = 149 args[0]->ToObject(env->context()).ToLocalChecked(); 150 151 uv_process_options_t options; 152 memset(&options, 0, sizeof(uv_process_options_t)); 153 154 options.exit_cb = OnExit; 155 156 // options.uid 157 Local<Value> uid_v = 158 js_options->Get(context, env->uid_string()).ToLocalChecked(); 159 if (!uid_v->IsUndefined() && !uid_v->IsNull()) { 160 CHECK(uid_v->IsInt32()); 161 const int32_t uid = uid_v.As<Int32>()->Value(); 162 options.flags |= UV_PROCESS_SETUID; 163 options.uid = static_cast<uv_uid_t>(uid); 164 } 165 166 // options.gid 167 Local<Value> gid_v = 168 js_options->Get(context, env->gid_string()).ToLocalChecked(); 169 if (!gid_v->IsUndefined() && !gid_v->IsNull()) { 170 CHECK(gid_v->IsInt32()); 171 const int32_t gid = gid_v.As<Int32>()->Value(); 172 options.flags |= UV_PROCESS_SETGID; 173 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 js_options->Get(context, env->file_string()).ToLocalChecked(); 181 CHECK(file_v->IsString()); 182 node::Utf8Value file(env->isolate(), file_v); 183 options.file = *file; 184 185 // options.args 186 Local<Value> argv_v = 187 js_options->Get(context, env->args_string()).ToLocalChecked(); 188 if (!argv_v.IsEmpty() && argv_v->IsArray()) { 189 Local<Array> js_argv = Local<Array>::Cast(argv_v); 190 int argc = js_argv->Length(); 191 CHECK_GT(argc + 1, 0); // Check for overflow. 192 193 // Heap allocate to detect errors. +1 is for nullptr. 194 options.args = new char*[argc + 1]; 195 for (int i = 0; i < argc; i++) { 196 node::Utf8Value arg(env->isolate(), 197 js_argv->Get(context, i).ToLocalChecked()); 198 options.args[i] = strdup(*arg); 199 CHECK_NOT_NULL(options.args[i]); 200 } 201 options.args[argc] = nullptr; 202 } 203 204 // options.cwd 205 Local<Value> cwd_v = 206 js_options->Get(context, env->cwd_string()).ToLocalChecked(); 207 node::Utf8Value cwd(env->isolate(), 208 cwd_v->IsString() ? cwd_v : Local<Value>()); 209 if (cwd.length() > 0) { 210 options.cwd = *cwd; 211 } 212 213 // options.env 214 Local<Value> env_v = 215 js_options->Get(context, env->env_pairs_string()).ToLocalChecked(); 216 if (!env_v.IsEmpty() && env_v->IsArray()) { 217 Local<Array> env_opt = Local<Array>::Cast(env_v); 218 int envc = env_opt->Length(); 219 CHECK_GT(envc + 1, 0); // Check for overflow. 220 options.env = new char*[envc + 1]; // Heap allocated to detect errors. 221 for (int i = 0; i < envc; i++) { 222 node::Utf8Value pair(env->isolate(), 223 env_opt->Get(context, i).ToLocalChecked()); 224 options.env[i] = strdup(*pair); 225 CHECK_NOT_NULL(options.env[i]); 226 } 227 options.env[envc] = nullptr; 228 } 229 230 // options.stdio 231 ParseStdioOptions(env, js_options, &options); 232 233 // options.windowsHide 234 Local<Value> hide_v = 235 js_options->Get(context, env->windows_hide_string()).ToLocalChecked(); 236 237 if (hide_v->IsTrue()) { 238 options.flags |= UV_PROCESS_WINDOWS_HIDE; 239 } 240 241 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 js_options->Get(context, env->windows_verbatim_arguments_string()) 248 .ToLocalChecked(); 249 250 if (wva_v->IsTrue()) { 251 options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; 252 } 253 254 // options.detached 255 Local<Value> detached_v = 256 js_options->Get(context, env->detached_string()).ToLocalChecked(); 257 258 if (detached_v->IsTrue()) { 259 options.flags |= UV_PROCESS_DETACHED; 260 } 261 262 int err = uv_spawn(env->event_loop(), &wrap->process_, &options); 263 wrap->MarkAsInitialized(); 264 265 if (err == 0) { 266 CHECK_EQ(wrap->process_.data, wrap); 267 wrap->object()->Set(context, env->pid_string(), 268 Integer::New(env->isolate(), 269 wrap->process_.pid)).Check(); 270 } 271 272 if (options.args) { 273 for (int i = 0; options.args[i]; i++) free(options.args[i]); 274 delete [] options.args; 275 } 276 277 if (options.env) { 278 for (int i = 0; options.env[i]; i++) free(options.env[i]); 279 delete [] options.env; 280 } 281 282 delete[] options.stdio; 283 284 args.GetReturnValue().Set(err); 285 } 286 Kill(const FunctionCallbackInfo<Value> & args)287 static void Kill(const FunctionCallbackInfo<Value>& args) { 288 Environment* env = Environment::GetCurrent(args); 289 ProcessWrap* wrap; 290 ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); 291 int signal = args[0]->Int32Value(env->context()).FromJust(); 292 int err = uv_process_kill(&wrap->process_, signal); 293 args.GetReturnValue().Set(err); 294 } 295 OnExit(uv_process_t * handle,int64_t exit_status,int term_signal)296 static void OnExit(uv_process_t* handle, 297 int64_t exit_status, 298 int term_signal) { 299 ProcessWrap* wrap = ContainerOf(&ProcessWrap::process_, handle); 300 CHECK_EQ(&wrap->process_, handle); 301 302 Environment* env = wrap->env(); 303 HandleScope handle_scope(env->isolate()); 304 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 }; 310 311 wrap->MakeCallback(env->onexit_string(), arraysize(argv), argv); 312 } 313 314 uv_process_t process_; 315 }; 316 317 318 } // anonymous namespace 319 } // namespace node 320 321 NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_wrap, node::ProcessWrap::Initialize) 322