Ai có đầu kia của sự thất vọng unix này?


54

Tôi muốn xác định quá trình nào có đầu kia của ổ cắm UNIX.

Cụ thể, tôi đang hỏi về một cái được tạo ra socketpair(), mặc dù vấn đề là giống nhau đối với bất kỳ ổ cắm UNIX nào.

Tôi có một chương trình parenttạo ra một socketpair(AF_UNIX, SOCK_STREAM, 0, fds), và fork()s. Quá trình cha mẹ đóng fds[1]và giữ fds[0]để giao tiếp. Đứa trẻ làm điều ngược lại close(fds[0]); s=fds[1],. Sau đó, exec()một chương trình khác child1. Cả hai có thể liên lạc qua lại thông qua sự kết nối này.

Bây giờ, hãy nói tôi biết ai parentlà ai , nhưng tôi muốn tìm ra ai child1là ai . Làm thế nào để tôi làm điều này?

Có một số công cụ theo ý của tôi, nhưng không có công cụ nào có thể cho tôi biết quá trình nào ở đầu kia của ổ cắm. Tôi đã thử:

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

Về cơ bản, tôi có thể thấy hai ổ cắm và mọi thứ về chúng, nhưng không thể nói rằng chúng được kết nối. Tôi đang cố gắng xác định FD nào trong cha mẹ đang giao tiếp với tiến trình con nào.

Câu trả lời:


27

Kể từ kernel 3.3, nó có thể sử dụng sshoặc lsof-4.89trở lên - xem câu trả lời Stéphane Chazelas của .

Trong các phiên bản cũ hơn, theo tác giả của lsof, không thể tìm ra điều này: nhân Linux không tiết lộ thông tin này. Nguồn: chủ đề 2003 trên comp.unix.admin .

Số hiển thị trong /proc/$pid/fd/$fdlà số inode của socket trong hệ thống tập tin socket ảo. Khi bạn tạo một cặp ống hoặc ổ cắm, mỗi đầu liên tiếp nhận được một số inode. Các số được quy cho tuần tự, do đó, có khả năng cao là các số khác nhau 1, nhưng điều này không được đảm bảo (vì ổ cắm đầu tiên là NN +1 đã được sử dụng do gói hoặc do một số luồng khác là được lên lịch giữa hai phân bổ inode và luồng đó cũng tạo ra một số inode).

Tôi đã kiểm tra định nghĩa của socketpairkernel 2.6.39 và hai đầu của socket không tương quan với nhau ngoại trừ theo socketpairphương thức đặc trưng cho loại . Đối với ổ cắm unix, đó là unix_socketpairtrongnet/unix/af_unix.c .


2
Cảm ơn @Gillles. Tôi nhớ lại đã đọc một cái gì đó về điều đó một thời gian, nhưng không thể tìm thấy nó một lần nữa. Tôi có thể phải viết một bản vá cho / Proc / net / unix.
Jonathon Reinhart

Và vâng, tôi đã thực hiện quan sát đó với số lượng inode ngày càng tăng, và hiện tại đó là những gì tôi đang làm việc. Tuy nhiên, như bạn lưu ý, nó không được bảo đảm. Quá trình tôi đang xem có ít nhất 40 ổ cắm unix mở và tôi đã thấy một trường hợp trong đó N + 1 không đúng. Bummer.
Jonathon Reinhart

1
@JonathonReinhart Tôi đã kiểm tra định nghĩasocketpair và hai đầu của ổ cắm không tương quan với nhau ngoại trừ theo socketpairphương pháp loại cụ thể . Đối với các ổ cắm unix, đó là unix_socketpairtrong `net / unix / af_unix.c . Nó sẽ là tốt đẹp để có thông tin này cho đường ống, quá.
Gilles 'SO- ngừng trở nên xấu xa'

36

Lưu ý : Bây giờ tôi duy trì lsoftrình bao bọc kết hợp cả hai cách tiếp cận được mô tả ở đây và cũng thêm thông tin cho các đồng nghiệp của kết nối TCP loopback tại https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc

Linux-3.3 trở lên.

Trên Linux, vì phiên bản kernel 3.3 (và được cung cấp UNIX_DIAGtính năng này được xây dựng trong kernel), có thể lấy được đồng đẳng của một ổ cắm miền unix nhất định (bao gồm cả socket) bằng cách sử dụng API dựa trên netlink mới .

lsof kể từ phiên bản 4.89 có thể sử dụng API đó:

lsof +E -aUc Xorg

Sẽ liệt kê tất cả các socket miền Unix có một quá trình có tên bắt đầu bằng Xorgmột trong hai định dạng tương tự như:

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

Nếu phiên bản của bạn lsofquá cũ, có một vài lựa chọn khác.

Các sstiện ích (từ iproute2) tận dụng cùng API để lấy và hiển thị thông tin về danh sách các ổ cắm miền unix trên hệ thống bao gồm thông tin ngang hàng.

Các ổ cắm được xác định bởi số inode của chúng . Lưu ý rằng nó không liên quan đến inode hệ thống tập tin của tệp socket.

Ví dụ:

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

nó nói rằng ổ cắm 3435997 (được gắn với ổ cắm ABSTRACT /tmp/.X11-unix/X0) được kết nối với ổ cắm 3435996. -pTùy chọn này có thể cho bạn biết quá trình nào mở ổ cắm đó. Nó thực hiện điều đó bằng cách thực hiện một số readlinks trên /proc/$pid/fd/*, vì vậy nó chỉ có thể làm điều đó vào quá trình bạn sở hữu (trừ khi bạn root). Ví dụ ở đây:

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

Để tìm hiểu quá trình nào có 3435996, bạn có thể tra cứu mục nhập riêng của nó trong đầu ra của ss -xp:

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

Bạn cũng có thể sử dụng tập lệnh này như một trình bao bọc xung quanh lsofđể dễ dàng hiển thị thông tin liên quan ở đó:

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

Ví dụ:

$ sudo that-lsof-Wrapper -ad3 -p 29215
NGƯỜI DÙNG ĐIỀU KHIỂN NGƯỜI DÙNG FD LOẠI THIẾT BỊ THIẾT BỊ / TẮT NODE
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 loại = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]

Trước linux-3.3

API Linux cũ để truy xuất thông tin ổ cắm unix là thông qua /proc/net/unixtệp văn bản. Nó liệt kê tất cả các socket miền Unix (bao gồm cả socketpairs). Trường đầu tiên trong đó (nếu không bị ẩn đối với những người không phải là siêu nhân với kernel.kptr_restricttham số sysctl) như đã giải thích bởi @Totor chứa địa chỉ kernel của unix_sockcấu trúc có chứa một peertrường trỏ đến ngang hàng tương ứng unix_sock. Đó cũng là những gì lsofđầu ra cho DEVICEcột trên ổ cắm Unix.

Bây giờ nhận được giá trị của peertrường đó có nghĩa là có thể đọc bộ nhớ kernel và biết phần bù của peertrường đó liên quan đến unix_sockđịa chỉ.

Một số gdbdựa trênsystemtapdựa trên các giải pháp đã được đưa ra nhưng họ yêu cầu gdb/ systemtapvà biểu tượng Linux kernel debug cho kernel chạy được cài đặt mà nói chung là không phải như vậy trên các hệ thống sản xuất.

Mã hóa phần bù không thực sự là một tùy chọn vì nó thay đổi theo phiên bản kernel.

Bây giờ chúng ta có thể sử dụng cách tiếp cận heuristic để xác định phần bù: yêu cầu công cụ của chúng ta tạo một hình nộm socketpair(sau đó chúng ta biết địa chỉ của cả hai đồng nghiệp) và tìm kiếm địa chỉ của đồng đẳng xung quanh bộ nhớ ở đầu kia để xác định phần bù.

Dưới đây là một kịch bản bằng chứng khái niệm chỉ sử dụng perl(đã thử nghiệm thành công với kernel 2.4.27 và 2.6.32 trên i386 và 3.13 và 3.16 trên amd64). Giống như ở trên, nó hoạt động như một trình bao bọc xung quanh lsof:

Ví dụ:

$ that-lsof-Wrapper -aUc nm-applet
NGƯỜI DÙNG ĐIỀU KHIỂN NGƯỜI DÙNG FD LOẠI THIẾT BỊ THIẾT BỊ / TẮT NODE
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 11u stephane 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]

Đây là kịch bả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);

1
@mikeerv, đó là phần tiếp theo của bình luận đó . Không thể tìm thấy đầu kia của ổ cắm unix là điều luôn làm tôi khó chịu (thường là khi cố gắng tìm khách hàng X và có một câu hỏi gần đây về điều đó ). Tôi sẽ thử và xem liệu một cách tiếp cận tương tự có thể được sử dụng cho các thiết bị đầu cuối giả và đề xuất chúng cho lsoftác giả.
Stéphane Chazelas 17/03/2015

1
Tôi vẫn không thể tin rằng điều này không được cung cấp bởi chính kernel! Tôi thực sự nên gửi một bản vá, nếu không có gì khác ngoài việc khám phá lý do tại sao nó không tồn tại.
Jonathon Reinhart

1
không sskhông làm điều này? Nó nằm trên đầu tôi, nhưng ss -pxliệt kê rất nhiều ổ cắm unix với thông tin ngang hàng như: 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* 15068và các tiêu đề cột là ...State\tRecv-Q\tSend-Q\tLocal Address:Port\tPeer Address:Port
mikeerv 17/03/2015

1
Ngoài ra, nếu tôi lsof -c terminologycó thể nhìn thấy terminolo 12731\tmikeserv\t12u\tunix\t0xffff880600e82680\t0t0\t1312426\ttype=STREAMnhưng nếu ss -px | grep terminologytôi nhận được:u_str\tESTAB\t0\t0\t* 1312426\t*1315046\tusers:(("terminology",pid=12731,fd=12))
mikeerv 17/03/2015

1
@mikeerv, có vẻ như nó thực sự làm! Dường như gần đây tôi đã lãng phí rất nhiều thời gian ...
Stéphane Chazelas 17/03/2015


8

Kể từ kernel 3.3

Bây giờ bạn có thể nhận thông tin này với :ss

# ss -xp

Bây giờ bạn có thể thấy trong Peercột một ID (số inode) tương ứng với một ID khác trong Localcột. ID trùng khớp là hai đầu của một ổ cắm.

Lưu ý: UNIX_DIAGTùy chọn phải được bật trong kernel của bạn.

Trước kernel 3.3

Linux đã không tiết lộ thông tin này cho người dùng.

Tuy nhiên, bằng cách xem xét bộ nhớ kernel , chúng ta có thể truy cập thông tin này.

Lưu ý: Câu trả lời này thực hiện bằng cách sử dụng gdb, tuy nhiên, vui lòng xem câu trả lời của @ StéphaneChazelas được xây dựng kỹ hơn về vấn đề này.

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

Có 2 ổ cắm khác nhau, 1 nghe và 1 được thiết lập. Số hexa là địa chỉ của unix_sockcấu trúc hạt nhân tương ứng , có một peerthuộc tính là địa chỉ của đầu kia của ổ cắm (cũng là một unix_sockthể hiện cấu trúc).

Bây giờ chúng ta có thể sử dụng gdbđể tìm peerbộ nhớ trong 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

Ở đây bạn đi, đầu kia của ổ cắm được giữ bởi mysql, PID 14815.

Nhân của bạn phải được biên dịch KCORE_ELFđể sử dụng /proc/kcore. Ngoài ra, bạn cần một phiên bản của hình ảnh hạt nhân của bạn với các biểu tượng gỡ lỗi. Trên Debian 7, apt-get install linux-image-3.2.0-4-amd64-dbgsẽ cung cấp tệp này.

Không cần hình ảnh kernel có thể sửa lỗi ...

Nếu bạn không có (hoặc không muốn giữ) hình ảnh hạt nhân gỡ lỗi trên hệ thống, bạn có thể cung cấp gdbbù cho bộ nhớ để "truy cập" thủ công peergiá trị. Giá trị offset này thường khác với phiên bản kernel hoặc kiến ​​trúc.

Trên kernel của tôi, tôi biết phần bù là 680 byte, tức là gấp 85 lần 64 bit. Vì vậy, tôi có thể làm:

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

Voilà, kết quả tương tự như trên.

Nếu bạn có cùng một kernel chạy trên một số máy, việc sử dụng biến thể này sẽ dễ dàng hơn vì bạn không cần hình ảnh gỡ lỗi, chỉ có giá trị offset.

Để (dễ dàng) khám phá giá trị bù này lúc đầu, bạn cần hình ảnh gỡ lỗi:

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

Ở đây bạn đi, 680 byte, đây là 85 x 64 bit, hoặc 170 x 32 bit.

Hầu hết tín dụng cho câu trả lời này được chuyển đến MvG .


2
Một cách tiếp cận khác để lấy phần bù có thể là tạo một ổ cắm, xác định các mục tương ứng trong / Proc / net / unix dựa trên số inode từ các liên kết đọc trên / Proc / pif / fd / * và quét bộ nhớ xung quanh địa chỉ của một ổ cắm cho địa chỉ của người kia. Điều đó có thể làm cho một thiết bị di động hợp lý (trên các phiên bản và kiến ​​trúc Linux) có thể được thực hiện bởi chính lsof. Tôi sẽ cố gắng đưa ra PoC.
Stéphane Chazelas

2
Bây giờ tôi đã thêm một PoC như vậy có vẻ hoạt động tốt trên các hệ thống tôi đã thử nghiệm.
Stéphane Chazelas 17/03/2015

5

Giải pháp này, mặc dù đang hoạt động, nhưng được quan tâm hạn chế vì nếu bạn có một systemtap đủ gần đây, rất có thể bạn sẽ có một kernel đủ gần đây, nơi bạn có thể sử dụng các cách tiếp cận ssdựa trên , và nếu bạn sử dụng kernel cũ hơn, thì khác giải pháp , mặc dù nhiều hacky có nhiều khả năng hoạt động hơn và không yêu cầu phần mềm bổ sung.

Vẫn hữu ích như một minh họa về cách sử dụng systemtapcho loại nhiệm vụ này.

Nếu trên một hệ thống Linux gần đây có hệ thống hoạt động (1.8 hoặc mới hơn), bạn có thể sử dụng tập lệnh bên dưới để xử lý hậu kỳ đầu ra của lsof:

Ví dụ:

$ lsof -aUc nm-applet | sudo đó-script
NGƯỜI DÙNG ĐIỀU KHIỂN NGƯỜI DÙNG FD LOẠI THIẾT BỊ THIẾT BỊ / TẮT NODE
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 11u stephane 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]

(nếu bạn thấy 0x0000000000000000 ở trên thay vì 0xffff ..., thì đó là vì kernel.kptr_restricttham số sysctl được đặt trên hệ thống của bạn khiến cho các con trỏ kernel bị ẩn khỏi các tiến trình không đặc quyền, trong trường hợp đó bạn sẽ cần chạy lsofnhư root để lấy kết quả có ý nghĩa).

Kịch bản lệnh này không thực hiện bất kỳ nỗ lực nào để đối phó với tên tệp ổ cắm với các ký tự dòng mới, nhưng cũng không lsof(cũng không lsofđối phó với khoảng trắng hoặc dấu hai chấm).

systemtapở đây được sử dụng để kết xuất địa chỉ và địa chỉ ngang hàng của tất cả các unix_sockcấu trúc trong unix_socket_tablehàm băm trong kernel.

Chỉ được thử nghiệm trên Linux 3.16 amd64 với systemtap 2.6 và 3.13 với 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: tui bỏ lỡ điều gì vậy?
Totor 13/03/2015

@Totor, @varđã được thêm vào trong systemtap 1.8, 2012-06-17 (mới nhất là 2.7)
Stéphane Chazelas

2

4,89 của lsof hỗ trợ hiển thị các tùy chọn điểm cuối.

Trích dẫn từ 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.

Ví dụ về đầu ra:

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

Do Linux kernel 4.2 tồn tại CONFIG_UNIX_DIAG, cung cấp thêm thông tin về các socket miền UNIX, cụ thể là Virtual File Systemthông tin (VFS), chứa thông tin còn thiếu cho đến nay để liên kết Inode từ đường dẫn đến tiến trình. Nó có thể được truy vấn bằng cách sử dụng sscông cụ từ iproute2 bắt đầu với phiên bản 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:

Số thiết bị và đường dẫn Inode bạn có thể nhận được từ

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

ss cũng hỗ trợ lọc:

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

nhưng xin lưu ý rằng điều này có thể không liệt kê đúng ổ cắm cho bạn, vì một quá trình tà ác có thể đổi tên ổ cắm ban đầu của bạn và thay thế nó bằng ổ cắm ác của chính nó:

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

lsof /tmp/socket, fuser /tmp/socketss --processes --unix --all --extended 'sport = /tmp/socket'tất cả sẽ liệt kê các quá trình ban đầu, không thay thế ác. Thay vào đó hãy sử dụng một cái gì đó như thế này:

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

Hoặc viết chương trình litte của riêng bạn dựa trên mẫu có trong man 7 sock_diag .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.