Name

global.EnrichProcessesAndConnections

Description

Adds data about connections to the running process records that own those connections, and adds a reference to the owning running process to the connections records.

Script

// Discovery

var EnrichProcessesAndConnections = Class.create();
EnrichProcessesAndConnections.prototype = {

  initialize: function(sensor) {
  	// a little initialization from our sensor...
  	this.sensor = sensor;
  	this.rps = sensor.running_processes;
  	this.conns = sensor.connections;

  	// map of process info maps by PID...
  	this.byPID;

  	// list of process tree roots...
  	this.processTreeRoots;

  	// localIPs - This should only be used for tcp connection manipulation and nothing else. 
  	this.localIPs = {};

  	// set the localIPs
  	this._setLocalIPs();

  	// map of process names that are always considered super-servers.
  	this.super_servers = {};
  	var sss = gs.getProperty('glide.discovery.super_servers', 'inetd,xinetd,launchd').split(',');
  	for (var i = 0; i < sss.length; i++)
  		this.super_servers[sss[i]] = true;

  	// map of service ports to names, for services defined in cmdb_ip_service...
  	// PRB1292267 removes local usage of this variable, keeping for compatibility if needed
  	this.service_names = this.get_services();

  	// map of super servers by port...
  	this.byPortAndIp = {};
  },

  /**
   * Enriches the running processes and connections (in sensor.running_processes and sensor.connections, respectively)
   * with information about each other.  Both of these inputs are arrays of information maps whose properties exactly
   * reflect the underlying records in cmdb_running_process and cmdb_tcp.  The results of the enrichment are
   * modifications to those same records.
   */
  process: function() {

  	this.indexPIDs();
  	this.buildProcessTree();

  	this.mapConnectionsByProcess();
  	this.rollupForked();
  	this.rollupProxied();
  	this.fixConnectionField('pid');
  	this.generateRPs();
  	this.runHandlers(this.rps);

  	// fix up the values in the sensor...
  	this.sensor.running_processes = this.rps;
  	this.sensor.connections = this.conns;
  },

  /**
   * Reconciles the running processes.
   */
  reconcile: function() {
  	// rebuild the process tree roots
  	this.rps = this.sensor.running_processes;
  	this.indexPIDs();
  	this.buildProcessTree();

  	// get rid of any nonsense connections...
  	this.noNonsense();

  	// flip any "to" connections to "on" connections that need to be flipped
  	this._flipAllConnections();

  	// Remove connections if process or any parent process has the same listening port as the process
  	// The order is important that this follows _flipAllConnections
  	this._removeConnections();

  	// fix up the values in the sensor...
  	this.sensor.running_processes = this.rps;
  	this.sensor.connections = this.conns;

  	this.fixProcessPorts();

  	// reconcile the running processes...
  	var rpr = new RunningProcessReconciler(this.sensor.getCmdbCi(), this.rps, this.processTreeRoots, this.byPID);
  	rpr.reconcile();

  	// now that we've got process sys_ids, fix up our connection references...
  	this.fixConnectionField('sys_id', 'process');
  },

  /**
   * Eliminate nonsense connections.
   */
  noNonsense: function() {
  	var i, conn, my_proc, portListName;

  	// for each of our connections...
  	for (i = 0; i < this.conns.length; i++) {
  		conn = this.conns[i];

  		// if we don't have a pid, delete it...
  		if (!conn.pid) {
  			this.conns.splice(i, 1);
  			i--;
  			continue;
  		}

  		// if our process doesn't know about us, delete it (this happens because of rollups)...
  		my_proc = this.byPID[conn.pid];

  		portListName = '_connecting_to_ports';
  		if (conn.type == 'on')
  			portListName = '_listening_on_ports';
  		if (my_proc && my_proc[portListName] && !my_proc[portListName][conn.port]) {
  			this.conns.splice(i, 1);
  			i--;
  		}
  	}
  },

  /**
   * Remove connections if process or any parent process has the same listening port as the process
   */
  _removeConnections: function() {
  	var i, conn, my_proc, proc, gr, sys_id;

  	// If the connection is from ADME probe, do not filter, since we already done that in the shell scripts.
  	if (this.fromADME)
  		return;

  	// for each of our connections...
  	for (i = 0; i < this.conns.length; i++) {
  		conn = this.conns[i];
  		my_proc = this.byPID[conn.pid];

  		// If we could not find a process based on the PID of the connection, then delete the conncetion
  		if (!my_proc) {
  			this.conns.splice(i, 1);
  			i--;
  			continue;
  		}

  		// Remove connection to if the process or any parent process has the same listening port as the process  
  		if (conn.type == 'to') {				
  			proc = this._listensOnPort(conn.local_port, my_proc);

  			if (proc) {
  				this.conns.splice(i, 1);
  				i--;

  				// remove from listening_on from process
  				delete proc._listening_on_ports[conn.port];
  			}
  		}
  	}
  },

  /**
   * Flip connections that are "to" connections to "on" if ip is a local ip and there is pid or ppid listening on same port
   */
  _flipAllConnections: function() {
  	var _this = this,
  		flipped = true;

  	// If the connections are from ADME probe, don't flip it.
  	if (this.fromADME)
  		return;

  	// Keep flipping until nothing to flip. This is required for multi level flipping that might be needed.
  	while (flipped)
  		_flipConnections();

  	function _flipConnections() {

  		flipped = false;
  		for (var i = 0; i < _this.conns.length; i++) {
  			var conn = _this.conns[i];
  			var my_proc = _this.byPID[conn.pid];

  			if (conn.type == 'to' && _this.localIPs[conn.ip]) {
  				var proc = _this._listensOnPort(conn.port, my_proc);

  				if (proc) {
  					// Remove it from connecting to and add to listening_to
  					if (proc._connecting_to_ports && proc._connecting_to_ports[conn.port])
  						delete proc._connecting_to_ports[conn.port];
  					proc._listening_on_ports[conn.port] = 1;

  					// flip it
  					conn.type = 'on';
  					conn.port = conn.local_port;
  					conn.ip = conn.local_ip;
  					flipped = true;
  				}
  			}
  		}

  		return flipped;
  	}
  },

  fixConnectionField: function(field, dest) {
  	var pid, conn, proc;

  	dest = dest || field;

  	// for each of our connections...
  	for (var i = 0; i < this.conns.length; i++) {
  		conn = this.conns[i];

  		// add a process reference...
  		proc = this.byPID[conn.pid];

  		conn.process = null;  // in case we can't resolve it...
  		if (proc && proc[field])
  			conn[dest] = proc[field];
  	}
  },

  fixProcessPorts: function() {
  	for (pid in this.byPID) {
  		proc = this.byPID[pid];
  		proc._listening_on_ports = { };
  		proc._connecting_to_ports = { };
  	}

  	// for each of our connections...
  	for (var i = 0; i < this.conns.length; i++) {
  		conn = this.conns[i];

  		// add a process reference...
  		proc = this.byPID[conn.pid];

  		if (proc) {
  			if (conn.type == 'on')
  				proc._listening_on_ports[conn.port] = 1;
  			else
  				proc._connecting_to_ports[conn.port] = 1;
  		}
  	}

  	for (pid in this.byPID) {
  		proc = this.byPID[pid];
  		proc.listening_on = this.portList(proc._listening_on_ports);
  		proc.connecting_to = this.portList(proc._connecting_to_ports);
  	}
  },

  /**
   * Runs the handlers that are enabled for running in the sensor, mainly to set key_parameters.
   */
  runHandlers: function(procs) {
  	// get a list of all our handlers, in name order...
  	var handlers = [];
  	var gr = new GlideRecord('discovery_proc_handler');
  	gr.addActiveQuery();
  	gr.orderBy('name');
  	gr.query();
  	while (gr.next()) {
  		var handler = {};
  		handler.condition = '' + gr.condition;
  		handler.classify  = ('true' == '' + gr.classify);
  		handler.script    = '' + gr.script;

  		//For efficiency, instantiate a "filter" object for checking the handler conditions:
  		if(handler.condition)
  			handler.filter = new SNC.Filter(handler.condition, "rule-condition");

  		handlers.push(handler);
  	}

  	// Traverse all running processes, and find out the largest count
  	var maxCount = 0;
  	if (this.checkCountForClassify) {
  		// When running process classification from ADME probe, only classify those processes
  		// with high count number, since low count processes may already dead
  		// TODO: This solution is temporary in K, in future release, should take a process
  		// snap shot when retrieving the ADME result, and run process classification for those
  		// proceses only.
  		var classifyRatio = 0.9;
  		for (var p = 0; p < procs.length; p ++) {
  			if (parseInt(procs[p].count) > maxCount)
  				maxCount = procs[p].count;
  		}
  	}
  	var classifyThreshold = Math.round(maxCount * classifyRatio);

  	// for each running process...
  	var rp_gr = new GlideRecord('cmdb_running_process');
  	for (var p = 0; p < procs.length; p++) {

  		// make a fake GlideRecord...
  		var proc = procs[p];
  		proc.key_parameters = proc.parameters;  // default...
  		rp_gr.initialize();
  		for (var field in proc)
  			rp_gr[field] = proc[field];

  		var shouldClassify = this.checkCountForClassify ? parseInt(proc.count) >= classifyThreshold : true;

  		// run our handlers against our fake record...
  		for (var i = 0; i < handlers.length; i++) {
  			var handler = handlers[i];
  			if (handler.filter && handler.filter.match(rp_gr, true)) {
  				shouldClassify = shouldClassify && handler.classify;
  				// update the classify setting...
  				rp_gr.setValue('classify', shouldClassify);

  				// run the script...
  				var old_current = current;
  				current = rp_gr;
  				eval(handler.script);
  				current = old_current;

  				// update the appropriate field in our proc record...
  				proc.name           = '' + rp_gr.name;
  				proc.pid            = '' + rp_gr.pid;
  				proc.ppid           = '' + rp_gr.ppid;
  				proc.command        = '' + rp_gr.command;
  				proc.parameters     = '' + rp_gr.parameters;
  				proc.key_parameters = '' + rp_gr.key_parameters;
  				proc.listening_on   = '' + rp_gr.listening_on;
  				proc.connecting_to  = '' + rp_gr.connecting_to;
  			}
  		}
  		proc.classify = shouldClassify;
  	}
  },

  /**
   * Regenerates the list of running processes from the index of PIDs.
   */
  generateRPs: function() {
  	// first we pull out all the processes we actually want to keep (those not redirected)...
  	var newRPs = [];
  	for (var pid in this.byPID) {
  		var rp = this.byPID[pid];
  		if (pid == rp.pid)
  			newRPs.push(rp);
  	}

  	// then we go through all the keepers and fixup any redirected PPIDs, and add our connections fields...
  	for (var i = 0; i < newRPs.length; i++) {
  		// set the PPID...
  		var rp = newRPs[i];

  		// Set the ppid only when the redirected process is not itself.
  		if(this.byPID[rp.ppid] && rp.pid != this.byPID[rp.ppid].pid)
  			rp.ppid = this.byPID[rp.ppid].pid;

  		// generate our connections fields...
  		if (rp._listening_on) {
  			rp._listening_on_ports = { };
  			rp.listening_on = this.portList(rp._listening_on_ports, rp._listening_on);

  			delete rp._listening_on;
  		}
  		if (rp._connecting_to) {
  			rp._connecting_to_ports = { };
  			rp.connecting_to = this.portList(rp._connecting_to_ports, rp._connecting_to);
  			
  			delete rp._connecting_to;
  		}

  		// get rid of any kids...
  		if (rp._kids)
  			delete rp._kids;
  	}

  	// finally, we stuff the result, reindex, and rebuild our tree...
  	this.rps = newRPs;
  	this.indexPIDs();
  	this.buildProcessTree();
  },

  portList: function(ports, portAndIps) {
  	var name, result;

  	if (portAndIps) {
  		for (name in portAndIps)
  			ports[portAndIps[name].port] = 1;
  	}
  	result = Object.keys(ports);
  	result.sort(function(a,b) { a = a|0; b = b|0; if (a > b) return 1; if (b > a) return -1; return 0; });

  	// Make sure we get leading and trailing colons.
  	result = result.join(':');
  	return result ? ':' + result + ':' : '';
  },

  /**
   * Identifies super-server processes in a depth-last recursive descent.  When a super-server process is detected, a
   * synthetic child process is created for each port that the super-server is listening on.  If there are child processes
   * with connections on one of the listening ports, those processes are rolled up into the corresponding synthetic 
   * process.  
   */
  rollupProxied: function() {
  	// for each process tree root...
  	for (var i = 0; i < this.processTreeRoots.length; i++)
  		// roll up proxied children, bottom-up, by working down the process tree...
  		this._rollupProxiedDFS(this.processTreeRoots[i]);
  },

  /**
   * Stack processes up using depth-first-search scheme then rolling up proxied.
   */
  _rollupProxiedDFS: function(proc) {
  	var procs = [];	
  	var executed = [];
  	procs.push(proc);
  	proc.visited = true;
  	while( procs.length > 0) {
  		var topProc = procs[procs.length - 1]; //peek()
  		var shouldPop = true;
  		if (topProc._kids) {
  			for (var kid_pid in topProc._kids) {
  				var kid = this.getProcess(kid_pid);
  				if( kid && !kid.visited) {
  					kid.visited = true;
  					procs.push(kid);
  					shouldPop = false;
  				}
  			}
  		} 

  		if( shouldPop ) {
  			procs.pop();
  			this._rollupProxied(topProc);
  			executed.push(topProc);
  		}
  	}
  	// clean up 'visited' flag
  	while( executed.length > 0 ) {
  		var prc = executed.pop();
  		prc.visited = undefined;
  	}
  },


  /**
   * Depth-first recursive descent super-server identification and rollup of proxied child processes.
   */
  _rollupProxied: function(proc) {
  	var portAndIp, conn, syn_pic, syn_proc;

  	// if this process is a super-server, make a synthetic process for any ports it's listening on...
  	var isSuperServer = this.isSuperServer(proc);
  	if (isSuperServer) {
  		for (portAndIp in proc._listening_on) {
  			conn = proc._listening_on[portAndIp];
  			syn_pid = '' + (1000000000 + (conn.port - 0));
  			syn_proc = this.byPID[syn_pid];

  			// make only one synthetic process per port...
  			if (syn_proc) {
  				conn.pid = syn_proc.pid;
  				continue;
  			}

  			// Find the ppid of the synthetic process, it should be the highest one in the process tree that listens on the port
  			var parentProcess = this.byPID[proc.ppid];
  			var childProcess = proc;
  			while (parentProcess && this.activeOn(parentProcess, conn.portAndIp)){
  				childProcess = parentProcess;
  				parentProcess = this.byPID[parentProcess.ppid];
  			}

  			// make our synthetic process...
  			var kid_name = 'Service on port ' + conn.port;
  			var kid_pid = '' + (1000000000 + (conn.port - 0));
  			syn_proc = {name:kid_name, command:kid_name, parameters:'', pid:kid_pid, ppid:childProcess.pid, _listening_on: { }, _kids:{}};
  			syn_proc._listening_on[conn.portAndIp] = conn;
  			conn.pid = syn_proc.pid;
  			this.rps.push(syn_proc);
  			this.byPID[kid_pid] = syn_proc;
  			this.byPortAndIp[conn.portAndIp] = syn_proc;
  		}

  		// drop the listening on ports from the super-server...
  		delete proc._listening_on;
  	} else {
  		// if this process is not itself a super-server, but is active on a super-server's port, roll it up into our synthetic process...
  		for (var ss_portAndIp in this.byPortAndIp) {
  			syn_proc = this.byPortAndIp[ss_portAndIp];
  			//Make sure it hasn't already matched to a real process.
  			if(syn_proc.matched_to_real_proc)
  				continue;

  			if (this.activeOn(proc, ss_portAndIp)) {
  				var syn_proc = this.byPortAndIp[ss_portAndIp];

  				// copy the details to our synthetic process...
  				syn_proc.name = proc.name;
  				syn_proc.command = proc.command;
  				syn_proc.parameters = proc.parameters;
  				syn_proc.matched_to_real_proc = true;

  				// tell the new parent about the new kids...
  				for (var kid_pid in proc._kids)
  					syn_proc._kids[kid_pid] = true;

  				// delete the kid from its original parent's kid collection...
  				if(this.getProcess(proc.ppid) && this.getProcess(proc.ppid)._kids[proc.pid])
  					delete this.getProcess(proc.ppid)._kids[proc.pid];

  				// send any reference to the child straight to the parent...
  				this.byPID[proc.pid] = syn_proc;

  				// if any other process was redirected to this one, redirect it to the parent as well...
  				for (var test_pid in this.byPID) {
  					var test_proc = this.byPID[test_pid];
  					if (test_pid == test_proc.pid)
  						continue;
  					if (test_proc.pid != proc.pid)
  						continue;
  					this.byPID[test_pid] = syn_proc;
  				}
  			}
  		}
  	}
  },

  /**
   * Returns true if the given process info map represents a super-server.  Detects a super-server either by matching the 
   * process name, or by finding child processes with connections on the same local port as the given process is listening
   * on.  If a super-server has already been identified for a given port, a new one will not be identified for that port.
   */
  isSuperServer: function(proc) {

  	// if this process' name is one of our defined super-server names, return true...
  	if (this.super_servers[proc.name])
  		return true;

  	// iterate over all the ports this process is listening on...
  	for (var lstn_portAndIp in proc._listening_on) {

  		// if we already have a super-server for this port, move along...
  		if (this.byPortAndIp[lstn_portAndIp])
  			continue;

  		// iterate over all the child processes...
  		for (var kid_pid in proc._kids) {
  			if (this.activeOn(this.getProcess(kid_pid), lstn_portAndIp))
  				return true;
  		}
  	}

  	// if we get here, it's not a super-server...
  	return false;
  },

  /**
   * Returns true if the given process is listening on the given port
   */
  activeOn: function(proc, portAndIp) {
  	return this.listeningOn(proc, portAndIp);
  },

  /**
   * Returns true if the given process is connecting from the given port.
   */
  connectingFrom: function(proc, portAndIp) {
  	return proc && proc._connecting_to && !!proc._connecting_to[portAndIp];
  },

  /**
   * Returns true if the given process is listening on the given port.
   */
  listeningOn: function(proc, portAndIp) {
  	return proc && proc._listening_on && !!proc._listening_on[portAndIp];
  },

  /**
   * Any process may be listening to any number of ports and connecting to any number of ports.
   * This method adds _listening_to and _connecting_to arrays to each process that has a connection,
   * and populates them.
   */
  mapConnectionsByProcess: function() {
  	for (var name in this.byPID) {
  		this.byPID[name]._listening_on = { };
  		this.byPID[name]._connecting_to = { };
  	}

  	// iterate over all our connections...
  	for (var i = 0; i < this.conns.length; i++) {
  		var conn = this.conns[i];
  		conn.portAndIp = conn.port + ':' + conn.ip;
  		var pid = '' + conn.pid;
  		var proc = this.byPID[pid];
  		if (!proc)
  			continue;

  		if (conn.type == 'on')
  			proc._listening_on[conn.portAndIp] = conn;
  		else if (conn.type == 'to')
  			proc._connecting_to[conn.portAndIp] = conn;
  	}
  },

  /**
   * Detects forked child processes and rolls them up into their parents.  A forked child process is identified by having
   * the identical command and parameters as its parent process.  Note that forked child processes sometimes occur over 
   * multiple levels: process A forks to process B, which then forks to process C, etc.  This can extend to any depth.  To
   * ensure that we catch all these cases, we do the rollup with a depth-first tree traversal, rolling a forked child up 
   * to its immediate parent.  This traveral is recursive, starting from each process tree root.
   */
  rollupForked: function() {
  	// for each process tree root...
  	for (var i = 0; i < this.processTreeRoots.length; i++)

  		// roll up forked children, bottom-up, by descending to deepest leaves and working up...
  		this._rollupForked(this.processTreeRoots[i], null);
  },

  /**
   * Rollup of forked child processes.
   */
  _rollupForked: function(proc, parent) {
  	// create a stack of all the child processes that may need to be rolled up...
  	var children = [];
  	var parents = [];
  	children.push([proc, parent]); 
  	parents.push(proc);
  	var parentIndex = 0; 
  	while (parentIndex < parents.length) {
  		var my_parent = parents[parentIndex];
  		for (var kid_pid in my_parent._kids) {
  			var kid = this.getProcess(kid_pid);
  			children.push([kid, my_parent]);
  			parents.push(kid);
  		}
  		parentIndex++;
  	}

  	// check all the child processes to see if they can be rolled up...
  	while (children.length > 0) {
  		var curr = children.pop();
  		var my_proc = curr[0];
  		var my_parent = curr[1];

  		// if we have no parent, we must be a process tree root - and we're done...
  		if (!my_parent)
  			continue;

  		// if our parent has the same command and parameters that we do, roll this process up to its parent...
  		var cmdMatch = ( my_proc.command    == my_parent.command    );
  		var parMatch = ( my_proc.parameters == my_parent.parameters );
  		if (cmdMatch && parMatch) {
  			// tell the new parent about the new kids...
  			if (!my_parent._kids)
  				my_parent._kids = {};

  			for (var kid_pid in my_proc._kids)
  				my_parent._kids[kid_pid] = true;

  			// send any reference to the child straight to the parent...
  			this.byPID[my_proc.pid] = my_parent;

  			//Pull in any tcp connections of the child
  			this._rollupConnections(my_proc, my_parent);

  			// delete the kid from the parent's collection...
  			delete my_parent._kids[my_proc.pid];
  		}
  	}
  },


  _rollupConnections: function(proc, parent) {
  	var portAndIp;

  	if (proc._listening_on) {
  		parent._listening_on = parent._listening_on || { };
  		for (portAndIp in proc._listening_on)
  			parent._listening_on[portAndIp] = proc._listening_on[portAndIp];
  	}

  	if (proc._connecting_to) {
  		parent._connecting_to = parent._connecting_to || { };
  		for (portAndIp in proc._connecting_to)
  			parent._connecting_to[portAndIp] = proc._connecting_to[portAndIp];
  	}
  },


  /**
   * Populates this.byPID map, using the string form of the PID as the key and the info map from this.rps as the value.
   */
  indexPIDs: function() {
  	this.byPID = {};
  	for (var i = 0; i < this.rps.length; i++) {
  		this.byPID['' + this.rps[i].pid] = this.rps[i];
  	}
  },

  /**
   * Gathers the children of all parent processes, and identifies and records the roots of all process trees.
   */
  buildProcessTree: function() {
  	this.processTreeRoots = [];

  	// iterate over all the indexed processes...
  	for (var pid in this.byPID) {
  		var proc = this.byPID[pid];

  		// if we don't have an info map for the parent, we've got a new root...
  		var parent = this.getProcess(proc.ppid);
  		if (!parent) {
  			this.processTreeRoots.push(proc);
  			continue;
  		}

  		// record the child PID...
  		if (!parent._kids)
  			parent._kids = {};
  		parent._kids[pid] = true;
  	}
  },

  /**
   * Given a PID, return the process info map (or null if that map doesn't exist).
   */
  getProcess: function(pid) {
  	return this.byPID['' + pid];
  },

  /**
   * Returns a map of port number to name for all IP services configured in an instance.
   */
  get_services: function() {
  	var gr = new GlideRecord('cmdb_ip_service');
  	gr.addQuery('protocol', '!=', 'UDP');
  	gr.query();
  	var result = {};
  	while (gr.next())
  		result['' + gr.port] = '' + gr.name;
  	return result;
  },

  /**
   * Eliminate any duplicate connections caused by rollups or synthetic processes...
   */
  dedupeConnections: function() {
  	var conns = this.sensor.connections;
  	var map = {};
  	for (var i = 0; i < conns.length; i++) {
  		var conn = conns[i];
  		var key = conn.type + ':' + conn.ip + ':' + conn.port + ':' + conn.process;

  		// if we've seen a connection with this key before...
  		if (map[key]) {
  			conns.splice(i, 1);  // delete the current connection...
  			i--;  // back up our index, 'cause we're gonna look at the same element again...
  			continue;
  		}
  		map[key] = true;
  	}
  },

  /**
   * Does process or any parent process listen on this port?
   */
  _listensOnPort : function(port, proc) {
  	var curr_pid = proc.pid;
  	var syn_pid = '' + (1000000000 + (port - 0)); // compute synthetic pid
  	var syn_proc = this.byPID[syn_pid]; // get the process for the synthetic, may not be there
  	var pids = [ ];

  	while (curr_pid) {
  		pids.push(curr_pid);

  		var curr_proc = this.byPID[curr_pid];

  		// Get out of here if we can't find process for whatever reason
  		if (!curr_proc)
  			return;

  		// If process there and it contains listening port same as ours get rid of it else traverse up the tree
  		// Also check that the synthetic process is listening on the same port and it's parent(ppid) matches current pid as we traverse up the tree
  		if (curr_proc._listening_on_ports && curr_proc._listening_on_ports[port])
  			return curr_proc; 

  		if (syn_proc && syn_proc.ppid == curr_pid)
  			return syn_proc; 

  		curr_pid = curr_proc.ppid;

  		// Make sure the process isn't its own ancestor - this would cause an infinite loop.
  		// A process can become its own ancestor during process rollup.
  		if (pids.some(function(pid) { return pid == curr_pid; } ))
  			return;
  	}
  },


  /**
   * Find local IPs from the tcp connections "on" connections.
   * This should only be used for tcp connection manipulation and nothing else. 
   * It is entirely possible "127.0.0.1" is not here because we are not listening on it.
   */	
  _setLocalIPs : function() {
  	for (var i = 0; i < this.conns.length; i++) {
  		var conn = this.conns[i];

  		if (conn.type == "on")
  			this.localIPs[conn.ip] = true;
  	}
  },

  type: 'EnrichProcessesAndConnections'
};

Sys ID

7ca965319721300010cb1bd74b297501

Offical Documentation

Official Docs: