domotz-remote-pawn
Version:
Domotz Agent
525 lines (430 loc) • 21.6 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2016 Domotz Ltd
*
* Domotz Agent is free software: you can redistribute it 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.
*
* Domotz Agent 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 Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
**/
/**
*
* Created by Iacopo Papalini <ipapalini@domotz.com> on 16/06/15.
*/
// External Dependencies
var amqplib = require('amqplib');
var async = require('async');
var bodyParser = require('body-parser');
var childProcess = require('child_process');
var crypto = require('crypto');
var deepEqual = require('deep-equal');
var dgram = require('dgram');
var dhcpjs = require('dhcpjs');
var dns = require('dns');
var express = require('express');
var events = require('events');
var favicon = require('serve-favicon');
var fs = require('fs');
var http = require('http');
var ip = require('ip');
var moment = require('moment-timezone');
var net = require('net');
var netSnmp = require('net-snmp');
var os = require('os');
var path = require('path');
var rootRequire = require('root-require');
var q = require('q');
var telnet = require('telnet-client');
var ssdp = require('node-ssdp');
var step = require('step');
var url = require('url');
var util = require('util');
var xml2js = require('xml2js');
var xml2jsProcessor = require('xml2js/lib/processors.js');
var whois = require('node-whois');
var winston = require('winston');
var ws = require("nodejs-websocket");
var _ = require('underscore');
// Internal Dependencies (no need for further initialization)
var pkgJson = rootRequire('package.json');
var event = rootRequire('src/events/utils');
var web = rootRequire('src/http/utils');
var request = rootRequire('src/http/request');
var mutex = rootRequire('src/utils/mutex');
var time = rootRequire('src/utils/time');
var command = rootRequire('src/utils/command');
var dto = rootRequire('src/utils/dto');
var number = rootRequire('src/utils/number');
var dataStructure = rootRequire('src/utils/data_structure');
var scheduler = rootRequire('src/utils/scheduler');
var geoLocation = require('../network_tools/geolocation');
var commandHttpRequest = require('../commands/httpRequest');
function initFileManager() {
return require('../persistence/file').factory(fs, q);
}
function getCacheUtils(procManager) {
return require('../persistence/cache').factory(procManager, fs);
}
function getRandomUtils() {
return require('../utils/random').factory(crypto);
}
function getHubConfigurator(request, file, web) {
return require('../bootstrap/configuration').factory(q, request, file, web);
}
function initStatusManager(configuration) {
return require('../bootstrap/status').factory(configuration);
}
function initLog(file, proc, configuration) {
return require('../bootstrap/logger').factory(_, winston, util, path, file, proc, configuration);
}
function initExternalProcessesManager() {
var Queue = require('../external_processes/queue');
var cpu = require('../external_processes/cpu').factory(_, childProcess, Queue);
return require('../external_processes/proc').factory(q, fs, childProcess, cpu);
}
function getHostInterfaces(procManager, fileManager) {
var networkInterfaces,
customInterfaces = require('../discovery/host/custom').factory(_, fileManager);
var netUtilsPath = path.join(process.env.DOMOTZ_BIN_DIR, 'domotz_network_utils');
var v2 = fileManager.getFileStats(netUtilsPath);
if (!v2) {
var nic = require('../discovery/host/nic').factory(os, util);
networkInterfaces = require('../discovery/host/interfaces').factory(q, procManager, nic);
}
return require('../discovery/host').factory(procManager, ip, networkInterfaces, customInterfaces);
}
function setupUpdateManager(log, status, random, file, proc, mutex, event, engineRequest) {
return require('../package/update').factory(q, path, random, http, log, status, file, proc, mutex, event, engineRequest);
}
function getAmqpListener(proc, status, configuration, eventHandler, resourceLocator) {
var amqpParser = require('../amqp/parser');
var amqpCommand = require('../amqp/command').factory(resourceLocator, amqpParser);
var amqpHandler = require('../amqp/handler').factory(amqpCommand);
var amqpConnection = require('../amqp/connection').factory(amqplib, util, configuration);
var amqpConsumer = require('../amqp/consumer').factory(configuration, amqpCommand, amqpHandler);
var amqpChannel = require('../amqp/channel').factory(eventHandler);
return require('../amqp/listener').factory(proc, status, amqpConnection, amqpConsumer, amqpChannel);
}
function initAvahiResolver(proc, engineRequest, devicesInfo) {
var avahiCommands = require('../discovery/avahi/commands').factory();
return require('../discovery/avahi/resolve').factory(engineRequest, devicesInfo, avahiCommands, proc);
}
function prepareNmapTranslator() {
return require('../discovery/nmap/xml2js').factory(q, xml2js);
}
function setupPortDiscovery(resourceLocator, nmapXml2Js, interfacesBindingStorage, nmapCommands) {
var portParser = require('../discovery/ports/parser');
return require('../discovery/ports/map').factory(resourceLocator, nmapXml2Js, interfacesBindingStorage, nmapCommands, portParser);
}
function setupTcpServicesMonitoring(proc, nmapXml2js, nmapCommands, interfacesBindingStorage, engineRequest, configuration) {
var tcpServicesParser = require('../discovery/tcp_services/parser');
return require('../discovery/tcp_services/tcp_monitor').factory(q, proc, nmapCommands, nmapXml2js, engineRequest, configuration, tcpServicesParser, interfacesBindingStorage);
}
function setupDiscoveryUtils(interfacesBindingStorage, devicesInfo) {
return require('../discovery/utils').factory(interfacesBindingStorage, devicesInfo);
}
function setupProtocolsDiscoveryOrchestrator(domotzApp) {
var discoverySenderFactory = require('../discovery/protocols/discovery_sender').factory;
return require('../discovery/protocols/protocols_discovery_orchestrator').factory(domotzApp, discoverySenderFactory);
}
function setupBonjourDiscovery() {
return require('../discovery/bonjour/bonjour_discovery').factory(mutex);
}
function setupUpnpDeviceDiscovery(resourceLocator) {
var messageBuilder = require('../discovery/upnp/message').factory(resourceLocator);
var responseParser = require('../discovery/upnp/parser').factory(resourceLocator, messageBuilder);
var deferredRequestHandler = require('../discovery/upnp/deferred_request').factory(resourceLocator, messageBuilder);
return require('../discovery/upnp/upnp_discovery').factory(resourceLocator, messageBuilder, responseParser, deferredRequestHandler);
}
function setupSnmpBasicDiscovery(resourceLocator) {
var commandBuilder = require('../discovery/snmp/command_builder').factory(resourceLocator);
var responseRendering = require('../discovery/snmp/response_processing').renderSnmpBasicData;
var snmpGetterFactory = require('../commands/snmp/get.js').command;
return require('../discovery/snmp/snmp_basic_discovery').factory(resourceLocator, commandBuilder, responseRendering, snmpGetterFactory);
}
function setupSnmpInterfaceDiscovery(resourceLocator) {
var commandBuilder = require('../discovery/snmp/command_builder').factory(resourceLocator);
var responseRendering = require('../discovery/snmp/response_processing').renderSnmpInterfaceData;
var snmpSubtreeFactory = require('../commands/snmp/subtrees.js').command;
return require('../discovery/snmp/snmp_interface_discovery').factory(resourceLocator, commandBuilder, responseRendering, snmpSubtreeFactory);
}
function setupDhcpDiscovery(random, discoveryUtils, engineRequest) {
return require('../discovery/dhcp/dhcp_discovery').factory(dhcpjs, random, discoveryUtils, engineRequest);
}
function setPeriodicalTasks(status, random, time, eventHandler, configuration) {
return require('../events/cron').factory(time, status, eventHandler, configuration, random);
}
function initAPIClient(request, configuration, endpoint) {
return require('../api_client/engineRequest').factory(q, request, configuration, endpoint);
}
function initEvents(events, domotzApp) {
return require('../events/eventHandler').factory(events, domotzApp);
}
function setupDeviceAnalyzer(configuration, engineRequest, devicesInfo, interfacesBindingStorage) {
return require('../devices/analyze').factory(configuration, engineRequest, devicesInfo, interfacesBindingStorage);
}
function initDevicesCache(time, engineRequest, configuration) {
return require('../devices/info').factory(q, async, time, engineRequest, configuration);
}
function createWebapp(status, configuration, eventHandler, domotzApp) {
var routes = require('../http/routes/index').create(express, status, configuration, domotzApp);
return require('../http/app').createApp(fs, http, express, eventHandler, bodyParser, favicon, path, routes);
}
function createWsServer(ws) {
return require('../websockets/wsServer').wsServer(ws);
}
function setSignalHandlers(proc, log, status, engineRequest) {
require('../bootstrap/signals').setHandler(proc, log, status, event, engineRequest);
}
function initUserEntity(userRequest, registerRequest) {
return require('../api_client/entities/user').factory(userRequest, registerRequest);
}
function initAgentEntity(random, web, host, configuration, userRequest) {
return require('../api_client/entities/agent').factory(random, url, web, host, configuration, userRequest);
}
function initNotConfiguredAgentEntity(host, pkgJson, registerRequest) {
return require('../api_client/entities/notConfiguredAgent').factory(host, pkgJson, registerRequest);
}
function initSocketListener() {
var requestHandler = require('../http/socketHandler');
return require('../http/socketListener').socketListener(http, fs, requestHandler);
}
function setupUpnpRegistry(deepEqual) {
return require('../upnp/upnpRegistry').registry(deepEqual);
}
function getNetInfoModule(proc, file, hostInfo) {
return require('../network_tools/netinfo').factory(os, path, http, dns, whois, proc, file, hostInfo);
}
function initUpnpEventHandler() {
return require('../upnp/upnpEventHandler');
}
function setupMtrTool(proc, dto) {
return require('../network_tools/mtr').factory(_, proc, dto);
}
function initUdpListener(request, configuration) {
return require('../udp/listener').factory(crypto, dgram, request, configuration);
}
function getUdpUtils() {
return require('../udp/utils').factory(dgram);
}
function initControl4Driver(sshTool) {
return require('../discovery/zigbee/control4').factory(async, q, sshTool, _, util);
}
function getSshTool(proc) {
return require('../network_tools/ssh').factory(proc);
}
function initFingDriver(engineRequest) {
return require('../network_tools/fingJamDevices').fingWrapper(childProcess, engineRequest);
}
function initStallWatcher(){
"use strict";
return require('../external_processes/stallWatcher').init(os, childProcess, fs);
}
function setupNdtServersFinder(request, random, cache, configuration) {
var distanceCalculator = require('../network_tools/earthDistance').distanceCalculator(configuration);
return require('../network_tools/ndtServers').factory(q, cache, configuration, request, random, distanceCalculator);
}
function setupSpeedTestTool(proc, dto, number) {
return require('../network_tools/speedTest').factory(_, proc, dto, number);
}
function initDiscoveryCoordinator(util, proc, hostInfo, eventHandler, nmapCommands, nmapXml2Js, interfacesBindingStorage, engineRequest, configuration) {
"use strict";
var ipDiscoveryParser = require('../discovery/ip_network/discovery_parser').factory(interfacesBindingStorage);
var bindingDiscoveryFactory = require('../discovery/ip_network/bind_discovery').factory(q, util, proc, hostInfo, nmapCommands, nmapXml2Js, ipDiscoveryParser);
var engineReportingStrategy = require('../discovery/ip_network/engine_report_strategy').engineIpDiscoveryReportStrategyFactory(q, engineRequest, interfacesBindingStorage, configuration);
return require('../discovery/ip_network/discovery_coordinator').factory(q, configuration, eventHandler, bindingDiscoveryFactory, interfacesBindingStorage, engineReportingStrategy);
}
function getNmapCommands(file, configuration, interfacesBindingStorage) {
var nmapBlackList = require('../discovery/nmap/blackList').factory(file, configuration, interfacesBindingStorage);
return require('../discovery/nmap/commands').factory(ip, configuration, nmapBlackList);
}
function initBindingsCache(fileManager, engineRequest, hostInfo) {
return require('../discovery/ip_network/interfaces_binding').factory(q, util, fileManager, engineRequest, hostInfo);
}
function setupOoklaServerFinder(domotzApp) {
return require('../network_tools/ooklaServers').factory(domotzApp);
}
function setupConnectivityChecker(domotzApp) {
return require('../network_tools/connectivity').factory(domotzApp);
}
function initUpnpSubscriptionManager(domotzApp) {
return require('../upnp/upnpSubscriptionsManager').factory(domotzApp);
}
function createUpnpServer(domotzApp) {
return require('../upnp/upnpHttpServer').httpServer(domotzApp);
}
module.exports.create = function (customLogger) {
var domotzApp = {};
// Npm Modules (for command or events that needs stuff at runtime)
domotzApp.async = async;
domotzApp.crypto = crypto;
domotzApp.dhcpjs = dhcpjs;
domotzApp.fs = fs;
domotzApp.http = http;
domotzApp.moment = moment;
domotzApp.netSnmp = netSnmp;
domotzApp.net = net;
domotzApp.path = path;
domotzApp.q = q;
domotzApp.request = request;
domotzApp.telnet = telnet;
domotzApp.step = step;
domotzApp.ssdp = ssdp;
domotzApp.url = url;
domotzApp.util = util;
domotzApp.xml2js = xml2js;
domotzApp.xml2jsProcessor = xml2jsProcessor;
domotzApp._ = _;
// Utils
var fileManager = initFileManager();
var random = getRandomUtils();
var procManager = initExternalProcessesManager();
var cacheUtils = getCacheUtils(procManager);
domotzApp.utils = {
proc: procManager,
random: random,
file: fileManager,
mutex: mutex,
scheduler: scheduler,
web: web,
time: time,
event: event,
command: command,
dto: dto,
number: number,
cache: cacheUtils,
dataStructure: dataStructure
};
// Pawn Configuration
var configurator = getHubConfigurator(request, fileManager, web);
var configuration = configurator.loadConfiguration();
domotzApp.configurator = configurator;
domotzApp.configuration = configuration;
// Status Manager
var status = initStatusManager(configuration);
domotzApp.status = status;
// Log Setup
var domotzLog = initLog(fileManager, procManager, configuration);
domotzApp.log = customLogger || domotzLog;
// Host Information
var host = getHostInterfaces(procManager, fileManager);
domotzApp.discovery = {
host: host
};
// API Clients. For OpenAPI limit the number of Sockets
http.globalAgent.maxSockets = 100;
var registerRequest = initAPIClient(request, configuration, 'register');
var userRequest = initAPIClient(request, configuration, 'user');
var engineRequest = (status.get() === status.CONFIGURED) ? initAPIClient(request, configuration, 'agent') : null;
domotzApp.registerRequest = registerRequest;
domotzApp.userRequest = userRequest;
domotzApp.engineRequest = engineRequest;
http.globalAgent.maxSockets = Infinity;
// Events Management
var eventHandler = initEvents(events, domotzApp);
domotzApp.event = eventHandler;
setSignalHandlers(procManager, domotzLog, status, engineRequest);
// Package Management
domotzApp.pkg = {
update: setupUpdateManager(domotzLog, status, random, fileManager, procManager, mutex, event, engineRequest),
json: pkgJson
};
// NetworkTools
domotzApp.networkTools = {
geolocation: geoLocation
};
// Entities
domotzApp.entities = {
agent: initAgentEntity(random, web, host, configuration, userRequest),
notConfiguredAgent: initNotConfiguredAgentEntity(host, pkgJson, registerRequest),
user: initUserEntity(userRequest, registerRequest)
};
// Web Sockets server
domotzApp.wsServer = createWsServer(ws);
// Internal Web Server
domotzApp.webapp = createWebapp(status, configuration, eventHandler, domotzApp);
domotzApp.distanceCalculator = require('../network_tools/earthDistance').distanceCalculator(configuration);
// Periodic Tasks
domotzApp.periodicalTasks = setPeriodicalTasks(status, random, time, eventHandler, configuration);
if (status.get() === status.CONFIGURED) {
var devicesInfo = initDevicesCache(time, engineRequest, configuration);
var interfacesBindingStorage = initBindingsCache(fileManager, engineRequest, host);
var discoveryUtils = setupDiscoveryUtils(interfacesBindingStorage, devicesInfo);
var nmapCommands = getNmapCommands(fileManager, configuration, interfacesBindingStorage);
var nmapXml2Js = prepareNmapTranslator(q, xml2js);
var sshTool = getSshTool(procManager);
// Amqp manager
domotzApp.amqpListener = getAmqpListener(procManager, status, configuration, eventHandler, domotzApp);
// Engine Clients
domotzApp.heartbeatRequest = initAPIClient(request, configuration, 'agent-heartbeat');
// IP Discovery - Bindings Cache
domotzApp.interfacesBindingStorage = interfacesBindingStorage;
// Device Utilities
domotzApp.devices = {
analyze: setupDeviceAnalyzer(configuration, engineRequest, devicesInfo, interfacesBindingStorage),
info: devicesInfo
};
// Discovery Engine
domotzApp.discovery.utils = discoveryUtils;
domotzApp.discovery.avahi = initAvahiResolver(procManager, engineRequest, devicesInfo);
domotzApp.discovery.port = setupPortDiscovery(domotzApp, nmapXml2Js, interfacesBindingStorage, nmapCommands);
domotzApp.discovery.tcp_services_monitoring = setupTcpServicesMonitoring(procManager, nmapXml2Js, nmapCommands, interfacesBindingStorage, engineRequest, configuration);
domotzApp.discovery.ip_network = {
discoveryCoordinator: initDiscoveryCoordinator(util, procManager, host, eventHandler, nmapCommands, nmapXml2Js, interfacesBindingStorage, engineRequest, configuration)
};
domotzApp.discovery.dhcp_discovery = setupDhcpDiscovery(random, discoveryUtils, engineRequest);
domotzApp.discovery.bonjour_discovery = setupBonjourDiscovery(domotzApp);
domotzApp.discovery.upnp_device_discovery = setupUpnpDeviceDiscovery(domotzApp);
domotzApp.discovery.snmp_basic_discovery = setupSnmpBasicDiscovery(domotzApp);
domotzApp.discovery.snmp_interface_discovery = setupSnmpInterfaceDiscovery(domotzApp);
domotzApp.discovery.protocols_orchestrator = setupProtocolsDiscoveryOrchestrator(domotzApp);
// Socket Http Server
domotzApp.socketListener = initSocketListener(eventHandler);
// Net Tools
domotzApp.networkTools.netInfo = getNetInfoModule(procManager, fileManager, host);
domotzApp.networkTools.mtr = setupMtrTool(procManager, dto);
domotzApp.networkTools.ndtServers = setupNdtServersFinder(request, random, cacheUtils, configuration);
domotzApp.networkTools.ooklaServers = setupOoklaServerFinder(domotzApp);
domotzApp.networkTools.speedTest = setupSpeedTestTool(procManager, dto, number);
domotzApp.networkTools.internetConnectionStatus = setupConnectivityChecker(domotzApp);
domotzApp.networkTools.ssh = sshTool;
// Http command interface
domotzApp.commandHttpRequest = commandHttpRequest;
// Upnp Server
domotzApp.upnp = {
registry: setupUpnpRegistry(deepEqual),
eventHandler: initUpnpEventHandler(),
subscriptionsManager: initUpnpSubscriptionManager(domotzApp),
httpServer: createUpnpServer(domotzApp)
};
// Udp Listener
domotzApp.udp = {
utils: getUdpUtils(),
listener: initUdpListener(request, configuration)
};
// Control4
domotzApp.control4Driver = initControl4Driver(sshTool);
// Fing
domotzApp.fingWrapper = initFingDriver(engineRequest);
// Self check
domotzApp.stallWatcher = initStallWatcher();
} else {
console.warn("INIT - Hub is not configured (invalid or absent conf file). Running in restricted mode");
}
var i = 0;
for (var property in domotzApp) {
if (domotzApp.hasOwnProperty(property)) {
console.verbose('INIT - --> ' + property + ';');
i++;
}
}
console.info('INIT - Finish Initialization of Domotz Application. %d modules loaded',i);
return domotzApp;
};