Added server self-update support along with many fixes.
This commit is contained in:
parent
de2e5402f6
commit
ac53c7ae3c
Binary file not shown.
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
Copyright 2017 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
// Polyfill String.endsWith
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function (searchString, position) {
|
||||
var subjectString = this.toString();
|
||||
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
|
||||
position -= searchString.length;
|
||||
var lastIndex = subjectString.lastIndexOf(searchString, position);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
||||
|
||||
// Replace a string with a number if the string is an exact number
|
||||
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) == x)) { x = parseInt(x); } return x; }
|
||||
|
||||
// Convert decimal to hex
|
||||
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
|
||||
|
||||
// Convert a raw string to a hex string
|
||||
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
|
||||
|
||||
// Convert a buffer into a string
|
||||
function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
|
||||
|
||||
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
|
||||
function hex2rstr(d) {
|
||||
if (typeof d != "string" || d.length == 0) return '';
|
||||
var r = '', m = ('' + d).match(/../g), t;
|
||||
while (t = m.shift()) r += String.fromCharCode('0x' + t);
|
||||
return r
|
||||
}
|
||||
|
||||
// Convert an object to string with all functions
|
||||
function objToString(x, p, ret) {
|
||||
if (ret == undefined) ret = '';
|
||||
if (p == undefined) p = 0;
|
||||
if (x == null) { return '[null]'; }
|
||||
if (p > 8) { return '[...]'; }
|
||||
if (x == undefined) { return '[undefined]'; }
|
||||
if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
|
||||
if (typeof x == 'buffer') { return '[buffer]'; }
|
||||
if (typeof x != 'object') { return x; }
|
||||
var r = '{' + (ret ? '\r\n' : ' ');
|
||||
for (var i in x) { r += (addPad(p + 2, ret) + i + ': ' + objToString(x[i], p + 2, ret) + (ret ? '\r\n' : ' ')); }
|
||||
return r + addPad(p, ret) + '}';
|
||||
}
|
||||
|
||||
// Return p number of spaces
|
||||
function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
|
||||
|
||||
// Split a string taking into account the quoats. Used for command line parsing
|
||||
function splitArgs(str) {
|
||||
var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
|
||||
do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
|
||||
return myArray;
|
||||
}
|
||||
|
||||
// Parse arguments string array into an object
|
||||
function parseArgs(argv) {
|
||||
var results = { '_': [] }, current = null;
|
||||
for (var i = 1, len = argv.length; i < len; i++) {
|
||||
var x = argv[i];
|
||||
if (x.length > 2 && x[0] == '-' && x[1] == '-') {
|
||||
if (current != null) { results[current] = true; }
|
||||
current = x.substring(2);
|
||||
} else {
|
||||
if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
|
||||
}
|
||||
}
|
||||
if (current != null) { results[current] = true; }
|
||||
return results;
|
||||
}
|
||||
|
||||
// Parge a URL string into an options object
|
||||
function parseUrl(url) {
|
||||
var x = url.split('/');
|
||||
if (x.length < 4) return null;
|
||||
var y = x[2].split(':');
|
||||
var options = {};
|
||||
var options = { protocol: x[0], hostname: y[0], path: '/' + x.splice(3).join('/') };
|
||||
if (y.length == 1) { options.port = ((x[0] == 'https:') || (x[0] == 'wss:')) ? 443 : 80; } else { options.port = parseInt(y[1]); }
|
||||
if (isNaN(options.port) == true) return null;
|
||||
return options;
|
||||
}
|
||||
|
||||
// Read a entire file into a buffer
|
||||
function readFileToBuffer(filePath) {
|
||||
try {
|
||||
var fs = require('fs');
|
||||
var stats = fs.statSync(filePath);
|
||||
if (stats == null) { return null; }
|
||||
var fileData = new Buffer(stats.size);
|
||||
var fd = fs.openSync(filePath, 'r');
|
||||
fs.readSync(fd, fileData, 0, stats.size, 0);
|
||||
fs.closeSync(fd);
|
||||
return fileData;
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
|
||||
// Performs an HTTP get on a URL and return the data back
|
||||
function makeHttpGetRequest(url, func) {
|
||||
var http = require('http');
|
||||
var request = http.get(url, function (res) {
|
||||
var htmlData = '';
|
||||
res.on('data', function (d) { htmlData += d; });
|
||||
res.on('end', function (d) { func(res.statusCode, htmlData); });
|
||||
}).on('error', function (e) { func(0, null); });
|
||||
}
|
||||
|
||||
// Performs an HTTP get on a URL and return the data back (Alternative implementation)
|
||||
function makeHttpGetRequest2(url, func) {
|
||||
var http = require('http');
|
||||
var options = http.parseUri(url);
|
||||
options.username = 'admin';
|
||||
options.password = 'P@ssw0rd';
|
||||
var request = http.request(options, function (res) {
|
||||
var htmlData = '';
|
||||
res.on('data', function (d) { htmlData += d; });
|
||||
res.on('end', function () { func(res.statusCode, htmlData); });
|
||||
});
|
||||
request.on('error', function (e) { func(0, null); });
|
||||
request.end();
|
||||
}
|
||||
|
||||
// Performs an HTTP get on a URL and return the data back (Alternative implementation)
|
||||
function intelAmtSetStorage(url, buffer, func) {
|
||||
var http = require('http');
|
||||
var options = http.parseUri(url);
|
||||
options.user = 'admin'; // TODO: Does not support HTTP digest auth yet!!!!!!!!!!!!!!!!
|
||||
options.pass = 'P@ssw0rd';
|
||||
var request = http.request(options, function (res) {
|
||||
var htmlData = '';
|
||||
res.on('data', function (d) { htmlData += d; });
|
||||
res.on('end', function () { func(res.statusCode, htmlData); });
|
||||
});
|
||||
request.on('error', function (e) { func(0, null); });
|
||||
request.end();
|
||||
}
|
||||
|
||||
//console.log(objToString(db2, 2, ' '));
|
||||
|
||||
console.log('--- Start ---');
|
||||
|
||||
var fileData = readFileToBuffer('MeshCommander-Small.gz');
|
||||
if (fileData != null) {
|
||||
makeHttpGetRequest2('http://192.168.2.105:16992/index.htm', function (status, htmlData) { console.log(status, htmlData); });
|
||||
|
||||
/*
|
||||
intelAmtSetStorage('http://192.168.2.105:16992/amt-storage/index.htm', fileData, function (status, htmlData) {
|
||||
console.log('intelAmtSetStorage', status, htmlData);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
console.log('--- End ---');
|
||||
//process.exit(2);
|
|
@ -300,7 +300,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
|
|||
// We have a location in the database for this remote IP
|
||||
var iploc = nodes[0], x = {};
|
||||
x.publicip = iploc.ip;
|
||||
x.iploc = iploc.loc + ',' + (Math.floor((new Date(command.value.date)) / 1000));
|
||||
x.iploc = iploc.loc + ',' + (Math.floor((new Date(iploc.date)) / 1000));
|
||||
ChangeAgentLocationInfo(x);
|
||||
} else {
|
||||
// Check if we need to ask for the IP location
|
||||
|
|
|
@ -34,6 +34,8 @@ function CreateMeshCentralServer() {
|
|||
obj.meshAgentBinaries = {}; // Mesh Agent Binaries, Architecture type --> { hash:(sha256 hash), size:(binary size), path:(binary path) }
|
||||
obj.meshAgentInstallScripts = {}; // Mesh Install Scripts, Script ID -- { hash:(sha256 hash), size:(binary size), path:(binary path) }
|
||||
obj.multiServer = null;
|
||||
obj.currentVer = null;
|
||||
obj.maintenanceTimer = null;
|
||||
|
||||
// Create data and files folders if needed
|
||||
try { obj.fs.mkdirSync(obj.datapath); } catch (e) { }
|
||||
|
@ -54,7 +56,7 @@ function CreateMeshCentralServer() {
|
|||
try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
|
||||
|
||||
// Check for invalid arguments
|
||||
var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport'];
|
||||
var validArguments = ['_', 'notls', 'user', 'port', 'mpsport', 'redirport', 'cert', 'deletedomain', 'deletedefaultdomain', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate'];
|
||||
for (var arg in obj.args) { if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
|
||||
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
|
||||
|
||||
|
@ -123,6 +125,14 @@ function CreateMeshCentralServer() {
|
|||
} else if (xprocess.xrestart == 2) {
|
||||
console.log('Expected exit...');
|
||||
process.exit(); // User CTRL-C exit.
|
||||
} else if (xprocess.xrestart == 3) {
|
||||
// Server self-update exit
|
||||
var child_process = require('child_process');
|
||||
var xxprocess = child_process.exec('npm install meshcentral', { cwd: obj.path.join(__dirname, '../..') }, function (error, stdout, stderr) { });
|
||||
xxprocess.data = '';
|
||||
xxprocess.stdout.on('data', function (data) { xxprocess.data += data; });
|
||||
xxprocess.stderr.on('data', function (data) { xxprocess.data += data; });
|
||||
xxprocess.on('close', function (code) { console.log('Update completed...'); setTimeout(function () { obj.launchChildServer(startLine); }, 1000); });
|
||||
} else {
|
||||
if (error != null) {
|
||||
// This is an un-expected restart
|
||||
|
@ -131,7 +141,7 @@ function CreateMeshCentralServer() {
|
|||
}
|
||||
}
|
||||
});
|
||||
xprocess.stdout.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } if (data.indexOf('Updating settings folder...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Server Ctrl-C exit...') >= 0) { xprocess.xrestart = 2; } console.log(data); });
|
||||
xprocess.stdout.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } if (data.indexOf('Updating settings folder...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Server Ctrl-C exit...') >= 0) { xprocess.xrestart = 2; } else if (data.indexOf('Starting self upgrade...') >= 0) { xprocess.xrestart = 3; } console.log(data); });
|
||||
xprocess.stderr.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } obj.fs.appendFileSync('mesherrors.txt', '-------- ' + new Date().toLocaleString() + ' --------\r\n\r\n' + data + '\r\n\r\n\r\n'); });
|
||||
xprocess.on('close', function (code) { if ((code != 0) && (code != 123)) { /* console.log("Exited with code " + code); */ } });
|
||||
}
|
||||
|
@ -140,25 +150,22 @@ function CreateMeshCentralServer() {
|
|||
obj.getLatestServerVersion = function (callback) {
|
||||
if (callback == undefined) return;
|
||||
var child_process = require('child_process');
|
||||
var xprocess = child_process.exec('npm view meshcentral dist-tags.latest', function (error, stdout, stderr) {
|
||||
if (xprocess.xrestart == true) {
|
||||
setTimeout(function () { obj.launchChildServer(startLine); }, 500); // If exit with restart requested, restart the server.
|
||||
} else {
|
||||
if (error != null) { console.log('ERROR: Unable to start MeshCentral: ' + error); process.exit(); }
|
||||
}
|
||||
});
|
||||
var xprocess = child_process.exec('npm view meshcentral dist-tags.latest', function (error, stdout, stderr) { });
|
||||
xprocess.data = '';
|
||||
xprocess.stdout.on('data', function (data) { xprocess.data += data; });
|
||||
xprocess.stderr.on('data', function (data) { });
|
||||
xprocess.on('close', function (code) {
|
||||
var currentVer = null;
|
||||
try { currentVer = JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version; } catch (e) { }
|
||||
try { currentVer = JSON.parse(require('fs').readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { }
|
||||
var latestVer = null;
|
||||
if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } }
|
||||
callback(currentVer, latestVer);
|
||||
});
|
||||
}
|
||||
|
||||
// Initiate server self-update
|
||||
obj.performServerUpdate = function () { console.log('Starting self upgrade...'); process.exit(200); }
|
||||
|
||||
obj.StartEx = function () {
|
||||
// Look to see if data and/or file path is specified
|
||||
if (obj.args.datapath) { obj.datapath = obj.args.datapath; }
|
||||
|
@ -310,6 +317,9 @@ function CreateMeshCentralServer() {
|
|||
obj.mpsserver = require('./mpsserver.js').CreateMpsServer(obj, obj.db, obj.args, obj.certificates);
|
||||
}
|
||||
|
||||
// Start periodic maintenance
|
||||
obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour
|
||||
|
||||
// Dispatch an event that the server is now running
|
||||
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' })
|
||||
|
||||
|
@ -319,7 +329,28 @@ function CreateMeshCentralServer() {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Perform maintenance operations (called every hour)
|
||||
obj.maintenanceActions = function () {
|
||||
// Check if we need to perform server self-update
|
||||
if (obj.args.selfupdate == true) {
|
||||
obj.db.getValueOfTheDay('performSelfUpdate', 1, function (performSelfUpdate) {
|
||||
if (performSelfUpdate.value > 0) {
|
||||
performSelfUpdate.value--;
|
||||
obj.db.Set(performSelfUpdate);
|
||||
obj.getLatestServerVersion(function (currentVer, latestVer) { if (currentVer != latestVer) { obj.performServerUpdate(); return; } });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Clear old event entries and power entires
|
||||
obj.db.clearOldEntries('event', 30); // Clear all event entires that are older than 30 days.
|
||||
obj.db.clearOldEntries('power', 10); // Clear all event entires that are older than 10 days. If a node is connected longer than 10 days, current power state will be used for everything.
|
||||
|
||||
// Perform other database cleanup
|
||||
obj.db.cleanup();
|
||||
}
|
||||
|
||||
// Stop the Meshcentral server
|
||||
obj.Stop = function (restoreFile) {
|
||||
// If the database is not setup, exit now.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.0.7-g",
|
||||
"version": "0.0.7-n",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
|
|
@ -616,7 +616,6 @@
|
|||
var features = {{{features}}};
|
||||
var serverPublicNamePort = "{{{serverDnsName}}}:{{{serverPublicPort}}}";
|
||||
var amtScanResults = null;
|
||||
//var xxmap = null;
|
||||
|
||||
function startup() {
|
||||
// Guard against other site's top frames (web bugs).
|
||||
|
@ -852,14 +851,17 @@
|
|||
}
|
||||
case 'serverversion': {
|
||||
if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerUpdate')) {
|
||||
console.log(message);
|
||||
var x = '<div style=width:100%;max-height:260px;overflow-x:hidden;overflow-y:auto;line-height:160%>';
|
||||
if (!message.current) { message.current = 'Unknown'; }
|
||||
if (!message.latest) { message.latest = 'Unknown'; }
|
||||
x += addHtmlValue2('Current Version', '<b>' + EscapeHtml(message.current) + '</b>');
|
||||
x += addHtmlValue2('Latest Version', '<b>' + EscapeHtml(message.latest) + '</b>');
|
||||
x += '</div>';
|
||||
QH('d2verinfo', x);
|
||||
if (message.current == message.latest) {
|
||||
setDialogMode(2, "MeshCentral Version", 1, null, x);
|
||||
} else {
|
||||
setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x + '<br />Select OK to start server self-update.');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1187,7 +1189,7 @@
|
|||
QV('devMapToolbar', view == 3);
|
||||
QV('devListToolbarSort', view < 3);
|
||||
if (view == 3) {
|
||||
setTimeout( function() { xxmap.map.updateSize();}, 200);
|
||||
setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200);
|
||||
// TODO
|
||||
} else {
|
||||
// 3 wide or list view
|
||||
|
@ -3688,10 +3690,14 @@
|
|||
|
||||
function server_showVersionDlg() {
|
||||
if (xxdialogMode) return;
|
||||
setDialogMode(2, "MeshCentral Version", 1, null, "<div id=d2verinfo>Loading...</div>", 'MeshCentralServerUpdate');
|
||||
setDialogMode(2, "MeshCentral Version", 1, null, "Loading...", 'MeshCentralServerUpdate');
|
||||
meshserver.Send({ action: 'serverversion' });
|
||||
}
|
||||
|
||||
function server_showVersionDlgEx() {
|
||||
meshserver.Send({ action: 'serverupdate' });
|
||||
}
|
||||
|
||||
//
|
||||
// MY MESHS
|
||||
//
|
||||
|
|
|
@ -1165,6 +1165,13 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
|
|||
obj.parent.getLatestServerVersion(function (currentVersion, latestVersion) { ws.send(JSON.stringify({ action: 'serverversion', current: currentVersion, latest: latestVersion })); });
|
||||
break;
|
||||
}
|
||||
case 'serverupdate':
|
||||
{
|
||||
// Perform server update
|
||||
if ((user.siteadmin & 16) == 0) break;
|
||||
obj.parent.performServerUpdate();
|
||||
break;
|
||||
}
|
||||
case 'createmesh':
|
||||
{
|
||||
// Create mesh
|
||||
|
|
Loading…
Reference in New Issue