Remarque : Je gère maintenant un lsof
wrapper qui combine les deux approches décrites ici et ajoute également des informations pour les homologues de connexions TCP en boucle à l' adresse https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc.
Linux-3.3 et supérieur.
Sous Linux, à partir de la version 3.3 du noyau (et à condition que la UNIX_DIAG
fonctionnalité soit intégrée au noyau), l'homologue d'un socket de domaine unix donné (y compris socketpairs) peut être obtenu à l'aide d'une nouvelle API basée sur netlink .
lsof
depuis la version 4.89, on peut utiliser cette API:
lsof +E -aUc Xorg
Répertoriera tous les sockets de domaine Unix ayant un processus dont le nom commence par un des Xorg
deux bouts dans un format semblable à celui-ci:
Xorg 2777 root 56u unix 0xffff8802419a7c00 0t0 34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
Si votre version de lsof
est trop ancienne, il existe quelques options supplémentaires.
L' ss
utilitaire (depuis iproute2
) utilise cette même API pour extraire et afficher des informations sur la liste des sockets de domaine Unix sur le système, y compris des informations d'homologue.
Les sockets sont identifiés par leur numéro d'inode . Notez que cela n'est pas lié à l'inode du système de fichiers du fichier de socket.
Par exemple dans:
$ ss -x
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996
il indique que le socket 3435997 (qui était lié au socket ABSTRACT /tmp/.X11-unix/X0
) est connecté au socket 3435996. L' -p
option peut vous indiquer le ou les processus pour lesquels cette socket est ouverte. Il le fait en faisant quelques readlink
s sur /proc/$pid/fd/*
, il ne peut le faire que sur les processus que vous possédez (sauf si vous êtes root
). Par exemple ici:
$ 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]
Pour savoir quel processus a 3435996, vous pouvez rechercher sa propre entrée dans le résultat de ss -xp
:
$ ss -xp | awk '$6 == 3435996'
u_str ESTAB 0 0 * 3435996 * 3435997 users:(("xterm",pid=29215,fd=3))
Vous pouvez également utiliser ce script comme enveloppe lsof
pour y afficher facilement les informations pertinentes:
#! /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);
Par exemple:
$ sud that-lsof-wrapper -ad3 -p 29215
COMMAND PID UTILISATEUR TYPE FD TAILLE DU DISPOSITIF / NOM DE NŒUDS
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 type = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]
Avant linux-3.3
L'ancienne API Linux permettant de récupérer les informations de socket Unix se fait via le /proc/net/unix
fichier texte. Il répertorie tous les sockets du domaine Unix (y compris socketpairs). Le premier champ qu'il contient (s'il n'est pas masqué par les non-utilisateurs avec le kernel.kptr_restrict
paramètre sysctl), comme expliqué par @Totor, contient l'adresse du noyau d'une unix_sock
structure contenant un peer
champ pointant sur l' homologue correspondant unix_sock
. C'est aussi ce qui lsof
sort pour la DEVICE
colonne sur un socket Unix.
Désormais, obtenir la valeur de ce peer
champ signifie pouvoir lire la mémoire du noyau et connaître le décalage de ce peer
champ par rapport à l' unix_sock
adresse.
Plusieurs à gdb
base et à systemtap
base de solutions ont déjà été données , mais ils ont besoin gdb
/ systemtap
et les symboles de débogage de Linux pour le noyau en cours d' exécution en cours d' installation qui est généralement pas le cas sur les systèmes de production.
Le codage en dur de l'offset n'est pas vraiment une option car cela varie avec la version du noyau.
Maintenant , nous pouvons utiliser une approche heuristique de déterminer l'offset: avoir notre outil créer un mannequin socketpair
(nous savons l'adresse des deux pairs), et rechercher l'adresse du poste autour de la mémoire à l'autre extrémité pour déterminer le décalage.
Voici un script de perl
validation de principe qui utilise précisément (testé avec succès avec les noyaux 2.4.27 et 2.6.32 sur i386 et 3.13 et 3.16 sur amd64). Comme ci-dessus, cela fonctionne comme un wrapper autour de lsof
:
Par exemple:
$ that-lsof-wrapper -aUc nm-applet
COMMAND PID UTILISATEUR TYPE FD TAILLE DU DISPOSITIF / NOM DE NŒUDS
nm-applet 4183 stephane 4u unix 0xffff8800a055eb40 0T0 de type 36888 = STREAM -> 0xffff8800a055e7c0 [dbus-daemon, 4190, @ / tmp / dbus-AiBCXOnuP6]
nm-applet 4183 stephane 7u unix 0xffff8800a055e440 0T0 36890 type = STREAM -> 0xffff8800a055e0c0 [Xorg, 3080, @ / tmp / .X11-unix / X0]
nm-applet 4183 stephane 8u unix 0xffff8800a05c1040 0t0 36201 type = STREAM -> 0xffff8800a05c13c0 [dbus-daemon, 4118, @ / tmp / dbus-
yxxNr unix 0xffff8800a055d080 0T0 de type 36219 = STREAM -> 0xffff8800a055d400 [dbus-daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet unix 12u 4183 stephane 0xffff88022e0dfb80 0T0 36221 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]
Voici le script:
#! /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);