Qui a l'autre extrémité de cette socket unix?

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

Linux-3.3 et plus.

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]

Avant linux-3.3

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 .

Depuis le kernel 3.3

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.

Avant le kernel 3.3

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.

Pas besoin de l'image du kernel débogable …

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