diff --git a/bin/genieacs-cwmp b/bin/genieacs-cwmp
index a135c9e..7dd92d9 100755
--- a/bin/genieacs-cwmp
+++ b/bin/genieacs-cwmp
@@ -2,9 +2,8 @@
var path = require('path');
var fs = require('fs');
-var dir = path.join(path.dirname(fs.realpathSync(__filename)), '../lib/');
-process.title = 'genieacs-cwmp';
+var dir = path.resolve(path.dirname(fs.realpathSync(__filename)), '..');
process.chdir(dir);
-require(path.join(dir, './cwmp'));
-
+cluster = require(path.resolve(dir, 'lib/cluster'));
+cluster.start('cwmp');
diff --git a/bin/genieacs-fs b/bin/genieacs-fs
index 67d4571..5d0253b 100755
--- a/bin/genieacs-fs
+++ b/bin/genieacs-fs
@@ -2,9 +2,8 @@
var path = require('path');
var fs = require('fs');
-var dir = path.join(path.dirname(fs.realpathSync(__filename)), '../lib/');
-process.title = 'genieacs-fs';
+var dir = path.resolve(path.dirname(fs.realpathSync(__filename)), '..');
process.chdir(dir);
-require(path.join(dir, './fs'));
-
+cluster = require(path.resolve(dir, 'lib/cluster'));
+cluster.start('fs');
diff --git a/bin/genieacs-nbi b/bin/genieacs-nbi
index 6bf4f72..5069105 100755
--- a/bin/genieacs-nbi
+++ b/bin/genieacs-nbi
@@ -2,9 +2,8 @@
var path = require('path');
var fs = require('fs');
-var dir = path.join(path.dirname(fs.realpathSync(__filename)), '../lib/');
-process.title = 'genieacs-nbi';
+var dir = path.resolve(path.dirname(fs.realpathSync(__filename)), '..');
process.chdir(dir);
-require(path.join(dir, './nbi'));
-
+cluster = require(path.resolve(dir, 'lib/cluster'));
+cluster.start('nbi');
diff --git a/config/config-sample.json b/config/config-sample.json
index 0105624..5e5b1b9 100644
--- a/config/config-sample.json
+++ b/config/config-sample.json
@@ -1,15 +1,15 @@
{
- "MONGODB_CONNECTION_URL" : "mongodb://localhost/genieacs",
- "REDIS_SOCKET" : "6379",
- "ACS_INTERFACE" : "0.0.0.0",
- "ACS_PORT" : 7547,
- "ACS_HTTPS_INTERFACE" : "0.0.0.0",
- "ACS_HTTPS_PORT" : 7548,
- "API_INTERFACE" : "0.0.0.0",
- "API_PORT" : 7557,
- "FILES_INTERFACE" : "0.0.0.0",
- "FILES_PORT" : 7567,
+ "MONGODB_CONNECTION_URL" : "mongodb://127.0.0.1/genieacs",
+ "REDIS_PORT" : "6379",
+ "REDIS_HOST" : "127.0.0.1",
+ "CWMP_INTERFACE" : "0.0.0.0",
+ "CWMP_PORT" : 7547,
+ "CWMP_SSL" : false,
+ "NBI_INTERFACE" : "0.0.0.0",
+ "NBI_PORT" : 7557,
+ "FS_INTERFACE" : "0.0.0.0",
+ "FS_PORT" : 7567,
"FILES_IP" : "192.168.0.1",
"LOG_INFORMS" : true,
- "DEBUG_DEVICES" : {}
+ "DEBUG" : false
}
diff --git a/config/httpscert-sample.crt b/config/httpscert-sample.crt
deleted file mode 100644
index 77af0ba..0000000
--- a/config/httpscert-sample.crt
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICqTCCAhICCQDSrolRw1qPBzANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMC
-SVExFTATBgNVBAgMDFN1bGF5bWFuaXlhaDEVMBMGA1UEBwwMU3VsYXltYW5peWFo
-MQ8wDQYDVQQKDAZGYW5vb3MxEzARBgNVBAsMClRlY2hub2xvZ3kxDjAMBgNVBAMM
-BWdlbmllMSUwIwYJKoZIhvcNAQkBFhZ6YWlkLmFiZHVsbGFAZmFub29zLmlxMB4X
-DTEzMDIxNzEyNTYzN1oXDTE0MDIxNzEyNTYzN1owgZgxCzAJBgNVBAYTAklRMRUw
-EwYDVQQIDAxTdWxheW1hbml5YWgxFTATBgNVBAcMDFN1bGF5bWFuaXlhaDEPMA0G
-A1UECgwGRmFub29zMRMwEQYDVQQLDApUZWNobm9sb2d5MQ4wDAYDVQQDDAVnZW5p
-ZTElMCMGCSqGSIb3DQEJARYWemFpZC5hYmR1bGxhQGZhbm9vcy5pcTCBnzANBgkq
-hkiG9w0BAQEFAAOBjQAwgYkCgYEAxtHC3jlOoVsZQ9I8Wgd+8NkEaRfc01lRIAzj
-eWuUO/xNY54EUDHecRxwolHtoGkkZAp4kKR3uUVdAo9uXmNT2CeeZ5lfRoa8WmgG
-Yd6PExsdc4ZeKcw20XrKtQICqmU6rGlLERMFX97ejQh3eUmqwbzNaXiURxcKGdeK
-2C/NxoUCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAYFw6dy/z74ahnA5QpuJOjjTrt
-AJaL9iuL1xS6TFfzne9UMIT+fFJsgSy6xx3TGkvbZJTnCzJN7I2uqmAhMoGFy7Ea
-svCCkj8U5qkjqgwwi7IbwQ7fXxTVdPqUlkIdNB2YLDELksxCX+ZzGQPiFB8WrSoO
-OBvB+hhPFpgp3aO2AQ==
------END CERTIFICATE-----
diff --git a/config/httpscert-sample.key b/config/httpscert-sample.key
deleted file mode 100644
index 6493262..0000000
--- a/config/httpscert-sample.key
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDG0cLeOU6hWxlD0jxaB37w2QRpF9zTWVEgDON5a5Q7/E1jngRQ
-Md5xHHCiUe2gaSRkCniQpHe5RV0Cj25eY1PYJ55nmV9GhrxaaAZh3o8TGx1zhl4p
-zDbResq1AgKqZTqsaUsREwVf3t6NCHd5SarBvM1peJRHFwoZ14rYL83GhQIDAQAB
-AoGAR4NpuW8ZhlZzNv7m8Gcj1NPqjzajGm5VDNc19xWoAw+UcpGC8amOXlcheHFJ
-rNpE2asehMBzw/NcKjCXPWIy5LnGmttJMN2EbT2h/+uxBIM5IUc+joF744xF4C0t
-5zrtqQYJxC7sBLX2vf7nY6iuovUM7fHEwACyV3Rpw3zdZf0CQQDiqW+ugLKcfS/c
-6hkGTxeYWF4gUJTf3ke3au8nyMmUhyBwOuP4kpO72mK8bMLtjZRk3g6YChA4qH41
-nfXUXIh7AkEA4I2/zj4GDjjspDR+/tX0iUHQk1Rm2CRmRXXFJV1f688OLODCcOFK
-Ms/+qnbe41wXo1NUxCqVrnPwEh3MVl88/wJBANUTpXqtX7s+8ETECTxmE+9Se0Hz
-ntCJXG/v5d1Ho0Cueu8mXXMrEBiTpygtMl4xL2RXX+7L3w30T5lF4nh9eIsCQQDY
-DmsbdL/81TaTc9MxPQlxOzsFVrMxiixA7uTGY8ZULD/2XDeZxprOsyPUPlWMquIv
-ipFHwbWNSg4WUEd4X2BfAkEAytze+0mCd0WCsKbKwfTQnmJ3bK7JQywtC03llWyA
-Om3Vl61Syaa7M2hl5rLvitZMvU1Wd0s8raOXN8RUuRXZBw==
------END RSA PRIVATE KEY-----
diff --git a/lib/cluster.coffee b/lib/cluster.coffee
new file mode 100644
index 0000000..9ea3d98
--- /dev/null
+++ b/lib/cluster.coffee
@@ -0,0 +1,45 @@
+###
+# Copyright 2013, 2014 Zaid Abdulla
+#
+# GenieACS is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# GenieACS 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with GenieACS. If not, see .
+###
+
+config = require './config'
+cluster = require 'cluster'
+util = require 'util'
+
+
+cluster.on('listening', (worker, address) ->
+ util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
+)
+
+cluster.on('exit', (worker, code, signal) ->
+ util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
+ cluster.fork()
+)
+
+
+start = (service) ->
+ workerProcesses = config["#{service.toUpperCase()}_WORKER_PROCESSES"]
+
+ cluster.setupMaster({
+ exec : 'lib/server',
+ args : [service]
+ })
+
+ for [0 ... workerProcesses]
+ cluster.fork()
+
+
+exports.start = start
diff --git a/lib/config.coffee b/lib/config.coffee
index 64ce694..2b5155e 100644
--- a/lib/config.coffee
+++ b/lib/config.coffee
@@ -15,43 +15,95 @@
# along with GenieACS. If not, see .
###
-exports.MONGODB_CONNECTION_URL = 'mongodb://localhost/genieacs'
-exports.REDIS_DB = 0
-exports.ACS_PORT = 7547 # CWMP port as assigned by IANA
-exports.ACS_HTTPS_PORT = 7548
-exports.ACS_INTERFACE = '0.0.0.0'
-exports.ACS_HTTPS_INTERFACE = '0.0.0.0'
-exports.API_PORT = 7557
-exports.API_INTERFACE = '0.0.0.0'
-exports.FILES_PORT = 7567
-exports.FILES_INTERFACE = '0.0.0.0'
-exports.FILES_IP = '172.240.21.2' # Used when sending download requests to devices
-exports.CACHE_DURATION = 60 # in seconds
-exports.PRESETS_CACHE_DURATION = 86400
-exports.PRESETS_TIME_PADDING = 1
-exports.MONGODB_SOCKET = '/tmp/mongodb-27017.sock'
-exports.REDIS_SOCKET = '/tmp/redis.sock'
-exports.WORKER_RESPAWN_TIME = 60000
-exports.LOG_INFORMS = false
-exports.DEBUG_DEVICES = {} # {'202BC1-BM632w-8KA8WA1151100043' : true}
-exports.DEVICE_ONLINE_THRESHOLD = 4000
-exports.RETRY_DELAY = 300
-exports.IGNORE_XML_NAMESPACES = false # Traverse XML using element's local name only
-exports.LIBXMLJS_OPTIONS = {} # Options passed directly to libxmljs
-exports.SESSION_TIMEOUT = 30
-exports.GET_PARAMETER_NAMES_DEPTH_THRESHOLD = 0
-exports.TASK_PARAMETERS_BATCH_SIZE = 32
+path = require 'path'
+common = require './common'
+
+
+defaults = {
+ CONFIG_DIR : './config',
+
+ MONGODB_CONNECTION_URL : 'mongodb://127.0.0.1/genieacs',
+ REDIS_PORT : 6379,
+ REDIS_HOST : '127.0.0.1',
+ REDIS_DB : 0,
+
+ CWMP_WORKER_PROCESSES : 4,
+ CWMP_PORT : 7547, # CWMP port as assigned by IANA
+ CWMP_INTERFACE : '0.0.0.0',
+ CWMP_SSL : false,
+ NBI_WORKER_PROCESSES : 2,
+ NBI_PORT : 7557,
+ NBI_INTERFACE : '0.0.0.0',
+ NBI_SSL : false,
+ FS_WORKER_PROCESSES : 2,
+ FS_PORT : 7567,
+ FS_INTERFACE : '0.0.0.0',
+ FS_SSL : false,
+ FILES_IP : '192.168.0.1', # Used when sending download requests to devices
+
+ CACHE_DURATION : 60, # in seconds
+ PRESETS_CACHE_DURATION : 86400,
+ PRESETS_TIME_PADDING : 1,
+
+ LOG_INFORMS : true,
+ DEBUG : false,
+ DEVICE_ONLINE_THRESHOLD : 4000,
+ RETRY_DELAY : 300,
+ IGNORE_XML_NAMESPACES : false, # Traverse XML using element's local name only
+ SESSION_TIMEOUT : 30,
+ GET_PARAMETER_NAMES_DEPTH_THRESHOLD : 0,
+ TASK_PARAMETERS_BATCH_SIZE : 32
+}
+
+
+# Command line arguments
+exports.argv = []
+argv = process.argv[2..]
+while argv.length
+ arg = argv.shift()
+ if arg[0] == '-'
+ v = argv.shift()
+ exports.argv[arg] = v
+ n = arg[2..].toUpperCase().replace(/-/g, '_')
+ if defaults[n]?
+ exports[n] ?= common.matchType(defaults[n], v)
+ # Save as environmnet variables to pass on to any child processes
+ process.env["GENIEACS_#{n}"] = v
+ else
+ exports.argv.push(arg)
+
+
+# Environment variable
+for k, v of process.env
+ k = k[9..] # remove "GENIEACS_" prefix
+ if defaults[k]?
+ exports[k] ?= common.matchType(defaults[k], v)
+
+
+# Find config dir
+if exports.argv['--config-dir']?
+ exports.CONFIG_DIR = exports.argv['--config-dir']
+else if process.env['GENIEACS_CONFIG_DIR']?
+ exports.CONFIG_DIR = process.env['GENIEACS_CONFIG_DIR']
+else
+ exports.CONFIG_DIR = defaults.CONFIG_DIR
+
+
+# Configuration file
+for k, v of require(path.resolve(exports.CONFIG_DIR, 'config'))
+ if defaults[k]?
+ exports[k] ?= common.matchType(defaults[k], v)
+
+
+# Defaults
+for k, v of defaults
+ exports[k] ?= defaults[k]
-# load configuration
-c = require('../config/config')
-for k, v of c
- exports[k] = v
# load parameter configurations
-exports.PARAMETERS = require('../config/parameters')
+exports.PARAMETERS = require(path.resolve(exports.CONFIG_DIR, 'parameters'))
# load authentication scripts
-exports.auth = require('../config/auth')
-
-exports.CUSTOM_COMMANDS = require('../config/custom_commands')
+exports.auth = require(path.resolve(exports.CONFIG_DIR, 'auth'))
+exports.CUSTOM_COMMANDS = require(path.resolve(exports.CONFIG_DIR, 'custom_commands'))
diff --git a/lib/cwmp.coffee b/lib/cwmp.coffee
index be25bef..aa74bbb 100644
--- a/lib/cwmp.coffee
+++ b/lib/cwmp.coffee
@@ -41,14 +41,11 @@
config = require './config'
common = require './common'
util = require 'util'
-http = require 'http'
-https = require 'https'
soap = require './soap'
tasks = require './tasks'
db = require './db'
presets = require './presets'
mongodb = require 'mongodb'
-fs = require 'fs'
apiFunctions = require './api-functions'
customCommands = require './custom-commands'
crypto = require 'crypto'
@@ -61,9 +58,9 @@ holdUntil = Date.now()
writeResponse = (currentRequest, res) ->
- if config.DEBUG_DEVICES[currentRequest.session.deviceId]
+ if config.DEBUG
dump = "# RESPONSE #{new Date(Date.now())}\n" + JSON.stringify(res.headers) + "\n#{res.data}\n\n"
- fs = require('fs').appendFile("../debug/#{currentRequest.session.deviceId}.dump", dump, (err) ->
+ require('fs').appendFile("../debug/#{currentRequest.session.deviceId}.dump", dump, (err) ->
throw err if err
)
@@ -454,7 +451,7 @@ listener = (httpRequest, httpResponse) ->
session : session
}
- if config.DEBUG_DEVICES[currentRequest.session.deviceId]
+ if config.DEBUG
dump = "# REQUEST #{new Date(Date.now())}\n" + JSON.stringify(httpRequest.headers) + "\n#{httpRequest.getBody()}\n\n"
require('fs').appendFile("../debug/#{currentRequest.session.deviceId}.dump", dump, (err) ->
throw err if err
@@ -562,39 +559,5 @@ listener = (httpRequest, httpResponse) ->
)
)
-cluster = require 'cluster'
-numCPUs = require('os').cpus().length
-if cluster.isMaster
- db.connect((err) ->
- throw err if err
- db.redisClient.flushdb()
- db.disconnect()
- cluster.on('listening', (worker, address) ->
- util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
- )
-
- cluster.on('exit', (worker, code, signal) ->
- util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
- setTimeout(()->
- cluster.fork()
- , config.WORKER_RESPAWN_TIME)
- )
-
- for [1 .. numCPUs]
- cluster.fork()
- )
-else
- options = {
- key: fs.readFileSync('../config/httpscert.key'),
- cert: fs.readFileSync('../config/httpscert.crt')
- }
-
- httpServer = http.createServer(listener)
- httpsServer = https.createServer(options, listener)
-
- db.connect((err) ->
- throw err if err
- httpServer.listen(config.ACS_PORT, config.ACS_INTERFACE)
- httpsServer.listen(config.ACS_HTTPS_PORT, config.ACS_HTTPS_INTERFACE)
- )
+exports.listener = listener
diff --git a/lib/db.coffee b/lib/db.coffee
index 24fbe71..f3c0633 100644
--- a/lib/db.coffee
+++ b/lib/db.coffee
@@ -70,7 +70,7 @@ connect = (callback) ->
return callback(err)
)
- exports.redisClient = redisClient = redis.createClient(config.REDIS_SOCKET)
+ exports.redisClient = redisClient = redis.createClient(config.REDIS_PORT, config.REDIS_HOST)
redisClient.select(config.REDIS_DB, (err) ->
if --callbackCounter == 0 or err
callbackCounter = 0
diff --git a/lib/fs.coffee b/lib/fs.coffee
index a377f51..26f368a 100644
--- a/lib/fs.coffee
+++ b/lib/fs.coffee
@@ -21,53 +21,29 @@
###
config = require './config'
-util = require 'util'
-http = require 'http'
url = require 'url'
mongodb = require 'mongodb'
querystring = require 'querystring'
+db = require './db'
-db = null
-cluster = require 'cluster'
-numCPUs = require('os').cpus().length
+listener = (request, response) ->
+ urlParts = url.parse(request.url, true)
+ if request.method == 'GET'
+ filename = querystring.unescape(urlParts.pathname.substring(1))
+ gs = new mongodb.GridStore(db.mongoDb, filename, 'r', {})
+ gs.open((err, gs) ->
+ if err
+ response.writeHead(404)
+ response.end()
+ return
+ stream = gs.stream(true)
+ response.writeHead(200, {'Content-Type' : 'application/octet-stream', 'Content-Length' : gs.length})
+ stream.pipe(response)
+ )
+ else
+ response.writeHead(405, {'Allow': 'GET'})
+ response.end('405 Method Not Allowed')
-if cluster.isMaster
- cluster.on('listening', (worker, address) ->
- util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
- )
- cluster.on('exit', (worker, code, signal) ->
- util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
- setTimeout(()->
- cluster.fork()
- , config.WORKER_RESPAWN_TIME)
- )
-
- for i in [1 .. numCPUs]
- cluster.fork()
-else
- server = http.createServer((request, response) ->
- urlParts = url.parse(request.url, true)
- if request.method == 'GET'
- filename = querystring.unescape(urlParts.pathname.substring(1))
- gs = new mongodb.GridStore(db, filename, 'r', {})
- gs.open((err, gs) ->
- if err
- response.writeHead(404)
- response.end()
- return
- stream = gs.stream(true)
- response.writeHead(200, {'Content-Type' : 'application/octet-stream', 'Content-Length' : gs.length})
- stream.pipe(response)
- )
- else
- response.writeHead(405, {'Allow': 'GET'})
- response.end('405 Method Not Allowed')
- )
-
- mongodb.MongoClient.connect("mongodb://#{config.MONGODB_SOCKET}/#{config.DATABASE_NAME}", config.MONGODB_OPTIONS, (err, _db) ->
- throw err if err
- db = _db
- server.listen config.FILES_PORT, config.FILES_INTERFACE
- )
+exports.listener = listener
diff --git a/lib/nbi.coffee b/lib/nbi.coffee
index 40facfa..bdecbe9 100644
--- a/lib/nbi.coffee
+++ b/lib/nbi.coffee
@@ -39,8 +39,6 @@
###
config = require './config'
-util = require 'util'
-http = require 'http'
url = require 'url'
common = require './common'
db = require './db'
@@ -62,343 +60,323 @@ QUERY_REGEX = /^\/([a-zA-Z0-9_]+s)\/?$/
DEVICE_PRESET_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/preset\/?$/
-cluster = require 'cluster'
-numCPUs = require('os').cpus().length
+listener = (request, response) ->
+ chunks = []
+ bytes = 0
-if cluster.isMaster
- cluster.on('listening', (worker, address) ->
- util.log("Worker #{worker.process.pid} listening to #{address.address}:#{address.port}")
- )
+ request.addListener 'data', (chunk) ->
+ chunks.push(chunk)
+ bytes += chunk.length
- cluster.on('exit', (worker, code, signal) ->
- util.log("Worker #{worker.process.pid} died (#{worker.process.exitCode})")
- setTimeout(()->
- cluster.fork()
- , config.WORKER_RESPAWN_TIME)
- )
+ request.getBody = () ->
+ # Write all chunks into a Buffer
+ body = new Buffer(bytes)
+ offset = 0
+ chunks.forEach((chunk) ->
+ chunk.copy(body, offset, 0, chunk.length)
+ offset += chunk.length
+ )
+ return body
- for [1 .. numCPUs]
- cluster.fork()
-else
- server = http.createServer (request, response) ->
- chunks = []
- bytes = 0
+ request.addListener 'end', () ->
+ body = request.getBody()
+ urlParts = url.parse(request.url, true)
+ if PRESETS_REGEX.test(urlParts.pathname)
+ presetName = querystring.unescape(PRESETS_REGEX.exec(urlParts.pathname)[1])
+ if request.method == 'PUT'
+ preset = JSON.parse(body)
+ preset._id = presetName
- request.addListener 'data', (chunk) ->
- chunks.push(chunk)
- bytes += chunk.length
-
- request.getBody = () ->
- # Write all chunks into a Buffer
- body = new Buffer(bytes)
- offset = 0
- chunks.forEach((chunk) ->
- chunk.copy(body, offset, 0, chunk.length)
- offset += chunk.length
- )
- return body
-
- request.addListener 'end', () ->
- body = request.getBody()
- urlParts = url.parse(request.url, true)
- if PRESETS_REGEX.test(urlParts.pathname)
- presetName = querystring.unescape(PRESETS_REGEX.exec(urlParts.pathname)[1])
- if request.method == 'PUT'
- preset = JSON.parse(body)
- preset._id = presetName
-
- db.presetsCollection.save(preset, (err) ->
- db.redisClient.del('presets', 'presets_hash', (err) ->
- throw err if err
- )
- if err
- response.writeHead(500)
- response.end(err)
- return
- response.writeHead(200)
- response.end()
+ db.presetsCollection.save(preset, (err) ->
+ db.redisClient.del('presets', 'presets_hash', (err) ->
+ throw err if err
)
- else if request.method == 'DELETE'
- db.presetsCollection.remove({'_id' : presetName}, (err, removedCount) ->
- db.redisClient.del('presets', 'presets_hash', (err) ->
- throw err if err
- )
- if err
- response.writeHead(500)
- response.end(err)
- return
- response.writeHead(200)
- response.end()
+ if err
+ response.writeHead(500)
+ response.end(err)
+ return
+ response.writeHead(200)
+ response.end()
+ )
+ else if request.method == 'DELETE'
+ db.presetsCollection.remove({'_id' : presetName}, (err, removedCount) ->
+ db.redisClient.del('presets', 'presets_hash', (err) ->
+ throw err if err
)
- else
- response.writeHead 405, {'Allow': 'PUT, DELETE'}
- response.end('405 Method Not Allowed')
- else if OBJECTS_REGEX.test(urlParts.pathname)
- objectName = querystring.unescape(OBJECTS_REGEX.exec(urlParts.pathname)[1])
- if request.method == 'PUT'
- object = JSON.parse(body)
- object._id = objectName
+ if err
+ response.writeHead(500)
+ response.end(err)
+ return
+ response.writeHead(200)
+ response.end()
+ )
+ else
+ response.writeHead 405, {'Allow': 'PUT, DELETE'}
+ response.end('405 Method Not Allowed')
+ else if OBJECTS_REGEX.test(urlParts.pathname)
+ objectName = querystring.unescape(OBJECTS_REGEX.exec(urlParts.pathname)[1])
+ if request.method == 'PUT'
+ object = JSON.parse(body)
+ object._id = objectName
- db.objectsCollection.save(object, (err) ->
- db.redisClient.del('objects', 'presets_hash', (err) ->
- throw err if err
- )
- if err
- response.writeHead(500)
- response.end(err)
- return
- response.writeHead(200)
- response.end()
+ db.objectsCollection.save(object, (err) ->
+ db.redisClient.del('objects', 'presets_hash', (err) ->
+ throw err if err
)
- else if request.method == 'DELETE'
- db.objectsCollection.remove({'_id' : objectName}, (err, removedCount) ->
- db.redisClient.del('objects', 'presets_hash', (err) ->
- throw err if err
- )
- if err
- response.writeHead(500)
- response.end(err)
- return
- response.writeHead(200)
- response.end()
+ if err
+ response.writeHead(500)
+ response.end(err)
+ return
+ response.writeHead(200)
+ response.end()
+ )
+ else if request.method == 'DELETE'
+ db.objectsCollection.remove({'_id' : objectName}, (err, removedCount) ->
+ db.redisClient.del('objects', 'presets_hash', (err) ->
+ throw err if err
)
- else
- response.writeHead 405, {'Allow': 'PUT, DELETE'}
- response.end('405 Method Not Allowed')
- else if TAGS_REGEX.test(urlParts.pathname)
- r = TAGS_REGEX.exec(urlParts.pathname)
- deviceId = querystring.unescape(r[1])
- tag = querystring.unescape(r[2])
- if request.method == 'POST'
- db.devicesCollection.update({'_id' : deviceId}, {'$addToSet' : {'_tags' : tag}}, {safe: true}, (err) ->
- db.redisClient.del("#{deviceId}_presets_hash", (err) ->
- throw err if err
- )
- if err
- response.writeHead(500)
- response.end(err)
- return
- response.writeHead(200)
- response.end()
+ if err
+ response.writeHead(500)
+ response.end(err)
+ return
+ response.writeHead(200)
+ response.end()
+ )
+ else
+ response.writeHead 405, {'Allow': 'PUT, DELETE'}
+ response.end('405 Method Not Allowed')
+ else if TAGS_REGEX.test(urlParts.pathname)
+ r = TAGS_REGEX.exec(urlParts.pathname)
+ deviceId = querystring.unescape(r[1])
+ tag = querystring.unescape(r[2])
+ if request.method == 'POST'
+ db.devicesCollection.update({'_id' : deviceId}, {'$addToSet' : {'_tags' : tag}}, {safe: true}, (err) ->
+ db.redisClient.del("#{deviceId}_presets_hash", (err) ->
+ throw err if err
)
- else if request.method == 'DELETE'
- db.devicesCollection.update({'_id' : deviceId}, {'$pull' : {'_tags' : tag}}, {safe: true}, (err) ->
- db.redisClient.del("#{deviceId}_presets_hash", (err) ->
- throw err if err
- )
- if err
- response.writeHead(500)
- response.end(err)
- return
- response.writeHead(200)
- response.end()
+ if err
+ response.writeHead(500)
+ response.end(err)
+ return
+ response.writeHead(200)
+ response.end()
+ )
+ else if request.method == 'DELETE'
+ db.devicesCollection.update({'_id' : deviceId}, {'$pull' : {'_tags' : tag}}, {safe: true}, (err) ->
+ db.redisClient.del("#{deviceId}_presets_hash", (err) ->
+ throw err if err
)
- else
- response.writeHead 405, {'Allow': 'POST, DELETE'}
- response.end('405 Method Not Allowed')
- else if DEVICE_TASKS_REGEX.test(urlParts.pathname)
- if request.method == 'POST'
- deviceId = querystring.unescape(DEVICE_TASKS_REGEX.exec(urlParts.pathname)[1])
- if body.length
- task = JSON.parse(body)
- task.device = deviceId
- db.getAliases((aliases) ->
- apiFunctions.insertTasks(task, aliases, (err) ->
- db.redisClient.del("#{deviceId}_presets_hash", (err) ->
- throw err if err
- )
- if err
- response.writeHead(500)
- response.end(err)
- return
-
- if urlParts.query.connection_request?
- apiFunctions.connectionRequest(deviceId, (err) ->
- if err
- response.writeHead(202)
- response.end()
- else
- apiFunctions.watchTask(task._id, config.DEVICE_ONLINE_THRESHOLD, (err) ->
- if err
- response.writeHead(202)
- response.end()
- return
- response.writeHead(200)
- response.end()
- )
- )
- else
- response.writeHead(202)
- response.end()
+ if err
+ response.writeHead(500)
+ response.end(err)
+ return
+ response.writeHead(200)
+ response.end()
+ )
+ else
+ response.writeHead 405, {'Allow': 'POST, DELETE'}
+ response.end('405 Method Not Allowed')
+ else if DEVICE_TASKS_REGEX.test(urlParts.pathname)
+ if request.method == 'POST'
+ deviceId = querystring.unescape(DEVICE_TASKS_REGEX.exec(urlParts.pathname)[1])
+ if body.length
+ task = JSON.parse(body)
+ task.device = deviceId
+ db.getAliases((aliases) ->
+ apiFunctions.insertTasks(task, aliases, (err) ->
+ db.redisClient.del("#{deviceId}_presets_hash", (err) ->
+ throw err if err
)
- )
- else if urlParts.query.connection_request?
- # no task, send connection request only
- apiFunctions.connectionRequest(deviceId, (err) ->
- if err
- response.writeHead 504
- response.end()
- return
- response.writeHead 200
- response.end()
- )
- else
- response.writeHead(400)
- response.end()
- else
- response.writeHead 405, {'Allow': 'POST'}
- response.end('405 Method Not Allowed')
- else if DEVICE_PRESET_REGEX.test(urlParts.pathname)
- deviceId = querystring.unescape(DEVICE_PRESET_REGEX.exec(urlParts.pathname)[1])
- if request.method is 'GET'
- db.getPresetsObjectsAliases((allPresets, allObjects, allAliases) ->
- presets.getDevicePreset(deviceId, allPresets, allObjects, allAliases, (devicePreset) ->
- response.writeHead(200, {'Content-Type' : 'application/json'})
- response.end(JSON.stringify(devicePreset))
- )
- )
- else
- response.writeHead 405, {'Allow': 'GET'}
- response.end('405 Method Not Allowed')
- else if TASKS_REGEX.test(urlParts.pathname)
- r = TASKS_REGEX.exec(urlParts.pathname)
- taskId = mongodb.ObjectID(querystring.unescape(r[1]))
- action = try querystring.unescape(r[2])
- if not action? or action is '/'
- if request.method == 'DELETE'
- db.tasksCollection.remove({'_id' : taskId}, (err, removedCount) ->
if err
response.writeHead(500)
response.end(err)
return
- response.writeHead(200)
- response.end()
- )
- else
- response.writeHead 405, {'Allow': 'PUT DELETE'}
- response.end('405 Method Not Allowed')
- else if action is '/retry'
- if request.method == 'POST'
- db.tasksCollection.update({_id : taskId}, {$unset : {fault : 1}, $set : {timestamp : new Date()}}, (err, count) ->
- # TODO need to invalidate presets hash for the device
- response.writeHead(200)
- response.end()
- )
- else
- response.writeHead 405, {'Allow': 'POST'}
- response.end('405 Method Not Allowed')
- else
- response.writeHead(404)
- response.end()
- else if FILES_REGEX.test(urlParts.pathname)
- filename = querystring.unescape(FILES_REGEX.exec(urlParts.pathname)[1])
- if request.method == 'PUT'
- metadata = {
- fileType : request.headers.filetype,
- manufacturer : request.headers.manufacturer,
- productClass : request.headers.productclass,
- version : request.headers.version,
- }
- gs = new mongodb.GridStore(db.mongoDb, filename, 'w', {metadata : metadata})
- gs.open((err, gs) ->
- gs.write(body, (err, res) ->
- throw err if err
- gs.close((err) ->
- throw err if err
- response.writeHead(201)
+ if urlParts.query.connection_request?
+ apiFunctions.connectionRequest(deviceId, (err) ->
+ if err
+ response.writeHead(202)
+ response.end()
+ else
+ apiFunctions.watchTask(task._id, config.DEVICE_ONLINE_THRESHOLD, (err) ->
+ if err
+ response.writeHead(202)
+ response.end()
+ return
+ response.writeHead(200)
+ response.end()
+ )
+ )
+ else
+ response.writeHead(202)
response.end()
- )
)
)
- else if request.method == 'DELETE'
- mongodb.GridStore.unlink(db.mongoDb, filename, (err) ->
+ else if urlParts.query.connection_request?
+ # no task, send connection request only
+ apiFunctions.connectionRequest(deviceId, (err) ->
+ if err
+ response.writeHead 504
+ response.end()
+ return
+ response.writeHead 200
+ response.end()
+ )
+ else
+ response.writeHead(400)
+ response.end()
+ else
+ response.writeHead 405, {'Allow': 'POST'}
+ response.end('405 Method Not Allowed')
+ else if DEVICE_PRESET_REGEX.test(urlParts.pathname)
+ deviceId = querystring.unescape(DEVICE_PRESET_REGEX.exec(urlParts.pathname)[1])
+ if request.method is 'GET'
+ db.getPresetsObjectsAliases((allPresets, allObjects, allAliases) ->
+ presets.getDevicePreset(deviceId, allPresets, allObjects, allAliases, (devicePreset) ->
+ response.writeHead(200, {'Content-Type' : 'application/json'})
+ response.end(JSON.stringify(devicePreset))
+ )
+ )
+ else
+ response.writeHead 405, {'Allow': 'GET'}
+ response.end('405 Method Not Allowed')
+ else if TASKS_REGEX.test(urlParts.pathname)
+ r = TASKS_REGEX.exec(urlParts.pathname)
+ taskId = mongodb.ObjectID(querystring.unescape(r[1]))
+ action = try querystring.unescape(r[2])
+ if not action? or action is '/'
+ if request.method == 'DELETE'
+ db.tasksCollection.remove({'_id' : taskId}, (err, removedCount) ->
+ if err
+ response.writeHead(500)
+ response.end(err)
+ return
response.writeHead(200)
response.end()
)
else
- response.writeHead 405, {'Allow': 'PUT, DELETE'}
+ response.writeHead 405, {'Allow': 'PUT DELETE'}
response.end('405 Method Not Allowed')
- else if PING_REGEX.test(urlParts.pathname)
- host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1])
- require('child_process').exec("ping -w 1 -i 0.2 -c 3 #{host}", (err, stdout, stderr) ->
- if err?
- response.writeHead(404, {'Cache-Control' : 'no-cache'})
+ else if action is '/retry'
+ if request.method == 'POST'
+ db.tasksCollection.update({_id : taskId}, {$unset : {fault : 1}, $set : {timestamp : new Date()}}, (err, count) ->
+ # TODO need to invalidate presets hash for the device
+ response.writeHead(200)
response.end()
- return
- response.writeHead(200, {'Content-Type' : 'text/plain', 'Cache-Control' : 'no-cache'})
- response.end(stdout)
- )
- else if QUERY_REGEX.test(urlParts.pathname)
- collectionName = QUERY_REGEX.exec(urlParts.pathname)[1]
- if request.method not in ['GET', 'HEAD']
- response.writeHead 405, {'Allow' : 'GET, HEAD'}
- response.end('405 Method Not Allowed')
- return
- collection = db["#{collectionName}Collection"]
- if not collection?
- response.writeHead 404
- response.end('404 Not Found')
- return
-
- func = (aliases) ->
- if urlParts.query.query?
- q = JSON.parse(urlParts.query.query)
- else
- q = {}
- q = query.expand(q, aliases) if collectionName is 'devices'
-
- if urlParts.query.projection?
- projection = {}
- for p in urlParts.query.projection.split(',')
- p = p.trim()
- projection[p] = 1
- if collectionName is 'devices'
- for k,v of aliases
- if k == p or common.startsWith(k, "#{p}.")
- projection[a] = 1 for a in v
-
- cur = collection.find(q, projection, {batchSize : 50})
- if urlParts.query.sort?
- s = JSON.parse(urlParts.query.sort)
- sort = {}
- for k,v of s
- if aliases[k]?
- sort[a] = v for a in aliases[k]
- else
- sort[k] = v
- cur.sort(sort)
-
- cur.skip(parseInt(urlParts.query.skip)) if urlParts.query.skip?
- cur.limit(parseInt(urlParts.query.limit)) if urlParts.query.limit?
- cur.count((err, total) ->
- response.writeHead(200, {'Content-Type' : 'application/json', 'total' : total})
- if request.method is 'HEAD'
- response.end()
- return
- response.write("[\n")
- i = 0
- cur.each((err, item) ->
- if item is null
- response.end("\n]")
- else
- response.write(",\n") if i++
- apiFunctions.addAliases(item, aliases) if collectionName is 'devices'
- response.write(JSON.stringify(item))
- )
- )
-
- if collectionName is 'devices'
- db.getAliases((aliases) ->
- func(aliases)
)
else
- func({})
+ response.writeHead 405, {'Allow': 'POST'}
+ response.end('405 Method Not Allowed')
else
+ response.writeHead(404)
+ response.end()
+ else if FILES_REGEX.test(urlParts.pathname)
+ filename = querystring.unescape(FILES_REGEX.exec(urlParts.pathname)[1])
+ if request.method == 'PUT'
+ metadata = {
+ fileType : request.headers.filetype,
+ manufacturer : request.headers.manufacturer,
+ productClass : request.headers.productclass,
+ version : request.headers.version,
+ }
+
+ gs = new mongodb.GridStore(db.mongoDb, filename, 'w', {metadata : metadata})
+ gs.open((err, gs) ->
+ gs.write(body, (err, res) ->
+ throw err if err
+ gs.close((err) ->
+ throw err if err
+ response.writeHead(201)
+ response.end()
+ )
+ )
+ )
+ else if request.method == 'DELETE'
+ mongodb.GridStore.unlink(db.mongoDb, filename, (err) ->
+ response.writeHead(200)
+ response.end()
+ )
+ else
+ response.writeHead 405, {'Allow': 'PUT, DELETE'}
+ response.end('405 Method Not Allowed')
+ else if PING_REGEX.test(urlParts.pathname)
+ host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1])
+ require('child_process').exec("ping -w 1 -i 0.2 -c 3 #{host}", (err, stdout, stderr) ->
+ if err?
+ response.writeHead(404, {'Cache-Control' : 'no-cache'})
+ response.end()
+ return
+ response.writeHead(200, {'Content-Type' : 'text/plain', 'Cache-Control' : 'no-cache'})
+ response.end(stdout)
+ )
+ else if QUERY_REGEX.test(urlParts.pathname)
+ collectionName = QUERY_REGEX.exec(urlParts.pathname)[1]
+ if request.method not in ['GET', 'HEAD']
+ response.writeHead 405, {'Allow' : 'GET, HEAD'}
+ response.end('405 Method Not Allowed')
+ return
+ collection = db["#{collectionName}Collection"]
+ if not collection?
response.writeHead 404
response.end('404 Not Found')
+ return
- db.connect((err) ->
- throw err if err
- server.listen config.API_PORT, config.API_INTERFACE
- )
+ func = (aliases) ->
+ if urlParts.query.query?
+ q = JSON.parse(urlParts.query.query)
+ else
+ q = {}
+ q = query.expand(q, aliases) if collectionName is 'devices'
+
+ if urlParts.query.projection?
+ projection = {}
+ for p in urlParts.query.projection.split(',')
+ p = p.trim()
+ projection[p] = 1
+ if collectionName is 'devices'
+ for k,v of aliases
+ if k == p or common.startsWith(k, "#{p}.")
+ projection[a] = 1 for a in v
+
+ cur = collection.find(q, projection, {batchSize : 50})
+ if urlParts.query.sort?
+ s = JSON.parse(urlParts.query.sort)
+ sort = {}
+ for k,v of s
+ if aliases[k]?
+ sort[a] = v for a in aliases[k]
+ else
+ sort[k] = v
+ cur.sort(sort)
+
+ cur.skip(parseInt(urlParts.query.skip)) if urlParts.query.skip?
+ cur.limit(parseInt(urlParts.query.limit)) if urlParts.query.limit?
+ cur.count((err, total) ->
+ response.writeHead(200, {'Content-Type' : 'application/json', 'total' : total})
+ if request.method is 'HEAD'
+ response.end()
+ return
+ response.write("[\n")
+ i = 0
+ cur.each((err, item) ->
+ if item is null
+ response.end("\n]")
+ else
+ response.write(",\n") if i++
+ apiFunctions.addAliases(item, aliases) if collectionName is 'devices'
+ response.write(JSON.stringify(item))
+ )
+ )
+
+ if collectionName is 'devices'
+ db.getAliases((aliases) ->
+ func(aliases)
+ )
+ else
+ func({})
+ else
+ response.writeHead 404
+ response.end('404 Not Found')
+
+
+exports.listener = listener
diff --git a/lib/server.coffee b/lib/server.coffee
new file mode 100644
index 0000000..b80acd2
--- /dev/null
+++ b/lib/server.coffee
@@ -0,0 +1,47 @@
+###
+# Copyright 2013, 2014 Zaid Abdulla
+#
+# GenieACS is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# GenieACS 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with GenieACS. If not, see .
+###
+
+config = require './config'
+db = require './db'
+
+service = config.argv[0]
+if not service?
+ throw new Error('Missing argument cwmp, fs, or nbi')
+
+networkInterface = config["#{service.toUpperCase()}_INTERFACE"]
+port = config["#{service.toUpperCase()}_PORT"]
+useHttps = config["#{service.toUpperCase()}_SSL"]
+
+listener = require("./#{service}").listener
+
+if useHttps
+ path = require 'path'
+ fs = require 'fs'
+ httpsKey = path.resolve(config.CONFIG_DIR, "#{service}.key")
+ httpsCert = path.resolve(config.CONFIG_DIR, "#{service}.crt")
+ options = {
+ key: fs.readFileSync(httpsKey),
+ cert: fs.readFileSync(httpsCert)
+ }
+ server = require('https').createServer(options, listener)
+else
+ server = require('http').createServer(listener)
+
+db.connect((err) ->
+ throw err if err
+ server.listen(port, networkInterface)
+)
diff --git a/package.json b/package.json
index d155f7e..95ee9a7 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"scripts": {
"prepublish": "npm run compile",
"compile": "coffee -cb lib/*.coffee test/*.coffee tools/*.coffee",
- "configure": "cp ./config/config-sample.json ./config/config.json; cp ./config/parameters-sample.json ./config/parameters.json; cp ./config/custom_commands-sample.json ./config/custom_commands.json; cp ./config/auth-sample.js ./config/auth.js; cp ./config/httpscert-sample.key ./config/httpscert.key; cp ./config/httpscert-sample.crt ./config/httpscert.crt",
+ "configure": "cp ./config/config-sample.json ./config/config.json; cp ./config/parameters-sample.json ./config/parameters.json; cp ./config/custom_commands-sample.json ./config/custom_commands.json; cp ./config/auth-sample.js ./config/auth.js",
"install": "npm run configure",
"test": "node test"
}