หมายเหตุ : ตอนนี้ฉันมีlsof
wrapper ที่รวมทั้งสองวิธีที่อธิบายไว้ที่นี่และยังเพิ่มข้อมูลสำหรับการเชื่อมต่อ TCP แบบวนกลับที่https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc
Linux-3.3 ขึ้นไป
บน Linux เนื่องจากเคอร์เนลเวอร์ชัน 3.3 (และจัดเตรียมUNIX_DIAG
คุณลักษณะถูกสร้างขึ้นในเคอร์เนล) เพียร์ของซ็อกเก็ตโดเมน unix ที่กำหนด (รวมถึงซ็อกเก็ตคู่) สามารถรับได้โดยใช้netlink API ใหม่
lsof
เนื่องจากเวอร์ชัน 4.89 สามารถใช้ประโยชน์ API ดังกล่าวได้:
lsof +E -aUc Xorg
จะแสดงรายการซ็อกเก็ตโดเมน Unix ทั้งหมดที่มีกระบวนการที่ชื่อขึ้นต้นด้วยXorg
ปลายทั้งสองในรูปแบบที่คล้ายกับ:
Xorg 2777 root 56u unix 0xffff8802419a7c00 0t0 34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
หากเวอร์ชั่นของlsof
คุณเก่าเกินไปจะมีตัวเลือกเพิ่มอีกสองสามตัว
ss
ยูทิลิตี้ (จากiproute2
) ทำให้การใช้ API เดียวกันกับที่จะดึงและแสดงข้อมูลในรายชื่อของซ็อกเก็ตโดเมนยูนิกซ์ในระบบรวมทั้งข้อมูลที่เพียร์
ซ็อกเก็ตมีการระบุโดยพวกเขาจำนวน inode โปรดทราบว่ามันไม่เกี่ยวข้องกับระบบไฟล์ inode ของไฟล์ซ็อกเก็ต
ตัวอย่างเช่นใน:
$ ss -x
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996
มันบอกว่าซ็อกเก็ต 3435997 (ที่ถูกผูกไว้กับซ็อกเก็ต ABSTRACT /tmp/.X11-unix/X0
) เชื่อมต่อกับซ็อกเก็ต 3435996 -p
ตัวเลือกสามารถบอกคุณว่ากระบวนการใดมีซ็อกเก็ตเปิดอยู่ มันไม่ได้ว่าการดำเนินการบางอย่างreadlink
บน/proc/$pid/fd/*
ดังนั้นจึงสามารถทำอย่างนั้นกับกระบวนการคุณเป็นเจ้าของ (เว้นแต่คุณroot
) เช่นที่นี่:
$ 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]
หากต้องการค้นหาว่ากระบวนการใดมี 3435996 คุณสามารถค้นหารายการของตนเองในผลลัพธ์ของss -xp
:
$ ss -xp | awk '$6 == 3435996'
u_str ESTAB 0 0 * 3435996 * 3435997 users:(("xterm",pid=29215,fd=3))
คุณสามารถใช้สคริปต์นี้เป็น wrapper lsof
เพื่อแสดงข้อมูลที่เกี่ยวข้องได้อย่างง่ายดายที่นั่น:
#! /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);
ตัวอย่างเช่น:
$ sudo that-lsof-wrapper -ad3 -p 29215
คำสั่ง PID USER FD ประเภทอุปกรณ์ขนาด / ปิดชื่อโหนด
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 ประเภท = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]
ก่อน linux-3.3
Linux API เก่าเพื่อดึงข้อมูลซ็อกเก็ตยูนิกซ์คือผ่าน/proc/net/unix
ไฟล์ข้อความ มันแสดงรายการซ็อกเก็ตโดเมน Unix ทั้งหมด (รวมถึงซ็อกเก็ตคู่) ฟิลด์แรกที่อยู่ในนั้น (หากไม่ได้ซ่อนไว้กับผู้ที่ไม่ใช่ผู้ใช้ระดับสูงด้วยkernel.kptr_restrict
พารามิเตอร์ sysctl) ดังที่อธิบายโดย @Totor แล้วมีเคอร์เนลที่อยู่ของunix_sock
โครงสร้างที่มีpeer
ฟิลด์ที่ชี้ไปยังเพียร์ที่ unix_sock
เกี่ยวข้อง นอกจากนี้ยังเป็นสิ่งที่lsof
ส่งออกสำหรับDEVICE
คอลัมน์ในซ็อกเก็ต Unix
ตอนนี้การรับค่าของpeer
ฟิลด์นั้นหมายถึงความสามารถในการอ่านหน่วยความจำเคอร์เนลและรู้ค่าออฟเซ็ตของpeer
ฟิลด์นั้นโดยคำนึงถึงที่unix_sock
อยู่
หลายgdb
ชั่นและsystemtap
-basedโซลูชั่นได้รับแล้วรับ แต่พวกเขาจำเป็นต้องมีgdb
/ systemtap
และลินุกซ์เคอร์เนลสัญลักษณ์การแก้ปัญหาสำหรับเคอร์เนลทำงานถูกติดตั้งซึ่งโดยทั่วไปไม่ได้กรณีที่เกี่ยวกับระบบการผลิต
ฮาร์ดโค้ดที่ออฟเซตนั้นไม่ใช่ตัวเลือกที่ต่างกันไปตามรุ่นเคอร์เนล
ตอนนี้เราสามารถใช้วิธีแก้ปัญหาแบบฮิวริสติกเพื่อกำหนดออฟเซ็ต: ให้เครื่องมือของเราสร้างดัมมี่socketpair
(จากนั้นเรารู้ที่อยู่ของเพื่อนทั้งคู่) และค้นหาที่อยู่ของเพียร์รอบหน่วยความจำที่ปลายอีกด้าน
นี่คือบทพิสูจน์แนวคิดของสคริปต์ที่ใช้perl
(ทดสอบกับเคอร์เนล 2.4.27 และ 2.6.32 บน i386 และ 3.13 และ 3.16 ใน amd64) เหมือนด้านบนมันทำงานเป็น wrapper รอบ ๆlsof
:
ตัวอย่างเช่น:
$ that-lsof-wrapper -aUc nm-applet
คำสั่ง PID USER FD ประเภทอุปกรณ์ขนาด / ปิดชื่อโหนด
นาโนเมตรแอปเพล็ 4183 Stephane 4u ยูนิกซ์ 0xffff8800a055eb40 0t0 36,888 type = STREAM -> 0xffff8800a055e7c0 [dbus-ภูต, 4190 @ / tmp / dbus-AiBCXOnuP6]
นาโนเมตรแอปเพล็ 4183 Stephane 7U ยูนิกซ์ 0xffff8800a055e440 0t0 36,890 type = STREAM -> 0xffff8800a055e0c0 [Xorg, 3080 @ / tmp / .X11-Unix / X 0]
นาโนเมตรแอปเพล็ 4183 8U Stephane ยูนิกซ์ 0xffff8800a05c1040 0t0 36201 type = STREAM -> 0xffff8800a05c13c0 [dbus-ภูต, 4118, @ / tmp / dbus-yxxNr1NkYC]
นาโนเมตรแอปเพล็ 4183 11u Stephane ยูนิกซ์ 0xffff8800a055d080 0t0 36,219 type = STREAM -> 0xffff8800a055d400 [dbus-ภูต, 4118, @ / tmp / dbus-yxxNr1NkYC]
นาโนเมตรแอปเพล็ 4183 Stephane 12U ยูนิกซ์ 0xffff88022e0dfb80 0t0 36,221 type = STREAM -> 0xffff88022e0df800 [dbus-ภูต, 2268, / var / ทำงาน / dbus / system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 ประเภท = STREAM -> 0xffff88022e29ec00 [dbus-daemon, 2268, / var / run / dbus / system_bus_socket]
นี่คือสคริปต์:
#! /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);