From 46844bb71720bbc033e5348091bd2ab80570e31c Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Sun, 29 Mar 2020 08:01:59 +0000
Subject: [PATCH] Add system.get_node() method to read a single node record in
 one shot: use this in place of system.node_list[] if you're going to be using
 a lot of the properties and passing them around to methods which are going to
 each possibly dereference the values, as *each* deference results in a read
 of the node record in the node.dab. On my system, a simple node list (e.g. /L
 command) would result in between 60 and 100 reads of the node.dab (for a 13
 node system), which was nuts.

The system.get_node() method currently leaves the node record unlocked and
there is currently no equivalent put_node() method, so you still need to use
the system.node_list[] for modification of node records. But, I now see there
are race conditions with the current methods of read-modify-writes of the
node_list[] properties. We should be locking a node.dab record, reading it,
modifying it, writing it, and then unlocking it - as is done in the C/C++
code. So... that's a todo item.

Also created system.stats.node_gets to track the number of node.dab reads
from a single instance of the system object. May remove this any time.
---
 src/sbbs3/js_system.c | 64 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c
index c3ef9f57c9..3cf5c30364 100644
--- a/src/sbbs3/js_system.c
+++ b/src/sbbs3/js_system.c
@@ -42,6 +42,7 @@
 typedef struct {
 	scfg_t* cfg;
 	int nodefile;
+	int nodegets;
 } js_system_private_t;
 
 extern JSClass js_system_class;
@@ -564,6 +565,8 @@ enum {
 	,SYSSTAT_PROP_TOTALMSGS
 	,SYSSTAT_PROP_TOTALMAIL
 	,SYSSTAT_PROP_FEEDBACK
+
+	,SYSSTAT_PROP_NODE_GETS
 };
 
 #ifndef JSDOOR
@@ -665,6 +668,10 @@ static JSBool js_sysstats_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 			*vp = INT_TO_JSVAL(getmail(cfg, /* user: */1, /* Sent: */FALSE, /* SPAM: */FALSE));
 			JS_RESUMEREQUEST(cx, rc);
 			break;
+
+		case SYSSTAT_PROP_NODE_GETS:
+			*vp = INT_TO_JSVAL(sys->nodegets);
+			break;
 	}
 
 	return(TRUE);
@@ -692,6 +699,7 @@ static jsSyncPropertySpec js_sysstats_properties[] = {
 	{	"feedback_sent_today",		SYSSTAT_PROP_FTODAY,		SYSSTAT_FLAGS,	310 },
 	{	"total_users",				SYSSTAT_PROP_TOTALUSERS,	SYSSTAT_FLAGS,	310 },
 	{	"new_users_today",			SYSSTAT_PROP_NUSERS,		SYSSTAT_FLAGS,	310 },
+	{	"node_gets",				SYSSTAT_PROP_NODE_GETS,		JSPROP_READONLY, 31702 },
 	{0}
 };
 
@@ -1384,6 +1392,54 @@ js_filter_ip(JSContext *cx, uintN argc, jsval *arglist)
 	return(JS_TRUE);
 }
 
+static JSBool
+js_get_node(JSContext *cx, uintN argc, jsval *arglist)
+{
+	JSObject*	obj=JS_THIS_OBJECT(cx, arglist);
+	JSObject*	nodeobj;
+	jsval*		argv=JS_ARGV(cx, arglist);
+	node_t		node = {0};
+	int32		node_num;
+	jsrefcount	rc;
+
+	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
+
+	js_system_private_t* sys;
+	if((sys = (js_system_private_t*)js_GetClassPrivate(cx,obj,&js_system_class))==NULL)
+		return JS_FALSE;
+	scfg_t* cfg = sys->cfg;
+
+	node_num=cfg->node_num;
+	if(argc) 
+		JS_ValueToInt32(cx,argv[0],&node_num);
+	if(node_num<1)
+		node_num=1;
+
+	rc=JS_SUSPENDREQUEST(cx);
+
+	int retval = getnodedat(sys->cfg, node_num, &node, /* lockit: */FALSE, &sys->nodefile);
+	sys->nodegets++;
+	JS_RESUMEREQUEST(cx, rc);
+	if(retval != 0) {
+		JS_ReportError(cx, "getnodat(%d) returned %d", node_num, retval);
+		return JS_TRUE;
+	}
+	if((nodeobj = JS_NewObject(cx, NULL, NULL, obj)) == NULL) {
+		JS_ReportError(cx, "JS_NewObject failure");
+		return JS_TRUE;
+	}
+	JS_DefineProperty(cx, nodeobj, "status", INT_TO_JSVAL((int)node.status), NULL, NULL, JSPROP_ENUMERATE);
+	JS_DefineProperty(cx, nodeobj, "errors", INT_TO_JSVAL((int)node.errors), NULL, NULL, JSPROP_ENUMERATE);
+	JS_DefineProperty(cx, nodeobj, "action", INT_TO_JSVAL((int)node.action), NULL, NULL, JSPROP_ENUMERATE);
+	JS_DefineProperty(cx, nodeobj, "useron", INT_TO_JSVAL((int)node.useron), NULL, NULL, JSPROP_ENUMERATE);
+	JS_DefineProperty(cx, nodeobj, "connection", INT_TO_JSVAL((int)node.connection), NULL, NULL, JSPROP_ENUMERATE);
+	JS_DefineProperty(cx, nodeobj, "misc", INT_TO_JSVAL((int)node.misc), NULL, NULL, JSPROP_ENUMERATE);
+	JS_DefineProperty(cx, nodeobj, "aux", INT_TO_JSVAL((int)node.aux), NULL, NULL, JSPROP_ENUMERATE);
+	JS_DefineProperty(cx, nodeobj, "extaux", INT_TO_JSVAL((int)node.extaux), NULL, NULL, JSPROP_ENUMERATE);
+	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(nodeobj));
+	return JS_TRUE;
+}
+
 static JSBool
 js_get_node_message(JSContext *cx, uintN argc, jsval *arglist)
 {
@@ -1908,7 +1964,12 @@ static jsSyncMethodSpec js_system_functions[] = {
 	{"filter_ip",		js_filter_ip,		4,	JSTYPE_BOOLEAN,	JSDOCSTR("[protocol, reason, host, ip, username, filename]")
 	,JSDOCSTR("add an IP address (with comment) to an IP filter file. If filename is not specified, the ip.can file is used")
 	,311
-	},		
+	},
+	{"get_node",		js_get_node,		1,	JSTYPE_OBJECT,	JSDOCSTR("node_number")
+	,JSDOCSTR("read a node data record all at once (and leaving the record unlocked) "
+		"returning an object matching the elements of <tt>system.node_list</tt>")
+	,31702
+	},
 	{"get_node_message",js_get_node_message,0,	JSTYPE_STRING,	JSDOCSTR("node_number")
 	,JSDOCSTR("read any messages waiting for the specified node and return in a single string")
 	,311
@@ -2034,6 +2095,7 @@ static JSBool js_node_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 		return(JS_TRUE);
 	}
 	JS_RESUMEREQUEST(cx, rc);
+	sys->nodegets++;
 	
     switch(tiny) {
 		case NODE_PROP_STATUS:
-- 
GitLab