Je veux déterminer quel process a l'autre extrémité d'un socket UNIX.
Plus précisément, je pose une question à propos de celui qui a été créé avec socketpair()
, bien que le problème soit le même pour n'importe quel socket UNIX.
J'ai un parent
programme qui crée une socketpair(AF_UNIX, SOCK_STREAM, 0, fds)
et fork()
s. Le process parent ferme fds[1]
et garde fds[0]
pour communiquer. L'enfant fait le contraire, close(fds[0]); s=fds[1]
close(fds[0]); s=fds[1]
. Ensuite, l'enfant exec()
s un autre programme, child1
. Les deux peuvent communiquer d'avant en arrière via cette paire de douilles.
Maintenant, disons que je sais qui est le parent
, mais je veux savoir qui est child1
. Comment puis-je faire cela?
Il y a plusieurs outils à ma disposition, mais aucun ne peut me dire quel process se trouve à l'autre bout du socket. J'ai essayé:
lsof -c progname
lsof -c parent -c child1
ls -l /proc/$(pidof server)/fd
cat /proc/net/unix
Fondamentalement, je peux voir les deux sockets, et tout à leur sujet, mais ne peut pas dire qu'ils sont connectés. J'essaie de déterminer quel FD dans le parent communique avec quel process enfant.
Depuis le kernel 3.3, il est possible d'utiliser ss
ou lsof-4.89
ou plus – voir la réponse de Stéphane Chazelas .
Dans les anciennes versions, selon l'auteur de lsof
, il était impossible de le découvrir: le kernel Linux n'expose pas cette information. Source: thread 2003 sur comp.unix.admin .
Le numéro affiché dans /proc/$pid/fd/$fd
est le numéro d'inode de la socket dans le système de files socket virtuel. Lorsque vous créez une paire de tuyaux ou de sockets, chaque extrémité reçoit successivement un numéro d'inode. Les nombres sont atsortingbués séquentiellement, donc il y a une forte probabilité que les nombres diffèrent de 1, mais cela n'est pas garanti (soit parce que le premier socket était N et N +1 était déjà utilisé en raison de l'emballage ou parce qu'un autre thread était programmé entre les deux allocations d'inode et ce thread a également créé des inodes).
J'ai vérifié la définition de socketpair
dans le kernel 2.6.39 , et les deux extrémités du socket ne sont pas corrélées sauf par la méthode socketpair
spécifique au socketpair
. Pour les sockets unix, c'est unix_socketpair
dans net/unix/af_unix.c
.
Note : Je maintiens maintenant un wrapper
lsof
qui combine les deux approches décrites ici et ajoute également des informations pour les paires de connections TCP de bouclage à https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc
Sous Linux, depuis la version 3.3 du kernel (et à condition que la fonctionnalité UNIX_DIAG
soit embeddede dans le kernel), l'homologue d'un socket de domaine unix donné (incluant les paires de socket) peut être obtenu en utilisant une nouvelle API basée sur netlink .
lsof
depuis la version 4.89 peut utiliser cette API:
lsof +E -aUc Xorg
Dresse la list de toutes les sockets de domaine Unix qui ont un process dont le nom commence par Xorg
à chaque extrémité dans un format similaire à:
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 y a quelques options supplémentaires.
L'utilitaire ss
(à partir d' iproute2
) utilise cette même API pour extraire et afficher des informations sur la list des sockets de domaine unix sur le système, y compris les informations d'homologue.
Les sockets sont identifiés par leur numéro d'inode . Notez que ce n'est pas lié à l'inode du système de files du file socket.
Par exemple dans:
$ ss -x [...] u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996
il indique que la socket 3435997 (qui était liée à la socket ABSTRACT /tmp/.X11-unix/X0
) est connectée à la socket 3435996. L'option -p
peut vous indiquer quel (s) process ont cette socket ouverte. Il le fait en faisant quelques readlink
s sur /proc/$pid/fd/*
, donc il ne peut le faire que sur les process 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 process a 3435996, vous pouvez searchr sa propre input dans la sortie 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 wrapper autour de lsof
pour 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. # resortingeve 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:
$ sudo que-lsof-wrapper -ad3 -p 29215 NOM DE NŒUD DE L'UTILISATEUR DU PID DE COMMANDE xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 type = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]
L'ancienne API Linux pour extraire les informations de socket Unix se trouve via le file text /proc/net/unix
. Il répertorie toutes les sockets du domaine Unix (y compris les paires de socket). Le premier champ (s'il n'est pas caché aux non-superusers avec le paramètre kernel.kptr_ressortingct
kernel.kptr_ressortingct) comme déjà expliqué par @Totor contient l'adresse du kernel d'une structure unix_sock
qui contient un champ peer
pointant vers le homologue unix_sock
correspondant. C'est aussi ce que lsof
sorties pour la colonne DEVICE
sur un socket Unix.
Obtenir la valeur de ce champ peer
signifie maintenant pouvoir lire la memory du kernel et connaître le décalage de ce champ peer
par rapport à l'adresse unix_sock
.
Plusieurs solutions basées sur gdb
et systemtap
ont déjà été fournies, mais elles nécessitent des symboles de debugging gdb
/ systemtap
et du kernel Linux pour le kernel en cours d'exécution, ce qui n'est généralement pas le cas sur les systèmes de production.
Le encoding en dur de l'offset n'est pas vraiment une option car cela varie avec la version du kernel.
Maintenant, nous pouvons utiliser une approche heuristique pour déterminer le décalage: faire en sorte que notre outil crée une socketpair
fictives (puis nous connaissons l'adresse des deux pairs) et chercher l'adresse du pair autour de la memory à l'autre extrémité pour déterminer le décalage.
Voici un script de preuve de concept qui fait exactement cela en utilisant perl
(testé avec succès avec les kernelx 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 NOM DE NŒUD DE L'UTILISATEUR DU PID DE COMMANDE nm-applet 4183 stephane 4u unix 0xffff8800a055eb40 0t0 36888 type = 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 [démon dbus, 4118, @ / tmp / dbus-yxxNr1NkYC] nm-applet 4183 stephane 11u unix 0xffff8800a055d080 0t0 36219 type = STREAM -> 0xffff8800a055d400 [dbus-démon, 4118, @ / tmp / dbus-yxxNr1NkYC] nm-applet 4183 stephane 12u unix 0xffff88022e0dfb80 0t0 36221 type = STREAM -> 0xffff88022e0df800 [démon dbus, 2268, / var / run / dbus / system_bus_socket] nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 type = STREAM -> 0xffff88022e29ec00 [démon dbus, 2268, / var / run / dbus / socket_système_bus]
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 resortingeve 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);
Erkki Seppala a en fait un outil qui récupère cette information du kernel Linux avec gdb .. Il est disponible ici .
Vous pouvez maintenant get cette information avec ss
:
# ss -xp
Vous pouvez maintenant voir dans la colonne Peer
un ID (numéro inode) qui correspond à un autre ID dans la colonne Local
. Les ID correspondants sont les deux extrémités d'un socket.
Remarque: L'option UNIX_DIAG
doit être activée dans votre kernel.
Linux n'a pas exposé cette information à userland.
Cependant, en regardant dans la memory du kernel , nous pouvons accéder à cette information.
Note: Cette réponse le fait en utilisant gdb
, cependant, s'il vous plaît voir la réponse de @ StéphaneChazelas qui est plus élaborée à cet égard.
# lsof | grep whatever mysqld 14450 (...) unix 0xffff8801011e8280 (...) /var/run/mysqld/mysqld.sock mysqld 14450 (...) unix 0xffff8801011e9600 (...) /var/run/mysqld/mysqld.sock
Il y a 2 sockets différentes, 1 écoute et 1 prise. Le numéro hexa est l'adresse de la structure unix_sock
kernel correspondant, peer
atsortingbut peer
étant l'adresse de l'autre extrémité du socket (également une unix_sock
structure unix_sock
).
Nous pouvons maintenant utiliser gdb
pour find l' peer
dans la memory du kernel:
# gdb /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64 /proc/kcore (gdb) print ((struct unix_sock*)0xffff8801011e9600)->peer $1 = (struct sock *) 0xffff880171f078c0 # lsof | grep 0xffff880171f078c0 mysql 14815 (...) unix 0xffff880171f078c0 (...) socket
Ici vous allez, l'autre bout du socket est tenu par mysql
, PID 14815.
Votre kernel doit être compilé avec KCORE_ELF
pour utiliser /proc/kcore
. En outre, vous avez besoin d'une version de votre image de kernel avec des symboles de debugging. Sur Debian 7, apt-get install linux-image-3.2.0-4-amd64-dbg
fournira ce file.
Si vous n'avez pas (ou ne voulez pas conserver) l'image de kernel de debugging sur le système, vous pouvez donner à gdb
le décalage de memory pour "accéder manuellement" à la valeur de peer
. Cette valeur de décalage diffère généralement avec la version ou l'architecture du kernel.
Sur mon kernel, je sais que le décalage est de 680 octets, soit 85 fois 64 bits. Donc je peux faire:
# gdb /boot/vmlinux-3.2.0-4-amd64 /proc/kcore (gdb) print ((void**)0xffff8801011e9600)[85] $1 = (void *) 0xffff880171f078c0
Voilà, même résultat que ci-dessus.
Si le même kernel est exécuté sur plusieurs machines, il est plus facile d'utiliser cette variante car vous n'avez pas besoin de l'image de debugging, mais seulement de la valeur de décalage.
Pour (facilement) découvrir cette valeur de décalage au début, vous avez besoin de l'image de debugging:
$ pahole -C unix_sock /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64 struct unix_sock { (...) struct sock * peer; /* 680 8 */ (...) }
Ici vous allez, 680 octets, c'est 85 x 64 bits, ou 170 x 32 bits.
La plupart du crédit pour cette réponse va à MvG .
Cette solution, même si elle fonctionne, est d'un intérêt limité car si vous avez un système assez récent, il y a de fortes chances que vous ayez un kernel assez récent où vous pouvez utiliser des approches basées sur
ss
et si vous êtes sur un kernel plus ancien, autre solution , bien que plus hacky est plus susceptible de fonctionner et ne nécessite pas de logiciel d'addition.Toujours utile comme une démonstration de la façon d'utiliser
systemtap
pour ce type de tâche.
Si sur un système Linux récent avec un systemtap actif (1.8 ou plus récent), vous pouvez utiliser le script ci-dessous pour post-traiter la sortie de lsof
:
Par exemple:
$ lsof -aUc nm-applet | sudo ce script NOM DE NŒUD DE L'UTILISATEUR DU PID DE COMMANDE nm-applet 4183 stephane 4u unix 0xffff8800a055eb40 0t0 36888 type = 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 [démon dbus, 4118, @ / tmp / dbus-yxxNr1NkYC] nm-applet 4183 stephane 11u unix 0xffff8800a055d080 0t0 36219 type = STREAM -> 0xffff8800a055d400 [dbus-démon, 4118, @ / tmp / dbus-yxxNr1NkYC] nm-applet 4183 stephane 12u unix 0xffff88022e0dfb80 0t0 36221 type = STREAM -> 0xffff88022e0df800 [démon dbus, 2268, / var / run / dbus / system_bus_socket] nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 type = STREAM -> 0xffff88022e29ec00 [démon dbus, 2268, / var / run / dbus / socket_système_bus]
(si vous voyez 0x0000000000000000 ci-dessus au lieu de 0xffff …, c'est parce que le paramètre kernel.kptr_ressortingct
sysctl est défini sur votre système, ce qui provoque la dissimulation des pointeurs du kernel des process non privilégiés, auquel cas vous devrez exécuter lsof
comme racine pour get un résultat significatif).
Ce script ne tente pas de faire face aux noms de files socket avec des caractères lsof
, mais pas lsof
(ni lsof
ne lsof
espaces ou les deux points).
systemtap
ici est utilisé pour vider l'adresse et l'adresse homologue de toutes les structures unix_socket_table
hachage unix_socket_table
dans le kernel.
Seulement testé sur Linux 3.16 amd64 avec systemtap 2.6, et 3.13 avec 2.3.
#! /usr/bin/perl # meant to process lsof output to try and find the peer of a given # unix domain socket. Needs a working systemtap, lsof, and superuser # privileges. Copyright Stephane Chazelas 2015, public domain. # Example: lsof -aUc X | sudo this-script open STAP, '-|', 'stap', '-e', q{ probe begin { offset = &@cast(0, "struct sock")->__sk_common->skc_node; for (i = 0; i < 512; i++) for (p = @var("unix_socket_table@net/unix/af_unix.c")[i]->first; p; p=@cast(p, "struct hlist_node")->next ) { sock = p - offset; printf("%p %p\n", sock, @cast(sock, "struct unix_sock")->peer); } exit() } }; my %peer; while (<STAP>) { chomp; my ($a, $b) = split; $peer{$a} = $b; } close STAP; my %f, %addr; open LSOF, '-|', 'lsof', '-nPUFpcfdn'; while (<LSOF>) { if (/(.)(.*)/) { $f{$1} = $2; if ($1 eq 'n') { $addr{$f{d}}->{"$f{c},$f{p}" . ($f{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = ""; } } } close LSOF; while (<>) { chomp; for my $addr (/0x[0-9a-f]+/g) { my $peer = $peer{$addr}; if (defined($peer)) { $_ .= $peer eq '0x0' ? "[LISTENING]" : " -> $peer\[" . join("|", keys%{$addr{$peer}}) . "]"; last; } } print "$_\n"; }
4.89 de lsof prend en charge l'affichage des options de sharepoint terminaison.
Cité de lsof.8:
+|-E +E specifies that process intercommunication channels should be displayed with endpoint information and the channels of the endpoints should also be displayed. Currently only pipe on Linux is implemented. Endpoint information is displayed in the NAME column in the form "PID,cmd,FDmode". PID is the endpoint process ID; cmd is the endpoint process command; FD is the endpoint file's descriptor; and mode is the endpoint file's access mode. Multiple occurrences of this information can appear in a file's NAME column. -E specfies that Linux pipe files should only be displayed with endpoint information.
Exemple de sortie:
mozStorag 21535 22254 yamato 6u unix 0xf... 0t0 348924 type=STREAM pino=351122 4249,dbus-daem,55u mozStorag 21535 22254 yamato 10u unix 0xf... 0t0 356193 type=STREAM pino=356194 21535,gdbus,11u mozStorag 21535 22254 yamato 11u unix 0xf... 0t0 356194 type=STREAM pino=356193 21535,gdbus,10u mozStorag 21535 22254 yamato 21u unix 0xf... 0t0 355141 type=STREAM pino=357544 4249,dbus-daem,60u mozStorag 21535 22254 yamato 26u unix 0xf... 0t0 351134 type=STREAM pino=355142 5015,gdbus,17u mozStorag 21535 22254 yamato 69u unix 0xf... 0t0 469354 type=STREAM pino=468160 4545,alsa-sink,21u mozStorag 21535 22254 yamato 82u unix 0xf... 0t0 449383 type=STREAM pino=449384 12257,Chrome_Ch,3u mozStorag 21535 22254 yamato 86u unix 0xf... 0t0 355174 type=SEQPACKET pino=355175 21535,gdbus,95u mozStorag 21535 22254 yamato 95u unix 0xf... 0t0 355175 type=SEQPACKET pino=355174 21535,gdbus,86u 12257,Chrome_Ch,4u mozStorag 21535 22254 yamato 100u unix 0xf... 0t0 449389 type=STREAM pino=456453 3614,Xorg,38u mozStorag 21535 22254 yamato 105u unix 0xf... 0t0 582613 type=STREAM pino=586261 obexd 22163 yamato 1u unix 0xf... 0t0 361859 type=STREAM pino=365931 obexd 22163 yamato 2u unix 0xf... 0t0 361860 type=STREAM pino=365934 obexd 22163 yamato 3u unix 0xf... 0t0 361241 type=DGRAM pino=10028 obexd 22163 yamato 6u unix 0xf... 0t0 361242 type=STREAM pino=361864 4249,dbus-daem,70u