Name
global.LSOFParser
Description
Parses output of the following command lsof -i4TCP -n -P -F pcnfT.
Script
// Discovery class
/**
* Parses output of the following command:
* lsof -i4TCP -n -P -F pcnfT
*
* author tom.dilatush@service-now.com
*/
var LSOFParser = Class.create();
LSOFParser.prototype = {
/**
* Parse the given lsof output. Results are in tcp[], each element of which is an object with the following fields:
* pid: process ID
* type: "on" or "to" for listening on or connecting to, respectively
* ip: the IP address we're listening on or connecting to (depending on type)
* port: the port we're listening on or connecting to (depending on type)
* local_ip: the local IP address we're connecting from (undefined if listening on)
* local_port: the local port we're connecting from (undefined if listening on)
*
* Errors are recorded in the error string if the isValid is false.
*/
parse: function(output) {
this.initProcess();
this.errorString = '';
this.tcp = [];
this.isValid = true;
var lines = output.trim().split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (gs.nil(line)) {
if (i != lines.length - 1)
this.error('Empty line in lsof output, line: ' + (i + 1));
continue;
}
var prefix = line.substring(0, 1);
var eventHandler = 'on_' + prefix;
if (this[eventHandler])
this[eventHandler](line.substring(1));
else
this.error('Bad line in lsof output, line '+(i+1)+':\n'+line);
}
this.on_endProcess();
},
on_p: function(line) {
var pid = new Number(line);
if (isNaN(pid)) {
this.error('Invalid PID: ' + line);
return;
}
this.on_endProcess();
this.currentPID = ''+pid;
},
on_c: function(line) {
if (gs.nil(line)) {
this.error('Empty command');
return;
}
this.currentCmd = line;
},
on_f: function(line) {
var fd = new Number(line);
//if there's more than 10000 file descriptors, lsof replaces higher digits with "*"
if(line[0] == "*") {
if(!this.lastPrefix)
this.lastPrefix = 10;
if(this.lastSuffix == '999')
this.lastPrefix++;
this.lastSuffix = line.substring(1);
fd = new Number(''+this.lastPrefix+this.lastSuffix);
}
if (isNaN(fd)) {
this.error('Invalid file descriptor: ' + line);
return;
}
this.on_endFileDescriptor();
this.currentFD = line;
},
on_n: function(line) {
this.currentAddress = line;
},
on_T: function(line) {
var parts = line.split('ST=');
if ((parts.length != 2) || (parts[0] != ''))
return;
this.currentState = parts[1];
},
on_endFileDescriptor: function() {
if (!this.currentFD)
return;
//Sometimes we get entries like this. The connection is closed. Skip it.
//n*:*
//TST=CLOSED
if (this.currentState == 'CLOSED')
return;
var isListening = (this.currentState == 'LISTEN');
if (!this.currentPID) {
this.error('Missing PID on file descriptor ' + this.currentFD);
return;
}
if (!this.currentAddress) {
this.error('Missing address on PID ' + this.currentPID + ', file descriptor ' + this.currentFD);
return;
}
// parse the connection data to get the bits we want...
var result = {};
result.type = isListening ? 'on' : 'to';
result.pid = this.currentPID;
var parsedIP = null;
var parsedLocalIP = null;
if (isListening)
parsedIP = this.parse_connection(this.currentAddress);
else {
var parts = this.currentAddress.split('->');
parsedIP = this.parse_connection(parts[1]);
parsedLocalIP = this.parse_connection(parts[0]);
//Need to properly parse both sides for a valid connection
if(parsedIP == null || parsedLocalIP == null)
return;
}
if (parsedIP == null) {
this.error('Connection address and port is not parseable: ' + this.currentAddress);
return;
}
result.ip = parsedIP.ip;
result.port = ''+parsedIP.port;
if (parsedLocalIP != null) {
result.local_ip = parsedLocalIP.ip;
result.local_port = parsedLocalIP.port;
}
this.tcp.push(result);
this.initFileDescriptor();
},
/**
* Returns an object with an ip and port fields, which are parsed from the given string. If the
* IP addresss section of the given string is not a valid IP address, it is replaced with 0.0.0.0.
* Returns null on any parsing error.
*/
parse_connection: function(str) {
if (!str)
return null;
var result = {};
if(str.indexOf('[') < 0) { // not an IPv6 address
var parts = str.split(':');
if (parts.length != 2)
return null;
result.port = Number(parts[1]);
if (isNaN(result.port))
return null;
result.ip = parts[0];
if (parts[0] == '*' || !SncIPAddressV4.getIPAddressV4Instance(parts[0]))
result.ip = '0.0.0.0';
}
else { //tis IPv6
var parts = /\[([^\]]+)\]:([0-9]+)/.exec(str);
if(!parts || parts.length != 3)
return null;
result.port = Number(parts[2]);
if (isNaN(result.port))
return null;
result.ip = parts[1];
}
return result;
},
on_endProcess: function() {
if (!this.currentPID)
return;
this.on_endFileDescriptor();
this.initProcess();
},
initProcess: function() {
this.currentPID = null;
this.currentCmd = null;
this.initFileDescriptor();
},
initFileDescriptor: function() {
this.currentFD = null;
this.currentAddress = null;
this.currentState = null;
},
error: function(msg) {
this.isValid = false;
if (!gs.nil(this.errorString))
this.errorString += '\n';
this.errorString += msg;
},
type: "LSOFParser"
}
Sys ID
16a060130a0a0bbe00b3c8825a8af54a