diff --git a/meshipkvm.js b/meshipkvm.js
index 114cb134..2242d774 100644
--- a/meshipkvm.js
+++ b/meshipkvm.js
@@ -9,10 +9,12 @@
function CreateIPKVMManager(parent) {
const obj = {};
const managedGroups = {} // meshid --> Manager
+ const managedPorts = {} // nodeid --> PortInfo
// Subscribe for mesh creation events
parent.AddEventDispatch(['server-createmesh', 'server-deletemesh'], obj);
obj.HandleEvent = function (source, event, ids, id) {
+ console.log();
if ((event != null) && (event.action == 'createmesh') && (event.mtype == 4)) {
// Start managing this new device group
startManagement(parent.webserver.meshes[event.meshid]);
@@ -36,6 +38,7 @@ function CreateIPKVMManager(parent) {
if (mesh.kvm.model == 1) { // Raritan KX III
const manager = CreateRaritanKX3Manager(host, port, mesh.kvm.user, mesh.kvm.pass);
manager.meshid = mesh._id;
+ manager.domainid = mesh._id.split('/')[1];
managedGroups[mesh._id] = manager;
manager.onStateChanged = onStateChanged;
manager.onPortsChanged = onPortsChanged;
@@ -46,7 +49,18 @@ function CreateIPKVMManager(parent) {
// Stop managing a IP KVM device
function stopManagement(meshid) {
const manager = managedGroups[meshid];
- if (manager != null) { delete managedGroups[meshid]; manager.stop(); }
+ if (manager != null) {
+ // Remove all managed ports
+ for (var i = 0; i < manager.ports.length; i++) {
+ const port = manager.ports[i];
+ const nodeid = generateIpKvmNodeId(manager.meshid, port.PortId, manager.domainid);
+ delete managedPorts[nodeid]; // Remove the managed port
+ }
+
+ // Remove the manager
+ delete managedGroups[meshid];
+ manager.stop();
+ }
}
// Called when a KVM device changes state
@@ -62,12 +76,82 @@ function CreateIPKVMManager(parent) {
function onPortsChanged(sender, updatedPorts) {
for (var i = 0; i < updatedPorts.length; i++) {
const port = sender.ports[updatedPorts[i]];
+ const nodeid = generateIpKvmNodeId(sender.meshid, port.PortId, sender.domainid);
if ((port.Status == 1) && (port.Class == 'KVM')) {
console.log(port.PortNumber + ', ' + port.PortId + ', ' + port.Name + ', ' + port.Type + ', ' + ((port.StatAvailable == 0) ? 'Idle' : 'Connected'));
+ if ((managedPorts[nodeid] == null) || (managedPorts[nodeid].name != port.Name)) {
+ parent.db.Get(nodeid, function (err, nodes) {
+ if ((err != null) || (nodes == null)) return;
+ const mesh = parent.webserver.meshes[sender.meshid];
+ if (nodes.length == 0) {
+ // The device does not exist, create it
+ const device = { type: 'node', mtype: 4, _id: nodeid, icon: 1, meshid: sender.meshid, name: port.Name, rname: port.Name, domain: sender.domainid, porttype: port.Type, portid: port.PortId, portnum: port.PortNumber };
+ parent.db.Set(device);
+
+ // Event the new node
+ parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(sender.meshid, [nodeid]), obj, { etype: 'node', action: 'addnode', nodeid: nodeid, node: device, msgid: 57, msgArgs: [port.Name, mesh.name], msg: ('Added device ' + port.Name + ' to device group ' + mesh.name), domain: sender.domainid });
+ } else {
+ // The device exists, update it
+ var changed = false;
+ const device = nodes[0];
+ if (device.rname != port.Name) { device.rname = port.Name; changed = true; } // Update the device port name
+ if ((mesh.flags) && (mesh.flags & 2) && (device.name != port.Name)) { device.name = port.Name; changed = true; } // Sync device name to port name
+ if (changed) {
+ // Update the database and event the node change
+ parent.db.Set(device);
+ parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(sender.meshid, [nodeid]), obj, { etype: 'node', action: 'changenode', nodeid: nodeid, node: device, domain: sender.domainid, nolog: 1 });
+ }
+ }
+
+ // Set the connectivity state if needed
+ if (managedPorts[nodeid] == null) {
+ parent.SetConnectivityState(sender.meshid, nodeid, Date.now(), 1, 1, null, null);
+ managedPorts[nodeid] = { name: port.Name, busy: false };
+ }
+
+ // Update busy state
+ const portInfo = managedPorts[nodeid];
+ if (portInfo.busy != (port.StatAvailable != 0)) {
+ console.log('Busy state', (port.StatAvailable != 0));
+ }
+ });
+ } else {
+ // Update busy state
+ const portInfo = managedPorts[nodeid];
+ if (portInfo.busy != (port.StatAvailable != 0)) {
+ console.log('Busy state', (port.StatAvailable != 0));
+ }
+ }
+ } else {
+ if (managedPorts[nodeid] != null) {
+ // This port is no longer connected
+ parent.ClearConnectivityState(sender.meshid, nodeid, 1, null, null);
+
+ // If the device group policy is set to auto-remove devices, remove it now
+ if ((mesh.flags) && (mesh.flags & 1)) { // Auto-remove devices
+ parent.db.Remove(nodeid); // Remove node with that id
+ parent.db.Remove('nt' + nodeid); // Remove notes
+ parent.db.Remove('lc' + nodeid); // Remove last connect time
+ parent.db.Remove('al' + nodeid); // Remove error log last time
+ parent.db.RemoveAllNodeEvents(nodeid); // Remove all events for this node
+ parent.db.removeAllPowerEventsForNode(nodeid); // Remove all power events for this node
+
+ // Event node deletion
+ parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(sender.meshid, [nodeid]), obj, { etype: 'node', action: 'removenode', nodeid: nodeid, domain: domain.id, nolog: 1 });
+ }
+
+ // Remove the managed port
+ delete managedPorts[nodeid];
+ }
}
}
}
+ // Generate the nodeid from the device group and device identifier
+ function generateIpKvmNodeId(meshid, portid, domainid) {
+ return 'node/' + domainid + '/' + parent.crypto.createHash('sha384').update(Buffer.from(meshid + '/' + portid)).digest().toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
+ }
+
return obj;
}
diff --git a/meshuser.js b/meshuser.js
index adef0414..f1911553 100644
--- a/meshuser.js
+++ b/meshuser.js
@@ -2528,7 +2528,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
parent.parent.DispatchEvent(targets, obj, event);
// Event the device group creation
- var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msgid: 76, msgArgs: [command.meshname], msg: 'Device group created: ' + command.meshname, domain: domain.id, creation: mesh.creation, creatorid: mesh.creatorid, creatorname: mesh.creatorname, flags: mesh.flags, consent: mesh.consent };
+ var event = { etype: 'mesh', userid: user._id, username: user.name, meshid: meshid, mtype: command.meshtype, mesh: parent.CloneSafeMesh(mesh), action: 'createmesh', msgid: 76, msgArgs: [command.meshname], msg: 'Device group created: ' + command.meshname, domain: domain.id };
parent.parent.DispatchEvent(['*', 'server-createmesh', meshid, user._id], obj, event); // Even if DB change stream is active, this event must be acted upon.
// Log in the auth log
diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars
index 33b5dbeb..f1efe098 100644
--- a/views/default-mobile.handlebars
+++ b/views/default-mobile.handlebars
@@ -1765,8 +1765,8 @@
}
case 'createmesh': {
// A new mesh was created
- if ((meshes[message.event.meshid] == null) && ((userinfo.manageAllDeviceGroups) || (message.event.links[userinfo._id] != null))) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
- meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
+ if ((meshes[message.event.meshid] == null) && ((userinfo.manageAllDeviceGroups) || (message.event.mesh.links[userinfo._id] != null))) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
+ meshes[message.event.meshid] = message.event.mesh;
mainUpdate(4 + 128);
meshserver.send({ action: 'files' });
}
@@ -3218,7 +3218,7 @@
var states = [];
if (node.state > 0 && node.state < powerStatetable.length) state.push(powerStatetable[node.state]);
if (node.conn) {
- if ((node.conn & 1) != 0) { states.push('' + "Agent" + ''); }
+ if ((node.conn & 1) != 0) { if (node.mtype == 4) { states.push('' + ((node.mtype == 4)?"IP-KVM":"Agent") + ''); } else { states.push('' + "IP KVM" + ''); } }
if ((node.conn & 2) != 0) { states.push('' + "CIRA" + ''); }
else if ((node.conn & 4) != 0) { states.push('' + "Intel® AMT" + ''); }
if ((node.conn & 8) != 0) { states.push('' + "Relay" + ''); }
@@ -3356,7 +3356,7 @@
if (node.rname != null) { x += addDeviceAttribute('' + "Name" + '', '' + EscapeHtml(node.rname) + ''); }
// Attribute: Host
- if ((features & 1) == 0) { // If not WAN-only, local hostname is in use
+ if (((features & 1) == 0) && (node.mtype != 4)) { // If not WAN-only, local hostname is in use
if ((meshrights & 4) != 0) {
if (node.host) {
x += addDeviceAttribute("Hostname", '' + EscapeHtml(node.host) + '');
@@ -3376,6 +3376,12 @@
x += addDeviceAttribute("Description", description);
}
+ // IP-KVM information
+ if (node.mtype == 4) {
+ if (node.portnum) { x += addDeviceAttribute("Port Number", node.portnum); }
+ if (node.porttype) { x += addDeviceAttribute("Port Type", node.porttype); }
+ }
+
// Attribute: Mesh Agent
if ((node.agent != null) && (node.agent.id != null) && (node.mtype == 3)) {
if (node.agent.id == 4) { x += addDeviceAttribute("Device Type", "Windows"); }
@@ -3476,7 +3482,7 @@
var connectivity = node.conn;
if (connectivity && connectivity > 1) {
var cstate = [];
- if ((node.conn & 1) != 0) cstate.push('' + "Agent" + '');
+ if ((node.conn & 1) != 0) cstate.push('' + ((node.mtype == 4) ? "IP-KVM" : "Agent") + '');
if ((node.conn & 2) != 0) cstate.push('' + "Intel® AMT CIRA" + '');
else if ((node.conn & 4) != 0) cstate.push('' + "Intel® AMT" + '');
if ((node.conn & 8) != 0) cstate.push('' + "Agent Relay" + '');
@@ -3488,7 +3494,7 @@
var groupingTags = '' + "None" + '';
if (node.tags != null) { groupingTags = ''; for (var i in node.tags) { groupingTags += '' + EscapeHtml(node.tags[i]) + ' '; } }
if ((meshrights & 4) != 0) {
- x += addDeviceAttribute("Tags", '' + groupingTags + '');
+ x += addDeviceAttribute("Tags", '' + groupingTags + '');
} else {
x += addDeviceAttribute("Tags", '' + groupingTags + '');
}
@@ -3536,7 +3542,7 @@
// Set the node power state
var powerstate = PowerStateStr(node.state);
//if (node.state == 0) { powerstate = 'Unknown State'; }
- if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Mesh Agent"; }
+ if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += ((mesh.mtype == 4) ? "IP-KVM" : "Mesh Agent"); }
if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT connected"; }
else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT detected"; }
if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "MQTT channel connected"; }
@@ -3600,17 +3606,17 @@
if ((currentDevicePanel != 1) &&
(currentNode != null) &&
((meshrights & 8) || (meshrights & 256)) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 65536) == 0)) &&
- (((currentNode.agent == null) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 1)))
+ (((currentNode.agent == null) && (currentNode.intelamt) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 1)))
) { menus.push({ n: "Desktop", f: 'setupDeviceMenu(1)' }); }
if ((currentDevicePanel != 5) &&
(currentNode != null) &&
((meshrights & 8) || (meshrights & 256)) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0)) &&
- (((currentNode.agent == null) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 2)))
+ (((currentNode.agent == null) && (currentNode.intelamt) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 2)))
) { menus.push({ n: "Terminal", f: 'setupDeviceMenu(5)' }); }
- if ((currentDevicePanel != 2) && (currentNode != null) && (meshrights & 8) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0)) && ((currentNode.mtype != 1) && (currentNode.agent.caps & 4))) { menus.push({ n: "Files", f: 'setupDeviceMenu(2)' }); }
- if ((currentDevicePanel != 3) && (currentNode != null) && (currentNode.mtype != 3) && ((meshrights & 1048576) != 0)) { menus.push({ n: "Details", f: 'setupDeviceMenu(3)' }); }
+ if ((currentDevicePanel != 2) && (currentNode != null) && (meshrights & 8) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0)) && ((currentNode.mtype != 1) && (currentNode.agent) && (currentNode.agent.caps & 4))) { menus.push({ n: "Files", f: 'setupDeviceMenu(2)' }); }
+ if ((currentDevicePanel != 3) && (currentNode != null) && (currentNode.mtype < 3) && ((meshrights & 1048576) != 0)) { menus.push({ n: "Details", f: 'setupDeviceMenu(3)' }); }
if ((currentDevicePanel != 4) && (currentNode != null) && (meshrights & 0x00000010) && (currentNode.mtype == 2)) { menus.push({ n: "Console", f: 'setupDeviceMenu(4)' }); }
updateFooterMenu(menus);
updateCurrentUrl();
@@ -5144,7 +5150,7 @@
}
function p13setActions() {
- var advancedFeatures = (currentNode.agent.id != 14); // Reduct file feature on some devices.
+ var advancedFeatures = ((currentNode.agent) && (currentNode.agent.id != 14)); // Reduct file feature on some devices.
if (p13filetree == null) {
QE('p13DeleteFileButton', false);
QE('p13NewFolderButton', false);
diff --git a/views/default.handlebars b/views/default.handlebars
index ad0eb872..61181e01 100644
--- a/views/default.handlebars
+++ b/views/default.handlebars
@@ -2984,8 +2984,8 @@
}
case 'createmesh': {
// A new mesh was created
- if ((meshes[message.event.meshid] == null) && ((serverinfo.manageAllDeviceGroups) || (message.event.links[userinfo._id] != null))) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
- meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links, creation: message.event.creation, creatorid: message.event.creatorid, creatorname: message.event.creatorname, flags: message.event.flags, consent: message.event.consent };
+ if ((meshes[message.event.meshid] == null) && ((serverinfo.manageAllDeviceGroups) || (message.event.mesh.links[userinfo._id] != null))) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
+ meshes[message.event.meshid] = message.event.mesh;
mainUpdate(4 + 128 + 8192 + 16384);
meshserver.send({ action: 'files' });
}
@@ -4366,7 +4366,11 @@
} else if (view == 2) {
var states = [];
if (node.conn) {
- if ((node.conn & 1) != 0) { states.push('' + "Agent" + ''); }
+ if (node.mtype == 4) {
+ if ((node.conn & 1) != 0) { states.push('' + "IP-KVM" + ''); }
+ } else {
+ if ((node.conn & 1) != 0) { states.push('' + "Agent" + ''); }
+ }
if ((node.conn & 2) != 0) { states.push('' + "CIRA" + ''); }
else if ((node.conn & 4) != 0) { states.push('' + "AMT" + ''); }
if ((node.conn & 8) != 0) { states.push('' + "Relay" + ''); }
@@ -5130,7 +5134,13 @@
var states = [];
if (node.state > 0 && node.state < powerStatetable.length) state.push(powerStatetable[node.state]);
if (node.conn) {
- if ((node.conn & 1) != 0) { states.push('' + "Agent" + ''); }
+ if ((node.conn & 1) != 0) {
+ if (node.mtype == 4) {
+ states.push('' + "IP-KVM" + '');
+ } else {
+ states.push('' + "Agent" + '');
+ }
+ }
if ((node.conn & 2) != 0) { states.push('' + "CIRA" + ''); }
else if ((node.conn & 4) != 0) { states.push('' + "AMT" + ''); }
if ((node.conn & 8) != 0) { states.push('' + "Relay" + ''); }
@@ -6659,7 +6669,7 @@
if ((node.rname != null) && (node.name != node.rname)) { x += addDeviceAttribute('' + "OS Name" + '', '' + EscapeHtml(node.rname) + ''); }
// Attribute: Host
- if ((features & 1) == 0) { // If not WAN-only, local hostname is in use
+ if (((features & 1) == 0) && (node.mtype != 4)) { // If not WAN-only, local hostname is in use
x += addDeviceAttribute("Hostname", addLinkConditional('' + (node.host?EscapeHtml(node.host):('' + "None" + '')) + '', 'showEditNodeValueDialog(1)', meshrights & 4));
}
@@ -6671,6 +6681,12 @@
x += addDeviceAttribute("Description", description);
}
+ // IP-KVM information
+ if (node.mtype == 4) {
+ if (node.portnum) { x += addDeviceAttribute("Port Number", node.portnum); }
+ if (node.porttype) { x += addDeviceAttribute("Port Type", node.porttype); }
+ }
+
// Attribute: Mesh Agent
if ((node.agent != null) && (node.agent.id != null) && (node.mtype == 3)) {
if (node.agent.id == 4) { x += addDeviceAttribute("Device Type", "Windows"); }
@@ -6854,20 +6870,22 @@
x += '
';
// Show action button, only show if we have permissions 4, 8, 64
- if (((meshrights & (4 + 8 + 64)) != 0) && (node.mtype != 3) && ((node.agent == null) || (node.agent.id != 34))) { x += ''; }
+ if (((meshrights & (4 + 8 + 64)) != 0) && (node.mtype < 3) && ((node.agent == null) || (node.agent.id != 34))) { x += ''; }
x += '';
x += '';
- if ((meshrights & 8) && ((connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0)))) { x += ''; }
- //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += ''; }
- if ((meshrights & 8) && (connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0))) { x += ''; }
- if ((serverinfo != null) && (serverinfo.altmessenging != null) && (meshrights & 8) && (connectivity & 1)) {
- for (var i in serverinfo.altmessenging) { x += ''; }
- }
- if ((serverinfo.guestdevicesharing !== false) && (node.agent != null) && (node.agent.caps & 3) && (connectivity & 1) && ((meshrights & 0x80008) == 0x80008) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 0x1000) == 0))) {
- x += '';
- QV('DeskGuestShareButton', true);
- } else {
- QV('DeskGuestShareButton', false);
+ if (node.mtype != 4) {
+ if ((meshrights & 8) && ((connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0)))) { x += ''; }
+ //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += ''; }
+ if ((meshrights & 8) && (connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0))) { x += ''; }
+ if ((serverinfo != null) && (serverinfo.altmessenging != null) && (meshrights & 8) && (connectivity & 1)) {
+ for (var i in serverinfo.altmessenging) { x += ''; }
+ }
+ if ((serverinfo.guestdevicesharing !== false) && (node.agent != null) && (node.agent.caps & 3) && (connectivity & 1) && ((meshrights & 0x80008) == 0x80008) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 0x1000) == 0))) {
+ x += '';
+ QV('DeskGuestShareButton', true);
+ } else {
+ QV('DeskGuestShareButton', false);
+ }
}
// Custom UI
@@ -6892,7 +6910,7 @@
// Show bottom buttons
x = '