LINUX.ORG.RU

Получить WID из PID

 , window id, ,


3

3

Запускаю через скрипт приложение

  • (appname 2>&1 > /dev/null) & echo $!
  • Читаю результат $! получив PID отпочковавшейся дочки
  • Запускаю wmctrl -lp ищу в строке PID и забираю соответствующий WID
  • Всё, я довольный, могу через WID манипулировать окном и пришибать процесс через PID точно зная что есть активная пара PID/WID и я не пришибу случайно что-то иное.

Всё работает. Но с некоторыми приложениями например glxgears беда. PID я его получаю, а вот WID найти не могу, да я могу wmctrl без параметров запустить, мышкой кликнуть на окошечко и получить WID, но это не то, не получается зная лишь PID получить WID. С большинством приложений проблем нет, а тут уже не знаю где искать. Сейчас ситуация такая что я запускаю приложеньку, жду несколько секунд пытаясь получить WID окна, если не получилось, грохаю процесс, ну и типа фиг с ним, не судьба :(

Не сообщает _NET_WM_PID оно, жопка такая. Выхода нет? Ключ поверни и по-ле-те-ли И однозначно не определить?

Варианты поиска по заголовкам окон и по содержанию командной строки не прокатят, заголовка может не быть вовсе и могут быть много окон с пустыми заголовками. Блин :(

Решение:

★★★★★

Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)

Я недавно на эту тему что-то создавал, и потом ещё была тема чья-то - прям с точно твоим вопросом.

Нет, нельзя. Заполнение pid в метаданных окна это целиком инициатива приложения, это окно создавшего. В тулкитах там наверно это автоматически реализовано, но никто не мешает это поле не заполнять или вписать туда любые фейковые данные. У xorg есть теоретическая возможность узнать pid инициатора подключения, если он через unix сокет (getsockopt какой-то вроде), но он её не использует (а так же надо понимать что pid инициатиора подключения может отличаться от pid настоящего его пользователя, инициатор подключения может форкнуться потом или послать свой x11-сокет кому-то другому, а сам вообще умереть), да и в x11-протоколе такое всё равно не предусмотрено.

С pid-ом из метаданных окна ещё одна проблема: даже если оно заполнено и процесс не злонамеренный и заполнил его честно - может оказаться что он на другом компе через ssh-туннель и pid опять окажется не тот.

Возможный костыль, который я придумал для своего недоделанного DE, но не реализовал и вообще не уверен что так делать хорошо: каждое приложение, запущеное «официально» через гуи, запускается в прозрачном контейнере (единственная функция которого - прицепить ему некоторый id, от которого оно не сможет избавиться форками), и ему сообщается уникальное XAUTHORITY и/или DISPLAY, на котором сидит x11-прокси и сортирует клиентов (смотрит по каким реквизитам они подключились). После этого у любого окна будет соответствие к id контейнера, через реквизиты которого оно создано. Впрочем, от опасности того что прога поделится с кем-то своим открытым сокетом или реквизитами, это не избавляет.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 4)
Ответ на: комментарий от firkax

Да, у меня и на счёт вывода wmctrl -lp сомнения были. Мдяяя… Ну ладно. Есть ещё ультракостыльный вариант в виде взятия последнего значения в выхлопе wmctrl -lp у которого PID равен 0 а WID в выхлопе «за секунду до» ещё не было. Технически это нужное окно и есть. Оно убого конечно, но работает. Раз прямого выхода нет то остановлюсь на этом, уродском, костыльном, убогом, но хоть как-то работающем варианте. Ну и опцию в конфиг добавлю дабы этот костыль отключать если возникнут проблемы с пришибанием не тех окон. Пасиба.

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)

Можно через расширение XResQueryClientIds получать соответствие: (см. https://lists.x.org/archives/xorg-devel/2010-December/016486.html)

#include <X11/Xlib.h>
#include <X11/extensions/XRes.h>

#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>

pid_t get_pid(Display *display, XID client) {
    XResClientIdSpec client_spec = {
        .mask = XRES_CLIENT_ID_PID_MASK,
        .client = client
    };

    long num_ids = 0;
    XResClientIdValue *client_ids = NULL;
    XResQueryClientIds(display, 1, &client_spec, &num_ids, &client_ids);
    
    pid_t pid = 0;
    for (long i = 0; i < num_ids; i++) {
        XID xid = client_ids[i].spec.client;
        if (client_ids[i].spec.mask & XRES_CLIENT_ID_PID_MASK) {
            pid = XResGetClientPid(&client_ids[i]);
            break;
        }
    }

    XResClientIdsDestroy(num_ids, client_ids);
    return pid;
}

void get_windows(Display *display, Window root) {
    Window parent;
    Window *children;
    unsigned int count;
    int ret = XQueryTree(display, root, &root, &parent, &children, &count);
    if (!ret) return;
    for(int i = 0; i < count; ++i) {
        Window ch = children[i];
        pid_t pid = get_pid(display, ch);
        if (pid) {
            printf("WID: 0x%x\tPID: %d\n", ch, pid);
        }
        get_windows(display, ch);
    }
}

int main() {
    Display *display = XOpenDisplay(NULL);
    if (!display) return 1;

    Window root = DefaultRootWindow(display);
    get_windows(display, root);
    XCloseDisplay(display);

    return 0;
}

}

Пример вывода:

WID: 0x20000c   PID: 6011
WID: 0x20000d   PID: 6011
WID: 0xe00007   PID: 6545
WID: 0x201d88   PID: 6011
WID: 0x201d89   PID: 6011
WID: 0x1800002  PID: 2439936
WID: 0x201d66   PID: 6011
WID: 0x201d67   PID: 6011
WID: 0x1400043  PID: 1988739
WID: 0x1400046  PID: 1988739
WID: 0x1400049  PID: 1988739
WID: 0x200001   PID: 6011
WID: 0x200000   PID: 6011
WID: 0x200002   PID: 6011
WID: 0x200003   PID: 6011
WID: 0x200004   PID: 6011
WID: 0x200005   PID: 6011
WID: 0x200006   PID: 6011
WID: 0x400005   PID: 6160
WID: 0x40000c   PID: 6160
WID: 0x800005   PID: 6279
WID: 0xa00005   PID: 6283
WID: 0xa00006   PID: 6283
WID: 0xc00001   PID: 6357
WID: 0xe00005   PID: 6545
WID: 0x1000005  PID: 6570
WID: 0xe00009   PID: 6545
WID: 0x100000c  PID: 6570
WID: 0xe0000b   PID: 6545
WID: 0x20000f   PID: 6011
WID: 0x2200001  PID: 530628
WID: 0x1600001  PID: 1988739
WID: 0x1600003  PID: 1988739
WID: 0x1600004  PID: 1988739
WID: 0x1600007  PID: 1988739
WID: 0x1600008  PID: 1988739
WID: 0x1600010  PID: 1988739
WID: 0x1600011  PID: 1988739
WID: 0x1600014  PID: 1988739
WID: 0x1600015  PID: 1988739
WID: 0x1400027  PID: 1988739

С glxgears естественно тоже работает.

Ja-Ja-Hey-Ho ★★★★★
()
Ответ на: комментарий от Ja-Ja-Hey-Ho

Тут только ещё нет проверки, что X11-сервер поддерживает это расширение и возможно ещё где-то коды возврата не проверяются.

Впрочем это есть в примере из рассылки.

Ja-Ja-Hey-Ho ★★★★★
()
Ответ на: комментарий от Ja-Ja-Hey-Ho

Большое спасибо, тогда наверное придётся Lua скрипт на С целиком переписывать, или через FFI LuaJit подёргать, хотя не хочется привязываться чисто к LuaJit, позадумке должно было работать на любой Lua… Или микроутилитку рядом поставлять. Но это ладно.

Только непонятна лицензия на этот код. Я всё обычно сам пишу и под Zlib выкладываю, попробую написать Эрику и Рами, хотя лучше просто пойдё гляну на доки и воспроизведу тоже самое, но уже сам, так душешьке будет спокойнее :).

Ещё раз спасибо, по сути можно в несколько этапов пробовать задетектить, сначала через wmctrl который есть везде, если не получится то через вот это, а если не получится потому что не поддерживается то через хак с нулевым пидом что выше, а если и там не прокатит что к моменту проверки появилось более одного окна, то тогда отказ в обслуживании.

Ещё раз большое спасибо. Полезно.

P.S. Дабы другим искать было легче

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)
Ответ на: комментарий от Ja-Ja-Hey-Ho

Тогда BSD получается, спасибо за уточнение =) Если возьму как есть с доработкой, то подпишу типа так

Window Detection source code based on gamescope source code

Copyright (c) 2013-2022, Valve Corporation
Copyright (c) 2022, NVIDIA CORPORATION
Copyright (c) 2024, Ja-Ja-Hey-Ho
Copyright (c) 2024, LINUX-ORG-RU

ЛИЦЕНЗИЯ БЗДА 

=) И норм, но сначала покумекаю сам, как минимум это интересно… ну когда время будет. А если не будет, а ехать надо то возьму как есть лишь добавив проверку на наличие расширения, в любом случае ещё раз спасибо.

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от Ja-Ja-Hey-Ho

Ты из gamescope ничего не копировал, а сам всё написал, я проверил, поэтому никакого BSD. Если ты будешь не против, я твой и свой копирайт в Zlib лицензию включу. Если против, назови лицензию и как там тебя подписать. Я всё равно отдельную утилиту буду оформлять наверное и вот туда надо что-то явно вписать по поводу копирайта.

LINUX-ORG-RU ★★★★★
() автор топика