diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 940819fbf8ae4dbd4bf9c7911aa40f510221b72a..244d151fd8fdd6e862e1754c2ba762d1353e178a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,7 +30,7 @@ Only approved and authenticaed "developers" can create new branches in the `main
 
 ### Commit messages
 * Try to keep the commit title (first line) to 70 characters or less.
-* When a comment is related to an [issue](https://gitlab.synchro.net/main/sbbs/-/issues), use the proper commit message syntax foir automatic issue management as documented [here](https://docs.gitlab.com/ce/user/project/issues/managing_issues.html#closing-issues-automatically).
+* When a comment is related to an [issue](https://gitlab.synchro.net/main/sbbs/-/issues), use the proper commit message syntax for automatic issue management as documented [here](https://docs.gitlab.com/ce/user/project/issues/managing_issues.html#closing-issues-automatically).
 
 ### Other types of contributions
 If you were interested in contributing money, not code, then paypal to rob at synchro dot net.
diff --git a/exec/archive.js b/exec/archive.js
index 85768a69b3229eede15d08df00b4571fdd5c9e68..9ac4931cd352d7aeb1815163bef7de3bee36262d 100755
--- a/exec/archive.js
+++ b/exec/archive.js
@@ -109,19 +109,19 @@ function list(filename, verbose)
 
 function install()
 {
-	var cnflib = load({}, "cnflib.js");
-	var file_cnf = cnflib.read("file.cnf");
-	if(!file_cnf) {
-		alert("Failed to read file.cnf");
+	var f = new File(system.ctrl_dir + "file.ini");
+	if(!f.open(f.exists ? 'r+':'w+')) {
+		alert("Failed to open " + f.name);
 		exit(-1);
 	}
-	file_cnf.fview.push({
+
+	if(!f.iniSetObject("viewer:new", {
 		extension: "*",
 		cmd: '?archive list %f'
-		});
-	if(!cnflib.write("file.cnf", undefined, file_cnf)) {
-		alert("Failed to write file.cnf");
+		})) {
+		alert("Failed to write " + f.name);
 		exit(-1);
 	}
+	f.close();
 	exit(0);
 }
diff --git a/exec/avatars.js b/exec/avatars.js
index fb770c2a246e68d79ab9610ebf3f117546bad9f2..416ed91f9c77bd023603c3c1eac4e7e191b1dc5d 100644
--- a/exec/avatars.js
+++ b/exec/avatars.js
@@ -471,20 +471,17 @@ function install()
 {
 	if(!file_exists(lib.local_library() + "*.bin"))
 		return "No avatars collections (.bin files) found in " + lib.local_library();
-	var cnflib = load({}, "cnflib.js");
-	var xtrn_cnf = cnflib.read("xtrn.cnf");
-	if(!xtrn_cnf)
-		return "Failed to read xtrn.cnf";
+	var f = new File(system.ctrl_dir + "xtrn.ini");
+	if(!f.open(f.exists ? 'r+':'w+'))
+		return "Failed to open " + f.name;
 
-	var changed = false;
-	if(!xtrn_area.prog["avatchoo"]) {
+	var section = "prog:MAIN:AVATCHOO";
+	if(!f.iniGetValue(section, "cmd")) {
 		printf("Adding external program: Avatar Chooser\r\n");
-		xtrn_cnf.xtrn.push( {
-				"sec": 0,
+		f.iniSetObject(section, {
 				"name": "Avatar Chooser",
-				"code": "AVATCHOO",
 				"ars": "",
-				"run_ars": "ANSI & !GUEST & REST ! Q",
+				"execution_ars": "ANSI & !GUEST & REST ! Q",
 				"type": 0,
 				"settings": 1,
 				"event": 3,
@@ -495,13 +492,12 @@ function install()
 				"textra": 0,
 				"max_time": 0
 				});
-		changed = true;
 	}
 
-	if(!xtrn_area.event["avat-in"]) {
+	section = "event:AVAT-IN";
+	if(!f.iniGetValue(section, "cmd")) {
 		printf("Adding timed event: AVAT-IN\r\n");
-		xtrn_cnf.event.push( {
-				"code": "AVAT-IN",
+		f.iniSetObject(section, {
 				"cmd": "?avatars import",
 				"days": 255,
 				"time": 0,
@@ -512,13 +508,12 @@ function install()
 				"mdays": 0,
 				"months": 0
 				});
-		changed = true;
 	}
 
-	if(!xtrn_area.event["avat-out"]) {
+	section = "event:AVAT-OUT";
+	if(!f.iniGetValue(section, "cmd")) {
 		printf("Adding timed event: AVAT-OUT\r\n");
-		xtrn_cnf.event.push( {
-				"code": "AVAT-OUT",
+		f.iniSetObject(section, {
 				"cmd": "?avatars export",
 				"days": 255,
 				"time": 0,
@@ -529,11 +524,8 @@ function install()
 				"mdays": 0,
 				"months": 0
 				});
-		changed = true;
 	}
-
-	if(changed && !cnflib.write("xtrn.cnf", undefined, xtrn_cnf))
-		return "Failed to write xtrn.cnf";
+	f.close();
 
 	var ini = new File(file_cfgname(system.ctrl_dir, "modopts.ini"));
 	if(!ini.open(file_exists(ini.name) ? 'r+':'w+'))
diff --git a/exec/emailfiles.js b/exec/emailfiles.js
index d7d2702fb3950e9bc3f7c01d63729c3c8640ec5e..712f044aa1d10fde38d9d5e3ede0caa89dc160a6 100755
--- a/exec/emailfiles.js
+++ b/exec/emailfiles.js
@@ -51,25 +51,23 @@ if(!options.maxpending)
 	options.maxpending = 100 * 1024 * 1024;
 
 if(argv[0] === '-install') {
-	var cnflib = load({}, "cnflib.js");
-	var file_cnf = cnflib.read("file.cnf");
-	if(!file_cnf) {
-		alert("Failed to read file.cnf");
+	var f = new File(system.ctrl_dir + "file.ini");
+	if(!f.open(f.exists ? 'r+':'w+')) {
+		alert("Failed to open " + f.name);
 		exit(-1);
 	}
-	file_cnf.prot.push({
+	if(!f.iniSetObject("protocol:new", {
 		  key: 'E'
 		, name: 'E-mail Attachment'
 		, dlcmd: '?emailfiles %f'
 		, batdlcmd: '?emailfiles +%f'
 		, ars: 'REST NOT M'
 		, settings: PROT_NATIVE | PROT_DSZLOG
-		});
-
-	if(!cnflib.write("file.cnf", undefined, file_cnf)) {
-		alert("Failed to write file.cnf");
+		})) {
+		alert("Failed to write " + f.name);
 		exit(-1);
 	}
+	f.close();
 	exit(0);
 }
 
diff --git a/exec/init-fidonet.js b/exec/init-fidonet.js
index 379fe7cac71aa31c95ba93e1b4b34a14548a0761..d4295eca2f65f17a1b0f8dffe14091a86e7310b1 100644
--- a/exec/init-fidonet.js
+++ b/exec/init-fidonet.js
@@ -1,6 +1,4 @@
-// $Id: init-fidonet.js,v 1.29 2020/05/12 17:23:30 rswindell Exp $
-
-// Initial FidoNet setup script - interactive, run via JSexec or ;exec
+// $Id: init-fidonet.tup script - interactive, run via JSexec or ;exec
 
 // usage: init-fidonet.js [zone | othernet-name] [http://path/to/echolist.na]
 
@@ -22,7 +20,7 @@
 
 "use strict";
 
-const REVISION = "$Revision: 1.30 $".split(' ')[1];
+const REVISION = "2.0;
 require('sbbsdefs.js', 'SUB_NAME');
 const temp_node = 9999;
 var netname;
@@ -472,10 +470,9 @@ if(netname) {
 } else
 	alert("Unrecognized network zone: " + netzone);
 
-print("Reading Message Area configuration file: msgs.cnf");
-var cnflib = load({}, "cnflib.js");
-var msgs_cnf = cnflib.read("msgs.cnf");
-if(!msgs_cnf) {
+var msgs_ini = new File(system.ctrl_dir + "msgs.ini");
+print("Reading Message Area configuration file: " + msgs_ini.name);
+if(!f.open(f.exists ? 'r+':'w+')) {
 	alert("Failed to read msgs.cnf");
 	exit(1);
 }
@@ -602,37 +599,33 @@ if(your.node === temp_node && network.email && network.email.indexOf('@') > 0
 }
 
 if(!find_sys_addr(fidoaddr.to_str(your))
-	&& confirm("Add node address " + fidoaddr.to_str(your) + " to your configuration"))
-	msgs_cnf.fido_addr_list.push(your);
+	&& confirm("Add node address " + fidoaddr.to_str(your) + " to your configuration")) {
+	var fido_addr_list = msgs_ini.iniGetValue("fidonet", "addr_list", []);
+	fido_addr_list.push(your);
+	msgs_ini.iniSetValue("fidonet", "addr_list", fido_addr_list);
+}
 
-if(!msgs_cnf.fido_default_origin)
-	msgs_cnf.fido_default_origin = system.name + " - " + system.inet_addr;
-while((!msgs_cnf.fido_default_origin
-	|| !confirm("Your origin line is '" +	msgs_cnf.fido_default_origin + "'")) && !aborted()) {
-	msgs_cnf.fido_default_origin = prompt("Your origin line");
+var fido_default_origin = msgs_ini.iniGetValue("fidonet", "default_origin");
+if(!fido_default_origin)
+	fido_default_origin = system.name + " - " + system.inet_addr;
+while((!fido_default_origin
+	|| !confirm("Your origin line is '" +	fido_default_origin + "'")) && !aborted()) {
+	fido_default_origin = prompt("Your origin line");
 }
+msgs_ini.iniSetValue("fidonet", "default_origin", fido_default_origin);
 
 /*******************/
-/* UPDATE MSGS.CNF */
+/* UPDATE MSGS.INI */
 /*******************/
-if(!msg_area.grp[netname]
+if(!msgs_ini.iniGetObject("grp:" + netname)
 	&& confirm("Create " + netname + " message group in SCFG->Message Areas")) {
 	print("Adding Message Group: " + netname);
-	msgs_cnf.grp.push( {
-		"name": netname,
+	msgs_ini.iniSetObject("grp:" + netname, {
 		"description": netname,
-		"ars": "",
 		"code_prefix": network.areatag_prefix === undefined
 			? (netname.toUpperCase() + "_") : network.areatag_prefix
 	});
 }
-if(confirm("Save Changes to Message Area configuration file: msgs.cnf")) {
-	if(!cnflib.write("msgs.cnf", undefined, msgs_cnf)) {
-		alert("Failed to write msgs.cnf");
-		exit(1);
-	}
-	print("msgs.cnf updated successfully.");
-}
 
 /*********************/
 /* DOWNLOAD ECHOLIST */
diff --git a/exec/letsyncrypt.js b/exec/letsyncrypt.js
index 7c47b521fe92a93dc0fe69cd8039577cdb0166a6..a9f5b6752940ffd6a986e973e40aa3a67296eb0f 100644
--- a/exec/letsyncrypt.js
+++ b/exec/letsyncrypt.js
@@ -7,7 +7,7 @@ var ks_fname = backslash(system.ctrl_dir)+"letsyncrypt.key";
 var setting_fname = backslash(system.ctrl_dir)+"letsyncrypt.ini";
 var sks_fname = backslash(system.ctrl_dir)+"ssl.cert";
 var maincnf_fname = backslash(system.ctrl_dir)+"main.cnf";
-var recycle_sem = backslash(system.ctrl_dir)+"recycle.web";
+var recycle_sem = backslash(system.ctrl_dir)+"recycle";
 
 function at_least_a_third()
 {
diff --git a/exec/localcopy.js b/exec/localcopy.js
index e5051e4760efabf19fba1527d5bd423181bba9e6..f5aae833c88a7ae2cbdd14b177857c13250c1ea7 100644
--- a/exec/localcopy.js
+++ b/exec/localcopy.js
@@ -42,29 +42,27 @@ function main(cmd) {
 	switch(cmd) {
 		case 'install':
 		case '-install':
-			var cnflib = load({}, "cnflib.js");
-			var file_cnf = cnflib.read("file.cnf");
-			if(!file_cnf) {
-				alert("Failed to read file.cnf");
+			var f = new File(system.ctrl_dir + "file.ini");
+			if(!f.open(f.exists ? 'r+':'w+')) {
+				alert("Failed to open " + f.name);
 				exit(-1);
 			}
-			file_cnf.prot.push({ 
+			if(!f.iniSetObject("protocol:new", {
 				  key: 'L'
 				, name: 'Local Copy'
-				, ulcmd: '?localcopy send %f' 
+				, ulcmd: '?localcopy send %f'
 				, dlcmd: '?localcopy recv %f'
 				, batulcmd: '?localcopy send %g'
 				, batdlcmd: '?localcopy recv %s'
 				, ars: 'SYSOP'
 				, settings: PROT_NATIVE
-				});
-		
-			if(!cnflib.write("file.cnf", undefined, file_cnf)) {
-				alert("Failed to write file.cnf");
+				})) {
+				alert("Failed to write " + f.name);
 				exit(-1);
 			}
+			f.close();
 			exit(0);
-	
+
 		case 'send':
 			if(file_isdir(argv[1])) { /* batch upload */
 				var dest = backslash(argv[1]);
diff --git a/exec/logonlist.js b/exec/logonlist.js
index e69d29811dfcb29c46670b7f9fc962be41feae44..bb47cdb1a583c23dfa8d035d899c9aa009b3a29b 100644
--- a/exec/logonlist.js
+++ b/exec/logonlist.js
@@ -11,17 +11,16 @@ require("sbbsdefs.js", 'SYS_LISTLOC');
 function install()
 {
 	var maint_event = "?logonlist -m";
-	var cnflib = load({}, "cnflib.js");
-	var main_cnf = cnflib.read("main.cnf");
-	if(!main_cnf)
-		return "Failed to read main.cnf";
-	if(main_cnf.sys_daily == maint_event)
+	var f = new File(system.ctrl_dir + "main.ini");
+	if(!f.open(f.exists ? 'r+':'w+'))
+		return "Failed to read " + f.name;
+	var cmd = f.iniGetValue("daily_event", "cmd");
+	if(cmd == maint_event)
 		return true;
-	if(main_cnf.sys_daily)
-		return format("System daily event already set to: '%s'", main_cnf.sys_daily);
-	main_cnf.sys_daily = maint_event;
-	if(!cnflib.write("main.cnf", undefined, main_cnf))
-		return "Failed to write main.cnf";
+	if(cmd)
+		return format("System daily event already set to: '%s'", cmd);
+	if(!f.iniSetValue("daily_event", "cmd", maint_event))
+		return "Failed to write " + f.name;
 	return true;
 }
 
diff --git a/exec/msglist.js b/exec/msglist.js
index 7b9b53ed058c333e57c99da076263f01c4cff903..94dae421dc2c7d6946f9d56ffe4e0f394037c49a 100644
--- a/exec/msglist.js
+++ b/exec/msglist.js
@@ -1,6 +1,3 @@
-// $Id: msglist.js,v 1.14 2020/08/13 20:05:23 rswindell Exp $
-// vi: tabstop=4
-
 // Message Listing Module
 
 /* To install manually into a Baja command shell (*.src) file,
@@ -32,14 +29,12 @@
 
 function install()
 {
-	var cnflib = load({}, "cnflib.js");
-	var main_cnf = cnflib.read("main.cnf");
-	if(!main_cnf)
-		return "Failed to read main.cnf";
-	main_cnf.readmail_mod = "msglist mail -preview";
-	main_cnf.listmsgs_mod = "msglist";
-	if(!cnflib.write("main.cnf", undefined, main_cnf))
-		return "Failed to write main.cnf";
+	var f = new File(system.ctrl_dir + "main.ini");
+	if(!f.open(f.exists ? 'r+':'w+'))
+		return "Failed to open " + f.name;
+	f.iniSetValue("module", "readmail", "msglist mail -preview");
+	f.iniSetValue("module", "listmsgs", "msglist");
+	f.close();
 	file_touch(system.ctrl_dir + "recycle");
 	return true;
 }
diff --git a/exec/sbbslist.js b/exec/sbbslist.js
index f8210c214859dfdde5b9a87a196bef5415a96b43..acd4b5912e3648fde9e437400aac71e842547860 100644
--- a/exec/sbbslist.js
+++ b/exec/sbbslist.js
@@ -2159,14 +2159,6 @@ function find_code(objs, code)
 	return -1;
 }
 
-function remove_from_list(list, value)
-{
-	var index = find_code(list, value);
-	if(index < 0)
-		return null;
-	return list.splice(index, 1);
-}
-
 function replace_in_list(list, value, obj)
 {
 	var index = find_code(list, value);
@@ -2179,11 +2171,8 @@ function replace_in_list(list, value, obj)
 function install()
 {
 	var sbbslist_cfg = {
-		"sec": 0,
 		"name": "Synchronet BBS List",
-		"code": "SBBSLIST",
 		"ars": "",
-		"run_ars": "",
 		"type": 0,
 		"settings": 1,
 		"event": 0,
@@ -2195,7 +2184,6 @@ function install()
 		"max_time": 0
 		};
 	var smb2sbl_cfg = {
-		"code": "SMB2SBL",
 		"cmd": "?sbbslist import",
 		"days": 255,
 		"time": 0,
@@ -2207,7 +2195,6 @@ function install()
 		"months": 0
 		};
 	var sbl2smb_cfg = {
-        "code": "SBL2SMB",
         "cmd": "?sbbslist export",
         "days": 255,
         "time": 0,
@@ -2219,7 +2206,6 @@ function install()
         "months": 0
 		};
 	var sblupdate_cfg = {
-        "code": "SBLUPDAT",
         "cmd": "?sbbslist update -preview",
         "days": 255,
         "time": 0,
@@ -2231,7 +2217,6 @@ function install()
         "months": 0
 		};
 	var sblmaint_cfg = {
-        "code": "SBLMAINT",
         "cmd": "?sbbslist maint",
         "days": 255,
         "time": 0,
@@ -2242,30 +2227,26 @@ function install()
         "mdays": 0,
         "months": 0
 		};
-	var cnflib = load({}, "cnflib.js");
-	var xtrn_cnf = cnflib.read("xtrn.cnf");
-	if(!xtrn_cnf)
-		return "Failed to read xtrn.cnf";
+	var f = new File(system.ctrl_dir + "xtrn.ini");
+	if(!f.open(f.exists ? 'r+':'w+'))
+		return "Failed to open " + f.name;
 
-	remove_from_list(xtrn_cnf.xtrn, "sbl");
 	printf("Adding external program: SBBSLIST\r\n");
-	replace_in_list(xtrn_cnf.xtrn, "sbbslist", sbbslist_cfg);
+	f.iniSetObject("prog:MAIN:SBBSLIST", sbbslist_cfg);
 
 	printf("Adding timed event: SMB2SBL\r\n");
-	replace_in_list(xtrn_cnf.event, "smb2sbl", smb2sbl_cfg);
+	f.iniSetObject("event:SMB2SBL", smb2sbl_cfg);
 
 	printf("Adding timed event: SBL2SMB\r\n");
-	replace_in_list(xtrn_cnf.event, "sbl2smb", sbl2smb_cfg);
+	f.iniSetObject("event:SBL2SMB", sbl2smb_cfg);
 
 	printf("Adding timed event: SBLUPDAT\r\n");
-	replace_in_list(xtrn_cnf.event, "sblupdat", sblupdate_cfg);
+	f.iniSetObject("event:SBLUPDAT", sblupdate_cfg);
 
 	printf("Adding timed event: SBLMAINT\r\n");
-	replace_in_list(xtrn_cnf.event, "sblmaint", sblmaint_cfg);
-
-	if(!cnflib.write("xtrn.cnf", undefined, xtrn_cnf))
-		return "Failed to write xtrn.cnf";
+	f.iniSetObject("event:SBLMAINT", sblmaint_cfg);
 
+	f.close();
 	return true;
 }
 
diff --git a/exec/update.js b/exec/update.js
index d9588ed3fe97388257bfec1c4a69fd790451848e..ceac12e74592bc002cd4e5c35b15d02b41d2dbec 100644
--- a/exec/update.js
+++ b/exec/update.js
@@ -87,15 +87,16 @@ function update_birthdates()
 function install_logonlist()
 {
 	var maint_event = "?logonlist -m";
-	var cnflib = load({}, "cnflib.js");
-	var main_cnf = cnflib.read("main.cnf");
-	if(!main_cnf)
-		return "!Failed to read main.cnf";
-	if(main_cnf.sys_daily)
-		return format("System daily event already set to: '%s'", main_cnf.sys_daily);
-	main_cnf.sys_daily = maint_event;
-	if(!cnflib.write("main.cnf", undefined, main_cnf))
-		return "!Failed to write main.cnf";
+	var f = new File(system.ctrl_dir + "main.ini");
+	if(!f.open(f.exists ? 'r+':'w+'))
+		return "!Failed to open " + f.name;
+	var cmd = f.iniGetValue("daily_event", "cmd");
+	if(cmd)
+		return format("System daily event already set to: '%s'", cmd);
+	var result = f.iniSetValue("daily_event", "cmd", maint_event);
+	f.close();
+	if(!result)
+		return "!Failed to write main.ini";
 	return "Successful";
 }
 
diff --git a/src/sbbs3/xtrn.cpp b/src/sbbs3/xtrn.cpp
index 3a581519a76926d31eaefbdcb0b09f06ddd7c7f7..70f651cdb80b27aecedab367a47fe7bae43380cc 100644
--- a/src/sbbs3/xtrn.cpp
+++ b/src/sbbs3/xtrn.cpp
@@ -1847,14 +1847,23 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 
 		if(waitpid(pid, &i, WNOHANG)==0)  {		// Child still running?
 			kill(pid, SIGHUP);					// Tell child user has hung up
-			time_t start=time(NULL);			// Wait up to 10 seconds
-			while(time(NULL)-start<10) {		// for child to terminate
+			time_t start=time(NULL);			// Wait up to 5 seconds
+			while(time(NULL)-start<5 ) {		// for child to terminate
 				if(waitpid(pid, &i, WNOHANG)!=0)
 					break;
 				mswait(500);
 			}
-			if(waitpid(pid, &i, WNOHANG)==0)	// Child still running?
-				kill(pid, SIGKILL);				// terminate child process
+			if(waitpid(pid, &i, WNOHANG)==0) {	// Child still running?
+				kill(pid, SIGTERM);				// terminate child process (gracefully)
+				start=time(NULL);				// Wait up to 5 (more) seconds
+				while(time(NULL)-start<5 ) {	// for child to terminate
+					if(waitpid(pid, &i, WNOHANG)!=0)
+						break;
+					mswait(500);
+				}
+				if(waitpid(pid, &i, WNOHANG)==0)// Child still running?
+					kill(pid, SIGKILL);			// terminate child process (ungracefully)
+			}
 		}
 		/* close unneeded descriptors */
 		if(mode&EX_STDIN)