diff --git a/lib/child_utils.js b/lib/child_utils.js new file mode 100644 index 0000000..1237c4e --- /dev/null +++ b/lib/child_utils.js @@ -0,0 +1,61 @@ +var processQueue = require('process-queue'); + +var default_queue = processQueue.createQueue({concurrency:1}); + +exports.exec = function (line, opts, cb) { + var stdout, stdout_length, stderr, stderr_length; + + // avoid allocation + if (cb) { + stdout = []; + stdout_length = 0; + stderr = []; + stderr_length = 0; + } + + var child; + var queue = opts.queue || default_queue; + if (cb) { + queue = queue.wrap({ + child: function (spawned_child, next) { + child = spawned_child; + if (cb) { + child.stdout.on('data', function (data) { + if (!Buffer.isBuffer(data)) data = new Buffer(data); + stdout_length += data.length; + stdout.push(data); + }); + child.stderr.on('data', function (data) { + if (!Buffer.isBuffer(data)) data = new Buffer(data); + stderr_length += data.length; + stderr.push(data); + }); + } + next(null, child); + } + }); + } + var cmd = { + spawnOptions: ['sh', ['-c', 'exec ' + line]] + }; + function onFinish(err) { + if (!cb) return; + if (!child) { + cb(new Error('there was a problem spawning the child process')); + return; + } + if (err) { + err.killed = child.killed; + err.code = child.exitCode; + err.signal = child.signalCode; + } + cb(err, + stdout_length ? String(Buffer.concat(stdout, stdout_length)) : '', + stderr_length ? String(Buffer.concat(stderr, stderr_length)) : '' + ); + } + queue.push(cmd, onFinish); + + // cleanup for gc + cmd = null; +} diff --git a/lib/ipset/add.js b/lib/ipset/add.js index 2c53f52..fd36502 100644 --- a/lib/ipset/add.js +++ b/lib/ipset/add.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Add a given entry to the set. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/create.js b/lib/ipset/create.js index ca9fc7b..a6bed4c 100644 --- a/lib/ipset/create.js +++ b/lib/ipset/create.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Create a set identified with setsetname and specified type. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/del.js b/lib/ipset/del.js index f1bfa39..3188fba 100644 --- a/lib/ipset/del.js +++ b/lib/ipset/del.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Delete an entry from a set. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/destroy.js b/lib/ipset/destroy.js index 6e34d29..683a342 100644 --- a/lib/ipset/destroy.js +++ b/lib/ipset/destroy.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Destroy the specified set or all the sets if none is given. @@ -44,7 +44,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -56,4 +56,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/flush.js b/lib/ipset/flush.js index b9121a3..b5f1d00 100644 --- a/lib/ipset/flush.js +++ b/lib/ipset/flush.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Flush all entries from the specified set or flush all sets if none is given. @@ -44,7 +44,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -56,4 +56,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/rename.js b/lib/ipset/rename.js index 8baaab7..974b011 100644 --- a/lib/ipset/rename.js +++ b/lib/ipset/rename.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Rename a set. @@ -35,7 +35,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -47,4 +47,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/swap.js b/lib/ipset/swap.js index 28b6db5..9897164 100644 --- a/lib/ipset/swap.js +++ b/lib/ipset/swap.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Swap the content of two sets. @@ -35,7 +35,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -47,4 +47,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/test.js b/lib/ipset/test.js index f31f9ba..3f4f289 100644 --- a/lib/ipset/test.js +++ b/lib/ipset/test.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Test wether an entry is in a set or not. @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null, 0); } }); -}; \ No newline at end of file +}; diff --git a/lib/ipset/version.js b/lib/ipset/version.js index 3627916..50fc45c 100644 --- a/lib/ipset/version.js +++ b/lib/ipset/version.js @@ -1,12 +1,16 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; /** * Print program version. * * @param cb */ -module.exports = function (cb) { - if (typeof arguments[0] != 'function') { +module.exports = function (options, cb) { + if (typeof options === 'function') { + cb = options; + options = {}; + } + if (typeof cb != 'function') { throw new Error('Invalid arguments. Signature: (callback)'); } @@ -22,7 +26,7 @@ module.exports = function (cb) { /* * Execute command. */ - exec(cmd.join(' '), function (error, stdout, stderror) { + exec(cmd.join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -34,4 +38,4 @@ module.exports = function (cb) { cb(null, stdout.split('\n')[0]); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/append.js b/lib/iptables/append.js index 9f0204a..c445bdc 100644 --- a/lib/iptables/append.js +++ b/lib/iptables/append.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -46,7 +46,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -58,4 +58,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/check.js b/lib/iptables/check.js index 707266c..4bcbf99 100644 --- a/lib/iptables/check.js +++ b/lib/iptables/check.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -51,7 +51,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); diff --git a/lib/iptables/delete.js b/lib/iptables/delete.js index 1dfb4c2..cfa3b19 100644 --- a/lib/iptables/delete.js +++ b/lib/iptables/delete.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -51,7 +51,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -63,4 +63,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/delete_chain.js b/lib/iptables/delete_chain.js index f187da3..049f051 100644 --- a/lib/iptables/delete_chain.js +++ b/lib/iptables/delete_chain.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -54,7 +54,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -66,4 +66,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/dump.js b/lib/iptables/dump.js index e2b9eb1..e81a976 100644 --- a/lib/iptables/dump.js +++ b/lib/iptables/dump.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var shell_quote = require('shell-quote'); var tables = require('./utils').tables; @@ -117,7 +117,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); diff --git a/lib/iptables/flush.js b/lib/iptables/flush.js index 6147cfc..0af81a9 100644 --- a/lib/iptables/flush.js +++ b/lib/iptables/flush.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -54,7 +54,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -66,4 +66,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/insert.js b/lib/iptables/insert.js index e95fabd..608547c 100644 --- a/lib/iptables/insert.js +++ b/lib/iptables/insert.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -51,7 +51,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -63,4 +63,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/new.js b/lib/iptables/new.js index f598f4c..8e6a32d 100644 --- a/lib/iptables/new.js +++ b/lib/iptables/new.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -41,7 +41,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -53,4 +53,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/policy.js b/lib/iptables/policy.js index 558fbcd..ff9a659 100644 --- a/lib/iptables/policy.js +++ b/lib/iptables/policy.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -45,7 +45,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -57,4 +57,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/rename.js b/lib/iptables/rename.js index fdcb5ff..e0f1025 100644 --- a/lib/iptables/rename.js +++ b/lib/iptables/rename.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -45,7 +45,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue:options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -57,4 +57,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/replace.js b/lib/iptables/replace.js index 7bac661..7092bf6 100644 --- a/lib/iptables/replace.js +++ b/lib/iptables/replace.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; var processCommonRuleSpecs = require('./utils').processCommonRuleSpecs; @@ -51,7 +51,7 @@ module.exports = function (options, cb) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -63,4 +63,4 @@ module.exports = function (options, cb) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/lib/iptables/zero.js b/lib/iptables/zero.js index 8d49d3f..64d8d23 100644 --- a/lib/iptables/zero.js +++ b/lib/iptables/zero.js @@ -1,4 +1,4 @@ -var exec = require('child_process').exec; +var exec = require('../child_utils').exec; var tables = require('./utils').tables; @@ -58,7 +58,7 @@ module.exports = function (/* options?, cb */) { /* * Execute command. */ - exec(cmd.concat(args).join(' '), function (error, stdout, stderror) { + exec(cmd.concat(args).join(' '), {queue: options.cmdQueue}, function (error, stdout, stderror) { if (error && cb) { var err = new Error(stderror.split('\n')[0]); err.cmd = cmd.concat(args).join(' '); @@ -70,4 +70,4 @@ module.exports = function (/* options?, cb */) { cb(null); } }); -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index f0d700d..a49cf78 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ "set" ], "dependencies": { - "async": "*", + "async": "~0.9.0", + "offspring": "~1.0.0", + "process-queue": "^1.0.1", "shell-quote": "^1.4.2" }, "engines": { @@ -46,5 +48,8 @@ "repository": { "type": "git", "url": "https://github.com/diosney/node-netfilter.git" + }, + "scripts": { + "test": "node test/*.js" } }