Browse Source

1. Flow the builder
2. Separate preprocessing from compile phases
3. Object.freeze() state and config after configuration stage is done
4. Make js `this` object which lives through interpretation of one file
5. Make the builder work fully incremental
6. Always use Js() macro instead of <?js
7. Moved test js to build dir and make it run during configure phase

Caleb James DeLisle 3 years ago
parent
commit
89f0ae89dc

+ 3 - 3
crypto/random/seed/SystemRandomSeed.c

@@ -16,9 +16,9 @@
 #include "crypto/random/seed/SystemRandomSeed.h"
 #include "util/log/Log.h"
 
-<?js file.RandomSeedProvider_providers = []; ?>
-#define RandomSeedProvider_register(name) <?js file.RandomSeedProvider_providers.push(#name) ?>
-#define RandomSeedProvider_list() <?js return file.RandomSeedProvider_providers.join(','); ?>
+Js({ this.RandomSeedProvider_providers = []; })
+#define RandomSeedProvider_register(name) Js({ this.RandomSeedProvider_providers.push(#name) })
+#define RandomSeedProvider_list() Js({ return this.RandomSeedProvider_providers.join(','); })
 
 #ifdef win32
     #include "crypto/random/seed/RtlGenRandomSeed.h"

+ 7 - 7
exception/Er.h

@@ -27,10 +27,10 @@ struct Er_Ret
     const char* message;
 };
 
-Js({ file.Er_JS = require("../exception/Er.js").create(); })
+Js({ this.Er_JS = require("../exception/Er.js").create(); })
 
 #define Er_DEFUN(...) \
-    Gcc_USE_RET Js_or({ return file.Er_JS.defun(Js_Q __VA_ARGS__ Js_Q) }, __VA_ARGS__)
+    Gcc_USE_RET Js_or({ return this.Er_JS.defun(Js_Q __VA_ARGS__ Js_Q) }, __VA_ARGS__)
 
 Gcc_PRINTF(4, 5)
 Gcc_USE_RET
@@ -42,15 +42,15 @@ struct Er_Ret* Er__raise(char* file, int line, struct Allocator* alloc, char* fo
     } while (0)
     // CHECKFILES_IGNORE missing ;
 
-#define Er(expr) Js_or({ return file.Er_JS.er(Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr)
+#define Er(expr) Js_or({ return this.Er_JS.er(Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr)
 
 #define Er_assert(expr) \
-    Js_or({ return file.Er_JS.assert(Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr)
+    Js_or({ return this.Er_JS.assert(Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr)
 
 #define Er_check(ret, expr) \
-    Js_or({ return file.Er_JS.check(#ret, Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr)
+    Js_or({ return this.Er_JS.check(#ret, Js_Q expr Js_Q, Gcc_SHORT_FILE, Gcc_LINE); }, expr)
 
-#define Er_ret(val) Js_or({ return file.Er_JS.ret(Js_Q val Js_Q); }, return val)
+#define Er_ret(val) Js_or({ return this.Er_JS.ret(Js_Q val Js_Q); }, return val)
 
 static inline struct Er_Ret* Er_unwind(const char* file, int line, struct Er_Ret* ret)
 {
@@ -59,4 +59,4 @@ static inline struct Er_Ret* Er_unwind(const char* file, int line, struct Er_Ret
 
 void Er__assertFail(struct Er_Ret* er);
 
-#endif
+#endif

+ 38 - 33
test/testcjdroute.js → node_build/CjdnsTest.js

@@ -12,10 +12,11 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-var Fs = require("fs");
-var nThen = require("nthen");
+'use strict';
+const Fs = require("fs");
+const nThen = require("nthen");
 
-var Fs_stat = function (file, callback) {
+const Fs_stat = function (file, callback) {
     Fs.stat(file, function (err, ret) {
         if (err === 'ENOENT') {
             console.log("Magical ENOENT on a file which definitely does exist, good job Linus");
@@ -28,7 +29,7 @@ var Fs_stat = function (file, callback) {
     });
 };
 
-var getTests = function (file, tests, isSubnode, callback) {
+const getTests = function (file, tests, isSubnode, callback) {
     if (/\/(.git|build_.*|node_build|contrib)\//.test(file)) { callback(); return; }
     if (isSubnode && /\/dht\//.test(file)) { callback(); return; }
     Fs_stat(file, function (err, stat) {
@@ -45,15 +46,19 @@ var getTests = function (file, tests, isSubnode, callback) {
                 callback();
             });
             return;
-        } else if (/_test\.c$/.test(file) && tests.indexOf(file) === -1) {
-            tests.push(file);
+        } else if (!/_test\.c$/.test(file)) {
+            // not a test
+        } else {
+            // get rid of the annoying ./
+            const f = file.replace(/^\.\//, '');
+            if (tests.indexOf(f) === -1) { tests.push(f); }
         }
         callback();
     });
 };
 
-var rmFuzz = function (builder, cb) {
-    var inputs = builder.config.buildDir + '/fuzz_inputs';
+const rmFuzz = function (builder, cb) {
+    const inputs = builder.config.buildDir + '/fuzz_inputs';
     Fs_stat(inputs, function (err, stat) {
         if (err && err.code === 'ENOENT') {
             Fs.mkdir(inputs, function (err, ret) {
@@ -69,7 +74,7 @@ var rmFuzz = function (builder, cb) {
         }
         Fs.readdir(inputs, function (err, list) {
             if (err) { throw err; }
-            var nt = nThen;
+            let nt = nThen;
             list.forEach(function (f) {
                 nt = nt(function (w) {
                     Fs.unlink(inputs + '/' + f, w(function (err) {
@@ -82,13 +87,13 @@ var rmFuzz = function (builder, cb) {
     });
 };
 
-var mkFuzzCase = function (inFile, outPath, testId, cb) {
+const mkFuzzCase = function (inFile, outPath, testId, cb) {
     Fs.readFile(inFile, 'utf8', function (err, ret) {
         if (err) { throw err; }
         ret = ret.replace(/#[^\n]*\n/g, '');
         ret = ret.replace(/[\s]*/g, '');
-        var out = Buffer.from(ret, 'hex');
-        var id = Buffer.alloc(4);
+        const out = Buffer.from(ret, 'hex');
+        const id = Buffer.alloc(4);
         id.writeInt32BE(testId, 0);
         Fs.writeFile(outPath, Buffer.concat([id, out]), function (err) {
             if (err) { throw err; }
@@ -97,22 +102,22 @@ var mkFuzzCase = function (inFile, outPath, testId, cb) {
     });
 };
 
-var mkFuzz = function (builder, testPath, testId, fuzzCases, cb) {
-    var inputs = builder.config.buildDir + '/fuzz_inputs';
+const mkFuzz = function (builder, testPath, testId, fuzzCases, cb) {
+    const inputs = builder.config.buildDir + '/fuzz_inputs';
     nThen(function (w) {
-        var casesPath = testPath.replace(/\.c$/, '_cases');
+        const casesPath = testPath.replace(/\.c$/, '_cases');
         Fs.readdir(casesPath, w(function (err, list) {
             if (err && err.code === 'ENOENT') {
                 return void console.log("Fuzz test [" + testPath + "] has no test cases");
             }
             if (err) { throw err; }
-            var nt = nThen;
+            let nt = nThen;
             list.forEach(function (f) {
                 nt = nt(function (w) {
-                    var fNoExt = f.replace(/\..*$/, '');
-                    var outPath = inputs + '/' + (testPath + fNoExt).replace(/[^a-zA-Z0-9_-]/g, '_');
+                    const fNoExt = f.replace(/\..*$/, '');
+                    const outPath = inputs + '/' + (testPath + fNoExt).replace(/[^a-zA-Z0-9_-]/g, '_');
                     fuzzCases.push('"' + outPath + '",');
-                    var inFile = casesPath + '/' + f;
+                    const inFile = casesPath + '/' + f;
                     mkFuzzCase(inFile, outPath, testId, w());
                 }).nThen;
             });
@@ -123,13 +128,13 @@ var mkFuzz = function (builder, testPath, testId, fuzzCases, cb) {
     });
 };
 
-var generate = module.exports.generate = function (file, builder, isSubnode, callback)
-{
-    var tests = [];
-    var fuzzCases = [];
-    var prototypes = [];
-    var listContent = [];
-    var fuzzTests = [];
+module.exports.generate = function (builder, isSubnode, callback) {
+    const tests = [];
+    const fuzzCases = [];
+    const prototypes = [];
+    const listContent = [];
+    const fuzzTests = [];
+    builder.config.cjdnsTest_files = builder.config.cjdnsTest_files || [];
 
     nThen(function (w) {
         getTests('.', tests, isSubnode, w());
@@ -137,9 +142,9 @@ var generate = module.exports.generate = function (file, builder, isSubnode, cal
     }).nThen(function (w) {
         tests.forEach(function (test) {
             //console.log(test);
-            var testProto = /^.*\/([^\/]+)\.c$/.exec(test)[1];
-            file.links.push(test);
-            var cflags = (builder.config['cflags'+test] = builder.config['cflags'+test] || []);
+            const testProto = /^.*\/([^\/]+)\.c$/.exec(test)[1];
+            builder.config.cjdnsTest_files.push(test);
+            const cflags = (builder.config.fileCflags[test] = builder.config.fileCflags[test] || []);
             if (test.indexOf('_fuzz_test') > -1) {
                 cflags.push(
                     '-D', 'CJDNS_FUZZ_INIT='+testProto+'_FUZZ_INIT',
@@ -166,10 +171,10 @@ var generate = module.exports.generate = function (file, builder, isSubnode, cal
             }
         });
     }).nThen(function (w) {
-        file.testcjdroute_fuzzCases = fuzzCases.join(' ');
-        file.testcjdroute_tests = listContent.join(' ');
-        file.testcjdroute_fuzzTests = fuzzTests.join(' ');
-        file.testcjdroute_prototypes = prototypes.join(' ');
+        builder.config.cjdnsTest_fuzzCases = fuzzCases.join(' ');
+        builder.config.cjdnsTest_tests = listContent.join(' ');
+        builder.config.cjdnsTest_fuzzTests = fuzzTests.join(' ');
+        builder.config.cjdnsTest_prototypes = prototypes.join(' ');
         callback();
     });
 };

File diff suppressed because it is too large
+ 324 - 377
node_build/builder.js


+ 2 - 2
node_build/dependencies/cnacl/node_build/PlanRunner.js

@@ -165,7 +165,7 @@ var getPrototypes = function (onComplete) {
 
 var getCompiler = function(cc) {
   return function(compileCall, onComplete) {
-    console.log('\033[2;32mCompiling ' + compileCall.outFile + '\033[0m');
+    console.log('\x1b[2;32mCompiling ' + compileCall.outFile + '\x1b[0m');
     /*console.log('cc -o ' + compileCall.outFile + ' -c ' + compileCall.inFile + ' ' +
                   compileCall.args.join(' ')); */
     var args = [];
@@ -224,7 +224,7 @@ var archive = function(ar, ranlib, onComplete) {
   Fs.readdir(OBJ_DIR, function(err, files) {
     if (err) { throw err; }
     var args = ['cr', BUILD_DIR + '/libnacl.a'];
-    console.log('\033[1;31mLinking static C library ' + args[1] + '\033[0m');
+    console.log('\x1b[1;31mLinking static C library ' + args[1] + '\x1b[0m');
     files.forEach(function(file) {
       args.push(OBJ_DIR + '/' + file);
     });

+ 2 - 2
node_build/dependencies/cnacl/node_build/TestRunner.js

@@ -9,7 +9,7 @@ var WORKERS = 4;
 
 var getCompiler = function(cc) {
   return function(compileCall, onComplete) {
-    console.log('\033[2;32mCompiling Test ' + compileCall.outFile + '\033[0m');
+    console.log('\x1b[2;32mCompiling Test ' + compileCall.outFile + '\x1b[0m');
     /*console.log('cc -o ' + compileCall.outFile + ' -c ' + compileCall.inFile + ' ' +
                   compileCall.args.join(' ')); */
     var args = [];
@@ -89,7 +89,7 @@ var getRunner = function() {
     var expectedFile = 'tests/' + entry.outFile.replace(/^.*\//, '') + '.out';
     Fs.exists(expectedFile, function(exists) {
       if (!exists) { expectedFile = expectedFile.replace(/_[^_]*/, '') + '.out'; }
-      console.log('\033[2;36mRunning Test ' + expectedFile + '\033[0m');
+      console.log('\x1b[2;36mRunning Test ' + expectedFile + '\x1b[0m');
       Fs.readFile(expectedFile, function(err, expected) {
         if (err) { throw err; }
         expected = expected.toString();

+ 23 - 17
node_build/make.js

@@ -12,6 +12,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+/*@flow*/
+'use strict';
 var Fs = require('fs');
 var nThen = require('nthen');
 var Codestyle = require('./Codestyle');
@@ -22,6 +24,7 @@ var FindPython = require('./FindPython');
 var CanCompile = require('./CanCompile');
 var Builder = require('./builder');
 var TestRunner = require('./TestRunner');
+const CjdnsTest = require('./CjdnsTest');
 
 // ['linux','darwin','sunos','win32','freebsd','openbsd','netbsd']
 var SYSTEM = process.env['SYSTEM'] || process.platform;
@@ -79,6 +82,10 @@ Builder.configure({
         '-D', 'PARANOIA=1'
     );
 
+    if (process.env["CJDNS_RELEASE_VERSION"]) {
+        builder.config.version = '' + process.env["CJDNS_RELEASE_VERSION"];
+    }
+
     if (process.env['SUBNODE']) { builder.config.cflags.push('-DSUBNODE=1'); }
 
     if (process.env['GCOV']) {
@@ -129,7 +136,7 @@ Builder.configure({
         }
     }
 
-    if (builder.config.compilerType.isClang) {
+    if (builder.compilerType().isClang) {
         // blows up when preprocessing before js preprocessor
         builder.config.cflags.push(
             '-Wno-invalid-pp-token',
@@ -186,9 +193,9 @@ Builder.configure({
         console.log("Link time optimization disabled");
     } else {
         CanCompile.check(builder,
-                        'int main() { return 0; }',
+                        'int main() { return 0; }\n',
                         [ builder.config.cflags, '-flto', '-x', 'c' ],
-                        function (err, can) {
+                        waitFor(function (err, can) {
             if (can) {
                 console.log("Compiler supports link time optimization");
                 builder.config.ldflags.push(
@@ -198,7 +205,7 @@ Builder.configure({
             } else {
                 console.log("Link time optimization not supported [" + err + "]");
             }
-        });
+        }));
     }
 
     var uclibc = process.env['UCLIBC'] == '1';
@@ -246,6 +253,10 @@ Builder.configure({
         );
     }
 
+    if (typeof(builder.config.cjdnsTest_files) === 'undefined') {
+        CjdnsTest.generate(builder, process.env['SUBNODE'] !== '', waitFor());
+    }
+
     var dependencyDir = builder.config.buildDir + '/dependencies';
     var libuvLib = dependencyDir + '/libuv/out/Release/libuv.a';
     if (['win32', 'netbsd'].indexOf(builder.config.systemName) >= 0) {//this might be needed for other BSDs
@@ -277,6 +288,7 @@ Builder.configure({
             var cwd = process.cwd();
             process.chdir(dependencyDir + '/cnacl/');
 
+            // $FlowFixMe non-static require
             var NaCl = require(process.cwd() + '/node_build/make.js');
             NaCl.build(function (args, callback) {
                 if (builder.config.systemName !== 'win32') {
@@ -396,7 +408,7 @@ Builder.configure({
             });
             gyp.on('close', waitFor(function () {
                 var args = [
-                    '-j', builder.processors,
+                    '-j', ''+builder.processors,
                     '-C', 'out',
                     'BUILDTYPE=Release',
                     'CC=' + builder.config.gcc,
@@ -419,11 +431,11 @@ Builder.configure({
 
                 make.on('error', function (err) {
                     if (err.code === 'ENOENT') {
-                        console.error('\033[1;31mError: ' + makeCommand + ' is required!\033[0m');
+                        console.error('\x1b[1;31mError: ' + makeCommand + ' is required!\x1b[0m');
                     } else {
                         console.error(
-                            '\033[1;31mFail run ' + process.cwd() + ': ' + makeCommand + ' '
-                            + args.join(' ') + '\033[0m'
+                            '\x1b[1;31mFail run ' + process.cwd() + ': ' + makeCommand + ' '
+                            + args.join(' ') + '\x1b[0m'
                         );
                         console.error('Message:', err);
                     }
@@ -476,17 +488,11 @@ Builder.configure({
 
 }).success(function (builder, waitFor) {
 
-    console.log('\033[1;32mBuild completed successfully, type ./cjdroute to begin setup.\033[0m');
+    console.log('\x1b[1;32mBuild completed successfully, type ./cjdroute to begin setup.\x1b[0m');
 
 }).failure(function (builder, waitFor) {
 
-    console.log('\033[1;31mFailed to build cjdns.\033[0m');
+    console.log('\x1b[1;31mFailed to build cjdns.\x1b[0m');
     process.exit(1);
 
-}).complete(function (builder, waitFor) {
-
-    if (builder.failure) {
-        process.exit(1);
-    }
-
-});
+});

+ 8 - 6
test/RootTest.h

@@ -16,17 +16,19 @@
 #define RootTest_H
 
 #include "util/CString.h"
+#include "util/Js.h"
+
 #include <stdio.h>
 
 #define RootTest_toStr(x) RootTest_toStr2(x)
 #define RootTest_toStr2(x) #x
-<?js
-    file.RootTest_mainFunc = RootTest_toStr(main);
-    file.RootTest_mainName = file.RootTest_mainFunc.replace(/_main$/, '');
-?>
-#define RootTest_mainName <?js return '"' + file.RootTest_mainName + '"' ?>
+Js({
+    this.RootTest_mainFunc = RootTest_toStr(main);
+    this.RootTest_mainName = this.RootTest_mainFunc.replace(/_main$/, '');
+})
+#define RootTest_mainName Js_or({ return '"' + this.RootTest_mainName + '"' }, "main")
 
-#define RootTest_main <?js return 'RootTest_'+file.RootTest_mainFunc; ?>
+#define RootTest_main Js_or({ return 'RootTest_' + this.RootTest_mainFunc; }, RootTest_main)
 
 int RootTest_main(int argc, char** argv);
 int main(int argc, char** argv)

+ 5 - 8
test/testcjdroute.c

@@ -33,11 +33,7 @@
     #define testcjdroute_SUBNODE 0
 #endif
 
-Js({
-    require("./test/testcjdroute.js").generate(
-        file, builder, testcjdroute_SUBNODE, this.async());
-})
-Js({ return file.testcjdroute_prototypes; })
+Js({ return builder.config.cjdnsTest_prototypes; })
 
 typedef int (* Test)(int argc, char** argv);
 typedef void* (* FuzzTestInit)(struct Allocator* alloc, struct Random* rand);
@@ -47,19 +43,20 @@ typedef struct FuzzTest* (* MkFuzz)(struct Allocator* alloc);
 static const struct {
     Test func;
     char* name;
-} TESTS[] = { Js({ return file.testcjdroute_tests }) };
+} TESTS[] = { Js({ return builder.config.cjdnsTest_tests }) };
 static const int TEST_COUNT = (int) (sizeof(TESTS) / sizeof(*TESTS));
 
 static const struct {
     FuzzTestInit init;
     FuzzTest fuzz;
     char* name;
-} FUZZ_TESTS[] = { Js({ return file.testcjdroute_fuzzTests }) };
+} FUZZ_TESTS[] = { Js({ return builder.config.cjdnsTest_fuzzTests }) };
 static const int FUZZ_TEST_COUNT = (int) (sizeof(FUZZ_TESTS) / sizeof(*FUZZ_TESTS));
 
-static const char* FUZZ_CASES[] = { Js({ return file.testcjdroute_fuzzCases }) };
+static const char* FUZZ_CASES[] = { Js({ return builder.config.cjdnsTest_fuzzCases }) };
 static const int FUZZ_CASE_COUNT = (int) (sizeof(FUZZ_CASES) / sizeof(*FUZZ_CASES));
 
+Js({ builder.config.cjdnsTest_files.forEach((f) => js.linkerDependency(f)); })
 
 static uint64_t runTest(Test test,
                         char* name,

+ 11 - 24
util/Constant.h

@@ -15,36 +15,23 @@
 #ifndef Constant_H
 #define Constant_H
 
-#ifdef __INTELLISENSE__
-#define Constant_stringForHex(hex) ""
+#include "util/Js.h"
 
-#define Constant_base2(num) 0xffffffff
+Js({ this.Constant_JS = require("../util/Constant.js"); })
 
-#define Constant_rand64() 0x12345678abcdefull
+#define Constant_stringForHex(hex) Js_or({ return this.Constant_JS.stringForHex( hex ) }, "")
 
-#define Constant_rand32() 0x12345678u
+#define Constant_base2(num) Js_or({ return this.Constant_JS.base2( #num ); }, 0xffffffff)
 
-#define Constant_randHexString(len) ""
+#define Constant_rand64() \
+    Js_or({ return this.Constant_JS.rand64(this, js.currentFile); }, 0x12345678abcdefull)
 
-#define Constant_log2(num) 0
-
-#else
-
-<?js file.Constant_JS = require("../util/Constant.js"); ?>
-
-#define Constant_stringForHex(hex) <?js return file.Constant_JS.stringForHex( hex ) ?>
-
-#define Constant_base2(num) <?js return file.Constant_JS.base2( #num ) ?>
-
-#define Constant_rand64() <?js return file.Constant_JS.rand64(file); ?>
-
-#define Constant_rand32() <?js return file.Constant_JS.rand32(file); ?>
+#define Constant_rand32() \
+    Js_or({ return this.Constant_JS.rand32(this, js.currentFile); }, 0x12345678u)
 
 #define Constant_randHexString(len) \
-    <?js return file.Constant_JS.randHexString(#len, file); ?>
-
-#define Constant_log2(num) <?js return file.Constant_JS.log2(num); ?>
+    Js_or({ return this.Constant_JS.randHexString(#len, this, js.currentFile); }, "")
 
-#endif
+#define Constant_log2(num) Js_or({ return this.Constant_JS.log2(num); }, 0)
 
-#endif
+#endif

+ 4 - 7
util/Defined.h

@@ -15,6 +15,8 @@
 #ifndef Defined_H
 #define Defined_H
 
+#include "util/Js.h"
+
 /**
  * Preprocesses to a 1 if the macro is defined, a 0 if it is not.
  * Example:
@@ -23,12 +25,7 @@
  *     doLinuxSpecificStuff();
  * }
  */
-#ifdef __INTELLISENSE__
-    #define Defined(macro) 1
-#else
-    #define Defined_Q <?js return String.fromCharCode(34); ?>
-    #define Defined(macro) \
-        <?js return ( Defined_Q macro Defined_Q === ' ' + #macro + ' ' ) ? '0' : '1'; ?>
-#endif
+#define Defined(macro) \
+    Js_or({ return ( Js_Q macro Js_Q === ' ' + #macro + ' ' ) ? '0' : '1'; }, 1)
 
 #endif

+ 2 - 2
util/Gcc.h

@@ -94,10 +94,10 @@
     #define Gcc_USE_RET
 #endif
 
-Js({ file.Gcc_shortFile = (x) => '"' + x.substring(x.lastIndexOf('/')+1) + '"'; })
+Js({ this.Gcc_shortFile = (x) => '"' + x.substring(x.lastIndexOf('/')+1) + '"'; })
 
 #define Gcc_SHORT_FILE \
-    Js_or({ return file.Gcc_shortFile(__FILE__); }, __FILE__)
+    Js_or({ return this.Gcc_shortFile(__FILE__); }, __FILE__)
 
 #define Gcc_FILE Gcc_SHORT_FILE
 #define Gcc_LINE __LINE__

+ 3 - 2
util/Identity.h

@@ -17,14 +17,15 @@
 
 #include "util/Assert.h"
 #include "util/Constant.h"
+#include "util/Js.h"
 
 #include <stdint.h>
 
 #if defined(Identity_CHECK)
 
-    <?js file.Identity_hash = "0x" + Constant_randHexString(16) + "ull"; ?>
+    Js({ this.Identity_hash = "0x" + Constant_randHexString(16) + "ull"; })
 
-    #define Identity_MAGIC ((uintptr_t) <?js return file.Identity_hash ?>)
+    #define Identity_MAGIC ((uintptr_t) Js_or({ return this.Identity_hash }, 1))
 
     /** This goes in each structure which will be checked. */
     #define Identity uintptr_t Identity_verifier;

+ 1 - 1
util/Js.h

@@ -29,4 +29,4 @@
     #define Js_or(x,y) Js(x)
 #endif
 
-#endif
+#endif

+ 5 - 6
util/Kbps.h

@@ -16,6 +16,7 @@
 #define Kbps_H
 
 #include "util/Constant.h"
+#include "util/Js.h"
 
 // Must be multiples of 2
 #define Kbps_WINDOW_SIZE 8
@@ -74,12 +75,10 @@ static inline uint32_t Kbps_accumulate(struct Kbps* ctx, uint32_t now, uint32_t
     // *= 2 ** 10               --> bytes per millisecond to bytes per second
     // *= 2 ** 3                --> bytes per second to bits per second
     // /= 2 ** 10               --> bits per second to kbits per second
-    return (
-        ctx->currentBpt <?js
-            var x = (Number(Kbps_TIMESPAN_SH) - 10 - 3 + 10);
-            return ( ((x) < 0) ? "<<" : ">>" ) + " " + Math.abs(x);
-        ?>
-    );
+    return Js_or({
+        var x = (Number(Kbps_TIMESPAN_SH) - 10 - 3 + 10);
+        return "ctx->currentBpt" + ( ((x) < 0) ? "<<" : ">>" ) + " " + Math.abs(x);
+    }, ctx->currentBpt);
 }
 
 #endif

+ 5 - 6
util/Linker.h

@@ -15,9 +15,8 @@
 #ifndef Linker_H
 #define Linker_H
 
-#ifdef __INTELLISENSE__
-#define Linker_require(req)
-#else
-#define Linker_require(req) <?js file.links.push(req); ?>
-#endif
-#endif
+#include "util/Js.h"
+
+#define Linker_require(req) Js({ js.linkerDependency(req); })
+
+#endif

+ 2 - 438
util/Seccomp.c

@@ -12,443 +12,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-
-// sigaction() siginfo_t SIG_UNBLOCK
-#define _POSIX_C_SOURCE 199309L
-
 #include "util/Seccomp.h"
-#include "util/Bits.h"
-#include "util/ArchInfo.h"
-#include "util/Defined.h"
-
-// getpriority()
-#include <sys/resource.h>
-#include <signal.h>
-#include <sys/prctl.h>
-#include <errno.h>
-#include <linux/filter.h>
-#include <linux/seccomp.h>
-#include <linux/audit.h>
-#include <linux/netlink.h>
-#include <sys/syscall.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-
-/**
- * A unique number which is returned as errno by getpriority(), a syscall we never use
- * this will be used by Seccomp_isWorking() to detect that the filter has been properly installed.
- */
-#define IS_WORKING_ERRNO 3333
-
-/**
- * Accessing the SIGSYS siginfo depends on the fields being defined by the libc.
- * Older libc do not yet include the needed definitions and accessor macros.
- * Work around that by falling back to si_value.sival_int which works on some
- * but not all architectures.
- */
-#if defined(si_syscall)
-# define GET_SYSCALL_NUM(si) ((si)->si_syscall)
-#else
-#pragma message "your libc doesn't define SIGSYS signal info! \
-info about syscall number in case of SECCOMP crash can be invalid"
-# define GET_SYSCALL_NUM(si) ((si)->si_value.sival_int)
-#endif
-
-static void catchViolation(int sig, siginfo_t* si, void* threadContext)
-{
-    printf("Attempted banned syscall number [%d] see doc/Seccomp.md for more information\n",
-           GET_SYSCALL_NUM(si));
-
-    if (Defined(si_syscall)) {
-        printf("Your libc doesn't define SIGSYS signal info. "
-               "Above information about syscall number can be invalid.\n");
-    }
-
-    Assert_failure("Disallowed Syscall");
-}
-
-struct Filter {
-    int label;
-    int jt;
-    int jf;
-    struct sock_filter sf;
-};
-
-static struct sock_fprog* compile(struct Filter* input, int inputLen, struct Allocator* alloc)
-{
-    // compute gotos
-    int totalOut = 0;
-    for (int i = inputLen-1; i >= 0; i--) {
-        struct Filter* a = &input[i];
-        if (a->label == 0) {
-            // check for unresolved gotos...
-            Assert_true(a->jt == 0 && a->jf == 0);
-            totalOut++;
-            continue;
-        }
-        int diff = 0;
-        for (int j = i-1; j >= 0; j--) {
-            struct Filter* b = &input[j];
-            if (b->label != 0) { continue; }
-            if (b->jt == a->label) {
-                b->sf.jt = diff;
-                b->jt = 0;
-            }
-            if (b->jf == a->label) {
-                b->sf.jf = diff;
-                b->jf = 0;
-            }
-            diff++;
-        }
-    }
-
-    // copy into output filter array...
-    struct sock_filter* sf = Allocator_calloc(alloc, sizeof(struct sock_filter), totalOut);
-    int outI = 0;
-    for (int i = 0; i < inputLen; i++) {
-        if (input[i].label == 0) {
-            Bits_memcpy(&sf[outI++], &input[i].sf, sizeof(struct sock_filter));
-        }
-        Assert_true(outI <= totalOut);
-        Assert_true(i != inputLen-1 || outI == totalOut);
-    }
-
-    struct sock_fprog* out = Allocator_malloc(alloc, sizeof(struct sock_fprog));
-    out->len = (unsigned short) totalOut;
-    out->filter = sf;
-
-    return out;
-}
-
-#define RET_TRAP      0x00030000u
-#define RET_ERRNO(x) (0x00050000u | ((x) & 0x0000ffffu))
-#define RET_SUCCESS   0x7fff0000u
-
-static Er_DEFUN(struct sock_fprog* mkFilter(struct Allocator* alloc))
-{
-    // Adding exceptions to the syscall filter:
-    //
-    // echo '#include <sys/syscall.h>' | gcc -E -dM - | grep 'define __NR_' | sort
-    // for the full list of system calls with syscall numbers (different per ABI)
-    //
-    // If gdb traps out it will look like this:
-    //
-    //     Program received signal SIGSYS, Bad system call.
-    //     [Switching to Thread 0x7ffff7fdd740 (LWP 14673)]
-    //     0x00007ffff74d1caa in mmap64 () at ../sysdeps/unix/syscall-template.S:81
-    //     81    ../sysdeps/unix/syscall-template.S: No such file or directory.
-    //
-    // %eax should contain the system call number (on different ABIs YMMV)
-    //
-    //     (gdb) print $eax
-    //     $1 = 9
-    //     (gdb)
-    //
-    // Consult your syscall table from the above gcc command...
-    //
-    //     #define __NR_mmap 9
-    //
-    // Then add:
-    //
-    //     IFEQ(__NR_mmap, success),
-    //
-    // And add a comment documenting where you needed that syscall :)
-
-    #define STMT(code, val) { .sf = BPF_STMT(code, val) }
-
-    #define JMPK(type, not, input, label) { \
-        .sf = BPF_JUMP(BPF_JMP+(type)+BPF_K, (input), 0, 0), \
-        .jt = (!(not) ? (label) : 0),                        \
-        .jf = ((not) ? (label) : 0)                          \
-    }
-
-    // Create a label for jumps, the label must be represented by a non-zero integer.
-    #define LABEL(lbl) { .label = (lbl) }
-
-    // Load offset into the register
-    #define LOAD(offset)    STMT(BPF_LD+BPF_W+BPF_ABS, (offset))
-
-    // Return constant value
-    #define RET(val)        STMT(BPF_RET+BPF_K, (val))
-
-    // If-equal if the currently loaded value equals input, jump to label.
-    #define IFEQ(input, label) JMPK(BPF_JEQ, 0, (input), (label))
-
-    // If-not-equal if the currently loaded value is not equal to input, jump to label.
-    #define IFNE(input, label) JMPK(BPF_JEQ, 1, (input), (label))
-
-    // If-greater-than
-    #define IFGT(input, label) JMPK(BPF_JGT, 0, (input), (label))
-
-    // If-greater-than-or-equal-to
-    #define IFGE(input, label) JMPK(BPF_JGE, 0, (input), (label))
-
-    // If-less-than
-    #define IFLT(input, label) JMPK(BPF_JGE, 1, (input), (label))
-
-    // If-less-than-or-equal-to
-    #define IFLE(input, label) JMPK(BPF_JGT, 1, (input), (label))
-
-
-    // labels are integers so they must be predefined
-    int success = 1;
-    int fail = 2;
-    int unmaskOnly = 3;
-    int isworking = 4;
-    int socket = 5;
-    int ioctl_setip = 6;
-    int bind_netlink = 7;
-
-    uint32_t auditArch = ArchInfo_getAuditArch();
-
-    struct Filter seccompFilter[] = {
-
-        LOAD(offsetof(struct seccomp_data, arch)),
-        IFNE(auditArch, fail),
-
-        // Get the syscall num.
-        LOAD(offsetof(struct seccomp_data, nr)),
-
-        // udp
-        #ifdef __NR_sendmsg
-            IFEQ(__NR_sendmsg, success),
-        #endif
-        #ifdef __NR_recvmsg
-            IFEQ(__NR_recvmsg, success),
-        #endif
-
-        // ETHInterface
-        #ifdef __NR_sendto
-            IFEQ(__NR_sendto, success),
-        #endif
-        #ifdef __NR_recvfrom
-            IFEQ(__NR_recvfrom, success),
-        #endif
-
-        #ifdef __NR_socketcall
-            // 32-bit: recvmsg is a socketcall
-            IFEQ(__NR_socketcall, success),
-        #endif
-
-        // libuv
-        IFEQ(__NR_epoll_ctl, success),
-        #ifdef __NR_epoll_wait
-            IFEQ(__NR_epoll_wait, success),
-        #endif
-        #ifdef __NR_epoll_pwait
-            IFEQ(__NR_epoll_pwait, success),
-        #endif
-
-        // gettimeofday is required on some architectures
-        #ifdef __NR_gettimeofday
-            IFEQ(__NR_gettimeofday, success),
-        #endif
-
-        // TUN (and logging)
-        IFEQ(__NR_write, success),
-        IFEQ(__NR_read, success),
-        // readv and writev are used by some libc (musl)
-        #ifdef __NR_readv
-            IFEQ(__NR_readv, success),
-        #endif
-        #ifdef __NR_writev
-            IFEQ(__NR_writev, success),
-        #endif
-
-        // modern librt reads a read-only mapped section of kernel space which contains the time
-        // older versions need system calls for getting the time.
-        // i686 glibc-2.18's time() uses __NR_time
-        // Raspberry Pi and BeagleBone Black don't provide __NR_time
-        // 32-bit systems with 64-bit time_t use __NR_clock_gettime64
-        #ifdef __NR_clock_gettime64
-            IFEQ(__NR_clock_gettime64, success),
-        #endif
-        #ifdef __NR_clock_gettime
-            IFEQ(__NR_clock_gettime, success),
-        #endif
-        #ifdef __NR_time
-            IFEQ(__NR_time, success),
-        #endif
-
-        // NetPlatform_linux.c send recv
-        #ifdef __NR_send
-            IFEQ(__NR_send, success),
-        #endif
-        #ifdef __NR_recv
-            IFEQ(__NR_recv, success),
-        #endif
-
-        // malloc()
-        IFEQ(__NR_brk, success),
-
-        // abort()
-        IFEQ(__NR_gettid, success),
-        IFEQ(__NR_tgkill, success),
-        IFEQ(__NR_rt_sigprocmask, unmaskOnly),
-
-        // exit()
-        IFEQ(__NR_exit_group, success),
-
-        // Seccomp_isWorking()
-        IFEQ(__NR_getpriority, isworking),
-
-        // Securiy_checkPermissions() -> canOpenFiles()
-        IFEQ(__NR_dup, success),
-        IFEQ(__NR_close, success),
-
-        // Security_checkPermissions() -> getMaxMem()
-        // x86/ARM use ugetrlimit and mmap2
-        // ARM does not even have __NR_getrlimit or __NR_mmap defined
-        // and AMD64 does not have __NR_ugetrlimit or __NR_mmap2 defined
-        #ifdef __NR_getrlimit
-            IFEQ(__NR_getrlimit, success),
-        #endif
-        #ifdef __NR_ugetrlimit
-            IFEQ(__NR_ugetrlimit, success),
-        #endif
-        #ifdef __NR_mmap
-            IFEQ(__NR_mmap, success),
-        #endif
-        #ifdef __NR_mmap2
-            IFEQ(__NR_mmap2, success),
-        #endif
-        IFEQ(__NR_munmap, success),
-
-        // printf()
-        IFEQ(__NR_fstat, success),
-        #ifdef __NR_fstat64
-            IFEQ(__NR_fstat64, success),
-        #endif
-
-        // for setting IP addresses
-        // socketForIfName()
-        // and ETHInterface_listDevices
-        #ifdef __NR_socket
-            IFEQ(__NR_socket, socket),
-        #endif
-        IFEQ(__NR_ioctl, ioctl_setip),
-
-        // Security_checkPermissions
-        IFEQ(__NR_getuid, success),
-        // Security_nofiles
-        IFEQ(__NR_setrlimit, success),
-
-        // for ETHInterface_listDevices (netlinkk)
-        #ifdef __NR_bind
-        IFEQ(__NR_bind, bind_netlink),
-        #endif
-        #ifdef __NR_getsockname
-        IFEQ(__NR_getsockname, success),
-        #endif
-
-        // musl free() calls madvise()
-        #ifdef __NR_madvise
-        IFEQ(__NR_madvise, success),
-        #endif
-
-        // accept() for PipeServer
-        #ifdef __NR_accept4
-        IFEQ(__NR_accept4, success),
-        #endif
-
-        #ifdef android
-            #ifdef __NR_rt_sigprocmask
-            IFEQ(__NR_rt_sigprocmask, success),
-            #endif
-        #endif
-
-        RET(SECCOMP_RET_TRAP),
-
-        LABEL(socket),
-        LOAD(offsetof(struct seccomp_data, args[1])),
-        IFEQ(SOCK_DGRAM, success),
-        LOAD(offsetof(struct seccomp_data, args[0])),
-        IFEQ(AF_NETLINK, success),
-        RET(SECCOMP_RET_TRAP),
-
-        LABEL(ioctl_setip),
-        LOAD(offsetof(struct seccomp_data, args[1])),
-        IFEQ(SIOCGIFINDEX, success),
-        IFEQ(SIOCGIFFLAGS, success),
-        IFEQ(SIOCSIFFLAGS, success),
-        IFEQ(SIOCSIFADDR, success),
-        IFEQ(SIOCSIFNETMASK, success),
-        IFEQ(SIOCSIFMTU, success),
-        RET(SECCOMP_RET_TRAP),
-
-        LABEL(bind_netlink),
-        LOAD(offsetof(struct seccomp_data, args[2])),
-        // Filter NETLINK by size of address.
-        // Most importantly INET and INET6
-        // are differnt.
-        IFEQ(sizeof(struct sockaddr_nl), success),
-        RET(SECCOMP_RET_TRAP),
-
-        // We allow sigprocmask to *unmask* signals but we don't allow it to mask them.
-        LABEL(unmaskOnly),
-        LOAD(offsetof(struct seccomp_data, args[0])),
-        IFEQ(SIG_UNBLOCK, success),
-        RET(SECCOMP_RET_TRAP),
-
-        LABEL(isworking),
-        RET(RET_ERRNO(IS_WORKING_ERRNO)),
-
-        LABEL(fail),
-        RET(SECCOMP_RET_TRAP),
-
-        LABEL(success),
-        RET(SECCOMP_RET_ALLOW),
-    };
-
-    Er_ret(compile(seccompFilter, sizeof(seccompFilter)/sizeof(seccompFilter[0]), alloc));
-}
-
-static Er_DEFUN(void installFilter(
-    struct sock_fprog* filter, struct Log* logger, struct Allocator* alloc))
-{
-    struct sigaction sa = { .sa_sigaction = catchViolation, .sa_flags = SA_SIGINFO };
-    if (sigaction(SIGSYS, &sa, NULL)) {
-        Log_warn(logger, "sigaction(SIGSYS) -> [%s]\n", strerror(errno));
-    }
-
-    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
-        // don't worry about it.
-        Log_warn(logger, "prctl(PR_SET_NO_NEW_PRIVS) -> [%s]\n", strerror(errno));
-    }
-    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, filter) == -1) {
-        Er_raise(alloc, "prctl(PR_SET_SECCOMP) -> [%s]\n", strerror(errno));
-    }
-    Er_ret();
-}
-
-Er_DEFUN(void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger))
-{
-    struct sock_fprog* filter = Er(mkFilter(tempAlloc));
-    Er(installFilter(filter, logger, tempAlloc));
-    if (!Seccomp_isWorking()) {
-        Er_raise(tempAlloc, "Seccomp filter not installed properly, Seccomp_isWorking() -> false");
-    }
-    Er_ret();
-}
-
-int Seccomp_isWorking(void)
-{
-    errno = 0;
-    // If seccomp is not working, this will fail setting errno to EINVAL
-    long ret = getpriority(1000, 1);
-
-    int err = errno;
-
-    // Inside of the kernel, it seems to check whether the errno return is sane
-    // and if it is not, it treates it as a return value, IS_WORKING_ERRNO (3333) is very unique so
-    // we'll check for either case just in case this changes.
-    return (ret == -1 && err == IS_WORKING_ERRNO) || (ret == -IS_WORKING_ERRNO && err == 0);
-}
+#include "util/Js.h"
 
-int Seccomp_exists(void)
-{
-    return 1;
-}
+Js({ require("../util/Seccomp.js").detect(js, builder); })

+ 3 - 12
util/Seccomp.h

@@ -15,17 +15,8 @@
 #ifndef Seccomp_H
 #define Seccomp_H
 
-#include "exception/Er.h"
-#include "memory/Allocator.h"
-#include "util/log/Log.h"
-#include "util/Js.h"
-
-Js({ require("../util/Seccomp.js").detect(this.async, file, builder); })
-
-Er_DEFUN(void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger));
-
-int Seccomp_isWorking(void);
-
-int Seccomp_exists(void);
+#include "util/Seccomp_impl.h"
+#include "util/Linker.h"
+Linker_require("util/Seccomp.c")
 
 #endif

+ 8 - 34
util/Seccomp.js

@@ -29,18 +29,6 @@ var TEST_PROGRAM = [
     "}"
 ].join('\n');
 
-var pushLinks = function (builder) {
-    builder.Seccomp_QUEUE.forEach(function (file) {
-        if (builder.Seccomp_EXISTS) {
-            file.links.push("util/Seccomp.c");
-        } else {
-            file.links.push("util/Seccomp_dummy.c");
-        }
-    });
-
-    builder.Seccomp_QUEUE = undefined;
-};
-
 // Turns a version string into an array of integers
 // 1.2.3-4-generic-x-5  -> [1, 2, 3, 4, 5]
 // 1.2.3-xx-14.2        -> [1, 2, 3, 14, 2]
@@ -80,23 +68,9 @@ var seccomp_version_check = function (version) {
     return compare_versions(ver_list, [3, 5, 0]);
 };
 
-var detect = module.exports.detect = function (async, file, builder)
+module.exports.detect = function (js, builder)
 {
-    if (typeof(builder.Seccomp_QUEUE) !== 'undefined') {
-        builder.Seccomp_QUEUE.push(file);
-        return;
-    }
-
-    builder.Seccomp_QUEUE = [ file ];
-
-    if (typeof(builder.Seccomp_EXISTS) !== 'undefined') {
-        pushLinks(builder);
-        return;
-    }
-
     console.log("Searching for SECCOMP");
-
-    var hasSeccomp = false;
     var osversion = Os.release();
 
     if (builder.config.systemName !== 'linux') {
@@ -106,24 +80,24 @@ var detect = module.exports.detect = function (async, file, builder)
     } else if (!builder.config.crossCompiling && (seccomp_version_check(osversion) === -1)) {
         console.log("SECCOMP filtering is only available in Linux 3.5+");
     } else {
-        var done = async();
+        var done = js.async();
         var CanCompile = require('../node_build/CanCompile');
         var cflags = [ builder.config.cflags, '-x', 'c' ];
 
         CanCompile.check(builder, TEST_PROGRAM, cflags, function (err, can) {
             builder.Seccomp_EXISTS = !!can;
 
-            if (!can) {
+            if (can) {
+                console.log("SECCOMP enabled");
+                js.linkerDependency("util/Seccomp_linux.c");
+            } else {
                 console.log("Failed to get SECCOMP, compile failure: [" + err + "]");
+                js.linkerDependency("util/Seccomp_dummy.c");
             }
-
-            pushLinks(builder);
             done();
         });
 
         return;
     }
-
-    builder.Seccomp_EXISTS = hasSeccomp;
-    pushLinks(builder);
+    js.linkerDependency("util/Seccomp_dummy.c");
 };

+ 1 - 1
util/Seccomp_dummy.c

@@ -12,7 +12,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-#include "util/Seccomp.h"
+#include "util/Seccomp_impl.h"
 
 // This file is for machines which do not support seccomp.
 

+ 28 - 0
util/Seccomp_impl.h

@@ -0,0 +1,28 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef Seccomp_impl_H
+#define Seccomp_impl_H
+
+#include "exception/Er.h"
+#include "memory/Allocator.h"
+#include "util/log/Log.h"
+
+Er_DEFUN(void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger));
+
+int Seccomp_isWorking(void);
+
+int Seccomp_exists(void);
+
+#endif

+ 454 - 0
util/Seccomp_linux.c

@@ -0,0 +1,454 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+// sigaction() siginfo_t SIG_UNBLOCK
+#define _POSIX_C_SOURCE 199309L
+
+#include "util/Seccomp_impl.h"
+#include "util/Bits.h"
+#include "util/ArchInfo.h"
+#include "util/Defined.h"
+
+// getpriority()
+#include <sys/resource.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <errno.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/audit.h>
+#include <linux/netlink.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * A unique number which is returned as errno by getpriority(), a syscall we never use
+ * this will be used by Seccomp_isWorking() to detect that the filter has been properly installed.
+ */
+#define IS_WORKING_ERRNO 3333
+
+/**
+ * Accessing the SIGSYS siginfo depends on the fields being defined by the libc.
+ * Older libc do not yet include the needed definitions and accessor macros.
+ * Work around that by falling back to si_value.sival_int which works on some
+ * but not all architectures.
+ */
+#if defined(si_syscall)
+# define GET_SYSCALL_NUM(si) ((si)->si_syscall)
+#else
+#pragma message "your libc doesn't define SIGSYS signal info! \
+info about syscall number in case of SECCOMP crash can be invalid"
+# define GET_SYSCALL_NUM(si) ((si)->si_value.sival_int)
+#endif
+
+static void catchViolation(int sig, siginfo_t* si, void* threadContext)
+{
+    printf("Attempted banned syscall number [%d] see doc/Seccomp.md for more information\n",
+           GET_SYSCALL_NUM(si));
+
+    if (Defined(si_syscall)) {
+        printf("Your libc doesn't define SIGSYS signal info. "
+               "Above information about syscall number can be invalid.\n");
+    }
+
+    Assert_failure("Disallowed Syscall");
+}
+
+struct Filter {
+    int label;
+    int jt;
+    int jf;
+    struct sock_filter sf;
+};
+
+static struct sock_fprog* compile(struct Filter* input, int inputLen, struct Allocator* alloc)
+{
+    // compute gotos
+    int totalOut = 0;
+    for (int i = inputLen-1; i >= 0; i--) {
+        struct Filter* a = &input[i];
+        if (a->label == 0) {
+            // check for unresolved gotos...
+            Assert_true(a->jt == 0 && a->jf == 0);
+            totalOut++;
+            continue;
+        }
+        int diff = 0;
+        for (int j = i-1; j >= 0; j--) {
+            struct Filter* b = &input[j];
+            if (b->label != 0) { continue; }
+            if (b->jt == a->label) {
+                b->sf.jt = diff;
+                b->jt = 0;
+            }
+            if (b->jf == a->label) {
+                b->sf.jf = diff;
+                b->jf = 0;
+            }
+            diff++;
+        }
+    }
+
+    // copy into output filter array...
+    struct sock_filter* sf = Allocator_calloc(alloc, sizeof(struct sock_filter), totalOut);
+    int outI = 0;
+    for (int i = 0; i < inputLen; i++) {
+        if (input[i].label == 0) {
+            Bits_memcpy(&sf[outI++], &input[i].sf, sizeof(struct sock_filter));
+        }
+        Assert_true(outI <= totalOut);
+        Assert_true(i != inputLen-1 || outI == totalOut);
+    }
+
+    struct sock_fprog* out = Allocator_malloc(alloc, sizeof(struct sock_fprog));
+    out->len = (unsigned short) totalOut;
+    out->filter = sf;
+
+    return out;
+}
+
+#define RET_TRAP      0x00030000u
+#define RET_ERRNO(x) (0x00050000u | ((x) & 0x0000ffffu))
+#define RET_SUCCESS   0x7fff0000u
+
+static Er_DEFUN(struct sock_fprog* mkFilter(struct Allocator* alloc))
+{
+    // Adding exceptions to the syscall filter:
+    //
+    // echo '#include <sys/syscall.h>' | gcc -E -dM - | grep 'define __NR_' | sort
+    // for the full list of system calls with syscall numbers (different per ABI)
+    //
+    // If gdb traps out it will look like this:
+    //
+    //     Program received signal SIGSYS, Bad system call.
+    //     [Switching to Thread 0x7ffff7fdd740 (LWP 14673)]
+    //     0x00007ffff74d1caa in mmap64 () at ../sysdeps/unix/syscall-template.S:81
+    //     81    ../sysdeps/unix/syscall-template.S: No such file or directory.
+    //
+    // %eax should contain the system call number (on different ABIs YMMV)
+    //
+    //     (gdb) print $eax
+    //     $1 = 9
+    //     (gdb)
+    //
+    // Consult your syscall table from the above gcc command...
+    //
+    //     #define __NR_mmap 9
+    //
+    // Then add:
+    //
+    //     IFEQ(__NR_mmap, success),
+    //
+    // And add a comment documenting where you needed that syscall :)
+
+    #define STMT(code, val) { .sf = BPF_STMT(code, val) }
+
+    #define JMPK(type, not, input, label) { \
+        .sf = BPF_JUMP(BPF_JMP+(type)+BPF_K, (input), 0, 0), \
+        .jt = (!(not) ? (label) : 0),                        \
+        .jf = ((not) ? (label) : 0)                          \
+    }
+
+    // Create a label for jumps, the label must be represented by a non-zero integer.
+    #define LABEL(lbl) { .label = (lbl) }
+
+    // Load offset into the register
+    #define LOAD(offset)    STMT(BPF_LD+BPF_W+BPF_ABS, (offset))
+
+    // Return constant value
+    #define RET(val)        STMT(BPF_RET+BPF_K, (val))
+
+    // If-equal if the currently loaded value equals input, jump to label.
+    #define IFEQ(input, label) JMPK(BPF_JEQ, 0, (input), (label))
+
+    // If-not-equal if the currently loaded value is not equal to input, jump to label.
+    #define IFNE(input, label) JMPK(BPF_JEQ, 1, (input), (label))
+
+    // If-greater-than
+    #define IFGT(input, label) JMPK(BPF_JGT, 0, (input), (label))
+
+    // If-greater-than-or-equal-to
+    #define IFGE(input, label) JMPK(BPF_JGE, 0, (input), (label))
+
+    // If-less-than
+    #define IFLT(input, label) JMPK(BPF_JGE, 1, (input), (label))
+
+    // If-less-than-or-equal-to
+    #define IFLE(input, label) JMPK(BPF_JGT, 1, (input), (label))
+
+
+    // labels are integers so they must be predefined
+    int success = 1;
+    int fail = 2;
+    int unmaskOnly = 3;
+    int isworking = 4;
+    int socket = 5;
+    int ioctl_setip = 6;
+    int bind_netlink = 7;
+
+    uint32_t auditArch = ArchInfo_getAuditArch();
+
+    struct Filter seccompFilter[] = {
+
+        LOAD(offsetof(struct seccomp_data, arch)),
+        IFNE(auditArch, fail),
+
+        // Get the syscall num.
+        LOAD(offsetof(struct seccomp_data, nr)),
+
+        // udp
+        #ifdef __NR_sendmsg
+            IFEQ(__NR_sendmsg, success),
+        #endif
+        #ifdef __NR_recvmsg
+            IFEQ(__NR_recvmsg, success),
+        #endif
+
+        // ETHInterface
+        #ifdef __NR_sendto
+            IFEQ(__NR_sendto, success),
+        #endif
+        #ifdef __NR_recvfrom
+            IFEQ(__NR_recvfrom, success),
+        #endif
+
+        #ifdef __NR_socketcall
+            // 32-bit: recvmsg is a socketcall
+            IFEQ(__NR_socketcall, success),
+        #endif
+
+        // libuv
+        IFEQ(__NR_epoll_ctl, success),
+        #ifdef __NR_epoll_wait
+            IFEQ(__NR_epoll_wait, success),
+        #endif
+        #ifdef __NR_epoll_pwait
+            IFEQ(__NR_epoll_pwait, success),
+        #endif
+
+        // gettimeofday is required on some architectures
+        #ifdef __NR_gettimeofday
+            IFEQ(__NR_gettimeofday, success),
+        #endif
+
+        // TUN (and logging)
+        IFEQ(__NR_write, success),
+        IFEQ(__NR_read, success),
+        // readv and writev are used by some libc (musl)
+        #ifdef __NR_readv
+            IFEQ(__NR_readv, success),
+        #endif
+        #ifdef __NR_writev
+            IFEQ(__NR_writev, success),
+        #endif
+
+        // modern librt reads a read-only mapped section of kernel space which contains the time
+        // older versions need system calls for getting the time.
+        // i686 glibc-2.18's time() uses __NR_time
+        // Raspberry Pi and BeagleBone Black don't provide __NR_time
+        // 32-bit systems with 64-bit time_t use __NR_clock_gettime64
+        #ifdef __NR_clock_gettime64
+            IFEQ(__NR_clock_gettime64, success),
+        #endif
+        #ifdef __NR_clock_gettime
+            IFEQ(__NR_clock_gettime, success),
+        #endif
+        #ifdef __NR_time
+            IFEQ(__NR_time, success),
+        #endif
+
+        // NetPlatform_linux.c send recv
+        #ifdef __NR_send
+            IFEQ(__NR_send, success),
+        #endif
+        #ifdef __NR_recv
+            IFEQ(__NR_recv, success),
+        #endif
+
+        // malloc()
+        IFEQ(__NR_brk, success),
+
+        // abort()
+        IFEQ(__NR_gettid, success),
+        IFEQ(__NR_tgkill, success),
+        IFEQ(__NR_rt_sigprocmask, unmaskOnly),
+
+        // exit()
+        IFEQ(__NR_exit_group, success),
+
+        // Seccomp_isWorking()
+        IFEQ(__NR_getpriority, isworking),
+
+        // Securiy_checkPermissions() -> canOpenFiles()
+        IFEQ(__NR_dup, success),
+        IFEQ(__NR_close, success),
+
+        // Security_checkPermissions() -> getMaxMem()
+        // x86/ARM use ugetrlimit and mmap2
+        // ARM does not even have __NR_getrlimit or __NR_mmap defined
+        // and AMD64 does not have __NR_ugetrlimit or __NR_mmap2 defined
+        #ifdef __NR_getrlimit
+            IFEQ(__NR_getrlimit, success),
+        #endif
+        #ifdef __NR_ugetrlimit
+            IFEQ(__NR_ugetrlimit, success),
+        #endif
+        #ifdef __NR_mmap
+            IFEQ(__NR_mmap, success),
+        #endif
+        #ifdef __NR_mmap2
+            IFEQ(__NR_mmap2, success),
+        #endif
+        IFEQ(__NR_munmap, success),
+
+        // printf()
+        IFEQ(__NR_fstat, success),
+        #ifdef __NR_fstat64
+            IFEQ(__NR_fstat64, success),
+        #endif
+
+        // for setting IP addresses
+        // socketForIfName()
+        // and ETHInterface_listDevices
+        #ifdef __NR_socket
+            IFEQ(__NR_socket, socket),
+        #endif
+        IFEQ(__NR_ioctl, ioctl_setip),
+
+        // Security_checkPermissions
+        IFEQ(__NR_getuid, success),
+        // Security_nofiles
+        IFEQ(__NR_setrlimit, success),
+
+        // for ETHInterface_listDevices (netlinkk)
+        #ifdef __NR_bind
+        IFEQ(__NR_bind, bind_netlink),
+        #endif
+        #ifdef __NR_getsockname
+        IFEQ(__NR_getsockname, success),
+        #endif
+
+        // musl free() calls madvise()
+        #ifdef __NR_madvise
+        IFEQ(__NR_madvise, success),
+        #endif
+
+        // accept() for PipeServer
+        #ifdef __NR_accept4
+        IFEQ(__NR_accept4, success),
+        #endif
+
+        #ifdef android
+            #ifdef __NR_rt_sigprocmask
+            IFEQ(__NR_rt_sigprocmask, success),
+            #endif
+        #endif
+
+        RET(SECCOMP_RET_TRAP),
+
+        LABEL(socket),
+        LOAD(offsetof(struct seccomp_data, args[1])),
+        IFEQ(SOCK_DGRAM, success),
+        LOAD(offsetof(struct seccomp_data, args[0])),
+        IFEQ(AF_NETLINK, success),
+        RET(SECCOMP_RET_TRAP),
+
+        LABEL(ioctl_setip),
+        LOAD(offsetof(struct seccomp_data, args[1])),
+        IFEQ(SIOCGIFINDEX, success),
+        IFEQ(SIOCGIFFLAGS, success),
+        IFEQ(SIOCSIFFLAGS, success),
+        IFEQ(SIOCSIFADDR, success),
+        IFEQ(SIOCSIFNETMASK, success),
+        IFEQ(SIOCSIFMTU, success),
+        RET(SECCOMP_RET_TRAP),
+
+        LABEL(bind_netlink),
+        LOAD(offsetof(struct seccomp_data, args[2])),
+        // Filter NETLINK by size of address.
+        // Most importantly INET and INET6
+        // are differnt.
+        IFEQ(sizeof(struct sockaddr_nl), success),
+        RET(SECCOMP_RET_TRAP),
+
+        // We allow sigprocmask to *unmask* signals but we don't allow it to mask them.
+        LABEL(unmaskOnly),
+        LOAD(offsetof(struct seccomp_data, args[0])),
+        IFEQ(SIG_UNBLOCK, success),
+        RET(SECCOMP_RET_TRAP),
+
+        LABEL(isworking),
+        RET(RET_ERRNO(IS_WORKING_ERRNO)),
+
+        LABEL(fail),
+        RET(SECCOMP_RET_TRAP),
+
+        LABEL(success),
+        RET(SECCOMP_RET_ALLOW),
+    };
+
+    Er_ret(compile(seccompFilter, sizeof(seccompFilter)/sizeof(seccompFilter[0]), alloc));
+}
+
+static Er_DEFUN(void installFilter(
+    struct sock_fprog* filter, struct Log* logger, struct Allocator* alloc))
+{
+    struct sigaction sa = { .sa_sigaction = catchViolation, .sa_flags = SA_SIGINFO };
+    if (sigaction(SIGSYS, &sa, NULL)) {
+        Log_warn(logger, "sigaction(SIGSYS) -> [%s]\n", strerror(errno));
+    }
+
+    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
+        // don't worry about it.
+        Log_warn(logger, "prctl(PR_SET_NO_NEW_PRIVS) -> [%s]\n", strerror(errno));
+    }
+    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, filter) == -1) {
+        Er_raise(alloc, "prctl(PR_SET_SECCOMP) -> [%s]\n", strerror(errno));
+    }
+    Er_ret();
+}
+
+Er_DEFUN(void Seccomp_dropPermissions(struct Allocator* tempAlloc, struct Log* logger))
+{
+    struct sock_fprog* filter = Er(mkFilter(tempAlloc));
+    Er(installFilter(filter, logger, tempAlloc));
+    if (!Seccomp_isWorking()) {
+        Er_raise(tempAlloc, "Seccomp filter not installed properly, Seccomp_isWorking() -> false");
+    }
+    Er_ret();
+}
+
+int Seccomp_isWorking(void)
+{
+    errno = 0;
+    // If seccomp is not working, this will fail setting errno to EINVAL
+    long ret = getpriority(1000, 1);
+
+    int err = errno;
+
+    // Inside of the kernel, it seems to check whether the errno return is sane
+    // and if it is not, it treates it as a return value, IS_WORKING_ERRNO (3333) is very unique so
+    // we'll check for either case just in case this changes.
+    return (ret == -1 && err == IS_WORKING_ERRNO) || (ret == -IS_WORKING_ERRNO && err == 0);
+}
+
+int Seccomp_exists(void)
+{
+    return 1;
+}

+ 5 - 5
util/Setuid.h

@@ -18,20 +18,20 @@
 #include "memory/Allocator.h"
 #include "exception/Except.h"
 #include "util/Linker.h"
-#define Setuid_IMPL "util/Setuid_" + builder.config.systemName + ".c"
 
 Er_DEFUN(void Setuid_preSetuid(struct Allocator* alloc));
 Er_DEFUN(void Setuid_postSetuid(struct Allocator* alloc));
 
 Js({
-    var done = this.async();
-    require("fs").exists(Setuid_IMPL, function (exists) {
+    const done = js.async();
+    const impl = "util/Setuid_" + builder.config.systemName + ".c";
+    require("fs").exists(impl, function (exists) {
         var out = "";
         if (!exists) {
             console.log("No setuid keepNetAdmin");
-            file.links.push("util/Setuid_dummy.c");
+            js.linkerDependency("util/Setuid_dummy.c");
         } else {
-            file.links.push(Setuid_IMPL);
+            js.linkerDependency(impl);
             console.log("Has setuid keepNetAdmin");
         }
         done();

+ 7 - 11
util/UniqueName.h

@@ -15,17 +15,13 @@
 #ifndef UniqueName_H
 #define UniqueName_H
 
-#ifdef __INTELLISENSE__
-#define UniqueName_get() UniqueName
-#define UniqueName_last() UniqueName
-#else
-<?js file.Constant_JS = file.Constant_JS || require('util/Constant.js'); ?>
+#include "util/Constant.h"
+#include "util/Js.h"
 
-#define UniqueName_get() <?js return \
-    file.UniqueName_last = 'UniqueName_' + \
-        JSON.parse(file.Constant_JS.randHexString(20, file)); \
-?>
+#define UniqueName_get() Js_or({ \
+    return this.UniqueName_last = 'UniqueName_' + Constant_randHexString(20); \
+}, UniqueName)
+
+#define UniqueName_last() Js_or({ return this.UniqueName_last; }, UniqueName)
 
-#define UniqueName_last() <?js return file.UniqueName_last; ?>
-#endif
 #endif

+ 2 - 6
util/events/libuv/UvWrapper.h

@@ -15,17 +15,13 @@
 #ifndef UvWrapper_H
 #define UvWrapper_H
 
+#include "util/Js.h"
+
 #ifndef _GNU_SOURCE
     #define _GNU_SOURCE // CHECKFILES_IGNORE libuv's fault
 #endif
 #ifdef win32
     #define _WIN32_WINNT 0x0600 // CHECKFILES_IGNORE
-
-    // Remove compiler flags which blow up when running with windows.
-    <?js
-        builder.config["cflags"+fileName] = builder.config["cflags"+fileName] || [];
-        builder.config["cflags"+fileName].push("!-pedantic");
-    ?>
 #endif
 
 #include <uv.h>

+ 8 - 7
util/version/Version.c

@@ -12,16 +12,17 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+#include "util/Js.h"
 
-#define Version_COMPAT(one, twoRange) <?js \
-    file.Version_COMPAT = file.Version_COMPAT || []; \
-    file.Version_COMPAT[one] = twoRange;             \
-?>
+#define Version_COMPAT(one, twoRange) Js({ \
+    this.Version_COMPAT = this.Version_COMPAT || []; \
+    this.Version_COMPAT[one] = twoRange;             \
+})
 
 #include "util/version/Version.h"
 
 static const uint8_t VERSION_MATRIX[Version_CURRENT_PROTOCOL+1][Version_CURRENT_PROTOCOL+1] =
-<?js
+Js_or({
     var matrix = [];
     for (var i = 0; i <= Version_CURRENT_PROTOCOL; i++) {
         var row = matrix[matrix.length] = [];
@@ -30,12 +31,12 @@ static const uint8_t VERSION_MATRIX[Version_CURRENT_PROTOCOL+1][Version_CURRENT_
                 row[j] = 1;
             } else {
                 row[j] =
-                    (file.Version_COMPAT[Math.max(i, j)].indexOf(Math.min(i,j)) > -1) ? 1 : 0;
+                    (this.Version_COMPAT[Math.max(i, j)].indexOf(Math.min(i,j)) > -1) ? 1 : 0;
             }
         }
     }
     return JSON.stringify(matrix).replace(/\[/g,'{').replace(/\]/g,'}');
-?>;
+}, {});
 
 int Version_isCompatible(uint32_t one, uint32_t two)
 {

Some files were not shown because too many files changed in this diff