Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

Commit 8e2caf81 authored by rswindell's avatar rswindell

Created new global object: branch, with properties that control the

JS_BranchCallback behavior in regards to infinite-loop detection, periodic
garbage collection, and periodic time-slice yields.
Eliminated global reset_loop() method, ill side-effects. Use branch.limit=0
instead to defeat infinite-loop detection mechanism (e.g. for static services).
parent a7cb1572
......@@ -532,25 +532,25 @@ js_BranchCallback(JSContext *cx, JSScript *script)
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
sbbs->js_loop++;
sbbs->js_branch.counter++;
/* Terminated? */
if(sbbs->terminated) {
JS_ReportError(cx,"Terminated");
sbbs->js_loop=0;
sbbs->js_branch.counter=0;
return(JS_FALSE);
}
/* Infinite loop? */
if(sbbs->js_loop>JAVASCRIPT_BRANCH_LIMIT) {
JS_ReportError(cx,"Infinite loop (%lu branches) detected",sbbs->js_loop);
sbbs->js_loop=0;
if(sbbs->js_branch.limit && sbbs->js_branch.counter>sbbs->js_branch.limit) {
JS_ReportError(cx,"Infinite loop (%lu branches) detected",sbbs->js_branch.counter);
sbbs->js_branch.counter=0;
return(JS_FALSE);
}
/* Give up timeslices every once in a while */
if(!(sbbs->js_loop%JAVASCRIPT_YIELD_FREQUENCY))
if(sbbs->js_branch.yield_freq && (sbbs->js_branch.counter%sbbs->js_branch.yield_freq)==0)
YIELD();
if(!(sbbs->js_loop%JAVASCRIPT_GC_FREQUENCY))
if(sbbs->js_branch.gc_freq && (sbbs->js_branch.counter%sbbs->js_branch.gc_freq)==0)
JS_MaybeGC(cx);
return(JS_TRUE);
......@@ -636,7 +636,7 @@ long sbbs_t::js_execfile(const char *cmd)
return(-1);
}
js_loop=0; // Reset loop counter
js_branch.counter=0; // Reset loop counter
JS_SetBranchCallback(js_cx, js_BranchCallback);
......
/* js_branch.c */
/* Synchronet "branch" object, for JavaScript BranchCallback control */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2003 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 *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "sbbs.h"
enum {
BRANCH_PROP_COUNTER
,BRANCH_PROP_LIMIT
,BRANCH_PROP_YIELD_FREQ
,BRANCH_PROP_GC_FREQ
};
static JSBool js_branch_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
jsint tiny;
js_branch_t* branch;
if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
return(JS_FALSE);
tiny = JSVAL_TO_INT(id);
switch(tiny) {
case BRANCH_PROP_COUNTER:
JS_NewNumberValue(cx,branch->counter,vp);
break;
case BRANCH_PROP_LIMIT:
JS_NewNumberValue(cx,branch->limit,vp);
break;
case BRANCH_PROP_GC_FREQ:
JS_NewNumberValue(cx,branch->gc_freq,vp);
break;
case BRANCH_PROP_YIELD_FREQ:
JS_NewNumberValue(cx,branch->yield_freq,vp);
break;
}
return(JS_TRUE);
}
static JSBool js_branch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
jsint tiny;
js_branch_t* branch;
if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
return(JS_FALSE);
tiny = JSVAL_TO_INT(id);
switch(tiny) {
case BRANCH_PROP_COUNTER:
JS_ValueToInt32(cx, *vp, (int32*)&branch->counter);
break;
case BRANCH_PROP_LIMIT:
JS_ValueToInt32(cx, *vp, (int32*)&branch->limit);
break;
case BRANCH_PROP_GC_FREQ:
JS_ValueToInt32(cx, *vp, (int32*)&branch->gc_freq);
break;
case BRANCH_PROP_YIELD_FREQ:
JS_ValueToInt32(cx, *vp, (int32*)&branch->yield_freq);
break;
}
return(TRUE);
}
static struct JSPropertySpec js_branch_properties[] = {
/* name, tinyid, flags, getter, setter */
{ "counter", BRANCH_PROP_COUNTER, JSPROP_ENUMERATE, NULL, NULL },
{ "limit", BRANCH_PROP_LIMIT, JSPROP_ENUMERATE, NULL, NULL },
{ "gc_freq", BRANCH_PROP_GC_FREQ, JSPROP_ENUMERATE, NULL, NULL },
{ "yield_freq", BRANCH_PROP_YIELD_FREQ, JSPROP_ENUMERATE, NULL, NULL },
{0}
};
#ifdef _DEBUG
static char* branch_prop_desc[] = {
"counter incremented for each branch"
,"maximum number of branches, used for infinite-loop detection (0=disabled)"
,"frequency of periodic garbage collection (0=disabled)"
,"frequency of periodic time-slice yields (0=disabled)"
,NULL
};
#endif
static JSClass js_branch_class = {
"Branch" /* name */
,JSCLASS_HAS_PRIVATE /* flags */
,JS_PropertyStub /* addProperty */
,JS_PropertyStub /* delProperty */
,js_branch_get /* getProperty */
,js_branch_set /* setProperty */
,JS_EnumerateStub /* enumerate */
,JS_ResolveStub /* resolve */
,JS_ConvertStub /* convert */
,JS_FinalizeStub /* finalize */
};
JSObject* DLLCALL js_CreateBranchObject(JSContext* cx, JSObject* parent, js_branch_t* branch)
{
JSObject* obj;
if((obj = JS_DefineObject(cx, parent, "branch", &js_branch_class, NULL
,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
return(NULL);
if(!JS_SetPrivate(cx, obj, branch)) /* Store a pointer to js_branch_t */
return(NULL);
if(!JS_DefineProperties(cx, obj, js_branch_properties)) /* expose them */
return(NULL);
#ifdef _DEBUG
js_DescribeObject(cx,obj,"JavaScript execution branch control object");
js_CreateArrayOfStrings(cx, obj, "_property_desc_list", branch_prop_desc, JSPROP_READONLY);
#endif
return(obj);
}
......@@ -44,13 +44,10 @@
JSRuntime* js_runtime;
JSContext* js_cx;
JSObject* js_glob;
ulong js_loop=0;
js_branch_t branch;
scfg_t scfg;
ulong js_max_bytes=JAVASCRIPT_MAX_BYTES;
ulong js_context_stack=JAVASCRIPT_CONTEXT_STACK;
ulong js_branch_limit=JAVASCRIPT_BRANCH_LIMIT;
ulong js_yield_frequency=JAVASCRIPT_YIELD_FREQUENCY;
ulong js_gc_frequency=JAVASCRIPT_GC_FREQUENCY;
FILE* confp;
FILE* errfp;
char revision[16];
......@@ -241,14 +238,6 @@ js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return(JS_TRUE);
}
static JSBool
js_reset_loop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
js_loop=0;
*rval = JSVAL_VOID;
return(JS_TRUE);
}
static jsMethodSpec js_global_functions[] = {
{"log", js_log, 1},
{"write", js_write, 0},
......@@ -258,7 +247,6 @@ static jsMethodSpec js_global_functions[] = {
{"alert", js_alert, 1},
{"prompt", js_prompt, 1},
{"confirm", js_confirm, 1},
{"reset_loop", js_reset_loop, 0},
{0}
};
......@@ -298,27 +286,19 @@ js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
static JSBool
js_BranchCallback(JSContext *cx, JSScript *script)
{
js_loop++;
branch.counter++;
#if 0
/* Terminated? */
if(sbbs->terminated) {
JS_ReportError(cx,"Terminated");
sbbs->js_loop=0;
return(JS_FALSE);
}
#endif
/* Infinite loop? */
if(js_branch_limit && js_loop>js_branch_limit) {
JS_ReportError(cx,"Infinite loop (%lu branches) detected",js_loop);
js_loop=0;
if(branch.limit && branch.counter > branch.limit) {
JS_ReportError(cx,"Infinite loop (%lu branches) detected",branch.counter);
branch.counter=0;
return(JS_FALSE);
}
/* Give up timeslices every once in a while */
if(js_yield_frequency && !(js_loop%js_yield_frequency))
if(branch.yield_freq && (branch.counter%branch.yield_freq)==0)
YIELD();
if(js_gc_frequency && !(js_loop%js_gc_frequency))
if(branch.gc_freq && (branch.counter%branch.gc_freq)==0)
JS_MaybeGC(cx);
return(JS_TRUE);
......@@ -374,6 +354,10 @@ static BOOL js_init(char** environ)
if((js_glob=js_CreateGlobalObject(js_cx, &scfg, js_global_functions))==NULL)
return(FALSE);
/* Branch Object */
if(js_CreateBranchObject(js_cx, js_glob, &branch)==NULL)
return(FALSE);
/* System Object */
if(js_CreateSystemObject(js_cx, js_glob, &scfg, time(NULL), scfg.sys_inetaddr)==NULL)
return(FALSE);
......@@ -484,6 +468,10 @@ int main(int argc, char **argv, char** environ)
confp=stdout;
errfp=stderr;
branch.limit=JAVASCRIPT_BRANCH_LIMIT;
branch.yield_freq=JAVASCRIPT_YIELD_FREQUENCY;
branch.gc_freq=JAVASCRIPT_GC_FREQUENCY;
sscanf("$Revision$", "%*s %s", revision);
p=getenv("SBBSCTRL");
......@@ -507,13 +495,13 @@ int main(int argc, char **argv, char** environ)
js_context_stack=strtoul(argv[++argn],NULL,0);
break;
case 'b':
js_branch_limit=strtoul(argv[++argn],NULL,0);
branch.limit=strtoul(argv[++argn],NULL,0);
break;
case 'y':
js_yield_frequency=strtoul(argv[++argn],NULL,0);
branch.yield_freq=strtoul(argv[++argn],NULL,0);
break;
case 'g':
js_gc_frequency=strtoul(argv[++argn],NULL,0);
branch.gc_freq=strtoul(argv[++argn],NULL,0);
break;
case 'e':
errfp=confp;
......
......@@ -604,19 +604,6 @@ js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return(JS_TRUE);
}
static JSBool
js_reset_loop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
sbbs_t* sbbs;
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
sbbs->js_loop=0;
*rval = JSVAL_VOID;
return(JS_TRUE);
}
static jsMethodSpec js_global_functions[] = {
{"log", js_log, 1, JSTYPE_STRING, JSDOCSTR("value [,value]")
,JSDOCSTR("add a line of text to the server and/or system log, "
......@@ -643,9 +630,6 @@ static jsMethodSpec js_global_functions[] = {
,JSDOCSTR("displays a Yes/No prompt and returns <i>true</i> or <i>false</i> "
"based on users confirmation (ala client-side JS)")
},
{"reset_loop", js_reset_loop, 0, JSTYPE_VOID, ""
,JSDOCSTR("reset internal loop counter, defeating infinite loop detection mechanism")
},
{0}
};
......@@ -716,7 +700,10 @@ bool sbbs_t::js_init()
if((js_cx = JS_NewContext(js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL)
return(false);
js_loop = 0; /* loop counter */
js_branch.limit = JAVASCRIPT_BRANCH_LIMIT;
js_branch.gc_freq = JAVASCRIPT_GC_FREQUENCY;
js_branch.yield_freq = JAVASCRIPT_YIELD_FREQUENCY;
js_branch.counter = 0; /* loop counter */
bool success=false;
......@@ -739,6 +726,10 @@ bool sbbs_t::js_init()
if(js_CreateSystemObject(js_cx, js_glob, &cfg, uptime, startup->host_name)==NULL)
break;
/* Branch Object */
if(js_CreateBranchObject(js_cx, js_glob, &js_branch)==NULL)
break;
/* Client Object */
if(js_CreateClientObject(js_cx, js_glob, "client", &client, client_socket)==NULL)
break;
......
......@@ -187,7 +187,7 @@ public:
JSRuntime* js_runtime;
JSContext* js_cx;
JSObject* js_glob;
ulong js_loop;
js_branch_t js_branch;
long js_execfile(const char *fname);
bool js_init(void);
void js_create_user_objects(void);
......@@ -884,6 +884,9 @@ extern "C" {
/* js_global.c */
DLLEXPORT JSObject* DLLCALL js_CreateGlobalObject(JSContext* cx, scfg_t* cfg, jsMethodSpec* methods);
/* js_branch.c */
DLLEXPORT JSObject* DLLCALL js_CreateBranchObject(JSContext* cx, JSObject* parent, js_branch_t* branch);
/* js_system.c */
DLLEXPORT JSObject* DLLCALL js_CreateSystemObject(JSContext* cx, JSObject* parent
,scfg_t* cfg, time_t uptime, char* host_name);
......
......@@ -70,6 +70,13 @@
#define JAVASCRIPT_YIELD_FREQUENCY 10000
#define JAVASCRIPT_GC_FREQUENCY 100
typedef struct {
ulong counter;
ulong limit;
ulong yield_freq;
ulong gc_freq;
} js_branch_t;
#ifndef __FLAT__
#define __FLAT__ /* 32-bit "flat" memory model */
#endif
......
......@@ -104,7 +104,7 @@ typedef struct {
user_t user;
client_t* client;
service_t* service;
ulong js_loop;
js_branch_t branch;
/* Initial UDP datagram */
BYTE* udp_buf;
int udp_len;
......@@ -445,19 +445,6 @@ js_logout(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return(JS_TRUE);
}
static JSBool
js_reset_loop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
service_client_t* client;
if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
client->js_loop=0;
*rval = JSVAL_VOID;
return(JS_TRUE);
}
static JSFunctionSpec js_global_functions[] = {
{"write", js_write, 0}, /* write to client socket */
{"writeln", js_writeln, 0}, /* write line to client socket */
......@@ -465,7 +452,6 @@ static JSFunctionSpec js_global_functions[] = {
{"log", js_log, 0}, /* Log a string */
{"login", js_login, 2}, /* Login specified username and password */
{"logout", js_logout, 0}, /* Logout user */
{"reset_loop", js_reset_loop, 0}, /* reset loop counter */
{0}
};
......@@ -587,6 +573,10 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client,
if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions))
break;
/* Branch Object */
if(js_CreateBranchObject(js_cx, js_glob, &service_client->branch)==NULL)
break;
/* Client Object */
if(service_client->client!=NULL)
if(js_CreateClientObject(js_cx, js_glob, "client", service_client->client, sock)==NULL)
......@@ -664,25 +654,25 @@ js_BranchCallback(JSContext *cx, JSScript *script)
if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
client->js_loop++;
client->branch.counter++;
/* Terminated? */
if(terminated) {
JS_ReportError(cx,"Terminated");
client->js_loop=0;
client->branch.counter=0;
return(JS_FALSE);
}
/* Infinite loop? */
if(client->js_loop>JAVASCRIPT_BRANCH_LIMIT) {
JS_ReportError(cx,"Infinite loop (%lu branches) detected",client->js_loop);
client->js_loop=0;
if(client->branch.limit && client->branch.counter > client->branch.limit) {
JS_ReportError(cx,"Infinite loop (%lu branches) detected",client->branch.counter);
client->branch.counter=0;
return(JS_FALSE);
}
/* Give up timeslices every once in a while */
if(!(client->js_loop%JAVASCRIPT_YIELD_FREQUENCY))
if(client->branch.yield_freq && (client->branch.counter%client->branch.yield_freq)==0)
YIELD();
if(!(client->js_loop%JAVASCRIPT_GC_FREQUENCY))
if(client->branch.gc_freq && (client->branch.counter%client->branch.gc_freq)==0)
JS_MaybeGC(cx);
return(JS_TRUE);
......@@ -928,6 +918,9 @@ static void js_static_service_thread(void* arg)
memset(&service_client,0,sizeof(service_client));
service_client.socket = service->socket;
service_client.service = service;
service_client.branch.limit = JAVASCRIPT_BRANCH_LIMIT;
service_client.branch.gc_freq = JAVASCRIPT_GC_FREQUENCY;
service_client.branch.yield_freq = JAVASCRIPT_YIELD_FREQUENCY;
if((js_runtime=JS_NewRuntime(startup->js_max_bytes))==NULL
|| (js_cx=js_initcx(js_runtime,service->socket,&service_client,&js_glob))==NULL) {
......@@ -1726,6 +1719,9 @@ void DLLCALL services_thread(void* arg)
client->service->clients++; /* this should be mutually exclusive */
client->udp_buf=udp_buf;
client->udp_len=udp_len;
client->branch.limit=JAVASCRIPT_BRANCH_LIMIT;
client->branch.gc_freq=JAVASCRIPT_GC_FREQUENCY;
client->branch.yield_freq=JAVASCRIPT_YIELD_FREQUENCY;
udp_buf = NULL;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment