v8::Platform *v8platform;
switch_hash_t *compiled_script_hash;
switch_mutex_t *compiled_script_hash_mutex;
+ map<string, Isolate *> *task_manager;
+ switch_mutex_t *task_manager_mutex;
char *script_caching;
switch_time_t cache_expires_seconds;
bool performance_monitor;
JSMain *js;
Isolate *isolate;
char *arg, *argv[512];
- int argc = 0, x = 0, y = 0;
+ int argc = 0;
unsigned int flags = 0;
char *path = NULL;
string result_string;
HandleScope scope(isolate);
// Store our object internally
- isolate->SetData(0, js);
+ isolate->SetData(ISOLATE_DATA_OBJECT, js);
+
+ // Set isolate related data.
+ switch_uuid_t task_id;
+ switch_uuid_get(&task_id);
+ char str_task_id[SWITCH_UUID_FORMATTED_LENGTH + 1];
+ switch_uuid_format(str_task_id, &task_id);
+
+ js_isolate_private_data_t *private_data = new js_isolate_private_data_t();
+ private_data->str_task_id = str_task_id;
+ private_data->input_code = input_code;
+ private_data->start_time = switch_time_now();
+
+ // Store private data internally
+ isolate->SetData(ISOLATE_DATA_PRIVATE, private_data);
+
+ // Add isolate to the task manager
+ switch_mutex_lock(globals.task_manager_mutex);
+ (*globals.task_manager)[str_task_id] = isolate;
+ switch_mutex_unlock(globals.task_manager_mutex);
// New global template
Handle<ObjectTemplate> global = ObjectTemplate::New(isolate);
if (global.IsEmpty()) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create JS global object template\n");
} else {
- /* Function to print current V8 version */
- global->Set(String::NewFromUtf8(isolate, "version"), FunctionTemplate::New(isolate, JSMain::Version));
-
/* Add all global functions */
for (size_t i = 0; i < js->GetExtenderFunctions().size(); i++) {
js_function_t *proc = js->GetExtenderFunctions()[i];
#ifdef V8_ENABLE_DEBUGGING
Persistent<Context> *debug_context = new Persistent<Context>();
- isolate->SetData(1, debug_context);
+ isolate->SetData(ISOLATE_DATA_DEBUG, debug_context);
debug_context->Reset(isolate, context);
//v8::Locker lck(isolate);
// Add arguments before running script.
Local<Array> arguments = Array::New(isolate, argc);
- for (y = 0; y < argc; y++) {
+ for (int y = 0; y < argc; y++) {
arguments->Set(Integer::New(isolate, y), String::NewFromUtf8(isolate, argv[y]));
}
context->Global()->Set(String::NewFromUtf8(isolate, "argv"), arguments);
if (!script_data) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No script to execute!\n");
} else {
- /* Store our base directoy in variable 'scriptPath' */
- char *path = v8_get_script_path(script_file);
- if (path) {
- context->Global()->Set(String::NewFromUtf8(isolate, "scriptPath"), String::NewFromUtf8(isolate, path));
- free(path);
+ /* Store our base directory in variable 'scriptPath' */
+ char *scriptPath = v8_get_script_path(script_file);
+ if (scriptPath) {
+ context->Global()->Set(String::NewFromUtf8(isolate, "scriptPath"), String::NewFromUtf8(isolate, scriptPath));
+ switch_safe_free(scriptPath);
}
TryCatch try_catch(isolate);
#endif
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
- Handle<Value> result;
+ Handle<Value> script_result;
if (!v8_script.IsEmpty()) {
- result = v8_script.ToLocalChecked()->Run();
+ script_result = v8_script.ToLocalChecked()->Run();
}
switch_mutex_lock(globals.mutex);
switch_log_printf(SWITCH_CHANNEL_ID_LOG, js->GetForcedTerminationScriptFile(), modname, js->GetForcedTerminationLineNumber(), NULL, SWITCH_LOG_NOTICE, "Script exited with info [%s]\n", js->GetForcedTerminationMessage());
}
- if (!result.IsEmpty()) {
+ if (!script_result.IsEmpty()) {
// Return result as string
- String::Utf8Value ascii(result);
+ String::Utf8Value ascii(script_result);
if (*ascii) {
res = *ascii;
}
#endif
}
#ifdef V8_ENABLE_DEBUGGING
- isolate->SetData(1, NULL);
+ isolate->SetData(ISOLATE_DATA_DEBUG, NULL);
if (debug_listen_port > 0) {
Debug::DisableAgent();
}
#endif
}
}
- isolate->SetData(0, NULL);
+
+ // Remove isolate from the task manager
+ switch_mutex_lock(globals.task_manager_mutex);
+ globals.task_manager->erase(str_task_id);
+ switch_mutex_unlock(globals.task_manager_mutex);
+
+ isolate->SetData(ISOLATE_DATA_PRIVATE, NULL);
+ isolate->SetData(ISOLATE_DATA_OBJECT, NULL);
+
+ delete private_data;
}
#ifdef V8_FORCE_GC_AFTER_EXECUTION
return SWITCH_STATUS_SUCCESS;
}
+SWITCH_STANDARD_API(kill_function)
+{
+ if (!zstr(cmd)) {
+ switch_mutex_lock(globals.task_manager_mutex);
+
+ auto isolate_it = globals.task_manager->find(cmd);
+ if (isolate_it != globals.task_manager->end()) {
+ Isolate * isolate = isolate_it->second;
+ JSMain *js = JSMain::GetScriptInstanceFromIsolate(isolate);
+ if (js)
+ js->ExitScript(isolate, "Script termination requested by jskill API.", true);
+ }
+
+ switch_mutex_unlock(globals.task_manager_mutex);
+
+ stream->write_function(stream, "+OK\n");
+ }
+ else {
+ stream->write_function(stream, "false");
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+inline static void stream_write_safe_d(switch_stream_handle_t *stream, const char *str) {
+ if (!str) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
+ stream->write_function(stream, "-ERR Memory Error!\n");
+ }
+ else {
+ stream->write_function(stream, "%s", str);
+ }
+}
+#define stream_write_safe(output_text) stream_write_safe_d(stream, output_text)
+
+SWITCH_STANDARD_API(process_status_function)
+{
+ char *mydata = NULL, *argv[3] = { 0 };
+ char *as = NULL, *output_text = NULL, *delim = ",";
+ cJSON *json = NULL, *row;
+ switch_xml_t xml = NULL, xml_row, xml_field;
+ int rows = 0, f_off = 0, count = 0;
+ char tmp_str[50];
+ std::vector<js_isolate_private_data_t> tasks;
+
+ if (cmd && *cmd && (mydata = strdup(cmd))) {
+ switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+ if (argv[1] && !strcasecmp(argv[0], "as")) {
+ as = argv[1];
+ if (!strcasecmp(as, "csv")) {
+ if (argv[2]) delim = argv[2];
+ }
+ }
+ }
+
+ if (!as) {
+ as = "plain";
+ }
+
+ if (!strcasecmp(as, "json")) {
+ if (!(json = cJSON_CreateArray())) {
+ goto end;
+ }
+ } else if (!strcasecmp(as, "xml")) {
+ if (!(xml = switch_xml_new("result"))) {
+ goto end;
+ }
+ } else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
+ stream->write_function(stream, "%s%s", "task_id", delim);
+ stream->write_function(stream, "%s%s", "input_code", delim);
+ stream->write_function(stream, "%s%s", "execution_time", delim);
+
+ stream->write_function(stream, "%s%s", "total_physical_size", delim);
+ stream->write_function(stream, "%s%s", "total_heap_size_executable", delim);
+ stream->write_function(stream, "%s%s", "total_heap_size", delim);
+ stream->write_function(stream, "%s%s", "used_heap_size", delim);
+ stream->write_function(stream, "%s%s", "heap_size_limit", delim);
+ stream->write_function(stream, "%s%s", "malloced_memory", delim);
+ stream->write_function(stream, "%s%s", "peak_malloced_memory", "\n");
+ } else {
+ stream->write_function(stream, "JavaScript process status.\n");
+ }
+
+ switch_mutex_lock(globals.task_manager_mutex);
+
+ for (auto isolate_pair : *globals.task_manager) {
+ Isolate *isolate = isolate_pair.second;
+
+ js_isolate_private_data_t *isolate_private_data = (js_isolate_private_data_t*)isolate->GetData(ISOLATE_DATA_PRIVATE);
+ js_isolate_private_data_t private_data = *isolate_private_data;
+
+ isolate->GetHeapStatistics(&private_data.stats);
+
+ tasks.push_back(private_data);
+ }
+
+ switch_mutex_unlock(globals.task_manager_mutex);
+
+ for (auto isolate_private_data : tasks) {
+ count++;
+
+ js_isolate_private_data_t *private_data = (js_isolate_private_data_t *)&(isolate_private_data);
+
+ switch_time_t end = switch_time_now();
+ unsigned int delay = (end - private_data->start_time) / 1000;
+
+ if (!strcasecmp(as, "plain")) {
+
+ stream->write_function(stream, "\nTask id: %s\n", private_data->str_task_id.c_str());
+ stream->write_function(stream, "input_code: %s\n", (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()));
+ stream->write_function(stream, "execution_time: %u ms\n", delay);
+
+ stream->write_function(stream, "total_physical_size: %u\n", private_data->stats.total_physical_size());
+ stream->write_function(stream, "total_heap_size_executable: %u\n", private_data->stats.total_heap_size_executable());
+ stream->write_function(stream, "total_heap_size: %u\n", private_data->stats.total_heap_size());
+ stream->write_function(stream, "used_heap_size: %u\n", private_data->stats.used_heap_size());
+ stream->write_function(stream, "heap_size_limit: %u\n", private_data->stats.heap_size_limit());
+ stream->write_function(stream, "malloced_memory: %u\n", private_data->stats.malloced_memory());
+ stream->write_function(stream, "peak_malloced_memory: %u\n", private_data->stats.peak_malloced_memory());
+ } else if (!strcasecmp(as, "json")) {
+ if (!(row = cJSON_CreateObject())) {
+ goto end;
+ }
+
+ cJSON_AddItemToArray(json, row);
+
+ cJSON_AddItemToObject(row, "task_id", cJSON_CreateString(private_data->str_task_id.c_str()));
+ cJSON_AddItemToObject(row, "input_code", cJSON_CreateString((private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str())));
+ cJSON_AddItemToObject(row, "execution_time", cJSON_CreateNumber(delay));
+
+ cJSON_AddItemToObject(row, "total_physical_size", cJSON_CreateNumber(private_data->stats.total_physical_size()));
+ cJSON_AddItemToObject(row, "total_heap_size_executable", cJSON_CreateNumber(private_data->stats.total_heap_size_executable()));
+ cJSON_AddItemToObject(row, "total_heap_size", cJSON_CreateNumber(private_data->stats.total_heap_size()));
+ cJSON_AddItemToObject(row, "used_heap_size", cJSON_CreateNumber(private_data->stats.used_heap_size()));
+ cJSON_AddItemToObject(row, "heap_size_limit", cJSON_CreateNumber(private_data->stats.heap_size_limit()));
+ cJSON_AddItemToObject(row, "malloced_memory", cJSON_CreateNumber(private_data->stats.malloced_memory()));
+ cJSON_AddItemToObject(row, "peak_malloced_memory", cJSON_CreateNumber(private_data->stats.peak_malloced_memory()));
+ } else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
+ stream->write_function(stream, "%s%s", private_data->str_task_id.c_str(), delim);
+ stream->write_function(stream, "%s%s", (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()), delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", delay);
+ stream->write_function(stream, "%s%s", tmp_str, delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_physical_size());
+ stream->write_function(stream, "%s%s", tmp_str, delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size_executable());
+ stream->write_function(stream, "%s%s", tmp_str, delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size());
+ stream->write_function(stream, "%s%s", tmp_str, delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.used_heap_size());
+ stream->write_function(stream, "%s%s", tmp_str, delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.heap_size_limit());
+ stream->write_function(stream, "%s%s", tmp_str, delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.malloced_memory());
+ stream->write_function(stream, "%s%s", tmp_str, delim);
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.peak_malloced_memory());
+ stream->write_function(stream, "%s%s", tmp_str, "\n");
+
+ } else if (!strcasecmp(as, "xml")) {
+ if (!(xml_row = switch_xml_add_child_d(xml, "row", rows++))) {
+ goto end;
+ }
+
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%d", rows);
+ switch_xml_set_attr(switch_xml_set_flag(xml_row, SWITCH_XML_DUP), strdup("row_id"), strdup(tmp_str));
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "task_id", f_off++))) {
+ goto end;
+ }
+ switch_xml_set_txt_d(xml_field, private_data->str_task_id.c_str());
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "input_code", f_off++))) {
+ goto end;
+ }
+ switch_xml_set_txt_d(xml_field, (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()));
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "execution_time", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", delay);
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "total_physical_size", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_physical_size());
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "total_heap_size_executable", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size_executable());
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "total_heap_size", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size());
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "used_heap_size", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.used_heap_size());
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "heap_size_limit", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.heap_size_limit());
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "malloced_memory", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.malloced_memory());
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ if (!(xml_field = switch_xml_add_child_d(xml_row, "peak_malloced_memory", f_off++))) {
+ goto end;
+ }
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.peak_malloced_memory());
+ switch_xml_set_txt_d(xml_field, tmp_str);
+
+ }
+ }
+
+ if (!strcasecmp(as, "json")) {
+ cJSON *result;
+
+ if (!(result = cJSON_CreateObject())) {
+ stream->write_function(stream, "-ERR Error creating json object!\n");
+ goto end;
+ }
+ else {
+ cJSON_AddItemToObject(result, "row_count", cJSON_CreateNumber(count));
+ cJSON_AddItemToObject(result, "rows", json);
+
+ output_text = cJSON_PrintUnformatted(result);
+ json = result;
+ }
+
+ stream_write_safe(output_text);
+
+ } else if (!strcasecmp(as, "xml")) {
+ switch_snprintf(tmp_str, sizeof(tmp_str), "%u", count);
+ switch_xml_set_attr(switch_xml_set_flag(xml, SWITCH_XML_DUP), strdup("row_count"), strdup(tmp_str));
+
+ output_text = switch_xml_toxml(xml, SWITCH_FALSE);
+
+ stream_write_safe(output_text);
+
+ } else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
+ stream->write_function(stream, "%s%u total.%s", "\n", count, "\n");
+ }
+
+end:
+
+ switch_xml_free(xml);
+ cJSON_Delete(json);
+ switch_safe_free(output_text);
+ switch_safe_free(mydata);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)
{
switch_application_interface_t *app_interface;
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
switch_mutex_init(&globals.compiled_script_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_mutex_init(&globals.task_manager_mutex, SWITCH_MUTEX_NESTED, globals.pool);
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
#endif
switch_mutex_init(&globals.event_mutex, SWITCH_MUTEX_NESTED, globals.pool);
globals.event_handlers = new set<FSEventHandler *>();
if (load_modules() != SWITCH_STATUS_SUCCESS) {
+ delete globals.event_handlers;
+ switch_event_unbind(&globals.event_node);
return SWITCH_STATUS_FALSE;
}
JSMain::Initialize(&globals.v8platform);
switch_core_hash_init(&globals.compiled_script_hash);
+ globals.task_manager = new map<string, Isolate *>();
#else
JSMain::Initialize();
#endif
SWITCH_ADD_API(jsrun_interface, "jsrun", "run a script", launch_async, "jsrun <script> [additional_vars [...]]");
SWITCH_ADD_API(jsapi_interface, "jsapi", "execute an api call", jsapi_function, "jsapi <script> [additional_vars [...]]");
SWITCH_ADD_API(jsmon_interface, "jsmon", "toggle performance monitor", jsmon_function, "jsmon on|off");
+ SWITCH_ADD_API(jsrun_interface, "jsps", "process status", process_status_function, "jsps [as plain|json|xml|delim|csv [<delimeter>]]");
+ SWITCH_ADD_API(jsrun_interface, "jskill", "kill a task", kill_function, "jskill <task_id>");
SWITCH_ADD_APP(app_interface, "javascript", "Launch JS ivr", "Run a javascript ivr on a channel", v8_dp_function, "<script> [additional_vars [...]]", SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_CHAT_APP(chat_app_interface, "javascript", "execute a js script", "execute a js script", v8_chat_function, "<script>", SCAF_NONE);
switch_core_hash_destroy(&globals.compiled_script_hash);
switch_mutex_destroy(globals.compiled_script_hash_mutex);
+ switch_mutex_destroy(globals.task_manager_mutex);
+ delete globals.task_manager;
switch_mutex_destroy(globals.mutex);
#endif