Как преобразовать идентификатор окна X11 в идентификатор процесса - PullRequest
9 голосов
/ 15 июля 2009

Я работаю над небольшим приложением, и мне нужно найти PID процесса, используя идентификатор окна X11 его главного окна или дочерних окон. Я видел примеры такого преобразования с использованием _NET_WM_PID, но я не могу понять, как это сделать, не используя его. Причина неиспользования _NET_WM_PID заключается в том, что он не реализован во всех доступных оконных менеджерах, и мое приложение должно работать с любым из них (или, по крайней мере, с большинством из них). Может ли кто-нибудь помочь мне, пожалуйста, и дать мне несколько советов / указаний о том, как решить эту проблему? Спасибо!

Ответы [ 3 ]

7 голосов
/ 31 июля 2013

Если ваш X-сервер не поддерживает XResQueryClientIds из X-Resource v1.2 extension Я не знаю легкий способ надежно идентификатор процесса запроса. Однако есть и другие способы.

Если у вас просто есть окно перед вами и вы еще не знаете его идентификатор - это легко узнать. Просто откройте терминал рядом с рассматриваемым окном, запустите xwininfo и нажмите на это окно. xwininfo покажет вам идентификатор окна.

Итак, давайте предположим, что вы знаете идентификатор окна, например, 0x1600045, и хотите узнать, каков процесс, которым он принадлежит.

Самый простой способ проверить, кому принадлежит это окно, - запустить для него XKillClient, т. Е .:

xkill -id 0x1600045

и посмотрите, какой процесс только что умер. Конечно, если ты не против умереть.

Другой простой, но ненадежный способ - проверить его свойства _NET_WM_PID и WM_CLIENT_MACHINE:

xprop -id 0x1600045

Вот что делают такие инструменты, как xlsclients и xrestop do.

К сожалению, эта информация может быть неверной не только потому, что процесс был злым и изменил его, но также и потому, что он глючил. Например, после некоторого сбоя / перезапуска Firefox я видел осиротевшие окна (я полагаю, из плагина flash) с _NET_WM_PID, указывающим на процесс, который давно умер.

Альтернативный способ - запустить

xwininfo -root -tree

и проверьте свойства родителей рассматриваемого окна. Это также может дать вам некоторые подсказки о происхождении окна.

Но! Хотя вы можете не узнать, какой процесс создал это окно, все же есть способ узнать, откуда этот процесс подключился к X-серверу. И этот путь для настоящих хакеров. :)

Идентификатор окна 0x1600045, который вы знаете с нулевыми младшими битами (т. Е. 0x1600000), является «клиентской базой». И все идентификаторы ресурсов, выделенные для этого клиента, «основаны» на нем (0x1600001, 0x1600002, 0x1600003 и т. Д.). X-сервер хранит информацию о своих клиентах в массиве clients [], а для каждого клиента его «база» хранится в переменной clients [i] -> clientAsMask. Чтобы найти X-сокет, соответствующий этому клиенту, вам нужно подключиться к X-серверу с gdb, пройтись по массиву clients [], найти клиента с этим clientAsMask и распечатать его дескриптор сокета, сохраненный в ((OsCommPtr) (клиенты [я] -> osPrivate)) -.> * FD 1044 *

Возможно, подключено много X-клиентов, поэтому, чтобы не проверять их все вручную, давайте воспользуемся функцией gdb:

define findclient
  set $ii = 0
  while ($ii < currentMaxClients)
    if (clients[$ii] != 0 && clients[$ii]->clientAsMask == $arg0 && clients[$ii]->osPrivate != 0)
      print ((OsCommPtr)(clients[$ii]->osPrivate))->fd
    end
    set $ii = $ii + 1
  end
end

Найдя сокет, вы можете проверить, кто к нему подключен, и, наконец, найти процесс.

ПРЕДУПРЕЖДЕНИЕ : НЕ подключайте GDB к X-серверу изнутри X-сервера. GDB приостанавливает процесс, к которому он присоединяется, поэтому, если вы подключитесь к нему изнутри X-сессии, вы заморозите свой X-сервер и не сможете взаимодействовать с GDB. Вы должны либо переключиться на текстовый терминал (Ctrl+Alt+F2), либо подключиться к своей машине через ssh.

* * Пример одна тысяча пятьдесят пять: * * одна тысяча пятьдесят-шесть
  1. Найдите PID вашего X-сервера:

    $ ps ax | grep X
     1237 tty1     Ssl+  11:36 /usr/bin/X :0 vt1 -nr -nolisten tcp -auth /var/run/kdm/A:0-h6syCa
    
  2. Идентификатор окна - 0x1600045, поэтому клиентская база - 0x1600000. Подключитесь к X-серверу и найдите дескриптор сокета клиента для этой клиентской базы. Вам понадобится отладочная информация установлен для X-сервера (пакет -debuginfo для rpm-дистрибутивов или пакет -dbg для deb).

    $ sudo gdb
    (gdb) define findclient
    Type commands for definition of "findclient".
    End with a line saying just "end".
    >  set $ii = 0
    >  while ($ii < currentMaxClients)
     >   if (clients[$ii] != 0 && clients[$ii]->clientAsMask == $arg0 && clients[$ii]->osPrivate != 0)
      >     print ((OsCommPtr)(clients[$ii]->osPrivate))->fd
      >     end
     >   set $ii = $ii + 1
     >   end
    >  end
    (gdb) attach 1237
    (gdb) findclient 0x1600000
    $1 = 31
    (gdb) detach
    (gdb) quit
    
  3. Теперь вы знаете, что клиент подключен к серверному сокету 31. Используйте lsof, чтобы найти, что это за сокет:

    $ sudo lsof -n | grep 1237 | grep 31
    X        1237    root   31u   unix 0xffff810008339340       8512422 socket
    

    (здесь «X» - это имя процесса, «1237» - его pid, «root» - пользователь, от которого он запускается, «31u» - дескриптор сокета)

    Там вы можете увидеть, что клиент подключен по TCP, затем вы можете перейти на компьютер, к которому он подключен, и проверить netstat -nap там, чтобы найти процесс. Но, скорее всего, вы увидите там сокет Unix, как показано выше, что означает, что это локальный клиент.

  4. Чтобы найти пару для этого сокета Unix, вы можете использовать метод MvG (вам также понадобится отладочная информация для вашего установленного ядра):

    $ sudo gdb -c /proc/kcore
    (gdb) print ((struct unix_sock*)0xffff810008339340)->peer
    $1 = (struct sock *) 0xffff810008339600
    (gdb) quit
    
  5. Теперь, когда вы знаете клиентский сокет, используйте lsof, чтобы найти PID, удерживающий его:

    $ sudo lsof -n | grep 0xffff810008339600
    firefox  7725  username  146u   unix 0xffff810008339600       8512421 socket
    

Вот и все. Процесс с этим окном называется «firefox» с идентификатором процесса 7725

6 голосов
/ 15 июля 2009

Как правило, невозможно определить PID процесса, который создал окно. Может случиться так, что процесс выполняется на машине удаленно, и, возможно, машина даже не имеет представления о процессах и PID.

Если вы не доверяете никому, кто сохранял эту информацию при первоначальном создании клиента, вам нужно будет самостоятельно отслеживать соединения. Узнайте, какое соединение (сокет и т. Д.) Использовал клиент, выясните, где заканчивается это соединение, и выясните, какой процесс удерживает этот конец. Как это сделать (и возможно ли это вообще) сильно зависит от операционной системы.

1 голос
/ 21 ноября 2012

Еще в 2004 году Харальд Велте опубликовал фрагмент кода, который оборачивает вызов XCreateWindow () через LD_PRELOAD и сохраняет идентификатор процесса в _NET_WM_PID. Это гарантирует, что каждое созданное окно имеет полезную запись _NET_WM_PID.

http://www.mail-archive.com/devel@xfree86.org/msg05806.html

...