list-open-files
lsof
for Node.js, with an asynchronous Promise-based API and JSON-serializable self-explained objects.
API
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 >;
}
Example
All open files in a primitive Node.js application
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)
View `output.txt`
[
{
"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
}
]
}
]
Listening and client connections
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( );
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( );
await new Promise( ( resolve, reject ) =>
{
const client = connect( port, 'localhost' );
client.on( 'connect', resolve );
client.on( 'error', reject );
} );
const [ result3 ] = await lsof( );
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 }
}
]
Reference
ProcessInfo
interface ProcessInfo
{
process: Process;
files: Array< File | FileDescriptorIP | UnknownFile >;
texts: Array< InternalFile >;
}
Process
interface Process
{
pid: number;
gpid?: number;
ppid?: number;
tid?: number;
user?: string;
name?: string;
cwd?: InternalFile;
}
InternalFile
interface InternalFile
{
fd: string;
type: Type;
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
}
File
interface File
{
fd: number;
type: Type;
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
majorMinorDeviceNumber: MajorMinorDeviceNumber | void;
flags: RawFileFlags | void;
offset: Offset | void;
}
UnknownFile
interface UnknownFile
{
fd: number;
type: Type;
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
cmd: string | undefined;
fileStructureShareCount: FileStructureShareCount | undefined;
charCode: CharacterCode | undefined;
majorMinorDeviceNumber: MajorMinorDeviceNumber | undefined;
fileStructureAddress: FileStructureAddress | undefined;
flags: RawFileFlags | undefined;
loginName: LoginName | undefined;
node: Node | undefined;
offset: Offset | undefined;
protocolName: ProtocolName | undefined;
deviceNumber: DeviceNumber | undefined
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;
}
FileDescriptorIP
interface FileDescriptorIP
{
fd: number;
type: 'IP';
access: Access | void;
lock: Lock | void;
size: Size | void;
iNode: INode | void;
linkCount: LinkCount | void;
name: string | void;
protocol: 'TCP' | 'UDP';
version: 4 | 6;
from: IP | void;
to: IP | void;
}
IP
export interface IP
{
address: string;
port: number;
}