--- sbbs/src/sbbs3/jsexec.c 2018/04/24 16:41:23 1.1 +++ sbbs/src/sbbs3/jsexec.c 2018/04/24 16:42:47 1.1.1.2 @@ -2,13 +2,13 @@ /* Execute a Synchronet JavaScript module from the command-line */ -/* $Id: jsexec.c,v 1.1 2018/04/24 16:41:23 root Exp $ */ +/* $Id: jsexec.c,v 1.1.1.2 2018/04/24 16:42:47 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -43,9 +43,11 @@ #include #endif -#include "ciolib.h" - #include "sbbs.h" +#include "ciolib.h" +#include "ini_file.h" +#include "js_rtpool.h" +#include "js_request.h" #define DEFAULT_LOG_LEVEL LOG_DEBUG /* Display all LOG levels */ #define DEFAULT_ERR_LOG_LVL LOG_WARNING @@ -66,6 +68,7 @@ char revision[16]; char compiler[32]; char* host_name=NULL; char host_name_buf[128]; +char* load_path_list=JAVASCRIPT_LOAD_PATH; BOOL pause_on_exit=FALSE; BOOL pause_on_error=FALSE; BOOL terminated=FALSE; @@ -76,6 +79,7 @@ pthread_mutex_t output_mutex; #if defined(__unix__) BOOL daemonize=FALSE; #endif +char orig_cwd[MAX_PATH+1]; void banner(FILE* fp) { @@ -112,8 +116,10 @@ void usage(FILE* fp) "\t-y set yield interval (default=%u, 0=never)\n" "\t-g set garbage collection interval (default=%u, 0=never)\n" "\t-h[hostname] use local or specified host name (instead of SCFG value)\n" + "\t-u set file creation permissions mask (in octal)\n" "\t-L set log level (default=%u)\n" - "\t-E set error log level threshold (default=%d)\n" + "\t-E set error log level threshold (default=%u)\n" + "\t-i set load() comma-sep search path list (default=\"%s\")\n" "\t-f use non-buffered stream for console messages\n" "\t-a append instead of overwriting message output files\n" "\t-e send error messages to file in addition to stderr\n" @@ -133,6 +139,7 @@ void usage(FILE* fp) ,JAVASCRIPT_GC_INTERVAL ,DEFAULT_LOG_LEVEL ,DEFAULT_ERR_LOG_LVL + ,load_path_list ,_PATH_DEVNULL ,_PATH_DEVNULL ); @@ -159,7 +166,7 @@ int mfprintf(FILE* fp, char *fmt, ...) } /* Log printf */ -int lprintf(int level, char *fmt, ...) +int lprintf(int level, const char *fmt, ...) { va_list argptr; char sbuf[1024]; @@ -245,7 +252,7 @@ static BOOL winsock_startup(void) return(TRUE); } - lprintf(LOG_ERR,"!WinSock startup ERROR %d", status); + lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status); return(FALSE); } @@ -256,9 +263,8 @@ static BOOL winsock_startup(void) #endif -void bail(int code) +static int do_bail(int code) { - #if defined(_WINSOCKAPI_) if(WSAInitialized && WSACleanup()!=0) lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE); @@ -271,7 +277,12 @@ void bail(int code) if(code) fprintf(statfp,"\nReturning error code: %d\n",code); - exit(code); + return(code); +} + +void bail(int code) +{ + exit(do_bail(code)); } static JSBool @@ -280,6 +291,7 @@ js_log(JSContext *cx, JSObject *obj, uin uintN i=0; int32 level=LOG_INFO; JSString* str=NULL; + jsrefcount rc; if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) JS_ValueToInt32(cx,argv[i++],&level); @@ -287,7 +299,9 @@ js_log(JSContext *cx, JSObject *obj, uin for(; i=0) *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,rd)); @@ -324,13 +341,16 @@ js_readln(JSContext *cx, JSObject *obj, char* buf; char* p; int32 len=128; + jsrefcount rc; if(argc) JS_ValueToInt32(cx,argv[0],&len); if((buf=alloca(len+1))==NULL) return(JS_TRUE); + rc=JS_SUSPENDREQUEST(cx); p=fgets(buf,len+1,stdin); + JS_RESUMEREQUEST(cx, rc); if(p!=NULL) *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,truncnl(p))); @@ -344,11 +364,14 @@ js_write(JSContext *cx, JSObject *obj, u { uintN i; JSString* str=NULL; + jsrefcount rc; for (i = 0; i < argc; i++) { if((str=JS_ValueToString(cx, argv[i]))==NULL) return(JS_FALSE); + rc=JS_SUSPENDREQUEST(cx); fprintf(confp,"%s",JS_GetStringBytes(str)); + JS_RESUMEREQUEST(cx, rc); } if(str==NULL) @@ -361,10 +384,14 @@ js_write(JSContext *cx, JSObject *obj, u static JSBool js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + jsrefcount rc; + if(!js_write(cx,obj,argc,argv,rval)) return(JS_FALSE); + rc=JS_SUSPENDREQUEST(cx); fprintf(confp,"\n"); + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } @@ -372,13 +399,16 @@ static JSBool js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char* p; + jsrefcount rc; if((p = js_sprintf(cx, 0, argc, argv))==NULL) { JS_ReportError(cx,"js_sprintf failed"); return(JS_FALSE); } + rc=JS_SUSPENDREQUEST(cx); fprintf(confp,"%s",p); + JS_RESUMEREQUEST(cx, rc); *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)); @@ -391,11 +421,14 @@ static JSBool js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString * str; + jsrefcount rc; if((str=JS_ValueToString(cx, argv[0]))==NULL) return(JS_FALSE); + rc=JS_SUSPENDREQUEST(cx); fprintf(confp,"!%s\n",JS_GetStringBytes(str)); + JS_RESUMEREQUEST(cx, rc); *rval = argv[0]; @@ -406,13 +439,47 @@ static JSBool js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString * str; + char * cstr; + char * p; + jsrefcount rc; + char instr[81]="y"; if((str=JS_ValueToString(cx, argv[0]))==NULL) return(JS_FALSE); - printf("%s (Y/N)?", JS_GetStringBytes(str)); + cstr = JS_GetStringBytes(str); + printf("%s (Y/n)? ", cstr); + rc=JS_SUSPENDREQUEST(cx); + fgets(instr,sizeof(instr),stdin); + JS_RESUMEREQUEST(cx, rc); + + p=instr; + SKIP_WHITESPACE(p); + *rval = BOOLEAN_TO_JSVAL(tolower(*p)!='n'); + return(JS_TRUE); +} + +static JSBool +js_deny(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString * str; + char * cstr; + char * p; + jsrefcount rc; + char instr[81]; + + if((str=JS_ValueToString(cx, argv[0]))==NULL) + return(JS_FALSE); - *rval = BOOLEAN_TO_JSVAL(FALSE); + cstr = JS_GetStringBytes(str); + printf("%s (N/y)? ", cstr); + rc=JS_SUSPENDREQUEST(cx); + fgets(instr,sizeof(instr),stdin); + JS_RESUMEREQUEST(cx, rc); + + p=instr; + SKIP_WHITESPACE(p); + *rval = BOOLEAN_TO_JSVAL(tolower(*p)!='y'); return(JS_TRUE); } @@ -422,11 +489,14 @@ js_prompt(JSContext *cx, JSObject *obj, char instr[81]; JSString * prompt; JSString * str; + jsrefcount rc; if(!JSVAL_IS_VOID(argv[0])) { if((prompt=JS_ValueToString(cx, argv[0]))==NULL) return(JS_FALSE); + rc=JS_SUSPENDREQUEST(cx); fprintf(confp,"%s: ",JS_GetStringBytes(prompt)); + JS_RESUMEREQUEST(cx, rc); } if(argc>1) { @@ -436,8 +506,12 @@ js_prompt(JSContext *cx, JSObject *obj, } else instr[0]=0; - if(!fgets(instr,sizeof(instr),stdin)) + rc=JS_SUSPENDREQUEST(cx); + if(!fgets(instr,sizeof(instr),stdin)) { + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); + } + JS_RESUMEREQUEST(cx, rc); if((str=JS_NewStringCopyZ(cx, truncnl(instr)))==NULL) return(JS_FALSE); @@ -450,13 +524,16 @@ static JSBool js_chdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char* p; + jsrefcount rc; if((p=JS_GetStringBytes(JS_ValueToString(cx, argv[0])))==NULL) { *rval = INT_TO_JSVAL(-1); return(JS_TRUE); } + rc=JS_SUSPENDREQUEST(cx); *rval = BOOLEAN_TO_JSVAL(chdir(p)==0); + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } @@ -485,6 +562,7 @@ static jsSyncMethodSpec js_global_functi {"alert", js_alert, 1}, {"prompt", js_prompt, 1}, {"confirm", js_confirm, 1}, + {"deny", js_deny, 1}, {"chdir", js_chdir, 1}, {"putenv", js_putenv, 1}, {0} @@ -496,9 +574,13 @@ js_ErrorReporter(JSContext *cx, const ch char line[64]; char file[MAX_PATH+1]; const char* warning; + jsrefcount rc; + int log_level; + rc=JS_SUSPENDREQUEST(cx); if(report==NULL) { lprintf(LOG_ERR,"!JavaScript: %s", message); + JS_RESUMEREQUEST(cx, rc); return; } @@ -517,10 +599,14 @@ js_ErrorReporter(JSContext *cx, const ch warning="strict warning"; else warning="warning"; - } else + log_level=LOG_WARNING; + } else { + log_level=LOG_ERR; warning=""; + } - lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message); + lprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message); + JS_RESUMEREQUEST(cx, rc); } static JSBool @@ -529,6 +615,19 @@ js_BranchCallback(JSContext *cx, JSScrip return(js_CommonBranchCallback(cx,&branch)); } +#ifdef USE_JS_OPERATION_CALLBACK +static JSBool +js_OperationCallback(JSContext *cx) +{ + JSBool ret; + + JS_SetOperationCallback(cx, NULL); + ret=js_BranchCallback(cx, NULL); + JS_SetOperationCallback(cx, js_OperationCallback); + return ret; +} +#endif + static BOOL js_CreateEnvObject(JSContext* cx, JSObject* glob, char** env) { char name[256]; @@ -561,12 +660,17 @@ static BOOL js_CreateEnvObject(JSContext static BOOL js_init(char** environ) { + js_startup_t startup; + + memset(&startup,0,sizeof(startup)); + SAFECOPY(startup.load_path, load_path_list); + fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion()); fprintf(statfp,"JavaScript: Creating runtime: %lu bytes\n" ,js_max_bytes); - if((js_runtime = JS_NewRuntime(js_max_bytes))==NULL) + if((js_runtime = jsrt_GetNew(js_max_bytes, 5000, __FILE__, __LINE__))==NULL) return(FALSE); fprintf(statfp,"JavaScript: Initializing context (stack: %lu bytes)\n" @@ -574,6 +678,7 @@ static BOOL js_init(char** environ) if((js_cx = JS_NewContext(js_runtime, js_cx_stack))==NULL) return(FALSE); + JS_BEGINREQUEST(js_cx); if(stack_limit) fprintf(statfp,"JavaScript: Thread stack limit: %lu bytes\n" @@ -584,18 +689,29 @@ static BOOL js_init(char** environ) /* Global Object */ if((js_glob=js_CreateCommonObjects(js_cx, &scfg, NULL, js_global_functions ,time(NULL), host_name, SOCKLIB_DESC /* system */ - ,&branch /* js */ + ,&branch,&startup /* js */ ,NULL,INVALID_SOCKET /* client */ ,NULL /* server */ - ))==NULL) + ))==NULL) { + JS_ENDREQUEST(js_cx); return(FALSE); + } /* Environment Object (associative array) */ - if(!js_CreateEnvObject(js_cx, js_glob, environ)) + if(!js_CreateEnvObject(js_cx, js_glob, environ)) { + JS_ENDREQUEST(js_cx); return(FALSE); + } - if(js_CreateUifcObject(js_cx, js_glob)==NULL) + if(js_CreateUifcObject(js_cx, js_glob)==NULL) { + JS_ENDREQUEST(js_cx); return(FALSE); + } + + if(js_CreateConioObject(js_cx, js_glob)==NULL) { + JS_ENDREQUEST(js_cx); + return(FALSE); + } return(TRUE); } @@ -627,14 +743,19 @@ long js_exec(const char *fname, char** a int32 result=0; long double start; long double diff; - + if(fname!=NULL) { - if(strcspn(fname,"/\\")==strlen(fname)) { - sprintf(path,"%s%s%s",scfg.mods_dir,fname,js_ext(fname)); - if(scfg.mods_dir[0]==0 || !fexistcase(path)) - sprintf(path,"%s%s%s",scfg.exec_dir,fname,js_ext(fname)); - } else + if(isfullpath(fname)) { SAFECOPY(path,fname); + } + else { + SAFEPRINTF3(path,"%s%s%s",orig_cwd,fname,js_ext(fname)); + if(!fexistcase(path)) { + SAFEPRINTF3(path,"%s%s%s",scfg.mods_dir,fname,js_ext(fname)); + if(scfg.mods_dir[0]==0 || !fexistcase(path)) + SAFEPRINTF3(path,"%s%s%s",scfg.exec_dir,fname,js_ext(fname)); + } + } if(!fexistcase(path)) { lprintf(LOG_ERR,"!Module file (%s) doesn't exist",path); @@ -693,7 +814,11 @@ long js_exec(const char *fname, char** a branch.terminated=&terminated; +#ifdef USE_JS_OPERATION_CALLBACK + JS_SetOperationCallback(js_cx, js_OperationCallback); +#else JS_SetBranchCallback(js_cx, js_BranchCallback); +#endif if(fp==stdin) /* Using stdin for script source */ SAFECOPY(path,"stdin"); @@ -731,22 +856,23 @@ long js_exec(const char *fname, char** a ,path ,diff); + js_PrepareToExecute(js_cx, js_glob, fname==NULL ? NULL : path, orig_cwd); start=xp_timer(); JS_ExecuteScript(js_cx, js_glob, js_script, &rval); + JS_GetProperty(js_cx, js_glob, "exit_code", &rval); + if(rval!=JSVAL_VOID && JSVAL_IS_NUMBER(rval)) { + mfprintf(statfp,"Using JavaScript exit_code: %s",JS_GetStringBytes(JS_ValueToString(js_cx,rval))); + JS_ValueToInt32(js_cx,rval,&result); + } js_EvalOnExit(js_cx, js_glob, &branch); + JS_ReportPendingException(js_cx); + if((diff=xp_timer()-start) > 0) mfprintf(statfp,"%s executed in %.2Lf seconds" ,path ,diff); - JS_GetProperty(js_cx, js_glob, "exit_code", &rval); - - if(rval!=JSVAL_VOID && JSVAL_IS_NUMBER(rval)) { - mfprintf(statfp,"Using JavaScript exit_code: %s",JS_GetStringBytes(JS_ValueToString(js_cx,rval))); - JS_ValueToInt32(js_cx,rval,&result); - } - JS_DestroyScript(js_cx, js_script); JS_GC(js_cx); @@ -772,13 +898,35 @@ void recycle_handler(int type) #if defined(_WIN32) -BOOL WINAPI ControlHandler(DWORD CtrlType) +BOOL WINAPI ControlHandler(unsigned long CtrlType) { break_handler((int)CtrlType); return TRUE; } #endif +int parseLogLevel(const char* p) +{ + str_list_t logLevelStringList=iniLogLevelStringList(); + int i; + + if(isdigit(*p)) + return strtol(p,NULL,0); + + /* Exact match */ + for(i=0;logLevelStringList[i]!=NULL;i++) { + if(stricmp(logLevelStringList[i],p)==0) + return i; + } + /* Partial match */ + for(i=0;logLevelStringList[i]!=NULL;i++) { + if(strnicmp(logLevelStringList[i],p,strlen(p))==0) + return i; + } + return DEFAULT_LOG_LEVEL; +} + + /*********************/ /* Entry point (duh) */ /*********************/ @@ -798,7 +946,7 @@ int main(int argc, char **argv, char** e errfp=stderr; if((nulfp=fopen(_PATH_DEVNULL,"w+"))==NULL) { perror(_PATH_DEVNULL); - bail(-1); + return(do_bail(-1)); } if(isatty(fileno(stderr))) statfp=stderr; @@ -810,14 +958,17 @@ int main(int argc, char **argv, char** e branch.gc_interval=JAVASCRIPT_GC_INTERVAL; branch.auto_terminate=TRUE; - sscanf("$Revision: 1.1 $", "%*s %s", revision); + sscanf("$Revision: 1.1.1.2 $", "%*s %s", revision); DESCRIBE_COMPILER(compiler); memset(&scfg,0,sizeof(scfg)); scfg.size=sizeof(scfg); if(!winsock_startup()) - bail(2); + return(do_bail(2)); + + getcwd(orig_cwd, sizeof(orig_cwd)); + backslash(orig_cwd); for(argn=1;argn