
Security News
npm Adopts OIDC for Trusted Publishing in CI/CD Workflows
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
list-open-files
Advanced tools
lsof
for Node.js, with an asynchronous Promise-based API and JSON-serializable self-explained objects.
import { lsof, Options } from "list-open-files"
lsof( opts?: Options ): Promise< Array< ProcessInfo > >;
The optional options object is defined as:
interface Options
{
pids?: Array< number >;
concurrency?: number;
}
If no pids
are provided, the current process will be queried.
If concurrency
is specified, it will be used to call the underlying lsof
command with that concurrency (at most). The default is built-in and environment specific - it depends on the underlying hardware and is optimized, and therefore generally a good choice. To affect the host computer the least, specify 1
, although this will have a very small effect.
The return value is eventually an array (even if only one pid is queried) of zero or more ProcessInfo
objects each on the form:
interface ProcessInfo
{
process: Process;
files: Array< File | FileDescriptorIP | UnknownFile >;
texts: Array< InternalFile >;
}
import { lsof } from "list-open-files"
lsof( ).then( result => console.log( result ) );
When run on MacOS as node . > output.txt
, will write a file called output.txt
(note fd 1)
[
{
"process": {
"pid": 26549,
"ppid": 13150,
"gpid": 26549,
"user": "501",
"name": "node",
"cwd": {
"fd": "cwd",
"type": "DIR",
"access": " ",
"lock": " ",
"size": 512,
"iNode": "10101630",
"linkCount": 16,
"name": "/tmp/list-open-files"
}
},
"texts": [
{
"fd": "txt",
"type": "REG",
"access": " ",
"lock": " ",
"size": 40180432,
"iNode": "9886022",
"linkCount": 1,
"name": "/tmp/node/bin/node"
},
{
"fd": "txt",
"type": "REG",
"access": " ",
"lock": " ",
"size": 973824,
"iNode": "9995914",
"linkCount": 1,
"name": "/usr/lib/dyld"
}
],
"files": [
{
"fd": 0,
"type": "CHR",
"access": "u",
"lock": " ",
"iNode": "689",
"linkCount": 1,
"name": "/dev/ttys026",
"majorMinorDeviceNumber": 1285824432,
"flags": "0x3;0x3",
"offset": 4568009
},
{
"fd": 1,
"type": "REG",
"access": "w",
"lock": " ",
"size": 0,
"iNode": "10603551",
"linkCount": 1,
"name": "/tmp/list-open-files/output.txt",
"majorMinorDeviceNumber": 16777220,
"flags": "0x2;0x2"
},
{
"fd": 2,
"type": "CHR",
"access": "u",
"lock": " ",
"iNode": "689",
"linkCount": 1,
"name": "/dev/ttys026",
"majorMinorDeviceNumber": 1285824432,
"flags": "0x3;0x3",
"offset": 4568009
},
{
"fd": 3,
"type": "KQUEUE",
"access": "u",
"lock": " ",
"name": "count=0, state=0",
"flags": "0x3;0x2"
},
{
"fd": 4,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319193139d"
},
{
"fd": 5,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319192f4dd"
},
{
"fd": 6,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319193019d"
},
{
"fd": 7,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319193025d"
},
{
"fd": 8,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319193121d"
},
{
"fd": 9,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c31919312dd"
},
{
"fd": 10,
"type": "KQUEUE",
"access": "u",
"lock": " ",
"name": "count=0, state=0xa",
"flags": "0x3;0x2"
},
{
"fd": 11,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319192f59d"
},
{
"fd": 12,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319193031d"
},
{
"fd": 13,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c3189f2939d"
},
{
"fd": 14,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c319193145d"
},
{
"fd": 15,
"type": "KQUEUE",
"access": "u",
"lock": " ",
"name": "count=0, state=0xa",
"flags": "0x3;0x2"
},
{
"fd": 16,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c3189f2945d"
},
{
"fd": 17,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c3189f2975d"
},
{
"fd": 18,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c3189f26c9d"
},
{
"fd": 19,
"type": "PIPE",
"access": " ",
"lock": " ",
"size": 16384,
"name": "->0xa5332c3189f2735d"
},
{
"fd": 20,
"type": "CHR",
"access": "r",
"lock": " ",
"iNode": "311",
"linkCount": 1,
"name": "/dev/null",
"majorMinorDeviceNumber": 1285824432,
"flags": "0x1;0x2",
"offset": 0
},
{
"fd": 21,
"type": "unix",
"access": "u",
"lock": " ",
"name": "->0xa5332c31a4d693a5",
"flags": "0x7;0x2",
"offset": 0
},
{
"fd": 23,
"type": "unix",
"access": "u",
"lock": " ",
"name": "->0xa5332c31a4d6b21d",
"flags": "0x7;0x2",
"offset": 0
},
{
"fd": 25,
"type": "unix",
"access": "u",
"lock": " ",
"name": "->0xa5332c31a4d6b6cd",
"flags": "0x7;0x2",
"offset": 0
}
]
}
]
The following application creates a listening port (which will happen to listen on IPv6, which also supports IPv4). It then connects to it, which results on two more sockets, one for the outgoing client socket, one for the incoming socket since the application connects to itself.
import { lsof } from "list-open-files"
import { createServer, connect, AddressInfo } from 'net'
async function testIP( ): Promise< void >
{
const [ result1 ] = await lsof( ); // Files before server is listening
const server = createServer( );
await new Promise( ( resolve, reject ) =>
{
server.listen( );
server.on( 'listening', resolve );
server.on( 'error', reject );
} );
const { port } = < AddressInfo >server.address( )
const [ result2 ] = await lsof( ); // Files after the server is listening
await new Promise( ( resolve, reject ) =>
{
const client = connect( port, 'localhost' );
client.on( 'connect', resolve );
client.on( 'error', reject );
} );
const [ result3 ] = await lsof( ); // Files after client connection is established
console.log( result1.files.filter( file => file.type === 'IP' ) );
console.log( result2.files.filter( file => file.type === 'IP' ) );
console.log( result3.files.filter( file => file.type === 'IP' ) );
}
testIP( )
.catch( err => console.error( err ) );
This will output:
[]
[
{
fd: 22,
type: 'IP',
access: 'u',
lock: ' ',
size: undefined,
iNode: undefined,
linkCount: undefined,
name: '*:50710',
protocol: 'TCP',
version: 6,
from: undefined,
to: { address: '*', port: 50710 }
}
]
[
{
fd: 22,
type: 'IP',
access: 'u',
lock: ' ',
size: undefined,
iNode: undefined,
linkCount: undefined,
name: '*:50710',
protocol: 'TCP',
version: 6,
from: undefined,
to: { address: '*', port: 50710 }
},
{
fd: 26,
type: 'IP',
access: 'u',
lock: ' ',
size: undefined,
iNode: undefined,
linkCount: undefined,
name: '127.0.0.1:50711->127.0.0.1:50710',
protocol: 'TCP',
version: 4,
from: { address: '127.0.0.1', port: 50711 },
to: { address: '127.0.0.1', port: 50710 }
},
{
fd: 28,
type: 'IP',
access: 'u',
lock: ' ',
size: undefined,
iNode: undefined,
linkCount: undefined,
name: '127.0.0.1:50710->127.0.0.1:50711',
protocol: 'TCP',
version: 6,
from: { address: '127.0.0.1', port: 50710 },
to: { address: '127.0.0.1', port: 50711 }
}
]
interface ProcessInfo
{
process: Process;
files: Array< File | FileDescriptorIP | UnknownFile >;
texts: Array< InternalFile >;
}
interface Process
{
pid: number;
gpid?: number;
ppid?: number;
tid?: number;
user?: string;
name?: string;
cwd?: InternalFile;
}
interface InternalFile
{
fd: string; // Note `string` - it's not a traditional file descriptor
type: Type;
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
}
interface File
{
// From base type
fd: number;
type: Type;
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
// Specific to File
majorMinorDeviceNumber: MajorMinorDeviceNumber | void;
flags: RawFileFlags | void;
offset: Offset | void;
}
interface UnknownFile
{
// From base type
fd: number;
type: Type;
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
// Specific to UnknownFile
cmd: string | undefined;
fileStructureShareCount: FileStructureShareCount | undefined; // FCT
charCode: CharacterCode | undefined;
majorMinorDeviceNumber: MajorMinorDeviceNumber | undefined;
fileStructureAddress: FileStructureAddress | undefined; // FILE-ADDR
flags: RawFileFlags | undefined; // FILE-FLAG
loginName: LoginName | undefined;
node: Node | undefined; // NODE
offset: Offset | undefined;
protocolName: ProtocolName | undefined;
deviceNumber: DeviceNumber | undefined // DEVICE
streamIdentification: StreamIdentification | undefined;
tcpTpiInformation: TcpTpiInformation | undefined;
solarisZoneName: SolarisZoneName | undefined;
seLinuxSecurityContext: SELinuxSecurityContext | undefined;
1: any | undefined;
2: any | undefined;
3: any | undefined;
4: any | undefined;
5: any | undefined;
6: any | undefined;
7: any | undefined;
8: any | undefined;
9: any | undefined;
}
interface FileDescriptorIP
{
// From base type
fd: number;
type: 'IP';
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
// Specific to FileDescriptorIP
protocol: 'TCP' | 'UDP';
version: 4 | 6;
from: IP | void;
to: IP | void;
}
export interface IP
{
address: string;
port: number;
}
FAQs
lsof for Node.js but with a modern API
The npm package list-open-files receives a total of 689 weekly downloads. As such, list-open-files popularity was classified as not popular.
We found that list-open-files demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
Security News
The CNA Scorecard ranks CVE issuers by data completeness, revealing major gaps in patch info and software identifiers across thousands of vulnerabilities.