From 8b2835d40a924aedbd6cc609d68ae7ac401aa2c9 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Sat, 17 Apr 2021 14:44:19 -0700 Subject: [PATCH] Added Agent Error Log Fetching. --- agents/meshcore.js | 3 +++ meshagent.js | 21 +++++++++++++++++++++ meshcentral-config-schema.json | 1 + meshcentral.js | 5 +++++ meshuser.js | 1 + public/styles/style.css | 7 +++++++ views/default.handlebars | 4 ++-- 7 files changed, 40 insertions(+), 2 deletions(-) diff --git a/agents/meshcore.js b/agents/meshcore.js index a47c8447..ee77f176 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -1249,6 +1249,9 @@ function handleServerCommand(data) { agentFileHttpPendingRequests.push(data); serverFetchFile(); break; + case 'errorlog': // Return agent error log + try { mesh.SendCommand(JSON.stringify({ action: 'errorlog', log: require('util-agentlog').read(data.startTime) })); } catch (ex) { } + break; default: // Unknown action, ignore it. break; diff --git a/meshagent.js b/meshagent.js index df7e4f6f..2bf1312a 100644 --- a/meshagent.js +++ b/meshagent.js @@ -68,6 +68,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { db.Remove('nt' + obj.dbNodeKey); // Remove notes db.Remove('lc' + obj.dbNodeKey); // Remove last connect time db.Remove('si' + obj.dbNodeKey); // Remove system information + db.Remove('al' + obj.dbNodeKey); // Remove error log last time if (db.RemoveSMBIOS) { db.RemoveSMBIOS(obj.dbNodeKey); } // Remove SMBios data db.RemoveAllNodeEvents(obj.dbNodeKey); // Remove all events for this node db.removeAllPowerEventsForNode(obj.dbNodeKey); // Remove all power events for this node @@ -1005,6 +1006,17 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if ((results != null) && (results.length == 1)) { obj.send(JSON.stringify({ action: 'sysinfo', hash: results[0].hash })); } else { obj.send(JSON.stringify({ action: 'sysinfo' })); } }); + // Agent error log dump + if (parent.parent.agentErrorLog != null) { + db.Get('al' + obj.dbNodeKey, function (err, docs) { // Agent Log + if ((docs != null) && (docs.length == 1) && (typeof docs[0].lastEvent)) { + obj.send('{"action":"errorlog","startTime":' + docs[0].lastEvent + '}'); // Ask all events after a given time + } else { + obj.send('{"action":"errorlog"}'); // Ask all + } + }); + } + // Set agent core dump if ((parent.parent.config.settings != null) && ((parent.parent.config.settings.agentcoredump === true) || (parent.parent.config.settings.agentcoredump === false))) { obj.send(JSON.stringify({ action: 'coredump', value: parent.parent.config.settings.agentcoredump })); @@ -1516,6 +1528,15 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { } break; } + case 'errorlog': { // This is the agent error log + if ((!Array.isArray(command.log)) || (command.log.length == 0) || (parent.parent.agentErrorLog == null)) break; + var lastLogEntry = command.log[command.log.length - 1]; + if ((lastLogEntry != null) && (typeof lastLogEntry == 'object') && (typeof lastLogEntry.t == 'number')) { + parent.fs.write(parent.parent.agentErrorLog, obj.dbNodeKey + ', ' + Date.now() + ', ' + str + '\r\n', function (err) { }); + db.Set({ _id: 'al' + obj.dbNodeKey, lastEvent: lastLogEntry.t }); + } + break; + } case '2faauth': { // Validate input if ((typeof command.url != 'string') || (typeof command.approved != 'boolean') || (command.url.startsWith('2fa://') == false)) return; diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 1a43bc92..e0cd8a30 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -61,6 +61,7 @@ "agentAliasPort": { "type": "integer", "minimum": 1, "maximum": 65535, "description": "When set, indicates the actual publically visible agent-only port. If not set, the AgentPort value is used." }, "agentAliasDNS": { "type": "string", "format": "hostname", "description": "When set, specified the DNS name used by agents to connect to the agent-only port." }, "agentPortTls": { "type": "boolean", "default": true, "description": "Indicates if the agent-only port must perform TLS, this should be set to false if TLS is performed in front of this server." }, + "agentLogDump": { "type": "boolean", "default": false, "description": "Automatically downloads all agent error logs into meshcentral-data/agenterrorlogs.txt." }, "agentCoreDump": { "type": "boolean", "default": false, "description": "Automatically activates and transfers any agent crash dump files to the server in meshcentral-data/coredumps." }, "agentCoreDumpUsers": { "type": "array", "description": "List of non-administrator users that have access to mesh agent crash dumps." }, "ignoreAgentHashCheck": { "type": [ "boolean", "string" ], "default": false, "description": "When true, the agent no longer checked the TLS certificate of the server. This should be used for debugging only. You can also set this to a comma seperated list of IP addresses to ignore, for example: \"192.168.2.100,192.168.1.0/24\"." }, diff --git a/meshcentral.js b/meshcentral.js index b0f632f3..36bbe813 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -1308,6 +1308,11 @@ function CreateMeshCentralServer(config, args) { return; } + // Setup agent error log + if ((obj.config) && (obj.config.settings) && (obj.config.settings.agentlogdump != null)) { + obj.fs.open(obj.path.join(obj.datapath, 'agenterrorlogs.txt'), 'a', function (err, fd) { obj.agentErrorLog = fd; }) + } + // Perform other database cleanup obj.db.cleanup(); diff --git a/meshuser.js b/meshuser.js index f0717e50..a8f17f05 100644 --- a/meshuser.js +++ b/meshuser.js @@ -3776,6 +3776,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use db.Remove('nt' + node._id); // Remove notes db.Remove('lc' + node._id); // Remove last connect time db.Remove('si' + node._id); // Remove system information + db.Remove('al' + node._id); // Remove error log last time if (db.RemoveSMBIOS) { db.RemoveSMBIOS(node._id); } // Remove SMBios data db.RemoveAllNodeEvents(node._id); // Remove all events for this node db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node diff --git a/public/styles/style.css b/public/styles/style.css index 5a1c77ac..4f05e38b 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -316,6 +316,13 @@ body { overflow: hidden; } +.boxsize { + -ms-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + -webkit-box-sizing:border-box; +} + .night #column_l { background-color: #000; } diff --git a/views/default.handlebars b/views/default.handlebars index e89a0773..e155e18c 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -10207,8 +10207,8 @@ var y = '', x = "Create a temporary username and password that can be used as alternative login to your account. This is useful for allowing tools or other services to access your account." + '

'; var options = { 0 : "Unlimited", 1 : "1 minute", 5 : "5 minutes", 10 : "10 minutes", 15 : "15 minutes", 30 : "30 minutes", 45 : "45 minutes", 60 : "60 minutes", 120 : "2 hours", 240 : "4 hours", 480 : "8 hours", 720 : "12 hours", 960 : "16 hours", 1440 : "24 hours", 2880 : "2 days", 5760 : "4 days" } for (var i in options) { y += ''; } - x += addHtmlValue("Token Name", ''); - x += addHtmlValue("Expire Time", ''); + x += addHtmlValue("Token Name", ''); + x += addHtmlValue("Expire Time", ''); setDialogMode(2, "Create Login Token", 3, account_createLoginTokenEx, x); QE('idx_dlgOkButton', false); Q('d2tokenName').focus();