Nota : ahora mantengo un lsof
contenedor que combina los dos enfoques descritos aquí y también agrega información para pares de conexiones TCP de bucle invertido en https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc
Linux-3.3 y superior.
En Linux, desde la versión 3.3 del kernel (y siempre que la UNIX_DIAG
característica esté integrada en el kernel), el par de un socket de dominio unix dado (incluye pares de socket) se puede obtener utilizando una nueva API basada en netlink .
lsof
desde la versión 4.89 puede hacer uso de esa API:
lsof +E -aUc Xorg
Enumerará todos los sockets de dominio de Unix que tienen un proceso cuyo nombre comienza Xorg
en cada extremo en un formato similar a:
Xorg 2777 root 56u unix 0xffff8802419a7c00 0t0 34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
Si su versión de lsof
es demasiado antigua, hay algunas opciones más.
La ss
utilidad (from iproute2
) utiliza esa misma API para recuperar y mostrar información en la lista de sockets de dominio Unix en el sistema, incluida la información de pares.
Los enchufes se identifican por su número de inodo . Tenga en cuenta que no está relacionado con el inodo del sistema de archivos del archivo socket.
Por ejemplo en:
$ ss -x
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996
dice que el zócalo 3435997 (que estaba vinculado al zócalo ABSTRACT /tmp/.X11-unix/X0
) está conectado con el zócalo 3435996. La -p
opción puede decirle qué proceso (s) tiene ese zócalo abierto. Lo hace haciendo algunos readlink
s on /proc/$pid/fd/*
, por lo que solo puede hacerlo en los procesos que posee (a menos que sea root
). Por ejemplo aquí:
$ sudo ss -xp
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996 users:(("Xorg",pid=3080,fd=83))
[...]
$ sudo ls -l /proc/3080/fd/23
lrwx------ 1 root root 64 Mar 12 16:34 /proc/3080/fd/83 -> socket:[3435997]
Para averiguar qué proceso (s) tiene 3435996, puede buscar su propia entrada en la salida de ss -xp
:
$ ss -xp | awk '$6 == 3435996'
u_str ESTAB 0 0 * 3435996 * 3435997 users:(("xterm",pid=29215,fd=3))
También puede usar este script como envoltorio lsof
para mostrar fácilmente la información relevante allí:
#! /usr/bin/perl
# lsof wrapper to add peer information for unix domain socket.
# Needs Linux 3.3 or above and CONFIG_UNIX_DIAG enabled.
# retrieve peer and direction information from ss
my (%peer, %dir);
open SS, '-|', 'ss', '-nexa';
while (<SS>) {
if (/\s(\d+)\s+\*\s+(\d+) ([<-]-[->])$/) {
$peer{$1} = $2;
$dir{$1} = $3;
}
}
close SS;
# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfin';
while (<LSOF>) {
if (/(.)(.*)/) {
$fields{$1} = $2;
if ($1 eq 'n') {
$proc{$fields{i}}->{"$fields{c},$fields{p}" .
($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
}
}
}
close LSOF;
# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
chomp;
if (/\sunix\s+\S+\s+\S+\s+(\d+)\s/) {
my $peer = $peer{$1};
if (defined($peer)) {
$_ .= $peer ?
" ${dir{$1}} $peer\[" . (join("|", keys%{$proc{$peer}})||"?") . "]" :
"[LISTENING]";
}
}
print "$_\n";
}
close LSOF or exit(1);
Por ejemplo:
$ sudo that-lsof-wrapper -ad3 -p 29215
COMANDO PID USUARIO FD TIPO DISPOSITIVO TAMAÑO / APAGADO NODO NOMBRE
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 type = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]
Antes de linux-3.3
La antigua API de Linux para recuperar información de socket de Unix es a través del /proc/net/unix
archivo de texto. Enumera todos los sockets de dominio de Unix (incluidos los pares de sockets). El primer campo allí (si no está oculto para los no superusuarios con el kernel.kptr_restrict
parámetro sysctl) como ya lo explicó @Totor contiene la dirección del núcleo de una unix_sock
estructura que contiene un peer
campo que apunta al par correspondiente unix_sock
. También es lo que lsof
genera la DEVICE
columna en un socket Unix.
Ahora obtener el valor de ese peer
campo significa poder leer la memoria del kernel y conocer el desplazamiento de ese peer
campo con respecto a la unix_sock
dirección.
Ya se han dado varias soluciones gdb
basadas y systemtap
basadas , pero requieren gdb
/ systemtap
y símbolos de depuración del kernel de Linux para la instalación del kernel en ejecución, lo que generalmente no es el caso en los sistemas de producción.
Codificar el desplazamiento no es realmente una opción, ya que varía con la versión del kernel.
Ahora podemos usar un enfoque heurístico para determinar el desplazamiento: haga que nuestra herramienta cree un maniquí socketpair
(entonces conocemos la dirección de ambos pares) y busque la dirección del par alrededor de la memoria en el otro extremo para determinar el desplazamiento.
Aquí hay un script de prueba de concepto que hace exactamente eso usando perl
(probado con éxito con el kernel 2.4.27 y 2.6.32 en i386 y 3.13 y 3.16 en amd64). Al igual que arriba, funciona como un envoltorio alrededor de lsof
:
Por ejemplo:
$ that-lsof-wrapper -aUc nm-applet
COMANDO PID USUARIO FD TIPO DISPOSITIVO TAMAÑO / APAGADO NODO NOMBRE
nm-applet 4183 stephane 4u UNIX 0xffff8800a055eb40 0T0 36,888 type = STREAM -> 0xffff8800a055e7c0 [dbus-daemon, 4190, @ / tmp / dbus-AiBCXOnuP6]
nm-applet 4183 stephane 7U UNIX 0xffff8800a055e440 0T0 36,890 type = STREAM -> 0xffff8800a055e0c0 [Xorg, 3080, @ / tmp / .X11-Unix / X0]
nm-applet 4183 stephane 8u UNIX 0xffff8800a05c1040 0T0 36,201 type = STREAM -> 0xffff8800a05c13c0 [dbus-daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet 4183 stephane 11u UNIX 0xffff8800a055d080 0T0 36,219 type = STREAM -> 0xffff8800a055d400 [dbus-daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet 4183 stephane 12u UNIX 0xffff88022e0dfb80 0T0 36,221 type = STREAM -> 0xffff88022e0df800 [dbus-daemon, 2268, / var / run / dbus / system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 type = STREAM -> 0xffff88022e29ec00 [dbus-daemon, 2268, / var / run / dbus / system_bus_socket]
Aquí está el guión:
#! /usr/bin/perl
# wrapper around lsof to add peer information for Unix
# domain sockets. needs lsof, and superuser privileges.
# Copyright Stephane Chazelas 2015, public domain.
# example: sudo this-lsof-wrapper -aUc Xorg
use Socket;
open K, "<", "/proc/kcore" or die "open kcore: $!";
read K, $h, 8192 # should be more than enough
or die "read kcore: $!";
# parse ELF header
my ($t,$o,$n) = unpack("x4Cx[C19L!]L!x[L!C8]S", $h);
$t = $t == 1 ? "L3x4Lx12" : "Lx4QQx8Qx16"; # program header ELF32 or ELF64
my @headers = unpack("x$o($t)$n",$h);
# read data from kcore at given address (obtaining file offset from ELF
# @headers)
sub readaddr {
my @h = @headers;
my ($addr, $length) = @_;
my $offset;
while (my ($t, $o, $v, $s) = splice @h, 0, 4) {
if ($addr >= $v && $addr < $v + $s) {
$offset = $o + $addr - $v;
if ($addr + $length - $v > $s) {
$length = $s - ($addr - $v);
}
last;
}
}
return undef unless defined($offset);
seek K, $offset, 0 or die "seek kcore: $!";
my $ret;
read K, $ret, $length or die "read($length) kcore \@$offset: $!";
return $ret;
}
# create a dummy socketpair to try find the offset in the
# kernel structure
socketpair(Rdr, Wtr, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
or die "socketpair: $!";
$r = readlink("/proc/self/fd/" . fileno(Rdr)) or die "readlink Rdr: $!";
$r =~ /\[(\d+)/; $r = $1;
$w = readlink("/proc/self/fd/" . fileno(Wtr)) or die "readlink Wtr: $!";
$w =~ /\[(\d+)/; $w = $1;
# now $r and $w contain the socket inodes of both ends of the socketpair
die "Can't determine peer offset" unless $r && $w;
# get the inode->address mapping
open U, "<", "/proc/net/unix" or die "open unix: $!";
while (<U>) {
if (/^([0-9a-f]+):(?:\s+\S+){5}\s+(\d+)/) {
$addr{$2} = hex $1;
}
}
close U;
die "Can't determine peer offset" unless $addr{$r} && $addr{$w};
# read 2048 bytes starting at the address of Rdr and hope to find
# the address of Wtr referenced somewhere in there.
$around = readaddr $addr{$r}, 2048;
my $offset = 0;
my $ptr_size = length(pack("L!",0));
my $found;
for (unpack("L!*", $around)) {
if ($_ == $addr{$w}) {
$found = 1;
last;
}
$offset += $ptr_size;
}
die "Can't determine peer offset" unless $found;
my %peer;
# now retrieve peer for each socket
for my $inode (keys %addr) {
$peer{$addr{$inode}} = unpack("L!", readaddr($addr{$inode}+$offset,$ptr_size));
}
close K;
# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfdn';
while (<LSOF>) {
if (/(.)(.*)/) {
$fields{$1} = $2;
if ($1 eq 'n') {
$proc{hex($fields{d})}->{"$fields{c},$fields{p}" .
($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
}
}
}
close LSOF;
# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
chomp;
for my $addr (/0x[0-9a-f]+/g) {
$addr = hex $addr;
my $peer = $peer{$addr};
if (defined($peer)) {
$_ .= $peer ?
sprintf(" -> 0x%x[", $peer) . join("|", keys%{$proc{$peer}}) . "]" :
"[LISTENING]";
last;
}
}
print "$_\n";
}
close LSOF or exit(1);