execl 使 C++ node.js-addon 崩溃
execl crashes C++ node.js-addon
正常的 C++ execl
工作正常(用 g++ ok.cc -o ok.elf
编译)
#include <unistd.h>
int main(){
execl("/usr/bin/python", "/usr/bin/python", nullptr);
}
但是当作为 node.js C++ 插件工作时崩溃
#include <node.h>
#include <unistd.h>
namespace bug{
void wtf(const v8::FunctionCallbackInfo<v8::Value>& args){
execl("/usr/bin/python", "/usr/bin/python", nullptr);
}
void init(v8::Local<v8::Object> exports){
NODE_SET_METHOD(exports, "wtf", bug::wtf);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
node.js v8.9.1
节点 gyp v3.6.2
gcc 版本 6.3.0 20170406 (Ubuntu 6.3.0-12ubuntu2)
节点不支持所有 posix 系统调用。
查看此线程
崩溃是预料之中的,因为您正在使用您无法使用的东西。正如上面讨论的那样,您需要创建自己的 exec
index.cc
#include <nan.h>
#include <fcntl.h>
#include <unistd.h>
int doNotCloseStreamsOnExit(int desc) {
int flags = fcntl(desc, F_GETFD, 0);
if (flags < 0) return flags;
flags &= ~FD_CLOEXEC; //clear FD_CLOEXEC bit
return fcntl(desc, F_SETFD, flags);
}
void copyArray(char* dest[], unsigned int offset, v8::Local<v8::Array> src) {
unsigned int length = src->Length();
for (unsigned int i = 0; i < length; i++) {
v8::String::Utf8Value arrayElem(Nan::Get(src, i).ToLocalChecked()->ToString());
std::string arrayElemStr (*arrayElem);
char* tmp = new char[arrayElemStr.length() +1];
strcpy(tmp, arrayElemStr.c_str());
dest[i + offset] = tmp;
}
}
void setEnv(v8::Local<v8::Array> src) {
unsigned int length = src->Length();
v8::Local<v8::String> keyProp = Nan::New<v8::String>("key").ToLocalChecked();
v8::Local<v8::String> valueProp = Nan::New<v8::String>("value").ToLocalChecked();
for (unsigned int i = 0; i < length; i++) {
v8::Local<v8::Object> obj = Nan::Get(src, i).ToLocalChecked()->ToObject();
v8::String::Utf8Value objKey(Nan::Get(obj, keyProp).ToLocalChecked()->ToString());
v8::String::Utf8Value objValue(Nan::Get(obj, valueProp).ToLocalChecked()->ToString());
std::string objKeyStr (*objKey);
char *key = const_cast<char*> ( objKeyStr.c_str() );
std::string objValueStr (*objValue);
char *value = const_cast<char*> ( objValueStr.c_str() );
setenv(key, value, 1);
}
}
void Method(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 3) {
return;
}
if (!info[0]->IsString()) {
return;
}
// get command
v8::String::Utf8Value val(info[0]->ToString());
std::string str (*val);
char *command = const_cast<char*> ( str.c_str() );
// set env on the current process
v8::Local<v8::Array> envArr = v8::Local<v8::Array>::Cast(info[1]);
setEnv(envArr);
// build args: command, ...args, NULL
v8::Local<v8::Array> argsArr = v8::Local<v8::Array>::Cast(info[2]);
char* args[argsArr->Length() + 2];
args[0] = command;
copyArray(args, 1, argsArr);
args[argsArr->Length() + 1] = NULL;
// fix stream flags
doNotCloseStreamsOnExit(0); //stdin
doNotCloseStreamsOnExit(1); //stdout
doNotCloseStreamsOnExit(2); //stderr
execvp(command, args);
}
void Init(v8::Local<v8::Object> exports) {
exports->Set(Nan::New("exec").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(Method)->GetFunction());
}
NODE_MODULE(exec, Init)
index.js
'use strict';
var addon = require('bindings')('addon');
var path = require('path');
var fs = require('fs');
module.exports = function(cmd, env, args) {
if (!cmd) {
throw new Error('Command is required');
}
var envArr = Object.keys(env || {}).map(key => {
return {
key,
value: env[key],
};
});
addon.exec(cmd, envArr, args || []);
};
PS:从 https://github.com/OrKoN/native-exec 发布的代码,以防 link 将来失效
还有一件事你不应该尝试做的是执行一些需要获取 TTY 的东西,在那种情况下你会增加很多复杂性。所以 运行 python 需要控制你的 TTY。
正常的 C++ execl
工作正常(用 g++ ok.cc -o ok.elf
编译)
#include <unistd.h>
int main(){
execl("/usr/bin/python", "/usr/bin/python", nullptr);
}
但是当作为 node.js C++ 插件工作时崩溃
#include <node.h>
#include <unistd.h>
namespace bug{
void wtf(const v8::FunctionCallbackInfo<v8::Value>& args){
execl("/usr/bin/python", "/usr/bin/python", nullptr);
}
void init(v8::Local<v8::Object> exports){
NODE_SET_METHOD(exports, "wtf", bug::wtf);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
node.js v8.9.1
节点 gyp v3.6.2
gcc 版本 6.3.0 20170406 (Ubuntu 6.3.0-12ubuntu2)
节点不支持所有 posix 系统调用。
查看此线程
崩溃是预料之中的,因为您正在使用您无法使用的东西。正如上面讨论的那样,您需要创建自己的 exec
index.cc
#include <nan.h>
#include <fcntl.h>
#include <unistd.h>
int doNotCloseStreamsOnExit(int desc) {
int flags = fcntl(desc, F_GETFD, 0);
if (flags < 0) return flags;
flags &= ~FD_CLOEXEC; //clear FD_CLOEXEC bit
return fcntl(desc, F_SETFD, flags);
}
void copyArray(char* dest[], unsigned int offset, v8::Local<v8::Array> src) {
unsigned int length = src->Length();
for (unsigned int i = 0; i < length; i++) {
v8::String::Utf8Value arrayElem(Nan::Get(src, i).ToLocalChecked()->ToString());
std::string arrayElemStr (*arrayElem);
char* tmp = new char[arrayElemStr.length() +1];
strcpy(tmp, arrayElemStr.c_str());
dest[i + offset] = tmp;
}
}
void setEnv(v8::Local<v8::Array> src) {
unsigned int length = src->Length();
v8::Local<v8::String> keyProp = Nan::New<v8::String>("key").ToLocalChecked();
v8::Local<v8::String> valueProp = Nan::New<v8::String>("value").ToLocalChecked();
for (unsigned int i = 0; i < length; i++) {
v8::Local<v8::Object> obj = Nan::Get(src, i).ToLocalChecked()->ToObject();
v8::String::Utf8Value objKey(Nan::Get(obj, keyProp).ToLocalChecked()->ToString());
v8::String::Utf8Value objValue(Nan::Get(obj, valueProp).ToLocalChecked()->ToString());
std::string objKeyStr (*objKey);
char *key = const_cast<char*> ( objKeyStr.c_str() );
std::string objValueStr (*objValue);
char *value = const_cast<char*> ( objValueStr.c_str() );
setenv(key, value, 1);
}
}
void Method(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 3) {
return;
}
if (!info[0]->IsString()) {
return;
}
// get command
v8::String::Utf8Value val(info[0]->ToString());
std::string str (*val);
char *command = const_cast<char*> ( str.c_str() );
// set env on the current process
v8::Local<v8::Array> envArr = v8::Local<v8::Array>::Cast(info[1]);
setEnv(envArr);
// build args: command, ...args, NULL
v8::Local<v8::Array> argsArr = v8::Local<v8::Array>::Cast(info[2]);
char* args[argsArr->Length() + 2];
args[0] = command;
copyArray(args, 1, argsArr);
args[argsArr->Length() + 1] = NULL;
// fix stream flags
doNotCloseStreamsOnExit(0); //stdin
doNotCloseStreamsOnExit(1); //stdout
doNotCloseStreamsOnExit(2); //stderr
execvp(command, args);
}
void Init(v8::Local<v8::Object> exports) {
exports->Set(Nan::New("exec").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(Method)->GetFunction());
}
NODE_MODULE(exec, Init)
index.js
'use strict';
var addon = require('bindings')('addon');
var path = require('path');
var fs = require('fs');
module.exports = function(cmd, env, args) {
if (!cmd) {
throw new Error('Command is required');
}
var envArr = Object.keys(env || {}).map(key => {
return {
key,
value: env[key],
};
});
addon.exec(cmd, envArr, args || []);
};
PS:从 https://github.com/OrKoN/native-exec 发布的代码,以防 link 将来失效
还有一件事你不应该尝试做的是执行一些需要获取 TTY 的东西,在那种情况下你会增加很多复杂性。所以 运行 python 需要控制你的 TTY。