From d006dbbff848487ce9954b769f5471409d802d84 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Sat, 31 Jul 2021 19:31:47 -0700 Subject: [PATCH] More last device connection improvements. --- meshagent.js | 9 ++------ meshcentral.js | 45 +++++++++++++++++++++++++++++++++------- meshuser.js | 3 ++- views/default.handlebars | 7 ++++--- 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/meshagent.js b/meshagent.js index a324848d..a9362b84 100644 --- a/meshagent.js +++ b/meshagent.js @@ -64,7 +64,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Remove this agent from the webserver list if (parent.wsagents[obj.dbNodeKey] == obj) { delete parent.wsagents[obj.dbNodeKey]; - parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1); + parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1, null, { remoteaddrport: obj.remoteaddrport }); } // Remove this agent from the list of agents with bad web certificates @@ -95,9 +95,6 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if ((state.connectivity & 1) != 0) { parent.wsagents[obj.dbNodeKey].close(); } // Disconnect mesh agent if ((state.connectivity & 2) != 0) { parent.parent.mpsserver.closeAllForNode(obj.dbNodeKey); } // Disconnect CIRA connection } - } else { - // Update the last connect time - if (obj.authenticated == 2) { db.Set({ _id: 'lc' + obj.dbNodeKey, type: 'lastconnect', domain: domain.id, time: Date.now(), addr: obj.remoteaddrport, cause: 1 }); } } // Set this agent as no longer authenticated @@ -787,7 +784,6 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Mark when this device connected obj.connectTime = Date.now(); - db.Set({ _id: 'lc' + obj.dbNodeKey, type: 'lastconnect', domain: domain.id, time: obj.connectTime, addr: obj.remoteaddrport, cause: 1 }); // Device already exists, look if changes have occured var changes = [], change = 0, log = 0; @@ -849,7 +845,6 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Mark when this device connected obj.connectTime = Date.now(); - db.Set({ _id: 'lc' + obj.dbNodeKey, type: 'lastconnect', domain: domain.id, time: obj.connectTime, addr: obj.remoteaddrport, cause: 1 }); // This node does not exist, create it. var agentName = obj.agentName ? obj.agentName : obj.agentInfo.computerName; @@ -893,7 +888,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { dupAgent.close(3); } else { // Indicate the agent is connected - parent.parent.SetConnectivityState(obj.dbMeshKey, obj.dbNodeKey, obj.connectTime, 1, 1); + parent.parent.SetConnectivityState(obj.dbMeshKey, obj.dbNodeKey, obj.connectTime, 1, 1, null, { remoteaddrport: obj.remoteaddrport }); } // We are done, ready to communicate with this agent diff --git a/meshcentral.js b/meshcentral.js index 413088fc..1cd71f24 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -2100,7 +2100,7 @@ function CreateMeshCentralServer(config, args) { // powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present //var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local', '', '', '', 'Intel AMT Relay', '', '', '', '', '', '', '', 'MQTT']; //var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present']; - obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid) { + obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) { //console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid))); if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'SetConnectivityState', meshid: meshid, nodeid: nodeid, connectTime: connectTime, connectType: connectType, powerState: powerState }); } @@ -2137,6 +2137,11 @@ function CreateMeshCentralServer(config, args) { // Event the node connection change if (eventConnectChange == 1) { obj.DispatchEvent(obj.webserver.CreateNodeDispatchTargets(meshid, nodeid), obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, domain: nodeid.split('/')[1], conn: state.connectivity, pwr: state.powerState, ct: connectTime, nolog: 1, nopeers: 1 }); + + // Save indication of node connection change + const lc = { _id: 'lc' + nodeid, type: 'lastconnect', domain: nodeid.split('/')[1], time: Date.now(), cause: 1, connectType: connectType, serverid: obj.serverId }; + if (extraInfo && extraInfo.remoteaddrport) { lc.addr = extraInfo.remoteaddrport; } + obj.db.Set(lc); } } else { // Multi server mode @@ -2144,14 +2149,16 @@ function CreateMeshCentralServer(config, args) { // Change the node connection state if (serverid == null) { serverid = obj.serverId; } if (obj.peerConnectivityByNode[serverid] == null) return; // Guard against unknown serverid's + var eventConnectChange = 0; var state = obj.peerConnectivityByNode[serverid][nodeid]; if (state) { // Change the connection in the node and mesh state lists - if ((state.connectivity & connectType) == 0) { state.connectivity |= connectType; } + if ((state.connectivity & connectType) == 0) { state.connectivity |= connectType; eventConnectChange = 1; } state.meshid = meshid; } else { // Add the connection to the node and mesh state list obj.peerConnectivityByNode[serverid][nodeid] = state = { connectivity: connectType, meshid: meshid }; + eventConnectChange = 1; } // Set node power state @@ -2160,6 +2167,7 @@ function CreateMeshCentralServer(config, args) { if ((state.connectivity & 1) != 0) { powerState = state.agentPower; } else if ((state.connectivity & 2) != 0) { powerState = state.ciraPower; } else if ((state.connectivity & 4) != 0) { powerState = state.amtPower; } if ((state.powerState == null) || (state.powerState != powerState)) { state.powerState = powerState; + eventConnectChange = 1; // Set new power state in database var record = { time: new Date(connectTime), nodeid: nodeid, power: powerState, server: obj.multiServer.serverid }; @@ -2167,9 +2175,18 @@ function CreateMeshCentralServer(config, args) { obj.db.storePowerEvent(record, obj.multiServer); } - // Update the combined node state - var x = {}; x[nodeid] = 1; - obj.UpdateConnectivityState(x); + if (eventConnectChange == 1) { + // Update the combined node state + var x = {}; x[nodeid] = 1; + obj.UpdateConnectivityState(x); + + // Save indication of node connection change + if (serverid == obj.serverId) { + const lc = { _id: 'lc' + nodeid, type: 'lastconnect', domain: nodeid.split('/')[1], time: Date.now(), cause: 1, connectType: connectType, serverid: obj.serverId }; + if (extraInfo && extraInfo.remoteaddrport) { lc.addr = extraInfo.remoteaddrport; } + obj.db.Set(lc); + } + } } }; @@ -2177,7 +2194,7 @@ function CreateMeshCentralServer(config, args) { // meshId: mesh identifier of format mesh/domain/meshidhex // nodeId: node identifier of format node/domain/nodeidhex // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 3 = Intel AMT local. - obj.ClearConnectivityState = function (meshid, nodeid, connectType, serverid) { + obj.ClearConnectivityState = function (meshid, nodeid, connectType, serverid, extraInfo) { //console.log('ClearConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + (serverid == null?(''):(', ServerId: ' + serverid))); if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'ClearConnectivityState', meshid: meshid, nodeid: nodeid, connectType: connectType }); } @@ -2192,6 +2209,11 @@ function CreateMeshCentralServer(config, args) { if ((state.connectivity & connectType) != 0) { state.connectivity -= connectType; + // Save indication of node connection change + const lc = { _id: 'lc' + nodeid, type: 'lastconnect', domain: nodeid.split('/')[1], time: Date.now(), cause: 0, connectType: connectType, serverid: obj.serverId }; + if (extraInfo && extraInfo.remoteaddrport) { lc.addr = extraInfo.remoteaddrport; } + obj.db.Set(lc); + // If the node is completely disconnected, clean it up completely if (state.connectivity == 0) { delete obj.connectivityByNode[nodeid]; } eventConnectChange = 1; @@ -2210,7 +2232,9 @@ function CreateMeshCentralServer(config, args) { } // Event the node connection change - if (eventConnectChange == 1) { obj.DispatchEvent(obj.webserver.CreateNodeDispatchTargets(meshid, nodeid), obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, domain: nodeid.split('/')[1], conn: state.connectivity, pwr: state.powerState, nolog: 1, nopeers: 1 }); } + if (eventConnectChange == 1) { + obj.DispatchEvent(obj.webserver.CreateNodeDispatchTargets(meshid, nodeid), obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, domain: nodeid.split('/')[1], conn: state.connectivity, pwr: state.powerState, nolog: 1, nopeers: 1 }); + } } else { // Multi server mode @@ -2224,6 +2248,13 @@ function CreateMeshCentralServer(config, args) { if ((state.connectivity & connectType) != 0) { state.connectivity -= connectType; // Remove one connectivity mode + // Save indication of node connection change + if (serverid == obj.serverId) { + const lc = { _id: 'lc' + nodeid, type: 'lastconnect', domain: nodeid.split('/')[1], time: Date.now(), cause: 0, connectType: connectType, serverid: obj.serverId }; + if (extraInfo && extraInfo.remoteaddrport) { lc.addr = extraInfo.remoteaddrport; } + obj.db.Set(lc); + } + // If the node is completely disconnected, clean it up completely if (state.connectivity == 0) { delete obj.peerConnectivityByNode[serverid][nodeid]; state.powerState = 0; } } diff --git a/meshuser.js b/meshuser.js index 193abeef..5a7d31be 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2568,6 +2568,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((common.validateString(command.desc, 0, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Group "' + mesh.name + '" description changed'; mesh.desc = command.desc; } if ((common.validateInt(command.flags) == true) && (command.flags != mesh.flags)) { if (change != '') change += ' and flags changed'; else change += 'Group "' + mesh.name + '" flags changed'; mesh.flags = command.flags; } if ((common.validateInt(command.consent) == true) && (command.consent != mesh.consent)) { if (change != '') change += ' and consent changed'; else change += 'Group "' + mesh.name + '" consent changed'; mesh.consent = command.consent; } + if ((common.validateInt(command.expireDevs, 0, 2000) == true) && (command.expireDevs != mesh.expireDevs)) { if (change != '') change += ' and auto-remove changed'; else change += 'Group "' + mesh.name + '" auto-remove changed'; if (command.expireDevs == 0) { delete mesh.expireDevs; } else { mesh.expireDevs = command.expireDevs; } } // See if we need to change device group invitation codes if (mesh.mtype == 2) { @@ -2598,7 +2599,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (change != '') { db.Set(mesh); - var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id, invite: mesh.invite }; + var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id, invite: mesh.invite, expireDevs: mesh.expireDevs }; if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the mesh. Another event will come. parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(mesh, [user._id]), obj, event); } diff --git a/views/default.handlebars b/views/default.handlebars index f0a320ba..b12ad533 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2912,6 +2912,7 @@ if (message.event.links) { meshes[message.event.meshid].links = message.event.links; } if (message.event.amt) { meshes[message.event.meshid].amt = message.event.amt; } if (message.event.invite != null) { meshes[message.event.meshid].invite = message.event.invite; } else { delete meshes[message.event.meshid].invite; } + if (message.event.expireDevs != null) { meshes[message.event.meshid].expireDevs = message.event.expireDevs; } else { delete meshes[message.event.meshid].expireDevs; } // Check if we lost rights to this mesh in this change. if (IsMeshViewable(message.event.meshid) == false) { @@ -4269,7 +4270,7 @@ if (deviceViewSettings.devsCols.indexOf('user') >= 0) { r += '' + getUserShortStr(node); } // User if (deviceViewSettings.devsCols.indexOf('ip') >= 0) { r += '' + (node.ip != null ? node.ip : ''); } // IP address if (deviceViewSettings.devsCols.indexOf('conn') >= 0) { r += '' + states.join(' + '); } // Connectivity - if (deviceViewSettings.devsCols.indexOf('lastseen') >= 0) { r += ''; if (node.conn & 1) { r += "Connected"; } else if (node.lastconnect != null) { r += printDateTime(new Date(node.lastconnect)); } } + if (deviceViewSettings.devsCols.indexOf('lastseen') >= 0) { r += ''; if (node.conn > 0) { r += "Connected"; } else if (node.lastconnect != null) { r += printDateTime(new Date(node.lastconnect)); } } div.innerHTML = r; } else if ((view == 3) || (view == 5)) { @@ -5300,7 +5301,7 @@ function powerSort(a, b) { var ap = a.pwr?a.pwr:0; var bp = b.pwr?b.pwr:0; if (ap > bp) return -1; if (ap < bp) return 1; if (ap == bp) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } return 0; } function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } function deviceHostSort(a, b) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } - function lastConnectSort(a, b) { var aa = a.lastconnect, bb = b.lastconnect; if (aa == null) { aa = 99999999999999; } if (bb == null) { bb = 99999999999999; } if (a.conn & 1) { aa = 99999999999998; } if (b.conn & 1) { bb = 99999999999998; } if (aa == bb) { return nameSort(a, b); } return (aa - bb); } + function lastConnectSort(a, b) { var aa = a.lastconnect, bb = b.lastconnect; if (aa == null) { aa = 99999999999999; } if (bb == null) { bb = 99999999999999; } if (a.conn > 0) { aa = 99999999999998; } if (b.conn > 0) { bb = 99999999999998; } if (aa == bb) { return nameSort(a, b); } return (aa - bb); } function onSearchFocus(x) { searchFocus = x; } function clearDeviceSearch() { Q('KvmSearchInput').value = Q('SearchInput').value = ''; Q('DevFilterSelect').value = 0; onOnlineCheckBox(); mainUpdate(1); } function onMapSearchFocus(x) { mapSearchFocus = x; } @@ -11489,7 +11490,7 @@ if (Q('d20flag2').checked) { flags += 2; } } if (serverinfo.devGroupSessionRecording == 1) { if (Q('d20flag4').checked) { flags += 4; } } - cmd.expireDevs = 0; + var expireDevs = 0; if (((flags & 1) == 0) && Q('d20expireDevice').checked) { expireDevs = parseInt(Q('d20expireDeviceDays').value); } meshserver.send({ action: 'editmesh', meshid: currentMesh._id, flags: flags, expireDevs: expireDevs }); }