Kto ma drugi koniec tej uniksowej pary gniazd?


54

Chcę ustalić, który proces ma drugi koniec gniazda UNIX.

W szczególności pytam o jeden, który został stworzony socketpair(), chociaż problem jest taki sam dla każdego gniazda UNIX.

Mam program, parentktóry tworzy socketpair(AF_UNIX, SOCK_STREAM, 0, fds)i fork(). Proces nadrzędny zamyka się fds[1]i kontynuuje fds[0]komunikację. Dziecko robi coś przeciwnego, close(fds[0]); s=fds[1]. Wtedy dziecko exec()s inny program child1. Obie mogą komunikować się tam iz powrotem za pośrednictwem tej pary gniazd.

Powiedzmy, że wiem, kto parentjest, ale chcę dowiedzieć się, kto child1jest. Jak mam to zrobic?

Do dyspozycji mam kilka narzędzi, ale żadne nie może mi powiedzieć, który proces jest na drugim końcu gniazda. Próbowałem:

  • lsof -c progname
  • lsof -c parent -c child1
  • ls -l /proc/$(pidof server)/fd
  • cat /proc/net/unix

Zasadniczo widzę dwa gniazda i wszystko na ich temat, ale nie mogę powiedzieć, że są ze sobą połączone. Próbuję ustalić, która FD u rodzica komunikuje się z którym procesem potomnym.

Odpowiedzi:


27

Ponieważ jądro 3.3, jest to możliwe przy użyciu sslub lsof-4.89lub powyżej - patrz odpowiedź Stéphane Chazelas użytkownika .

W starszych wersjach, według autora lsof, nie można było tego dowiedzieć: jądro Linuksa nie ujawnia tych informacji. Źródło: Wątek 2003 na comp.unix.admin .

Liczba pokazana w /proc/$pid/fd/$fdto numer i-węzła gniazda w systemie plików wirtualnego gniazda. Podczas tworzenia pary rur lub gniazd każdy koniec otrzymuje kolejno numer i-węzła. Liczby są przypisywane sekwencyjnie, więc istnieje duże prawdopodobieństwo, że liczby różnią się o 1, ale nie jest to gwarantowane (albo dlatego, że pierwsze gniazdo było N, a N +1 było już w użyciu z powodu zawijania, lub ponieważ jakiś inny wątek był zaplanowane między dwoma przydziałami i-węzłów, a ten wątek również stworzył niektóre i-węzły).

Sprawdziłem definicję socketpairw jądrze 2.6.39 , a dwa końce gniazda nie są skorelowane, z wyjątkiem socketpairmetody specyficznej dla typu . Dla gniazd Unix, to unix_socketpairwnet/unix/af_unix.c .


2
Dzięki @Gillles. Pamiętam, że czytałem o tym jakiś czas temu, ale nie byłem w stanie go znaleźć ponownie. Być może będę musiał napisać łatkę dla / proc / net / unix.
Jonathon Reinhart

I tak, dokonałem tej obserwacji ze wzrostem liczby i-węzłów i obecnie nad tym pracuję. Jednak, jak zauważyłeś, nie jest to gwarantowane. Proces, na który patrzę, ma co najmniej 40 otwartych gniazd unixowych i widziałem jeden przypadek, w którym N + 1 nie było prawdziwe. Porażka.
Jonathon Reinhart

1
@JonathonReinhart Sprawdziłem definicjęsocketpair , a dwa końce gniazda nie są skorelowane inaczej niż za pomocą socketpairmetody specyficznej dla typu . Dla gniazd unixowych znajduje się unix_socketpairw `net / unix / af_unix.c . Byłoby miło mieć tę informację również dla rur.
Gilles „SO- przestań być zły”

36

Uwaga : Teraz utrzymuję lsofopakowanie, które łączy oba opisane tutaj podejścia, a także dodaje informacje dla peerów połączeń zwrotnych TCP na https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc

Linux-3.3 i nowsze wersje.

W systemie Linux, ponieważ jądro w wersji 3.3 (i pod warunkiem, że UNIX_DIAGfunkcja jest wbudowana w jądro), element równorzędny danego gniazda domeny unix (obejmuje pary gniazd) można uzyskać za pomocą nowego interfejsu API opartego na netlink .

lsof ponieważ wersja 4.89 może korzystać z tego interfejsu API:

lsof +E -aUc Xorg

Wyświetli listę wszystkich gniazd domeny Unix, które mają proces, którego nazwa zaczyna się Xorgna dowolnym końcu, w formacie podobnym do:

Xorg       2777       root   56u  unix 0xffff8802419a7c00      0t0   34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u

Jeśli Twoja wersja lsofjest za stara, istnieje kilka innych opcji.

ssGospodarczy (z iproute2) sprawia, że korzystanie z tego samego API do pobierania i wyświetlania informacji na liście gniazd domeny UNIX w systemie, w tym informacji o partnerze.

Gniazda są identyfikowane przez ich numer i-węzła . Zauważ, że nie jest to związane z i-węzłem systemu plików pliku gniazda.

Na przykład w:

$ ss -x
[...]
u_str  ESTAB    0    0   @/tmp/.X11-unix/X0 3435997     * 3435996

mówi, że gniazdo 3435997 (które było powiązane z gniazdem ABSTRACT /tmp/.X11-unix/X0) jest połączone z gniazdem 3435996. Ta -popcja pozwala określić, które procesy mają otwarte to gniazdo. Robi to, wykonując kilka readlinks /proc/$pid/fd/*, więc może to zrobić tylko w procesach, które posiadasz (chyba że jesteś root). Na przykład tutaj:

$ 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]

Aby dowiedzieć się, jaki proces (y) ma 3435996, możesz poszukać własnego wpisu w wynikach ss -xp:

$ ss -xp | awk '$6 == 3435996'
u_str  ESTAB  0  0  * 3435996  * 3435997 users:(("xterm",pid=29215,fd=3))

Możesz również użyć tego skryptu jako opakowania, lsofaby łatwo pokazać odpowiednie informacje:

#! /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);

Na przykład:

$ sudo that-lsof-wrapper -ad3 -p 29215
POLECENIE PID UŻYTKOWNIK TYP FD ROZMIAR URZĄDZENIA / WYŁĄCZ NAZWA NAZWY
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 type = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]

Przed linux-3.3

Stary Linux API do pobierania informacji o gniazdach unixowych jest za pośrednictwem /proc/net/unixpliku tekstowego. Zawiera listę wszystkich gniazd domeny uniksowej (w tym par gniazd). Pierwsze pole tam (jeśli nie jest ukryte dla nieurządzających z kernel.kptr_restrictparametrem sysctl), jak już wyjaśniono przez @Totor, zawiera adres jądra unix_sockstruktury zawierającej peerpole wskazujące odpowiedni element równorzędny unix_sock . To także dane lsofwyjściowe dla DEVICEkolumny w gnieździe Uniksa.

Teraz uzyskanie wartości tego peerpola oznacza możliwość odczytu pamięci jądra i znania przesunięcia tego peerpola względem unix_sockadresu.

Podano już kilka rozwiązań gdbopartych na i systemtapopartych na nich, ale wymagają one gdb/ systemtapi symboli debugowania jądra Linuksa dla zainstalowanego jądra, co na ogół nie ma miejsca w systemach produkcyjnych.

Przesunięcie na stałe przesunięcia nie jest tak naprawdę opcją, ponieważ różni się w zależności od wersji jądra.

Teraz możemy użyć heurystycznego podejścia do określania przesunięcia: pozwól naszemu narzędziu utworzyć manekina socketpair(wtedy znamy adres obu peerów) i wyszukaj adres peera wokół pamięci na drugim końcu, aby ustalić przesunięcie.

Oto skrypt perlsprawdzający koncepcję, który właśnie to robi (pomyślnie przetestowany z jądrem 2.4.27 i 2.6.32 na i386 oraz 3.13 i 3.16 na amd64). Podobnie jak powyżej, działa jak opakowanie lsof:

Na przykład:

$ that-lsof-wrapper -aUc nm-applet
POLECENIE PID UŻYTKOWNIK TYP FD ROZMIAR URZĄDZENIA / WYŁĄCZ NAZWA NAZWY
nm aplet 4183 Stephane 4u Unix 0xffff8800a055eb40 0T0 36888 Typ = STREAM -> 0xffff8800a055e7c0 [dbus-demon 4190 @ / tmp / dbus-AiBCXOnuP6] 
nm aplet 4183 Stephane 7u Unix 0xffff8800a055e440 0T0 36890 Typ = STREAM -> 0xffff8800a055e0c0 [Xorg, 3080 @ / tmp / .X11 Unix / X0] 
nm aplet 4183 Stephane 8U Unix 0xffff8800a05c1040 0T0 36201 Typ = STREAM -> 0xffff8800a05c13c0 [dbus-demon 4118 @ / tMP / dBUS-yxxNr1NkYC] 
nm aplet 4183 Stephane 11U unix 0xffff8800a055d080 0T0 36219 Typ = STREAM -> 0xffff8800a055d400 [dbus-demon 4118 @ / tmp / dbus-yxxNr1NkYC] 
nm aplet 4183 Stephane 12u unix 0xffff88022e0dfb80 0T0 36221 type = STREAM -> 0xffff88022e0df800 [dbus-demon 2268 / var / run / dbus / system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 typ = STREAM -> 0xffff88022e29ec00 [dbus-demon, 2268, / var / run / dbus / system_bus_socket]

Oto skrypt:

#! /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);

1
@ Mikeserv, to kontynuacja tego komentarza . Brak możliwości znalezienia drugiego końca gniazd unixowych zawsze mnie denerwuje (często przy próbie znalezienia klientów X i ostatnio pojawiło się na ten temat pytanie ). Spróbuję sprawdzić, czy podobne podejście można zastosować w przypadku pseudo-terminali, i zasugeruję je lsofautorowi.
Stéphane Chazelas,

1
Nadal nie mogę uwierzyć, że nie zapewnia tego samo jądro! Naprawdę powinienem przesłać łatkę, choćby po to, by dowiedzieć się, dlaczego ona jeszcze nie istnieje.
Jonathon Reinhart

1
nie ssnie to zrobić? To coś w rodzaju mojej głowy, ale ss -pxwymienia wiele gniazd unixowych z równorzędnymi informacjami, takimi jak: users: ("nacl_helper",pid=18992,fd=6),("chrome",pid=18987,fd=6),("chrome",pid=18975,fd=5)) u_str ESTAB\t0\t0\t/run/dbus/system_bus_socket 8760\t\t* 15068a nagłówki kolumn są ...State\tRecv-Q\tSend-Q\tLocal Address:Port\tPeer Address:Port
mikeserv

1
Ponadto, jeśli to zrobię lsof -c terminology, zobaczę, terminolo 12731\tmikeserv\t12u\tunix\t0xffff880600e82680\t0t0\t1312426\ttype=STREAMale jeśli to zrobię ss -px | grep terminology, otrzymam:u_str\tESTAB\t0\t0\t* 1312426\t*1315046\tusers:(("terminology",pid=12731,fd=12))
mikeserv

1
@ mikeserv, wygląda na to, że rzeczywiście! Wygląda na to, że ostatnio marnowałem dużo czasu ...
Stéphane Chazelas,


8

Od jądra 3.3

Państwo może teraz uzyskać te informacje ss:

# ss -xp

Teraz możesz zobaczyć w Peerkolumnie identyfikator (numer i-węzła), który odpowiada innemu identyfikatorowi w Localkolumnie. Identyfikatory pasujące to dwa końce gniazda.

Uwaga: UNIX_DIAGOpcja musi być włączona w twoim jądrze.

Przed jądrem 3.3

Linux nie ujawnił tych informacji użytkownikom.

Jednak patrząc na pamięć jądra , możemy uzyskać dostęp do tych informacji.

Uwaga: Ta odpowiedź jest dostępna przy użyciu gdb, jednak proszę zobaczyć odpowiedź @ StéphaneChazelas, która jest bardziej rozwinięta w tym zakresie.

# lsof | grep whatever
mysqld 14450 (...) unix 0xffff8801011e8280 (...) /var/run/mysqld/mysqld.sock
mysqld 14450 (...) unix 0xffff8801011e9600 (...) /var/run/mysqld/mysqld.sock

Istnieją 2 różne gniazda, 1 słuchanie i 1 ustanowione. Liczba szesnastkowa jest adresem odpowiedniej unix_sockstruktury jądra , której peeratrybut jest adresem drugiego końca gniazda (również unix_sockinstancji struktury).

Teraz możemy użyć gdbdo znalezienia peerwewnętrznej pamięci jądra:

# 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

Proszę bardzo, drugi koniec gniazda trzyma mysqlPID 14815.

Twoje jądro musi zostać skompilowane, KCORE_ELFaby móc z niego korzystać /proc/kcore. Potrzebujesz także wersji obrazu jądra z symbolami debugowania. Na Debianie 7 apt-get install linux-image-3.2.0-4-amd64-dbgudostępni ten plik.

Nie ma potrzeby debugowania obrazu jądra ...

Jeśli nie masz (lub nie chcesz zachować) obrazu jądra debugowania w systemie, możesz gdbustawić przesunięcie pamięci, aby „ręcznie” uzyskać dostęp do peerwartości. Ta wartość przesunięcia zwykle różni się w zależności od wersji lub architektury jądra.

W moim jądrze wiem, że przesunięcie wynosi 680 bajtów, czyli 85 razy 64 bitów. Więc mogę zrobić:

# gdb /boot/vmlinux-3.2.0-4-amd64 /proc/kcore
(gdb) print ((void**)0xffff8801011e9600)[85]
$1 = (void *) 0xffff880171f078c0

Voilà, taki sam wynik jak powyżej.

Jeśli masz to samo jądro uruchomione na kilku komputerach, łatwiej jest użyć tego wariantu, ponieważ nie potrzebujesz obrazu debugowania, tylko wartość przesunięcia.

Aby (łatwo) odkryć tę wartość przesunięcia na początku, potrzebujesz obrazu debugowania:

$ pahole -C unix_sock /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64
struct unix_sock {
  (...)
  struct sock *              peer;                 /*   680     8 */
  (...)
}

Proszę, 680 bajtów, to 85 x 64 bitów lub 170 x 32 bitów.

Większość zasług za tę odpowiedź należy do MvG .


2
Innym podejściem do odzyskania offsetu może być utworzenie pary gniazd, zidentyfikowanie odpowiednich wpisów w / proc / net / unix na podstawie numerów i-węzłów z linków odczytu w / proc / pif / fd / * i skanowanie pamięci wokół adresu jednego gniazda dla adres drugiego. To może uczynić rozsądnie przenośny (we wszystkich wersjach i architekturach Linuksa), który mógłby być wdrożony przez sam lsof. Spróbuję wymyślić PoC.
Stéphane Chazelas,

2
Dodałem teraz taki PoC, który wydaje się działać dobrze na testowanych systemach.
Stéphane Chazelas

5

To rozwiązanie, choć działa, ma ograniczone zainteresowanie, ponieważ jeśli masz wystarczająco niedawne podsłuchy systemowe, istnieje duże prawdopodobieństwo, że będziesz miał wystarczająco nowe jądro, w którym możesz używać metod ssopartych na podejściu , a jeśli używasz starszego jądra, to inne rozwiązanie , choć bardziej hacky jest bardziej prawdopodobne i nie wymaga dodatkowego oprogramowania.

Nadal przydatny jako demonstracja użycia systemtaptego rodzaju zadania.

Jeśli na najnowszym systemie Linux z działającym paskiem systemowym (1.8 lub nowszym), możesz użyć poniższego skryptu, aby przetworzyć dane wyjściowe lsof:

Na przykład:

$ lsof -aUc nm-applet | sudo that-script
POLECENIE PID UŻYTKOWNIK TYP FD ROZMIAR URZĄDZENIA / WYŁĄCZ NAZWA NAZWY
nm aplet 4183 Stephane 4u Unix 0xffff8800a055eb40 0T0 36888 Typ = STREAM -> 0xffff8800a055e7c0 [dbus-demon 4190 @ / tmp / dbus-AiBCXOnuP6] 
nm aplet 4183 Stephane 7u Unix 0xffff8800a055e440 0T0 36890 Typ = STREAM -> 0xffff8800a055e0c0 [Xorg, 3080 @ / tmp / .X11 Unix / X0] 
nm aplet 4183 Stephane 8U Unix 0xffff8800a05c1040 0T0 36201 Typ = STREAM -> 0xffff8800a05c13c0 [dbus-demon 4118 @ / tMP / dBUS-yxxNr1NkYC] 
nm aplet 4183 Stephane 11U unix 0xffff8800a055d080 0T0 36219 Typ = STREAM -> 0xffff8800a055d400 [dbus-demon 4118 @ / tmp / dbus-yxxNr1NkYC] 
nm aplet 4183 Stephane 12u unix 0xffff88022e0dfb80 0T0 36221 type = STREAM -> 0xffff88022e0df800 [dbus-demon 2268 / var / run / dbus / system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 typ = STREAM -> 0xffff88022e29ec00 [dbus-demon, 2268, / var / run / dbus / system_bus_socket]

(jeśli widzisz 0x0000000000000000 powyżej zamiast 0xffff ..., to dlatego, że kernel.kptr_restrictparametr sysctl jest ustawiony w twoim systemie, co powoduje, że wskaźniki jądra są ukryte przed procesami nieuprzywilejowanymi, w takim przypadku musisz uruchomić lsofjako root, aby uzyskać znaczący wynik).

Ten skrypt nie próbuje poradzić sobie z nazwami plików gniazd ze znakami nowej linii, ale nie robi tego lsof(nie lsofradzi sobie z pustymi znakami lub dwukropkami).

systemtaptutaj służy do zrzucenia adresu i adresu równorzędnego wszystkich unix_sockstruktur w unix_socket_tablehaszu w jądrze.

Testowane tylko w systemie Linux 3.16 amd64 z systemtap 2.6 i 3.13 z 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";
}

parse error: unknown statistic operator @var: czy coś mi brakuje?
Totor

@Totor, @vardodano w systemtap 1.8, 2012-06-17 (najnowszy jest 2.7)
Stéphane Chazelas

2

4.89 z lsof obsługuje wyświetlanie opcji punktów końcowych.

Cytat z 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.

Przykład wyniku:

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

2

Od jądra Linuksa 4.2 istnieje CONFIG_UNIX_DIAGdodatkowe informacje o gniazdach domeny UNIX, a mianowicie informacje Virtual File System(VFS), które zawierają dotychczas brakujące informacje pozwalające połączyć i-węzeł ze ścieżką do procesu. Można go już zapytać za pomocą ssnarzędzia z iproute2, począwszy od wersji v4.19.0 ~ 55 :

$ ss --processes --unix --all --extened
...
Netid  State   Recv-Q  Send-Q  Local Address:Port      Peer Address:Port
u_str  LISTEN  0       5         /tmp/socket 13381347             * 0     users:(("nc",pid=12550,fd=3)) <-> ino:1569897 dev:0/65025 peers:

Numer urządzenia i ścieżka i-węzła, które można uzyskać

$ stat -c 'ino:%i dev:0/%d' /tmp/socket
ino:1569946 dev:0/65025

ss obsługuje również filtrowanie:

 ss --processes --unix --all --extended 'sport = /tmp/socket'

ale pamiętaj, że może to nie zawierać odpowiedniego gniazda, ponieważ zły proces może zmienić nazwę oryginalnego gniazda i zastąpić go własnym złym:

mv /tmp/socket /tmp/socket.orig
nc -U -l /tmp/socket.evil &
mv /tmp/socket.evil /tmp/socket

lsof /tmp/socket, fuser /tmp/socketI ss --processes --unix --all --extended 'sport = /tmp/socket'będzie wszystko notować oryginalny proces, a nie zło wymiany. Zamiast tego użyj czegoś takiego:

id=$(stat -c 'ino:%i dev:0/%d' /tmp/socket)
ss --processes --unix --all --extended | grep -F "$id"

Lub napisz własny program litte na podstawie szablonu zawartego w man 7 sock_diag .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.