UNPKG

list-open-files

Version:
282 lines 8.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const child_process_1 = require("child_process"); const util_1 = require("util"); const lsof_to_api_1 = require("./lsof-to-api"); const utils_1 = require("../../utils"); const exec = util_1.promisify(child_process_1.exec); const oneToNineDescription = "1-9 dialect-specific field identifiers (The output\n" + " of -F? identifies the information to be found\n" + " in dialect-specific fields.)"; const fields = { a: { code: "a".charCodeAt(0), description: "file access mode r w u(=rw)", }, c: { code: "c".charCodeAt(0), description: "process command name (all characters from proc or user structure)", }, C: { code: "C".charCodeAt(0), description: "file structure share count", handler: utils_1.toInteger, }, d: { code: "d".charCodeAt(0), description: "file's device character code", handler: utils_1.toInteger, }, D: { code: "D".charCodeAt(0), description: "file's major/minor device number (0x<hexadecimal>)", handler: utils_1.toInteger, }, f: { code: "f".charCodeAt(0), description: "file descriptor (always selected)", }, F: { code: "F".charCodeAt(0), description: "file structure address (0x<hexadecimal>)", handler: utils_1.toInteger, }, G: { code: "G".charCodeAt(0), description: "file flaGs (0x<hexadecimal>; names if +fg follows)", }, g: { code: "g".charCodeAt(0), description: "process group ID", handler: utils_1.toInteger, }, i: { code: "i".charCodeAt(0), description: "file's inode number", }, K: { code: "K".charCodeAt(0), description: "tasK ID", handler: utils_1.toInteger, }, k: { code: "k".charCodeAt(0), description: "link count", handler: utils_1.toInteger, }, l: { code: "l".charCodeAt(0), description: "file's lock status", }, L: { code: "L".charCodeAt(0), description: "process login name", }, n: { code: "n".charCodeAt(0), description: "file name, comment, Internet address", }, N: { code: "N".charCodeAt(0), description: "node identifier (ox<hexadecimal>", }, o: { code: "o".charCodeAt(0), description: "file's offset (decimal)", handler: utils_1.toInteger, }, p: { code: "p".charCodeAt(0), description: "process ID (always selected)", }, P: { code: "P".charCodeAt(0), description: "protocol name", }, r: { code: "r".charCodeAt(0), description: "raw device number (0x<hexadecimal>)", handler: utils_1.toInteger, }, R: { code: "R".charCodeAt(0), description: "parent process ID", handler: utils_1.toInteger, }, s: { code: "s".charCodeAt(0), description: "file's size (decimal)", handler: utils_1.toInteger, }, S: { code: "S".charCodeAt(0), description: "file's stream identification", }, t: { code: "t".charCodeAt(0), description: "file's type", }, T: { code: "T".charCodeAt(0), description: "TCP/TPI information, identified by prefixes (the '='" + " is part of the prefix):\n" + "QR=<read queue size>\n" + "QS=<send queue size>\n" + "SO=<socket options and values> (not all dialects)\n" + "SS=<socket states> (not all dialects)\n" + "ST=<connection state>\n" + "TF=<TCP flags and values> (not all dialects)\n" + "WR=<window read size> (not all dialects)\n" + "WW=<window write size> (not all dialects)\n" + "(TCP/TPI information isn't reported for all supported\n" + "UNIX dialects. The -h or -? help output for the\n" + "-T option will show what TCP/TPI reporting can be\n" + "requested.)", }, u: { code: "u".charCodeAt(0), description: "process user ID", }, z: { code: "z".charCodeAt(0), description: "Solaris 10 and higher zone name", }, Z: { code: "Z".charCodeAt(0), description: "SELinux security context (inhibited when SELinux is disabled)", }, 1: { code: "1".charCodeAt(0), description: oneToNineDescription, }, 2: { code: "2".charCodeAt(0), description: oneToNineDescription, }, 3: { code: "3".charCodeAt(0), description: oneToNineDescription, }, 4: { code: "4".charCodeAt(0), description: oneToNineDescription, }, 5: { code: "5".charCodeAt(0), description: oneToNineDescription, }, 6: { code: "6".charCodeAt(0), description: oneToNineDescription, }, 7: { code: "7".charCodeAt(0), description: oneToNineDescription, }, 8: { code: "8".charCodeAt(0), description: oneToNineDescription, }, 9: { code: "9".charCodeAt(0), description: oneToNineDescription, }, "\n": { code: "\n".charCodeAt(0), description: "New file delimiter", }, }; const fieldMap = {}; Object.keys(fields).forEach(key => { const field = fields[key]; const code = field.code; fieldMap[code] = key; }); function getParts(buffer) { const parts = []; let pos = 0; while (true) { const end = buffer.indexOf(0, pos); if (end === -1) { parts.push(buffer.slice(pos)); break; } else { parts.push(buffer.slice(pos, end)); pos = end + 1; } } return parts; } function parseParts(parts) { const processes = []; let cur; const parsePart = (part) => { const fieldCode = part[0]; const fieldKey = fieldMap[fieldCode]; if (!fieldKey) throw new Error(`Invalid field in reponse from 'lsof': ${part}`); if (fieldKey === 'p') { // New process cur = { proc: { p: utils_1.toInteger(part.slice(1).toString()), }, files: [], }; processes.push(cur); return; } if (!cur) throw new Error("Invalid response from 'lsof', missing pid"); if (fieldKey === "\n") { cur.files.push({}); parsePart(part.slice(1)); return; } // Regular field, generic parsing const field = fieldKey; const rawValue = part.slice(1).toString(); const fieldInfo = fields[field]; const value = fieldInfo && fieldInfo.handler ? fieldInfo.handler(rawValue) : rawValue; if (cur.files.length > 0) // File part cur.files[cur.files.length - 1][field] = value; else // Process part cur.proc[field] = value; }; parts = parts.length > 0 && parts[parts.length - 1].length === 1 && parts[parts.length - 1][0] === 10 ? parts.slice(0, parts.length - 1) : parts; parts.forEach(parsePart); return processes; } async function invoke(pids) { const { stdout } = await exec(`lsof -M -P -n -F 0 +fG -p ${pids.join(',')}`, { encoding: "buffer" }); return stdout; } async function lsofAndTransform(pids) { const buf = await invoke(pids); const parts = getParts(buf); const rawResult = parseParts(parts); return lsof_to_api_1.transformParts(rawResult); } async function lsof(pids, concurrency) { const nchunks = Math.max(Math.min(concurrency, pids.length), 1); const chunks = []; const chunksize = pids.length / nchunks; for (var i = 0; i < nchunks; ++i) { chunks.push(pids.slice(i * chunksize, (i + 1) * chunksize)); } const chunkedProcesses = await Promise.all(chunks.map(pids => lsofAndTransform(pids))); return [].concat(...chunkedProcesses); } exports.lsof = lsof; //# sourceMappingURL=index.js.map