Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

v0.0.1.9 -- added config file parsing and basic logging facility, as …

…well as corrected significant issues with working directory path detection
  • Loading branch information...
commit fe439582591e19365d7b949a8e8894aecea84a3c 1 parent b03e206
@getify authored
View
18 README.txt
@@ -1,5 +1,5 @@
BikechainJS Engine
-v0.0.1.4 (c) 2010 Kyle Simpson
+v0.0.1.9 (c) 2010 Kyle Simpson
MIT License
@@ -19,12 +19,16 @@ Installation:
3. If you created the "static" V8 library, edit the makefile to reference to proper static library file.
-4. Run "make" and then "make clean".
+4. Run "make install" and then "make clean".
-5. You should now have a "engine" executable in the root directory. You can execute a JavaScript file by passing it as a parameter to engine, like this:
+5. You should now have a "engine" executable in the [root]/engine/ directory. You can execute a JavaScript file by passing it as a parameter to engine, like this:
./engine dosomething.js
+6. You can configure your BikechainJS instance by editing values in the engine.json file, also in the [root]/engine/ directory.
+
+7. Make sure the [root]/logs directory is writeable by the user/process executing bikechain.
+
--------------
@@ -36,14 +40,14 @@ Provided global environment:
3. include_once(path-to-file): will ensure an exact file (via path) only gets loaded/parsed once.
-4. alert(), console.log(), console.warn(), and console.error() are all defined and mapped to [system].stdout.print().
+4. alert() maps to [system].stdout.print(). console.info(), console.log(), console.warn(), and console.error() all send messages to the logs.
5. exit() to immediately stop execution of any javascript in this instance and flush output.
--------------
-Modules:
+Modules (provided in [root]/modules/):
Several modules are available to be loaded into the executing environment by using the global require() function.
@@ -52,7 +56,7 @@ Several modules are available to be loaded into the executing environment by usi
* [system].stdout.write(str): writes "str" to the standard output
* [system].stdout.print(str): writes "str" to the standard output, followed by a new-line
* [system].stdout.flush(): flushes the output buffer (if necessary)
- * [system].stdin.read(): if stdin has any buffered, reads from stdin up until an EOF. Otherwise, read returns empty immediately (non-blocking).
+ * [system].stdin.read(nonblocking[=true]): if non-blocking (default), if stdin has any buffered, reads from stdin up until an EOF. Otherwise, read returns empty immediately. If not non-blocking, read() blocks waiting for input.
* [system].stderr.write(str): same as stdout.write()
* [system].stderr.print(str): same as stdout.print()
* [system].stderr.flush(): same as stdout.flush()
@@ -66,7 +70,7 @@ Several modules are available to be loaded into the executing environment by usi
3. "os": Operating System (process execution)
- * [os].command(cmd, [..cmds, ..]): execute a command on the local system, specified by "cmd" and "cmds" parameters
+ * [os].execute(input, cmd, [..cmds, ..]): execute a command on the local system, specified by "cmd" and "cmds" parameters
-- returns iopipe:
[iopipe].stdout.read(): reads the output from the executed command
View
45 engine/backchannel.js
@@ -0,0 +1,45 @@
+/*! BikechainJS (backchannel.js)
+ v0.0.1.9 (c) Kyle Simpson
+ MIT License
+*/
+
+(function(imports){
+ var global = this,
+ cfg = {},
+ default_cfg = {
+ logging: false,
+ log_format: "common",
+ log_level: 1,
+ error_log_level: 2,
+ file_logging: false,
+ file_log_file: "logs/bikechain.json",
+ file_log_error_file: "logs/bikechain.error.json",
+ system_event_logging: false,
+ display_errors: false
+ }
+ ;
+
+ try {
+ cfg = JSON.parse(JSON.minify(imports.cfg));
+ }
+ catch (err) {
+ throw new Error("Failed parsing engine configuration");
+ }
+ try {
+ // merge default values for engine configuration
+ for (var k in default_cfg) { if (default_cfg.hasOwnProperty(k)) {
+ if (!cfg.hasOwnProperty(k)) cfg[k] = default_cfg[k];
+ }}
+ }
+ catch (err) {
+ throw new Error("Failed initializing engine configuration");
+ }
+
+ global.getCfg = function(name) {
+ return name ? cfg[name] : cfg;
+ };
+
+ global.setCfg = function(name,value) {
+ cfg[name] = value;
+ };
+})
View
49 engine/engine.js
@@ -1,10 +1,10 @@
/*! BikechainJS (engine.js)
- v0.0.1.4 (c) Kyle Simpson
+ v0.0.1.9 (c) Kyle Simpson
MIT License
*/
-(function(imports){
+(function(bikechain_root_path, imports){
var global = this,
FS,
SYSTEM,
@@ -25,7 +25,7 @@
// special function `require` for loading modules
global.require = function(module_path,forceReload) {
forceReload = !(!forceReload);
- var module_repo_path = "bikechain/modules/",
+ var module_repo_path = bikechain_root_path+"modules/",
source, module_name
;
@@ -43,18 +43,22 @@
module_name = module_name[1];
}
else { // otherwise, bail now
- return void 0;
+ throw new Error("Failed loading unknown module from path: "+module_path);
}
if (!FS) {
- source = imports.__FSRead__(module_repo_path+"fs.js");
+ try { source = imports.__FSRead__(module_repo_path+"fs.js"); }
+ catch (err) { throw new Error("Failed loading module `fs` from path: "+module_repo_path+"fs.js"); }
+
loaded_modules["fs"] = FS = Function.apply(global,imports_names.concat([source])).apply(global,imports_funcs);
}
if (!forceReload && typeof loaded_modules[module_name] !== "undefined") {
return loaded_modules[module_name];
}
if (module_name != "fs" || forceReload) {
- source = FS.read(module_path);
+ try { source = FS.read(module_path); }
+ catch (err) { throw new Error("Failed loading module `"+module_name+"` from path: "+module_path); }
+
return (loaded_modules[module_name] = Function.apply(global,imports_names.concat([source])).apply(global,imports_funcs));
}
return FS;
@@ -74,24 +78,47 @@
if (!forceReload && typeof loaded_includes[src] != "undefined") {
return loaded_includes[src];
}
- loaded_includes[src] = FS.read(src);
+ try { loaded_includes[src] = FS.read(src); }
+ catch (err) { throw new Error("Failed including file from path: "+src); }
+
eval.call(this,loaded_includes[src]);
});
// sandbox `include_once` as special core function
global.include_once = SANDBOX(function(src){
if (typeof loaded_includes[src] == "undefined") {
- global.include.call(this,src);
+ try { global.include.call(this,src); }
+ catch (err) { throw new Error("Failed including file from path: "+src); }
}
});
// sandbox alert and console.xxx as special core functions
global.console = {};
- global.alert = global.console.log = global.console.warn = global.console.error = SANDBOX(function(){
+ global.alert = SANDBOX(function(){
for (var i=0, len=arguments.length; i<len; i++) {
SYSTEM.stdout.print(arguments[i]);
}
});
+ global.console.info = SANDBOX(function(){
+ for (var i=0, len=arguments.length; i<len; i++) {
+ imports.__Console__("notice",arguments[i]);
+ }
+ });
+ global.console.log = SANDBOX(function(){
+ for (var i=0, len=arguments.length; i<len; i++) {
+ imports.__Console__("log",arguments[i]);
+ }
+ });
+ global.console.warn = SANDBOX(function(){
+ for (var i=0, len=arguments.length; i<len; i++) {
+ imports.__Console__("warn",arguments[i]);
+ }
+ });
+ global.console.error = SANDBOX(function(){
+ for (var i=0, len=arguments.length; i<len; i++) {
+ imports.__Console__("error",arguments[i]);
+ }
+ });
// sandbox "exit" as special core function
global.exit = SANDBOX(function(exit_code){
@@ -100,8 +127,8 @@
});
// pull in custom extension JSON.minify() to strip comments from JSON strings read from files
- include_once("bikechain/misc/minify.json.js");
+ include_once(bikechain_root_path+"misc/minify.json.js");
// pull in String.trim() if not defined yet by the JavaScript environment
- include_once("bikechain/misc/string.trim.js");
+ include_once(bikechain_root_path+"misc/string.trim.js");
})
View
25 engine/engine.json
@@ -0,0 +1,25 @@
+/* BikechainJS (engine.json configuration)
+ v0.0.1.9 (c) Kyle Simpson
+ MIT License
+*/
+
+{
+ // controls log/error output
+ "logging": true, // default: false
+ "log_format": "json", // default: "common"; values: "common", "json"
+ "log_level": 1, // default: 1; values: 0(notice), 1(console)
+ "error_log_level": 2, // default: 2; values: 0(warning), 1(error), 2(fatal)
+
+ // controls log/error output to bikechain log files
+ "file_logging": true, // default: false
+
+ // NOTE: relative paths will be relative to bikechain's root directory
+ "file_log_file": "logs/bikechain.json", // default: "logs/bikechain.json"
+ "file_log_error_file": "logs/bikechain.error.json", // default: "logs/bikechain.error.json"
+
+ // controls log/error output to standard system event queue
+ "system_event_logging": false, // default: false
+
+ // dumps errors to stdout as well
+ "display_errors": false // default: false
+}
View
664 engine/src/engine.cpp
@@ -1,29 +1,50 @@
/* BikechainJS (engine)
-v0.0.1.4b (c) Kyle Simpson
-MIT License
+ v0.0.1.9 (c) Kyle Simpson
+ MIT License
*/
#include <v8.h>
#include <fcntl.h>
#include <string.h>
+#include <vector>
+#include <map>
#include <stdio.h>
+#include <iostream>
+#include <sstream>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string>
+#include <time.h>
#include <cstdlib>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <sys/stat.h>
#ifdef _WIN32
-#include <sys/time.h>
+ #include <sys/time.h>
+ #include <windows.h>
+ #include <winevt.h>
+#else
+ #include <syslog.h>
#endif
#include <engine.h>
int content_type_needed = 0;
+bool backchannel_ready = false;
+
+FILE *file_log_file_handle;
+FILE *file_log_error_file_handle;
+std::string bikechain_root_path_directory;
+v8::Handle<v8::Context> backchannel_context;
+v8::Handle<v8::Context> engine_context;
+v8::Handle<v8::Value> tmpval;
+v8::Handle<v8::Function> jsonstringify_func;
+v8::Handle<v8::Function> getcfg_func;
+v8::Handle<v8::Function> setcfg_func;
// From: http://www.gnu.org/s/libc/manual/html_node/Working-Directory.html
char* gnu_getcwd() {
@@ -46,6 +67,11 @@ const char* ToCString(const v8::String::Utf8Value& value) {
return *value ? *value : "<string conversion failed>";
}
+// converts a v8::Value handle to a std::string
+std::string ToStdString(v8::Handle<v8::Value> value) {
+ return std::string(ToCString(v8::String::Utf8Value(value)));
+}
+
v8::Handle<v8::Value> ioRead(const v8::Arguments& args) {
v8::String::Utf8Value nonblocking(args[0]);
return ReadIO((strcmp(ToCString(nonblocking),"true") == 0));
@@ -115,9 +141,27 @@ v8::Handle<v8::Value> sysExit(const v8::Arguments& args) {
v8::String::Utf8Value exitStatus(args[0]);
int status = atoi(ToCString(exitStatus));
+ v8::V8::Dispose();
+ fclose(file_log_file_handle);
+ fclose(file_log_error_file_handle);
exit(status);
}
+v8::Handle<v8::Value> sysConsole(const v8::Arguments& args) {
+ if (args.Length() != 2) {
+ return v8::ThrowException(v8::String::New("Bad parameters"));
+ }
+
+ std::string console_type = ToStdString(args[0]);
+ std::string console_msg = ToStdString(args[1]);
+
+ if (console_type == "notice") return v8::Boolean::New(log_notice(console_msg));
+ else if (console_type == "log") return v8::Boolean::New(log_console(console_msg));
+ else if (console_type == "warn") return v8::Boolean::New(log_warning(console_msg));
+ else if (console_type == "error") return v8::Boolean::New(log_error(console_msg));
+ else return v8::Undefined();
+}
+
v8::Handle<v8::Value> setContentTypeNeeded(const v8::Arguments& args) {
v8::String::Utf8Value flag(args[0]);
content_type_needed = atoi(ToCString(flag));
@@ -131,7 +175,10 @@ v8::Handle<v8::Value> isContentTypeNeeded(const v8::Arguments& args) {
// Reads a file into a v8 string.
v8::Handle<v8::Value> ReadFile(const char* name) {
FILE* file = fopen(name, "rb");
- if (file == NULL) return v8::ThrowException(v8::String::New((std::string("Error reading from file: ")+std::string(name)).c_str()));
+ if (file == NULL) {
+ v8::ThrowException(v8::String::New((std::string("Error reading from file: ")+std::string(name)).c_str()));
+ return v8::Undefined();
+ }
fseek(file, 0, SEEK_END);
int size = ftell(file);
@@ -238,8 +285,7 @@ v8::Handle<v8::Value> executeProcess(const v8::Arguments& args) {
// take off args[0], since will be sent into stdin of process
num_args--;
- v8::String::Utf8Value v8_input_data(args[0]);
- std::string input_data(ToCString(v8_input_data));
+ std::string input_data = ToStdString(args[0]);
// copy the args array (starting at index 1) into cmd_args
cmd_args = (char**)malloc((num_args+1)*sizeof(char*));
@@ -278,7 +324,7 @@ v8::Handle<v8::Value> executeProcess(const v8::Arguments& args) {
default: // parent process
close(pipe_to_child[0]);
close(pipe_from_child[1]);
- if (input_data != "") {
+ if (!input_data.empty()) {
write(pipe_to_child[1],input_data.c_str(),strlen(input_data.c_str()));
}
close(pipe_to_child[1]);
@@ -323,121 +369,591 @@ v8::Handle<v8::Value> executeProcess(const v8::Arguments& args) {
void ReportException(v8::TryCatch* try_catch) {
v8::HandleScope handle_scope;
- v8::String::Utf8Value exception(try_catch->Exception());
- const char* exception_string = ToCString(exception);
+ std::string exception_string = ToStdString(try_catch->Exception());
v8::Handle<v8::Message> message = try_catch->Message();
if (content_type_needed) {
printf("Content-type: text/plain\n\n");
+ content_type_needed = 0;
}
if (message.IsEmpty()) {
// V8 didn't provide any extra information about this error; just
// print the exception.
- printf("%s\n", exception_string);
+ log_error(exception_string);
}
else {
- // Print (filename):(line number): (message).
- v8::String::Utf8Value filename(message->GetScriptResourceName());
- const char* filename_string = ToCString(filename);
+ // Print (filename):(line number):(char position) (message).
+ std::string filename_string = ToStdString(message->GetScriptResourceName());
int linenum = message->GetLineNumber();
- printf("%s:%i: %s\n", filename_string, linenum, exception_string);
- // Print line of source code.
- v8::String::Utf8Value sourceline(message->GetSourceLine());
- const char* sourceline_string = ToCString(sourceline);
- printf("%s\n", sourceline_string);
- // Print wavy underline (GetUnderline is deprecated).
int start = message->GetStartColumn();
- for (int i = 0; i < start; i++) {
- printf(" ");
- }
- int end = message->GetEndColumn();
- for (int i = start; i < end; i++) {
- printf("^");
- }
- printf("\n");
+ std::string sourceline_string = ToStdString(message->GetSourceLine());
+
+ log_error(filename_string + ":" + stringify(linenum) + ":" + stringify(start) + " " + exception_string + "\n" + sourceline_string);
}
}
bool ExecuteString(v8::Handle<v8::String> source, v8::Handle<v8::Value> name) {
v8::HandleScope handle_scope;
+ v8::Handle<v8::Value> result = ExecuteStringValue(source, name);
+
+ return !result.IsEmpty();
+}
+
+v8::Handle<v8::Value> ExecuteStringValue(v8::Handle<v8::String> source, v8::Handle<v8::Value> name) {
+ v8::HandleScope handle_scope;
v8::TryCatch try_catch;
v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
if (script.IsEmpty()) {
// Print errors that happened during compilation.
ReportException(&try_catch);
+ return v8::Undefined();
+ }
+ else {
+ return handle_scope.Close(script->Run());
+ }
+}
+
+std::vector<std::string> split_path(std::string path) {
+ std::vector<std::string> ret(2);
+ int pos = path.find_last_of("/");
+ ret[0] = path.substr(0,pos) + "/";
+ ret[1] = path.substr(pos+1);
+ return ret;
+}
+
+std::string get_canonical_path(std::string cwd, std::string path) {
+ // if path is relative, prepend cwd
+ if (path[0] != '/') {
+ path = cwd + ((cwd[(cwd.length()-1)] != '/') ? "/" : "") + path;
+ }
+
+ // now, remove any null directories (ie, './') from the path string
+ int null_dir_pos;
+ if (path.substr(0,2) == "./") path = path.substr(2);
+ while ((null_dir_pos = path.find("/./")) != std::string::npos) {
+ path = path.replace(null_dir_pos,3,"/");
+ }
+
+ return path;
+}
+
+v8::Handle<v8::Value> getCfgVal(std::string cfg_name) {
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(backchannel_context);
+
+ // prepare getCfg() parameter
+ v8::Handle<v8::Value> getcfg_args[1] = {v8::String::New(cfg_name.c_str())};
+
+ // run getCfg()
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> res = getcfg_func->Call(backchannel_context->Global(), 1, getcfg_args);
+ if (!res.IsEmpty()) {
+ return handle_scope.Close(res);
+ }
+ else {
+ return v8::Undefined();
+ }
+}
+
+v8::Handle<v8::Object> getCfgObj() {
+ return v8::Handle<v8::Object>::Cast(getCfgVal(""));
+}
+
+bool setCfgVal(std::string cfg_name, v8::Handle<v8::Value> cfg_val) {
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(backchannel_context);
+
+ // prepare setCfg() parameters
+ v8::Handle<v8::Value> setcfg_args[2] = {v8::String::New(cfg_name.c_str()), cfg_val};
+
+ // run setCfg()
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> res = setcfg_func->Call(backchannel_context->Global(), 2, setcfg_args);
+ if (res.IsEmpty()) {
+ ReportException(&try_catch); // setCfg() failed
return false;
}
+ return true;
+}
+
+std::string formatLogMessageBasic(std::vector< std::pair<std::string,std::string> > msg, std::string format) {
+ std::string formatted = "";
+ std::string log_format;
+
+ if (format == "*") {
+ log_format = "common";
+ }
else {
- v8::Handle<v8::Value> result = script->Run();
- if (result.IsEmpty()) {
- // Print errors that happened during execution.
- ReportException(&try_catch);
- return false;
+ log_format = format;
+ }
+
+ for(std::vector< std::pair<std::string,std::string> >::iterator ii=msg.begin(); ii!=msg.end(); ++ii) {
+ // default ("common") format: display each value, with space as separator
+ formatted += ((!formatted.empty()) ? " " : "") + ii->second;
+ }
+
+ return formatted;
+}
+
+std::string formatLogMessage(std::vector< std::pair<std::string,std::string> > msg, std::string format) {
+ if (!backchannel_ready) {
+ return formatLogMessageBasic(msg,format);
+ }
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(backchannel_context);
+
+ std::string formatted = "";
+ std::string log_format = "";
+ v8::Handle<v8::Object> obj_inject;
+
+ if (format == "*") {
+ obj_inject = v8::Object::New();
+ log_format = ToStdString(getCfgVal(std::string("log_format")));
+ }
+ else {
+ log_format = format;
+ }
+
+ for(std::vector< std::pair<std::string,std::string> >::iterator ii=msg.begin(); ii!=msg.end(); ++ii) {
+ if (log_format == "json") {
+ obj_inject->Set(v8::String::New(ii->first.c_str()), v8::String::New(ii->second.c_str()));
+ }
+ else { // default ("common") format: display each value, with space as separator
+ formatted += ((!formatted.empty()) ? " " : "") + ii->second;
+ }
+ }
+
+ if (log_format == "json") {
+ v8::Handle<v8::Value> obj_args[1] = {obj_inject};
+
+ // run JSON.stringify() on object to format it to JSON
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> res = jsonstringify_func->Call(backchannel_context->Global(), 1, obj_args);
+ if (res.IsEmpty()) {
+ ReportException(&try_catch); // JSON.stringify() failed to be called
+ return "";
+ }
+
+ formatted = ToStdString(res);
+ }
+
+ return formatted;
+}
+
+off_t file_size(std::string filename) {
+ struct stat fileinfo;
+ int res;
+
+ res = stat(filename.c_str(),&fileinfo);
+ if (res == 0) {
+ return fileinfo.st_size;
+ }
+ else {
+ return -1;
+ }
+}
+
+bool file_exists(std::string filename) {
+ return file_size(filename) >= 0;
+}
+
+void init_log_file(std::string log_format, std::string filename, FILE* fh) {
+ if (file_size(filename) == 0) {
+ std::vector< std::pair<std::string,std::string> > msg_vec;
+ msg_vec.push_back(std::pair<std::string,std::string>("type","Init"));
+ msg_vec.push_back(std::pair<std::string,std::string>("timestamp",current_timestamp()));
+ std::string msg;
+ if (log_format == "json") msg = "[\n " + formatLogMessage(msg_vec) + "\n]\n";
+ else msg = formatLogMessage(msg_vec) + "\n";
+
+ fwrite(msg.c_str(),sizeof(char),msg.length(),fh);
+ fflush(fh);
+ }
+}
+
+void write_to_log_file(std::string log_format, std::string str, FILE* fh) {
+ if (log_format == "json") {
+ fseek(fh, -3, SEEK_END);
+ }
+ fwrite(str.c_str(),sizeof(char),str.length(),fh);
+ fflush(fh);
+}
+
+bool logMessage(std::string message, int msg_type) {
+// msg_type:
+// 0: notice
+// 1: console
+// 2: warning
+// 3: error
+
+ if (message.empty()) {
+ return false;
+ }
+
+ std::string write_msg = message;
+
+ if (backchannel_ready) {
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(backchannel_context);
+
+ v8::Handle<v8::Object> cfg = getCfgObj();
+ if (!cfg.IsEmpty() && !cfg->IsUndefined()) {
+ std::string log_format = ToStdString(cfg->Get(v8::String::New("log_format")));
+ std::string file_log_file = ToStdString(cfg->Get(v8::String::New("file_log_file")));
+ std::string file_log_error_file = ToStdString(cfg->Get(v8::String::New("file_log_error_file")));
+ int log_level = cfg->Get(v8::String::New("log_level"))->IntegerValue();
+ int error_log_level = cfg->Get(v8::String::New("error_log_level"))->IntegerValue();
+
+ if (log_format == "json") write_msg = ",\n " + write_msg + "\n]\n";
+ else write_msg += "\n";
+
+ if (cfg->Get(v8::String::New("logging"))->BooleanValue()) {
+ if (cfg->Get(v8::String::New("file_logging"))->BooleanValue()) {
+ if ((msg_type == 0 || msg_type == 1) && (log_level <= msg_type)) {
+ init_log_file(log_format,file_log_file,file_log_file_handle);
+ write_to_log_file(log_format,write_msg,file_log_file_handle);
+ }
+ else if (msg_type >= 2 && (error_log_level+2) <= msg_type) {
+ init_log_file(log_format,file_log_error_file,file_log_error_file_handle);
+ write_to_log_file(log_format,write_msg,file_log_error_file_handle);
+ }
+ }
+ if (cfg->Get(v8::String::New("system_event_logging"))->BooleanValue()) {
+
+ }
+ }
+ if (msg_type >= 3 && (!cfg->Get(v8::String::New("logging"))->BooleanValue() || cfg->Get(v8::String::New("display_errors"))->BooleanValue())) {
+ if (content_type_needed) {
+ printf("Content-type: text/plain\n\n");
+ content_type_needed = 0;
+ }
+ if (cfg->Get(v8::String::New("display_errors"))->BooleanValue()) { // forced display of error
+ printf("%s\n",message.c_str());
+ }
+ else { // error was not logged or displayed to stdout, so at least send to stderr
+ fprintf(stderr, "%s\n",message.c_str());
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ else { // logging before backchannel is ready
+ // so, only log errors (and above), to stderr
+ if (msg_type >= 3) {
+ if (content_type_needed) {
+ printf("Content-type: text/plain\n\n");
+ content_type_needed = 0;
+ }
+ fprintf(stderr, "%s\n",message.c_str());
}
return true;
}
}
+std::string stringify(int num) {
+ std::ostringstream o;
+ o << num;
+ return o.str();
+}
+
+std::string left_pad_str(std::string str, int toLength, char pad) {
+ if (toLength < 1 || str.length() >= toLength) return str;
+ return std::string(toLength-str.length(), pad) + str;
+}
+
+std::string current_timestamp() {
+ std::string dow[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+ std::string mon[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
+ std::string ts;
+ time_t rawtime;
+ struct tm* timeinfo;
+ std::string tz;
+
+ time(&rawtime);
+ timeinfo = localtime(&rawtime);
+
+ std::string tzoffset = stringify(0-(timezone/36));
+ if (tzoffset[0] != '-') tzoffset = "+" + tzoffset;
+ tzoffset = "GMT" + tzoffset.substr(0,1) + left_pad_str(tzoffset.substr(1),4,'0');
+
+ ts = dow[timeinfo->tm_wday] + " " + mon[timeinfo->tm_mon] + " " + stringify(timeinfo->tm_mday) + " " + stringify(1900+timeinfo->tm_year);
+ ts += " " + left_pad_str(stringify(timeinfo->tm_hour),2,'0') + ":" + left_pad_str(stringify(timeinfo->tm_min),2,'0') + ":" + left_pad_str(stringify(timeinfo->tm_sec),2,'0');
+ ts += " " + tzoffset + " (" + std::string(*tzname) + ")";
+
+ return ts;
+}
+
+bool log(std::string msg, int msg_type=0, int errorNumber) {
+// msg_type:
+// 0: notice
+// 1: console
+// 2: warning
+// 3: error
+
+ std::string types[4] = {"Notice", "Console", "Warning", "Error"};
+
+ if (!msg.empty()) {
+ std::vector< std::pair<std::string,std::string> > msg_vec;
+
+ msg_vec.push_back(std::pair<std::string,std::string>("type",types[msg_type]));
+ msg_vec.push_back(std::pair<std::string,std::string>("timestamp",current_timestamp()));
+ if (msg_type >= 3 && errorNumber > 0) {
+ msg_vec.push_back(std::pair<std::string,std::string>("code",stringify(errorNumber)));
+ }
+ msg_vec.push_back(std::pair<std::string,std::string>("msg",msg));
+
+ return logMessage(formatLogMessage(msg_vec),msg_type);
+ }
+ else {
+ return false;
+ }
+}
+
+bool log_notice(std::string msg) { return log(msg,0); }
+bool log_console(std::string msg) { return log(msg,1); }
+bool log_warning(std::string msg) { return log(msg,2); }
+bool log_error(std::string msg, int error_number) { return log(msg,3,error_number); }
+bool log_fatal(std::string msg, int error_number) { return log(msg,3,error_number); }
+
+
int RunMain(int argc, char* argv[]) {
+
+ // initialize the timezone info
+ tzset();
+
+// ***** SETUP *****
// get the current working directory and full executable path
- char* cwd_str = gnu_getcwd();
- std::string path_cwd(cwd_str);
- free(cwd_str);
+ std::string path_cwd(gnu_getcwd());
std::string path_exec(argv[0]);
- std::string full_path = path_cwd + "/" + path_exec;
+
+ // parse path parts
+ std::string path_parts_str = get_canonical_path(path_cwd,path_exec);
+ std::vector<std::string> path_parts = split_path(path_parts_str);
+ std::string exec_path_directory = path_parts[0];
+ std::string exec_path_name = path_parts[1];
+
+ bikechain_root_path_directory = exec_path_directory.substr(0,exec_path_directory.find_last_of("/",exec_path_directory.length()-2)+1);
+
+ log_notice("Starting BikechainJS in " + bikechain_root_path_directory);
// start the V8 environment
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
v8::HandleScope handle_scope;
- v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
-
- v8::Handle<v8::Context> context = v8::Context::New(NULL, global);
- v8::Context::Scope context_scope(context);
-
- v8::Handle<v8::String> source = v8::Handle<v8::String>::Cast(ReadFile((full_path+".js").c_str()));
- v8::Handle<v8::Script> compiled_source = v8::Script::Compile(source);
- if (compiled_source.IsEmpty()) {
- printf("Error reading engine.js\n");
- return 1;
- }
- v8::Handle<v8::Value> enginejs = compiled_source->Run();
- v8::Handle<v8::Function> enginejs_func = v8::Handle<v8::Function>::Cast(enginejs);
-
- v8::Handle<v8::Object> inject = v8::Object::New();
- inject->Set(v8::String::New("__IORead__"), v8::FunctionTemplate::New(ioRead)->GetFunction());
- inject->Set(v8::String::New("__IOWrite__"), v8::FunctionTemplate::New(ioWrite)->GetFunction());
- inject->Set(v8::String::New("__IOFlush__"), v8::FunctionTemplate::New(ioFlush)->GetFunction());
- inject->Set(v8::String::New("__FSRead__"), v8::FunctionTemplate::New(fsRead)->GetFunction());
- inject->Set(v8::String::New("__FSWrite__"), v8::FunctionTemplate::New(fsWrite)->GetFunction());
- inject->Set(v8::String::New("__SysExec__"), v8::FunctionTemplate::New(sysExec)->GetFunction());
- inject->Set(v8::String::New("__SysExit__"), v8::FunctionTemplate::New(sysExit)->GetFunction());
- inject->Set(v8::String::New("__SetContentTypeNeeded__"), v8::FunctionTemplate::New(setContentTypeNeeded)->GetFunction());
- inject->Set(v8::String::New("__IsContentTypeNeeded__"), v8::FunctionTemplate::New(isContentTypeNeeded)->GetFunction());
- v8::Handle<v8::Value> args[1] = {inject};
-
- enginejs_func->Call(context->Global(), 1, args);
-
- for (int i = 1; i < argc; i++) {
- const char* str = argv[i];
- // Use all arguments as names of files to load and run.
- v8::HandleScope handle_scope;
- v8::Handle<v8::String> file_name = v8::String::New(str);
- v8::Handle<v8::String> source = v8::Handle<v8::String>::Cast(ReadFile((path_cwd + "/" + std::string(str)).c_str()));
- if (source.IsEmpty()) {
- printf("Error reading '%s'\n", str);
+ v8::Handle<v8::String> bikechain_root_path = v8::String::New(bikechain_root_path_directory.c_str());
+
+// ***** BACKCHANNEL *****
+ // set up the backchannel controller (for processing config file and formatting log messages)
+ v8::Handle<v8::ObjectTemplate> backchannel_global = v8::ObjectTemplate::New();
+ backchannel_context = v8::Context::New(NULL, backchannel_global);
+ {
+ v8::Context::Scope scope(backchannel_context);
+
+ // prepare JSON serializer
+ jsonstringify_func = v8::Handle<v8::Function>::Cast(ExecuteStringValue(v8::String::New("(function(j){return JSON.stringify(j);})"),v8::String::New("inline::code")));
+ if (jsonstringify_func.IsEmpty() || jsonstringify_func->IsUndefined()) {
+ log_fatal("Failed preparing engine backchannel JSON utility");
+ return 1;
+ }
+
+ // load JSON.minify() utility
+ tmpval = ReadFile((bikechain_root_path_directory+"misc/minify.json.js").c_str());
+ if (tmpval.IsEmpty() || tmpval->IsUndefined()) {
+ log_fatal("Failed loading engine backchannel: `minify.json.js`");
+ return 1;
+ }
+ v8::Handle<v8::String> json_minify_source = v8::Handle<v8::String>::Cast(tmpval);
+ if (!ExecuteString(json_minify_source, v8::String::New("minify.json.js"))) {
+ log_fatal("Failed parsing engine backchannel: `minify.json.js`");
+ return 1;
+ }
+
+ // load String.trim() utility
+ tmpval = ReadFile((bikechain_root_path_directory+"misc/string.trim.js").c_str());
+ if (tmpval.IsEmpty() || tmpval->IsUndefined()) {
+ log_fatal("Failed loading engine backchannel; `string.trim.js`");
+ return 1;
+ }
+ v8::Handle<v8::String> string_trim_source = v8::Handle<v8::String>::Cast(tmpval);
+ if (!ExecuteString(string_trim_source, v8::String::New("string.trim.js"))) {
+ log_fatal("Failed parsing engine backchannel: `string.trim.js`");
+ return 1;
+ }
+
+ // load/prepare backchannel controller
+ tmpval = ReadFile((exec_path_directory+"backchannel.js").c_str());
+ if (tmpval.IsEmpty() || tmpval->IsUndefined()) {
+ log_fatal("Failed loading engine backchannel: `backchannel.js`");
return 1;
}
- if (!ExecuteString(source, file_name))
- return 1;
+ v8::Handle<v8::Function> backchannel_func = v8::Handle<v8::Function>::Cast(ExecuteStringValue(v8::Handle<v8::String>::Cast(tmpval),v8::String::New("backchannel.js")));
+ if (backchannel_func.IsEmpty() || backchannel_func->IsUndefined()) {
+ log_fatal("Failed parsing engine backchannel: `backchannel.js`");
+ return 1;
+ }
+
+ // read/parse config file
+ tmpval = ReadFile((exec_path_directory+exec_path_name+".json").c_str());
+ if (tmpval.IsEmpty() || tmpval->IsUndefined()) {
+ log_fatal("Failed loading engine configuration: `" + exec_path_name + ".json`");
+ return 1;
+ }
+
+ // prepare backchannel init parameters
+ v8::Handle<v8::Object> backchannel_inject = v8::Object::New();
+ backchannel_inject->Set(v8::String::New("cfg"), v8::Handle<v8::String>::Cast(tmpval));
+ v8::Handle<v8::Value> backchannel_args[1] = {backchannel_inject};
+
+ // run backchannel init
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> res = backchannel_func->Call(backchannel_context->Global(), 1, backchannel_args);
+ if (res.IsEmpty()) {
+ ReportException(&try_catch);
+ log_fatal("Failed initializing backchannel");
+ return 1;
+ }
+
+ // store references to backchannel configuration get/set JS functions
+ getcfg_func = v8::Handle<v8::Function>::Cast(ExecuteStringValue(v8::String::New("getCfg"),v8::String::New("inline::code")));
+ if (getcfg_func.IsEmpty() || getcfg_func->IsUndefined()) {
+ log_fatal("Failed preparing engine configuration management");
+ return 1;
+ }
+ v8::TryCatch try_catch2;
+ setcfg_func = v8::Handle<v8::Function>::Cast(ExecuteStringValue(v8::String::New("setCfg"),v8::String::New("inline::code")));
+ if (setcfg_func.IsEmpty() || setcfg_func->IsUndefined()) {
+ ReportException(&try_catch2);
+ log_fatal("Failed preparing engine configuration management");
+ return 1;
+ }
+
+ // manage engine configuration
+ v8::Handle<v8::Object> tmp_cfg = getCfgObj();
+ if (!tmp_cfg.IsEmpty() && !tmp_cfg->IsUndefined()) {
+ // canonicalize the `file_log_*` paths from the engine configuration
+ std::string file_log_file = get_canonical_path(bikechain_root_path_directory,ToStdString(tmp_cfg->Get(v8::String::New("file_log_file"))));
+ std::string file_log_error_file = get_canonical_path(bikechain_root_path_directory,ToStdString(tmp_cfg->Get(v8::String::New("file_log_error_file"))));
+
+ // push the updated path values back into the configuration cache
+ if (!setCfgVal("file_log_file", v8::String::New(file_log_file.c_str()))) {
+ log_fatal("Failed normalizing engine configuration");
+ return 1;
+ }
+ if (!setCfgVal("file_log_error_file", v8::String::New(file_log_error_file.c_str()))) {
+ log_fatal("Failed normalizing engine configuration");
+ return 1;
+ }
+
+ // make sure the log files exist and are properly initialized
+ if (!file_exists(file_log_file)) file_log_file_handle = fopen(file_log_file.c_str(),"w+t");
+ else file_log_file_handle = fopen(file_log_file.c_str(),"r+t");
+ if (file_log_file_handle == NULL) {
+ log_fatal("Failed opening log file: `" + file_log_file + "`");
+ return 1;
+ }
+ chmod(file_log_file.c_str(),0664);
+
+ if (!file_exists(file_log_error_file)) file_log_error_file_handle = fopen(file_log_error_file.c_str(),"w+t");
+ else file_log_error_file_handle = fopen(file_log_error_file.c_str(),"r+t");
+ if (file_log_error_file_handle == NULL) {
+ log_fatal("Failed opening error log file: `" + file_log_error_file + "`");
+ return 1;
+ }
+ chmod(file_log_error_file.c_str(),0664);
+ }
+ else {
+ log_fatal("Failed executing engine configuration management");
+ return 1;
+ }
+
+ backchannel_ready = true;
+ }
+
+
+// ***** ENGINE *****
+ v8::Handle<v8::ObjectTemplate> engine_global = v8::ObjectTemplate::New();
+ engine_context = v8::Context::New(NULL, engine_global);
+ {
+ v8::Context::Scope scope(engine_context);
+
+ // load engine source
+ tmpval = ReadFile((exec_path_directory+exec_path_name+".js").c_str());
+ if (tmpval.IsEmpty() || tmpval->IsUndefined()) {
+ log_fatal("Failed loading engine: `" + exec_path_name + ".js`");
+ return 1;
+ }
+
+ v8::Handle<v8::Function> engine_func = v8::Handle<v8::Function>::Cast(ExecuteStringValue(v8::Handle<v8::String>::Cast(tmpval),v8::String::New(std::string(exec_path_name+".js").c_str())));
+ if (engine_func.IsEmpty() || engine_func->IsUndefined()) {
+ log_fatal("Failed parsing engine: `" + exec_path_name + ".js`");
+ return 1;
+ }
+
+ // prepare engine init parameters
+ v8::Handle<v8::Object> engine_inject = v8::Object::New();
+ engine_inject->Set(v8::String::New("__IORead__"), v8::FunctionTemplate::New(ioRead)->GetFunction());
+ engine_inject->Set(v8::String::New("__IOWrite__"), v8::FunctionTemplate::New(ioWrite)->GetFunction());
+ engine_inject->Set(v8::String::New("__IOFlush__"), v8::FunctionTemplate::New(ioFlush)->GetFunction());
+ engine_inject->Set(v8::String::New("__FSRead__"), v8::FunctionTemplate::New(fsRead)->GetFunction());
+ engine_inject->Set(v8::String::New("__FSWrite__"), v8::FunctionTemplate::New(fsWrite)->GetFunction());
+ engine_inject->Set(v8::String::New("__SysExec__"), v8::FunctionTemplate::New(sysExec)->GetFunction());
+ engine_inject->Set(v8::String::New("__SysExit__"), v8::FunctionTemplate::New(sysExit)->GetFunction());
+ engine_inject->Set(v8::String::New("__Console__"), v8::FunctionTemplate::New(sysConsole)->GetFunction());
+ engine_inject->Set(v8::String::New("__SetContentTypeNeeded__"), v8::FunctionTemplate::New(setContentTypeNeeded)->GetFunction());
+ engine_inject->Set(v8::String::New("__IsContentTypeNeeded__"), v8::FunctionTemplate::New(isContentTypeNeeded)->GetFunction());
+ v8::Handle<v8::Value> engine_args[2] = {bikechain_root_path,engine_inject};
+
+ // run engine
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> res = engine_func->Call(engine_context->Global(), 2, engine_args);
+ if (res.IsEmpty()) {
+ ReportException(&try_catch); // engine failed to init
+ log_fatal("Failed initializing engine");
+ return 1;
+ }
+
+ log_notice("Engine and backchannel ready");
+
+
+// ***** COMMAND-LINE *****
+
+ std::string filename_str;
+
+ // run parameter .js files
+ for (int i = 1; i < argc; i++) {
+ v8::HandleScope handle_scope;
+ const char* str = argv[i];
+
+ // Use all arguments as names of files to load and run.
+ filename_str = get_canonical_path(path_cwd,std::string(str));
+ v8::Handle<v8::String> file_name = v8::String::New(filename_str.c_str());
+
+ log_notice("Loading `" + filename_str + "`");
+
+ // load and run file specified
+ tmpval = ReadFile(filename_str.c_str());
+ if (tmpval.IsEmpty() || tmpval->IsUndefined()) {
+ log_fatal("Failed loading file: `" + filename_str + "`");
+ return 1;
+ }
+ if (!ExecuteString(v8::Handle<v8::String>::Cast(tmpval),file_name)) {
+ log_fatal("Failed executing file: `" + filename_str + "`");
+ return 1;
+ }
+ }
}
+
return 0;
}
int main(int argc, char* argv[]) {
int result = RunMain(argc, argv);
v8::V8::Dispose();
+ fclose(file_log_file_handle);
+ fclose(file_log_error_file_handle);
return result;
}
View
32 engine/src/engine.h
@@ -1,9 +1,10 @@
/* BikechainJS (engine)
- v0.0.1.4 (c) Kyle Simpson
+ v0.0.1.9 (c) Kyle Simpson
MIT License
*/
const char* ToCString(const v8::String::Utf8Value& value);
+std::string ToStdString(v8::Handle<v8::Value> value);
v8::Handle<v8::Value> ioRead();
v8::Handle<v8::Value> ioWrite(const v8::Arguments& args);
@@ -22,4 +23,31 @@ void FlushIO();
v8::Handle<v8::Value> executeProcess(const v8::Arguments& args);
void ReportException(v8::TryCatch* handler);
-bool ExecuteString(v8::Handle<v8::String> source, v8::Handle<v8::Value> name, bool print_result, bool report_exceptions);
+bool ExecuteString(v8::Handle<v8::String> source, v8::Handle<v8::Value> name);
+v8::Handle<v8::Value> ExecuteStringValue(v8::Handle<v8::String> source, v8::Handle<v8::Value> name);
+
+std::vector<std::string> split_path(std::string path);
+std::string get_canonical_path(std::string cwd, std::string path);
+
+v8::Handle<v8::Value> getCfgVal(std::string cfg_name);
+v8::Handle<v8::Object> getCfgObj(void);
+bool setCfgVal(std::string cfg_name, v8::Handle<v8::Value> cfg_val);
+std::string formatLogMessageBasic(std::vector< std::pair<std::string,std::string> > msg, std::string format="*");
+std::string formatLogMessage(std::vector< std::pair<std::string,std::string> > msg, std::string format="*");
+bool logMessage(std::string message, int msg_type=0);
+
+off_t file_size(std::string filename);
+bool file_exists(std::string filename);
+void init_log_file(std::string log_format, std::string filename, FILE* fh);
+void write_to_log_file(std::string log_format, std::string str, FILE* fh);
+
+std::string stringify(int num);
+std::string left_pad_str(std::string str, int to_length, char pad=' ');
+std::string current_timestamp();
+
+bool log(std::string msg, int msg_type, int error_number=0);
+bool log_notice(std::string msg);
+bool log_console(std::string msg);
+bool log_warning(std::string msg);
+bool log_error(std::string msg, int error_number=0);
+bool log_fatal(std::string msg, int error_number=0);
View
12 engine/src/makefile
@@ -1,12 +1,18 @@
## BikechainJS (makefile)
-## v0.0.1.3 (c) Kyle Simpson
+## v0.0.1.9 (c) Kyle Simpson
## MIT License
+
engine : engine.o
- g++ -o ../engine -lv8 engine.o
+ g++ -o engine -lv8 engine.o
engine.o : engine.cpp
g++ -c engine.cpp -I/path/to/V8/include -I./
+install : engine
+ strip ./engine
+ cp ./engine ../engine
+ chmod 750 ../engine
+
clean:
- rm *.o; strip ../engine
+ rm *.o
View
1  misc/string.trim.js
@@ -1,3 +1,4 @@
+// String.trim() is defined as a static utility function rather than a prototype extension
if (!String.trim) {
String.trim = function(str) { // from: http://blog.stevenlevithan.com/archives/faster-trim-javascript
var str = str.replace(/^\s\s*/, ''),ws = /\s/,i = str.length;
Please sign in to comment.
Something went wrong with that request. Please try again.