neoss
Version:
<div align="center"> <h1>neoss</h1> <i>Socket statistics, with a UI.</i> </div> <p align="center"> <a href="https://img.shields.io/github/v/release/pablolec/neoss" target="_blank"> <img src="https://img.shields.io/github/v/release/pablolec/neo
123 lines (104 loc) • 2.85 kB
text/typescript
import { readdirSync, readFileSync, readlinkSync } from "fs";
import { execSync } from "child_process";
let socketMap: {};
/**
* Fetch in-use sockets and query their users statistics.
*
* @returns Map of used sockets and their users
*/
export async function getUsedSockets(): Promise<{}> {
socketMap = {};
let processes: string[] = [];
let files: string[] = readdirSync("/proc/");
files.forEach((file: any) => {
if (isNumeric(file)) {
processes.push(file);
}
});
let procPromises: Promise<any>[] = [];
processes.forEach((proc, i) => {
procPromises.push(Promise.race([timeout(100), getProcSockets(proc)]));
});
await Promise.allSettled(procPromises);
return socketMap;
}
/**
* Get users statistics for a given socket.
*
* @param proc - Socket inode
*/
async function getProcSockets(proc: string) {
let fd = "/proc/" + proc + "/fd/";
let files: string[];
try {
files = readdirSync(fd);
} catch (EACCES) {
return;
}
let sockets: string[] = [];
files.forEach((file: string) => {
let linkString: string;
try {
linkString = readlinkSync(fd + file);
} catch (ENOENT) {
return;
}
if (linkString && linkString.includes("socket")) {
let match: RegExpMatchArray | null = linkString.match(/socket:\[(\d+)]/);
if (match != null) {
sockets.push(match[1]);
}
}
});
sockets.forEach((socket) => {
if (socket in socketMap) {
socketMap[socket].push(proc);
} else {
socketMap[socket] = [proc];
}
});
}
/**
* Get detailed information about given user.
*
* @param user - User PID
* @returns - Username, owner and init command line
*/
export async function getUserData(user: string): Promise<string[]> {
let status: string;
try {
status = readFileSync("/proc/" + user + "/status", "utf8");
} catch (ENOENT) {
return ["error", "error", "error"];
}
let lines = status.split(/\r\n|\r|\n/);
let name = lines[0].trim().split(/\s+/)[1];
let uid = lines[9].trim().split(/\s+/)[1];
let owner = execSync("id -nu " + uid) + "";
let cmdline: string;
try {
let cmdlineFile: string = readFileSync("/proc/" + user + "/cmdline", "utf8");
cmdline = cmdlineFile.split("\0").join(" ");
} catch (ENOENT) {
cmdline = "Unable to retrieve cmdline. Process already terminated.";
}
return [name, owner.trim(), cmdline];
}
/**
* Check if file descriptor name is numeric to determine if it is related to a process.
*
* @param str - File name
* @returns - Is numeric
*/
function isNumeric(str: string): boolean {
return !isNaN(parseInt(str));
}
/**
* Timeout function to limit fs wait time.
*
* @param ms - Time to wait in ms
* @returns - void
*/
function timeout(ms: number): Promise<void> {
return new Promise((resolve: any) => setTimeout(resolve, ms));
}