|
|
1.1 root 1: /* jsexec.c */
2:
3: /* Execute a Synchronet JavaScript module from the command-line */
4:
1.1.1.2 ! root 5: /* $Id: jsexec.c,v 1.141 2011/08/30 23:37:28 rswindell Exp $ */
1.1 root 6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
1.1.1.2 ! root 11: * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html *
1.1 root 12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #ifndef JAVASCRIPT
39: #define JAVASCRIPT
40: #endif
41:
42: #ifdef __unix__
43: #include <signal.h>
44: #endif
45:
46: #include "sbbs.h"
1.1.1.2 ! root 47: #include "ciolib.h"
! 48: #include "ini_file.h"
! 49: #include "js_rtpool.h"
! 50: #include "js_request.h"
1.1 root 51:
52: #define DEFAULT_LOG_LEVEL LOG_DEBUG /* Display all LOG levels */
53: #define DEFAULT_ERR_LOG_LVL LOG_WARNING
54:
55: JSRuntime* js_runtime;
56: JSContext* js_cx;
57: JSObject* js_glob;
58: js_branch_t branch;
59: scfg_t scfg;
60: ulong js_max_bytes=JAVASCRIPT_MAX_BYTES;
61: ulong js_cx_stack=JAVASCRIPT_CONTEXT_STACK;
62: ulong stack_limit=JAVASCRIPT_THREAD_STACK;
63: FILE* confp;
64: FILE* errfp;
65: FILE* nulfp;
66: FILE* statfp;
67: char revision[16];
68: char compiler[32];
69: char* host_name=NULL;
70: char host_name_buf[128];
1.1.1.2 ! root 71: char* load_path_list=JAVASCRIPT_LOAD_PATH;
1.1 root 72: BOOL pause_on_exit=FALSE;
73: BOOL pause_on_error=FALSE;
74: BOOL terminated=FALSE;
75: BOOL recycled;
76: int log_level=DEFAULT_LOG_LEVEL;
77: int err_level=DEFAULT_ERR_LOG_LVL;
78: pthread_mutex_t output_mutex;
79: #if defined(__unix__)
80: BOOL daemonize=FALSE;
81: #endif
1.1.1.2 ! root 82: char orig_cwd[MAX_PATH+1];
1.1 root 83:
84: void banner(FILE* fp)
85: {
86: fprintf(fp,"\nJSexec v%s%c-%s (rev %s)%s - "
87: "Execute Synchronet JavaScript Module\n"
88: ,VERSION,REVISION
89: ,PLATFORM_DESC
90: ,revision
91: #ifdef _DEBUG
92: ," Debug"
93: #else
94: ,""
95: #endif
96: );
97:
98: fprintf(fp, "Compiled %s %s with %s\n"
99: ,__DATE__, __TIME__, compiler);
100: }
101:
102: void usage(FILE* fp)
103: {
104: banner(fp);
105:
106: fprintf(fp,"\nusage: jsexec [-opts] [path]module[.js] [args]\n"
107: "\navailable opts:\n\n"
108: "\t-c<ctrl_dir> specify path to Synchronet CTRL directory\n"
109: #if defined(__unix__)
110: "\t-d run in background (daemonize)\n"
111: #endif
112: "\t-m<bytes> set maximum heap size (default=%u bytes)\n"
113: "\t-s<bytes> set context stack size (default=%u bytes)\n"
114: "\t-S<bytes> set thread stack limit (default=%u, 0=unlimited)\n"
115: "\t-b<limit> set branch limit (default=%u, 0=unlimited)\n"
116: "\t-y<interval> set yield interval (default=%u, 0=never)\n"
117: "\t-g<interval> set garbage collection interval (default=%u, 0=never)\n"
118: "\t-h[hostname] use local or specified host name (instead of SCFG value)\n"
1.1.1.2 ! root 119: "\t-u<mask> set file creation permissions mask (in octal)\n"
1.1 root 120: "\t-L<level> set log level (default=%u)\n"
1.1.1.2 ! root 121: "\t-E<level> set error log level threshold (default=%u)\n"
! 122: "\t-i<path_list> set load() comma-sep search path list (default=\"%s\")\n"
1.1 root 123: "\t-f use non-buffered stream for console messages\n"
124: "\t-a append instead of overwriting message output files\n"
125: "\t-e<filename> send error messages to file in addition to stderr\n"
126: "\t-o<filename> send console messages to file instead of stdout\n"
127: "\t-n send status messages to %s instead of stderr\n"
128: "\t-q send console messages to %s instead of stdout\n"
129: "\t-v display version details and exit\n"
130: "\t-x disable auto-termination on local abort signal\n"
131: "\t-l loop until intentionally terminated\n"
132: "\t-p wait for keypress (pause) on exit\n"
133: "\t-! wait for keypress (pause) on error\n"
134: ,JAVASCRIPT_MAX_BYTES
135: ,JAVASCRIPT_CONTEXT_STACK
136: ,JAVASCRIPT_THREAD_STACK
137: ,JAVASCRIPT_BRANCH_LIMIT
138: ,JAVASCRIPT_YIELD_INTERVAL
139: ,JAVASCRIPT_GC_INTERVAL
140: ,DEFAULT_LOG_LEVEL
141: ,DEFAULT_ERR_LOG_LVL
1.1.1.2 ! root 142: ,load_path_list
1.1 root 143: ,_PATH_DEVNULL
144: ,_PATH_DEVNULL
145: );
146: }
147:
148: int mfprintf(FILE* fp, char *fmt, ...)
149: {
150: va_list argptr;
151: char sbuf[1024];
152: int ret=0;
153:
154: va_start(argptr,fmt);
155: ret=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
156: sbuf[sizeof(sbuf)-1]=0;
157: va_end(argptr);
158:
159: /* Mutex-protect stdout/stderr */
160: pthread_mutex_lock(&output_mutex);
161:
162: ret = fprintf(fp, "%s\n", sbuf);
163:
164: pthread_mutex_unlock(&output_mutex);
165: return(ret);
166: }
167:
168: /* Log printf */
1.1.1.2 ! root 169: int lprintf(int level, const char *fmt, ...)
1.1 root 170: {
171: va_list argptr;
172: char sbuf[1024];
173: int ret=0;
174:
175: if(level > log_level)
176: return(0);
177:
178: va_start(argptr,fmt);
179: ret=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
180: sbuf[sizeof(sbuf)-1]=0;
181: va_end(argptr);
182: #if defined(__unix__)
183: if(daemonize) {
184: syslog(level,"%s",sbuf);
185: return(ret);
186: }
187: #endif
188:
189: /* Mutex-protect stdout/stderr */
190: pthread_mutex_lock(&output_mutex);
191:
192: if(level<=err_level) {
193: ret=fprintf(errfp,"%s\n",sbuf);
194: if(errfp!=stderr && confp!=stdout)
195: ret=fprintf(statfp,"%s\n",sbuf);
196: }
197: if(level>err_level || errfp!=stderr)
198: ret=fprintf(confp,"%s\n",sbuf);
199:
200: pthread_mutex_unlock(&output_mutex);
201: return(ret);
202: }
203:
204: #if defined(__unix__) && defined(NEEDS_DAEMON)
205: /****************************************************************************/
206: /* Daemonizes the process */
207: /****************************************************************************/
208: int
209: daemon(int nochdir, int noclose)
210: {
211: int fd;
212:
213: switch (fork()) {
214: case -1:
215: return (-1);
216: case 0:
217: break;
218: default:
219: _exit(0);
220: }
221:
222: if (setsid() == -1)
223: return (-1);
224:
225: if (!nochdir)
226: (void)chdir("/");
227:
228: if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
229: (void)dup2(fd, STDIN_FILENO);
230: (void)dup2(fd, STDOUT_FILENO);
231: (void)dup2(fd, STDERR_FILENO);
232: if (fd > 2)
233: (void)close(fd);
234: }
235: return (0);
236: }
237: #endif
238:
239: #if defined(_WINSOCKAPI_)
240:
241: WSADATA WSAData;
242: #define SOCKLIB_DESC WSAData.szDescription
243: static BOOL WSAInitialized=FALSE;
244:
245: static BOOL winsock_startup(void)
246: {
247: int status; /* Status Code */
248:
249: if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
250: /* fprintf(statfp,"%s %s\n",WSAData.szDescription, WSAData.szSystemStatus); */
251: WSAInitialized=TRUE;
252: return(TRUE);
253: }
254:
1.1.1.2 ! root 255: lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
1.1 root 256: return(FALSE);
257: }
258:
259: #else /* No WINSOCK */
260:
261: #define winsock_startup() (TRUE)
262: #define SOCKLIB_DESC NULL
263:
264: #endif
265:
1.1.1.2 ! root 266: static int do_bail(int code)
1.1 root 267: {
268: #if defined(_WINSOCKAPI_)
269: if(WSAInitialized && WSACleanup()!=0)
270: lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
271: #endif
272:
273: if(pause_on_exit || (code && pause_on_error)) {
274: fprintf(statfp,"\nHit enter to continue...");
275: getchar();
276: }
277:
278: if(code)
279: fprintf(statfp,"\nReturning error code: %d\n",code);
1.1.1.2 ! root 280: return(code);
! 281: }
! 282:
! 283: void bail(int code)
! 284: {
! 285: exit(do_bail(code));
1.1 root 286: }
287:
288: static JSBool
289: js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
290: {
291: uintN i=0;
292: int32 level=LOG_INFO;
293: JSString* str=NULL;
1.1.1.2 ! root 294: jsrefcount rc;
1.1 root 295:
296: if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
297: JS_ValueToInt32(cx,argv[i++],&level);
298:
299: for(; i<argc; i++) {
300: if((str=JS_ValueToString(cx, argv[i]))==NULL)
301: return(JS_FALSE);
1.1.1.2 ! root 302: rc=JS_SUSPENDREQUEST(cx);
1.1 root 303: lprintf(level,"%s",JS_GetStringBytes(str));
1.1.1.2 ! root 304: JS_RESUMEREQUEST(cx, rc);
1.1 root 305: }
306:
307: if(str==NULL)
308: *rval = JSVAL_VOID;
309: else
310: *rval = STRING_TO_JSVAL(str);
311:
312: return(JS_TRUE);
313: }
314:
315: static JSBool
316: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
317: {
318: char* buf;
319: int rd;
320: int32 len=128;
1.1.1.2 ! root 321: jsrefcount rc;
1.1 root 322:
323: if(argc)
324: JS_ValueToInt32(cx,argv[0],&len);
325: if((buf=alloca(len))==NULL)
326: return(JS_TRUE);
327:
1.1.1.2 ! root 328: rc=JS_SUSPENDREQUEST(cx);
1.1 root 329: rd=fread(buf,sizeof(char),len,stdin);
1.1.1.2 ! root 330: JS_RESUMEREQUEST(cx, rc);
1.1 root 331:
332: if(rd>=0)
333: *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,rd));
334:
335: return(JS_TRUE);
336: }
337:
338: static JSBool
339: js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
340: {
341: char* buf;
342: char* p;
343: int32 len=128;
1.1.1.2 ! root 344: jsrefcount rc;
1.1 root 345:
346: if(argc)
347: JS_ValueToInt32(cx,argv[0],&len);
348: if((buf=alloca(len+1))==NULL)
349: return(JS_TRUE);
350:
1.1.1.2 ! root 351: rc=JS_SUSPENDREQUEST(cx);
1.1 root 352: p=fgets(buf,len+1,stdin);
1.1.1.2 ! root 353: JS_RESUMEREQUEST(cx, rc);
1.1 root 354:
355: if(p!=NULL)
356: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,truncnl(p)));
357:
358: return(JS_TRUE);
359: }
360:
361:
362: static JSBool
363: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
364: {
365: uintN i;
366: JSString* str=NULL;
1.1.1.2 ! root 367: jsrefcount rc;
1.1 root 368:
369: for (i = 0; i < argc; i++) {
370: if((str=JS_ValueToString(cx, argv[i]))==NULL)
371: return(JS_FALSE);
1.1.1.2 ! root 372: rc=JS_SUSPENDREQUEST(cx);
1.1 root 373: fprintf(confp,"%s",JS_GetStringBytes(str));
1.1.1.2 ! root 374: JS_RESUMEREQUEST(cx, rc);
1.1 root 375: }
376:
377: if(str==NULL)
378: *rval = JSVAL_VOID;
379: else
380: *rval = STRING_TO_JSVAL(str);
381: return(JS_TRUE);
382: }
383:
384: static JSBool
385: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
386: {
1.1.1.2 ! root 387: jsrefcount rc;
! 388:
1.1 root 389: if(!js_write(cx,obj,argc,argv,rval))
390: return(JS_FALSE);
391:
1.1.1.2 ! root 392: rc=JS_SUSPENDREQUEST(cx);
1.1 root 393: fprintf(confp,"\n");
1.1.1.2 ! root 394: JS_RESUMEREQUEST(cx, rc);
1.1 root 395: return(JS_TRUE);
396: }
397:
398: static JSBool
399: js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
400: {
401: char* p;
1.1.1.2 ! root 402: jsrefcount rc;
1.1 root 403:
404: if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
405: JS_ReportError(cx,"js_sprintf failed");
406: return(JS_FALSE);
407: }
408:
1.1.1.2 ! root 409: rc=JS_SUSPENDREQUEST(cx);
1.1 root 410: fprintf(confp,"%s",p);
1.1.1.2 ! root 411: JS_RESUMEREQUEST(cx, rc);
1.1 root 412:
413: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p));
414:
415: js_sprintf_free(p);
416:
417: return(JS_TRUE);
418: }
419:
420: static JSBool
421: js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
422: {
423: JSString * str;
1.1.1.2 ! root 424: jsrefcount rc;
1.1 root 425:
426: if((str=JS_ValueToString(cx, argv[0]))==NULL)
427: return(JS_FALSE);
428:
1.1.1.2 ! root 429: rc=JS_SUSPENDREQUEST(cx);
1.1 root 430: fprintf(confp,"!%s\n",JS_GetStringBytes(str));
1.1.1.2 ! root 431: JS_RESUMEREQUEST(cx, rc);
1.1 root 432:
433: *rval = argv[0];
434:
435: return(JS_TRUE);
436: }
437:
438: static JSBool
439: js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
440: {
441: JSString * str;
1.1.1.2 ! root 442: char * cstr;
! 443: char * p;
! 444: jsrefcount rc;
! 445: char instr[81]="y";
1.1 root 446:
447: if((str=JS_ValueToString(cx, argv[0]))==NULL)
448: return(JS_FALSE);
449:
1.1.1.2 ! root 450: cstr = JS_GetStringBytes(str);
! 451: printf("%s (Y/n)? ", cstr);
! 452: rc=JS_SUSPENDREQUEST(cx);
! 453: fgets(instr,sizeof(instr),stdin);
! 454: JS_RESUMEREQUEST(cx, rc);
! 455:
! 456: p=instr;
! 457: SKIP_WHITESPACE(p);
! 458: *rval = BOOLEAN_TO_JSVAL(tolower(*p)!='n');
! 459: return(JS_TRUE);
! 460: }
! 461:
! 462: static JSBool
! 463: js_deny(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
! 464: {
! 465: JSString * str;
! 466: char * cstr;
! 467: char * p;
! 468: jsrefcount rc;
! 469: char instr[81];
! 470:
! 471: if((str=JS_ValueToString(cx, argv[0]))==NULL)
! 472: return(JS_FALSE);
1.1 root 473:
1.1.1.2 ! root 474: cstr = JS_GetStringBytes(str);
! 475: printf("%s (N/y)? ", cstr);
! 476: rc=JS_SUSPENDREQUEST(cx);
! 477: fgets(instr,sizeof(instr),stdin);
! 478: JS_RESUMEREQUEST(cx, rc);
! 479:
! 480: p=instr;
! 481: SKIP_WHITESPACE(p);
! 482: *rval = BOOLEAN_TO_JSVAL(tolower(*p)!='y');
1.1 root 483: return(JS_TRUE);
484: }
485:
486: static JSBool
487: js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
488: {
489: char instr[81];
490: JSString * prompt;
491: JSString * str;
1.1.1.2 ! root 492: jsrefcount rc;
1.1 root 493:
494: if(!JSVAL_IS_VOID(argv[0])) {
495: if((prompt=JS_ValueToString(cx, argv[0]))==NULL)
496: return(JS_FALSE);
1.1.1.2 ! root 497: rc=JS_SUSPENDREQUEST(cx);
1.1 root 498: fprintf(confp,"%s: ",JS_GetStringBytes(prompt));
1.1.1.2 ! root 499: JS_RESUMEREQUEST(cx, rc);
1.1 root 500: }
501:
502: if(argc>1) {
503: if((str=JS_ValueToString(cx, argv[1]))==NULL)
504: return(JS_FALSE);
505: SAFECOPY(instr,JS_GetStringBytes(str));
506: } else
507: instr[0]=0;
508:
1.1.1.2 ! root 509: rc=JS_SUSPENDREQUEST(cx);
! 510: if(!fgets(instr,sizeof(instr),stdin)) {
! 511: JS_RESUMEREQUEST(cx, rc);
1.1 root 512: return(JS_TRUE);
1.1.1.2 ! root 513: }
! 514: JS_RESUMEREQUEST(cx, rc);
1.1 root 515:
516: if((str=JS_NewStringCopyZ(cx, truncnl(instr)))==NULL)
517: return(JS_FALSE);
518:
519: *rval = STRING_TO_JSVAL(str);
520: return(JS_TRUE);
521: }
522:
523: static JSBool
524: js_chdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
525: {
526: char* p;
1.1.1.2 ! root 527: jsrefcount rc;
1.1 root 528:
529: if((p=JS_GetStringBytes(JS_ValueToString(cx, argv[0])))==NULL) {
530: *rval = INT_TO_JSVAL(-1);
531: return(JS_TRUE);
532: }
533:
1.1.1.2 ! root 534: rc=JS_SUSPENDREQUEST(cx);
1.1 root 535: *rval = BOOLEAN_TO_JSVAL(chdir(p)==0);
1.1.1.2 ! root 536: JS_RESUMEREQUEST(cx, rc);
1.1 root 537: return(JS_TRUE);
538: }
539:
540: static JSBool
541: js_putenv(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
542: {
543: char* p;
544:
545: if((p=JS_GetStringBytes(JS_ValueToString(cx, argv[0])))==NULL) {
546: *rval = INT_TO_JSVAL(-1);
547: return(JS_TRUE);
548: }
549:
550: *rval = BOOLEAN_TO_JSVAL(putenv(p)==0);
551: return(JS_TRUE);
552: }
553:
554: static jsSyncMethodSpec js_global_functions[] = {
555: {"log", js_log, 1},
556: {"read", js_read, 1},
557: {"readln", js_readln, 1},
558: {"write", js_write, 0},
559: {"writeln", js_writeln, 0},
560: {"print", js_writeln, 0},
561: {"printf", js_printf, 1},
562: {"alert", js_alert, 1},
563: {"prompt", js_prompt, 1},
564: {"confirm", js_confirm, 1},
1.1.1.2 ! root 565: {"deny", js_deny, 1},
1.1 root 566: {"chdir", js_chdir, 1},
567: {"putenv", js_putenv, 1},
568: {0}
569: };
570:
571: static void
572: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
573: {
574: char line[64];
575: char file[MAX_PATH+1];
576: const char* warning;
1.1.1.2 ! root 577: jsrefcount rc;
! 578: int log_level;
1.1 root 579:
1.1.1.2 ! root 580: rc=JS_SUSPENDREQUEST(cx);
1.1 root 581: if(report==NULL) {
582: lprintf(LOG_ERR,"!JavaScript: %s", message);
1.1.1.2 ! root 583: JS_RESUMEREQUEST(cx, rc);
1.1 root 584: return;
585: }
586:
587: if(report->filename)
588: sprintf(file," %s",report->filename);
589: else
590: file[0]=0;
591:
592: if(report->lineno)
593: sprintf(line," line %d",report->lineno);
594: else
595: line[0]=0;
596:
597: if(JSREPORT_IS_WARNING(report->flags)) {
598: if(JSREPORT_IS_STRICT(report->flags))
599: warning="strict warning";
600: else
601: warning="warning";
1.1.1.2 ! root 602: log_level=LOG_WARNING;
! 603: } else {
! 604: log_level=LOG_ERR;
1.1 root 605: warning="";
1.1.1.2 ! root 606: }
1.1 root 607:
1.1.1.2 ! root 608: lprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message);
! 609: JS_RESUMEREQUEST(cx, rc);
1.1 root 610: }
611:
612: static JSBool
613: js_BranchCallback(JSContext *cx, JSScript *script)
614: {
615: return(js_CommonBranchCallback(cx,&branch));
616: }
617:
1.1.1.2 ! root 618: #ifdef USE_JS_OPERATION_CALLBACK
! 619: static JSBool
! 620: js_OperationCallback(JSContext *cx)
! 621: {
! 622: JSBool ret;
! 623:
! 624: JS_SetOperationCallback(cx, NULL);
! 625: ret=js_BranchCallback(cx, NULL);
! 626: JS_SetOperationCallback(cx, js_OperationCallback);
! 627: return ret;
! 628: }
! 629: #endif
! 630:
1.1 root 631: static BOOL js_CreateEnvObject(JSContext* cx, JSObject* glob, char** env)
632: {
633: char name[256];
634: char* val;
635: uint i;
636: JSObject* js_env;
637:
638: if((js_env=JS_NewObject(js_cx, NULL, NULL, glob))==NULL)
639: return(FALSE);
640:
641: if(!JS_DefineProperty(cx, glob, "env", OBJECT_TO_JSVAL(js_env)
642: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
643: return(FALSE);
644:
645: for(i=0;env[i]!=NULL;i++) {
646: SAFECOPY(name,env[i]);
647: truncstr(name,"=");
648: val=strchr(env[i],'=');
649: if(val==NULL)
650: val="";
651: else
652: val++;
653: if(!JS_DefineProperty(cx, js_env, name, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,val))
654: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
655: return(FALSE);
656: }
657:
658: return(TRUE);
659: }
660:
661: static BOOL js_init(char** environ)
662: {
1.1.1.2 ! root 663: js_startup_t startup;
! 664:
! 665: memset(&startup,0,sizeof(startup));
! 666: SAFECOPY(startup.load_path, load_path_list);
! 667:
1.1 root 668: fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion());
669:
670: fprintf(statfp,"JavaScript: Creating runtime: %lu bytes\n"
671: ,js_max_bytes);
672:
1.1.1.2 ! root 673: if((js_runtime = jsrt_GetNew(js_max_bytes, 5000, __FILE__, __LINE__))==NULL)
1.1 root 674: return(FALSE);
675:
676: fprintf(statfp,"JavaScript: Initializing context (stack: %lu bytes)\n"
677: ,js_cx_stack);
678:
679: if((js_cx = JS_NewContext(js_runtime, js_cx_stack))==NULL)
680: return(FALSE);
1.1.1.2 ! root 681: JS_BEGINREQUEST(js_cx);
1.1 root 682:
683: if(stack_limit)
684: fprintf(statfp,"JavaScript: Thread stack limit: %lu bytes\n"
685: ,stack_limit);
686:
687: JS_SetErrorReporter(js_cx, js_ErrorReporter);
688:
689: /* Global Object */
690: if((js_glob=js_CreateCommonObjects(js_cx, &scfg, NULL, js_global_functions
691: ,time(NULL), host_name, SOCKLIB_DESC /* system */
1.1.1.2 ! root 692: ,&branch,&startup /* js */
1.1 root 693: ,NULL,INVALID_SOCKET /* client */
694: ,NULL /* server */
1.1.1.2 ! root 695: ))==NULL) {
! 696: JS_ENDREQUEST(js_cx);
1.1 root 697: return(FALSE);
1.1.1.2 ! root 698: }
1.1 root 699:
700: /* Environment Object (associative array) */
1.1.1.2 ! root 701: if(!js_CreateEnvObject(js_cx, js_glob, environ)) {
! 702: JS_ENDREQUEST(js_cx);
1.1 root 703: return(FALSE);
1.1.1.2 ! root 704: }
1.1 root 705:
1.1.1.2 ! root 706: if(js_CreateUifcObject(js_cx, js_glob)==NULL) {
! 707: JS_ENDREQUEST(js_cx);
1.1 root 708: return(FALSE);
1.1.1.2 ! root 709: }
! 710:
! 711: if(js_CreateConioObject(js_cx, js_glob)==NULL) {
! 712: JS_ENDREQUEST(js_cx);
! 713: return(FALSE);
! 714: }
1.1 root 715:
716: return(TRUE);
717: }
718:
719: static const char* js_ext(const char* fname)
720: {
721: if(strchr(fname,'.')==NULL)
722: return(".js");
723: return("");
724: }
725:
726: long js_exec(const char *fname, char** args)
727: {
728: ulong stack_frame;
729: int argc=0;
730: uint line_no;
731: char path[MAX_PATH+1];
732: char line[1024];
733: char rev_detail[256];
734: size_t len;
735: char* js_buf=NULL;
736: size_t js_buflen;
737: JSScript* js_script=NULL;
738: JSString* arg;
739: JSObject* argv;
740: FILE* fp=stdin;
741: jsval val;
742: jsval rval=JSVAL_VOID;
743: int32 result=0;
744: long double start;
745: long double diff;
1.1.1.2 ! root 746:
1.1 root 747: if(fname!=NULL) {
1.1.1.2 ! root 748: if(isfullpath(fname)) {
1.1 root 749: SAFECOPY(path,fname);
1.1.1.2 ! root 750: }
! 751: else {
! 752: SAFEPRINTF3(path,"%s%s%s",orig_cwd,fname,js_ext(fname));
! 753: if(!fexistcase(path)) {
! 754: SAFEPRINTF3(path,"%s%s%s",scfg.mods_dir,fname,js_ext(fname));
! 755: if(scfg.mods_dir[0]==0 || !fexistcase(path))
! 756: SAFEPRINTF3(path,"%s%s%s",scfg.exec_dir,fname,js_ext(fname));
! 757: }
! 758: }
1.1 root 759:
760: if(!fexistcase(path)) {
761: lprintf(LOG_ERR,"!Module file (%s) doesn't exist",path);
762: return(-1);
763: }
764:
765: if((fp=fopen(path,"r"))==NULL) {
766: lprintf(LOG_ERR,"!Error %d (%s) opening %s",errno,STRERROR(errno),path);
767: return(-1);
768: }
769: }
770: JS_ClearPendingException(js_cx);
771:
772: if(stack_limit) {
773: #if JS_STACK_GROWTH_DIRECTION > 0
774: stack_frame=((ulong)&stack_frame)+stack_limit;
775: #else
776: stack_frame=((ulong)&stack_frame)-stack_limit;
777: #endif
778: JS_SetThreadStackLimit(js_cx, stack_frame);
779: }
780:
781: argv=JS_NewArrayObject(js_cx, 0, NULL);
782: JS_DefineProperty(js_cx, js_glob, "argv", OBJECT_TO_JSVAL(argv)
783: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
784:
785: for(argc=0;args[argc];argc++) {
786: arg = JS_NewStringCopyZ(js_cx, args[argc]);
787: if(arg==NULL)
788: break;
789: val=STRING_TO_JSVAL(arg);
790: if(!JS_SetElement(js_cx, argv, argc, &val))
791: break;
792: }
793: JS_DefineProperty(js_cx, js_glob, "argc", INT_TO_JSVAL(argc)
794: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
795:
796: JS_DefineProperty(js_cx, js_glob, "jsexec_revision"
797: ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,revision))
798: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
799:
800: sprintf(rev_detail,"JSexec %s%s "
801: "Compiled %s %s with %s"
802: ,revision
803: #ifdef _DEBUG
804: ," Debug"
805: #else
806: ,""
807: #endif
808: ,__DATE__, __TIME__, compiler
809: );
810:
811: JS_DefineProperty(js_cx, js_glob, "jsexec_revision_detail"
812: ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,rev_detail))
813: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
814:
815: branch.terminated=&terminated;
816:
1.1.1.2 ! root 817: #ifdef USE_JS_OPERATION_CALLBACK
! 818: JS_SetOperationCallback(js_cx, js_OperationCallback);
! 819: #else
1.1 root 820: JS_SetBranchCallback(js_cx, js_BranchCallback);
1.1.1.2 ! root 821: #endif
1.1 root 822:
823: if(fp==stdin) /* Using stdin for script source */
824: SAFECOPY(path,"stdin");
825:
826: fprintf(statfp,"Reading script from %s\n",path);
827: line_no=0;
828: js_buflen=0;
829: while(!feof(fp)) {
830: if(!fgets(line,sizeof(line),fp))
831: break;
832: line_no++;
833: #if defined(__unix__) /* Support Unix Shell Scripts that start with #!/path/to/jsexec */
834: if(line_no==1 && strncmp(line,"#!",2)==0)
835: strcpy(line,"\n"); /* To keep line count correct */
836: #endif
837: len=strlen(line);
838: if((js_buf=realloc(js_buf,js_buflen+len))==NULL) {
839: lprintf(LOG_ERR,"!Error allocating %u bytes of memory"
840: ,js_buflen+len);
841: return(-1);
842: }
843: memcpy(js_buf+js_buflen,line,len);
844: js_buflen+=len;
845: }
846: if(fp!=NULL && fp!=stdin)
847: fclose(fp);
848:
849: start=xp_timer();
850: if((js_script=JS_CompileScript(js_cx, js_glob, js_buf, js_buflen, fname==NULL ? NULL : path, 1))==NULL) {
851: lprintf(LOG_ERR,"!Error compiling script from %s",path);
852: return(-1);
853: }
854: if((diff=xp_timer()-start) > 0)
855: mfprintf(statfp,"%s compiled in %.2Lf seconds"
856: ,path
857: ,diff);
858:
1.1.1.2 ! root 859: js_PrepareToExecute(js_cx, js_glob, fname==NULL ? NULL : path, orig_cwd);
1.1 root 860: start=xp_timer();
861: JS_ExecuteScript(js_cx, js_glob, js_script, &rval);
1.1.1.2 ! root 862: JS_GetProperty(js_cx, js_glob, "exit_code", &rval);
! 863: if(rval!=JSVAL_VOID && JSVAL_IS_NUMBER(rval)) {
! 864: mfprintf(statfp,"Using JavaScript exit_code: %s",JS_GetStringBytes(JS_ValueToString(js_cx,rval)));
! 865: JS_ValueToInt32(js_cx,rval,&result);
! 866: }
1.1 root 867: js_EvalOnExit(js_cx, js_glob, &branch);
868:
1.1.1.2 ! root 869: JS_ReportPendingException(js_cx);
! 870:
1.1 root 871: if((diff=xp_timer()-start) > 0)
872: mfprintf(statfp,"%s executed in %.2Lf seconds"
873: ,path
874: ,diff);
875:
876: JS_DestroyScript(js_cx, js_script);
877:
878: JS_GC(js_cx);
879:
880: if(js_buf!=NULL)
881: free(js_buf);
882:
883: return(result);
884: }
885:
886: void break_handler(int type)
887: {
888: lprintf(LOG_NOTICE,"\n-> Terminated Locally (signal: %d)",type);
889: terminated=TRUE;
890: }
891:
892: void recycle_handler(int type)
893: {
894: lprintf(LOG_NOTICE,"\n-> Recycled Locally (signal: %d)",type);
895: recycled=TRUE;
896: branch.terminated=&recycled;
897: }
898:
899:
900: #if defined(_WIN32)
1.1.1.2 ! root 901: BOOL WINAPI ControlHandler(unsigned long CtrlType)
1.1 root 902: {
903: break_handler((int)CtrlType);
904: return TRUE;
905: }
906: #endif
907:
1.1.1.2 ! root 908: int parseLogLevel(const char* p)
! 909: {
! 910: str_list_t logLevelStringList=iniLogLevelStringList();
! 911: int i;
! 912:
! 913: if(isdigit(*p))
! 914: return strtol(p,NULL,0);
! 915:
! 916: /* Exact match */
! 917: for(i=0;logLevelStringList[i]!=NULL;i++) {
! 918: if(stricmp(logLevelStringList[i],p)==0)
! 919: return i;
! 920: }
! 921: /* Partial match */
! 922: for(i=0;logLevelStringList[i]!=NULL;i++) {
! 923: if(strnicmp(logLevelStringList[i],p,strlen(p))==0)
! 924: return i;
! 925: }
! 926: return DEFAULT_LOG_LEVEL;
! 927: }
! 928:
! 929:
1.1 root 930: /*********************/
931: /* Entry point (duh) */
932: /*********************/
933: int main(int argc, char **argv, char** environ)
934: {
935: char error[512];
936: char* module=NULL;
937: char* p;
938: char* omode="w";
939: int argn;
940: long result;
941: ulong exec_count=0;
942: BOOL loop=FALSE;
943: BOOL nonbuffered_con=FALSE;
944:
945: confp=stdout;
946: errfp=stderr;
947: if((nulfp=fopen(_PATH_DEVNULL,"w+"))==NULL) {
948: perror(_PATH_DEVNULL);
1.1.1.2 ! root 949: return(do_bail(-1));
1.1 root 950: }
951: if(isatty(fileno(stderr)))
952: statfp=stderr;
953: else /* if redirected, don't send status messages to stderr */
954: statfp=nulfp;
955:
956: branch.limit=JAVASCRIPT_BRANCH_LIMIT;
957: branch.yield_interval=JAVASCRIPT_YIELD_INTERVAL;
958: branch.gc_interval=JAVASCRIPT_GC_INTERVAL;
959: branch.auto_terminate=TRUE;
960:
1.1.1.2 ! root 961: sscanf("$Revision: 1.141 $", "%*s %s", revision);
1.1 root 962: DESCRIBE_COMPILER(compiler);
963:
964: memset(&scfg,0,sizeof(scfg));
965: scfg.size=sizeof(scfg);
966:
967: if(!winsock_startup())
1.1.1.2 ! root 968: return(do_bail(2));
! 969:
! 970: getcwd(orig_cwd, sizeof(orig_cwd));
! 971: backslash(orig_cwd);
1.1 root 972:
973: for(argn=1;argn<argc && module==NULL;argn++) {
974: if(argv[argn][0]=='-') {
975: p=argv[argn]+2;
976: switch(argv[argn][1]) {
977: case 'a':
978: omode="a";
979: break;
980: case 'f':
981: nonbuffered_con=TRUE;
982: break;
983: case 'm':
984: if(*p==0) p=argv[++argn];
985: js_max_bytes=strtoul(p,NULL,0);
986: break;
987: case 's':
988: if(*p==0) p=argv[++argn];
989: js_cx_stack=strtoul(p,NULL,0);
990: break;
991: case 'S':
992: if(*p==0) p=argv[++argn];
993: stack_limit=strtoul(p,NULL,0);
994: break;
995: case 'b':
996: if(*p==0) p=argv[++argn];
997: branch.limit=strtoul(p,NULL,0);
998: break;
999: case 'y':
1000: if(*p==0) p=argv[++argn];
1001: branch.yield_interval=strtoul(p,NULL,0);
1002: break;
1003: case 'g':
1004: if(*p==0) p=argv[++argn];
1005: branch.gc_interval=strtoul(p,NULL,0);
1006: break;
1007: case 'h':
1008: if(*p==0)
1009: gethostname(host_name=host_name_buf,sizeof(host_name_buf));
1010: else
1011: host_name=p;
1012: break;
1.1.1.2 ! root 1013: case 'u':
! 1014: if(*p==0) p=argv[++argn];
! 1015: umask(strtol(p,NULL,8));
! 1016: break;
1.1 root 1017: case 'L':
1018: if(*p==0) p=argv[++argn];
1.1.1.2 ! root 1019: log_level=parseLogLevel(p);
1.1 root 1020: break;
1021: case 'E':
1022: if(*p==0) p=argv[++argn];
1.1.1.2 ! root 1023: err_level=parseLogLevel(p);
1.1 root 1024: break;
1025: case 'e':
1026: if(*p==0) p=argv[++argn];
1027: if((errfp=fopen(p,omode))==NULL) {
1028: perror(p);
1.1.1.2 ! root 1029: return(do_bail(1));
1.1 root 1030: }
1031: break;
1032: case 'o':
1033: if(*p==0) p=argv[++argn];
1034: if((confp=fopen(p,omode))==NULL) {
1035: perror(p);
1.1.1.2 ! root 1036: return(do_bail(1));
1.1 root 1037: }
1038: break;
1039: case 'q':
1040: confp=nulfp;
1041: break;
1042: case 'n':
1043: statfp=nulfp;
1044: break;
1045: case 'x':
1046: branch.auto_terminate=FALSE;
1047: break;
1048: case 'l':
1049: loop=TRUE;
1050: break;
1051: case 'p':
1052: pause_on_exit=TRUE;
1053: break;
1054: case '!':
1055: pause_on_error=TRUE;
1056: break;
1057: case 'c':
1058: if(*p==0) p=argv[++argn];
1059: SAFECOPY(scfg.ctrl_dir,p);
1060: break;
1.1.1.2 ! root 1061: case 'i':
! 1062: if(*p==0) p=argv[++argn];
! 1063: load_path_list=p;
! 1064: break;
1.1 root 1065: case 'v':
1066: banner(statfp);
1067: fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion());
1.1.1.2 ! root 1068: return(do_bail(0));
1.1 root 1069: #if defined(__unix__)
1070: case 'd':
1071: daemonize=TRUE;
1072: break;
1073: #endif
1074: default:
1075: fprintf(errfp,"\n!Unsupported option: %s\n",argv[argn]);
1076: case '?':
1077: usage(errfp);
1.1.1.2 ! root 1078: return(do_bail(1));
1.1 root 1079: }
1080: continue;
1081: }
1082: module=argv[argn];
1083: }
1084:
1085: if(scfg.ctrl_dir[0]==0) {
1086: if((p=getenv("SBBSCTRL"))==NULL) {
1087: fprintf(errfp,"\nSBBSCTRL environment variable not set and -c option not specified.\n");
1088: fprintf(errfp,"\nExample: SET SBBSCTRL=/sbbs/ctrl\n");
1089: fprintf(errfp,"\n or: %s -c /sbbs/ctrl [module]\n",argv[0]);
1.1.1.2 ! root 1090: return(do_bail(1));
1.1 root 1091: }
1092: SAFECOPY(scfg.ctrl_dir,p);
1093: }
1094:
1095: if(module==NULL && isatty(fileno(stdin))) {
1096: fprintf(errfp,"\n!Module name not specified\n");
1097: usage(errfp);
1.1.1.2 ! root 1098: return(do_bail(1));
1.1 root 1099: }
1100:
1101: banner(statfp);
1102:
1103: if(chdir(scfg.ctrl_dir)!=0)
1104: fprintf(errfp,"!ERROR changing directory to: %s\n", scfg.ctrl_dir);
1105:
1106: fprintf(statfp,"\nLoading configuration files from %s\n",scfg.ctrl_dir);
1107: if(!load_cfg(&scfg,NULL,TRUE,error)) {
1108: fprintf(errfp,"!ERROR loading configuration files: %s\n",error);
1.1.1.2 ! root 1109: return(do_bail(1));
1.1 root 1110: }
1111: SAFECOPY(scfg.temp_dir,"../temp");
1112: prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
1113:
1114: if(host_name==NULL)
1115: host_name=scfg.sys_inetaddr;
1116:
1117: #if defined(__unix__)
1118: if(daemonize) {
1119: fprintf(statfp,"\nRunning as daemon\n");
1120: if(daemon(TRUE,FALSE)) { /* Daemonize, DON'T switch to / and DO close descriptors */
1121: fprintf(statfp,"!ERROR %d running as daemon\n",errno);
1122: daemonize=FALSE;
1123: }
1124: }
1125: #endif
1126:
1127: /* Don't cache error log */
1128: setvbuf(errfp,NULL,_IONBF,0);
1129:
1130: if(nonbuffered_con)
1131: setvbuf(confp,NULL,_IONBF,0);
1132:
1133: /* Seed random number generator */
1134: sbbs_srand();
1135:
1136: /* Install Ctrl-C/Break signal handler here */
1137: #if defined(_WIN32)
1138: SetConsoleCtrlHandler(ControlHandler, TRUE /* Add */);
1139: #elif defined(__unix__)
1140: signal(SIGQUIT,break_handler);
1141: signal(SIGINT,break_handler);
1142: signal(SIGTERM,break_handler);
1143:
1144: signal(SIGHUP,recycle_handler);
1145:
1146: /* Don't die on SIGPIPE */
1147: signal(SIGPIPE,SIG_IGN);
1148: #endif
1149:
1150: pthread_mutex_init(&output_mutex,NULL);
1151:
1152: do {
1153:
1154: if(exec_count++)
1155: lprintf(LOG_INFO,"\nRe-running: %s", module);
1156:
1157: recycled=FALSE;
1158:
1159: if(!js_init(environ)) {
1160: lprintf(LOG_ERR,"!JavaScript initialization failure");
1.1.1.2 ! root 1161: return(do_bail(1));
1.1 root 1162: }
1163: fprintf(statfp,"\n");
1164:
1165: result=js_exec(module,&argv[argn]);
1.1.1.2 ! root 1166: JS_ENDREQUEST(js_cx);
! 1167: YIELD();
1.1 root 1168:
1169: if(result)
1170: lprintf(LOG_ERR,"!Module set exit_code: %ld", result);
1171:
1172: fprintf(statfp,"\n");
1173: fprintf(statfp,"JavaScript: Destroying context\n");
1174: JS_DestroyContext(js_cx);
1175: fprintf(statfp,"JavaScript: Destroying runtime\n");
1.1.1.2 ! root 1176: jsrt_Release(js_runtime);
1.1 root 1177:
1178: } while((recycled || loop) && !terminated);
1179:
1.1.1.2 ! root 1180: return(do_bail(result));
1.1 root 1181: }
1182:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.