Как определить запуск программ в Linux?

#c #linux #pid

#c #linux #pid

Вопрос:

Я написал простой демон. Этот демон должен реагировать, когда я запускаю любую программу. Как это сделать? В большом цикле демона:

 while(1)
{
   /* function which catches new programm running */
}
  

Какие функции вызывать в Linux, когда я запускаю новую программу (создаю новый процесс)?

Комментарии:

1. Я думаю, нам понадобится немного больше информации

2. @Adam Batkin — Я не согласен, вопрос достаточно сформулирован, участник хочет быть проинформирован о создании процесса

3. Этот демон запускает firefox или firefox запускается пользователем извне, и вы хотите получать уведомления? Ваш вопрос не помешало бы немного прояснить.

4. @Chris: Это, безусловно, правильная интерпретация. Не стесняйтесь редактировать вопрос, чтобы сделать его более понятным

5. Демон аудита может регистрировать запущенные программы, отслеживая execve системный вызов. Может быть, вы сможете вдохновиться этим.

Ответ №1:

Для Linux, похоже, есть интерфейс в ядре. Исследуя эту проблему, я наткнулся на людей, использующих конфигурацию ядра CONFIG_CONNECTOR и CONFIG_PROC_EVENTS для получения событий о завершении процесса.

Еще немного погуглил, и я нашел это:

http://netsplit.com/2011/02/09/the-proc-connector-and-socket-filters/

Фильтры Proc Connector и Socket, опубликованные 9 февраля 2011 года Скоттом

Соединитель proc является одной из тех интересных функций ядра, с которыми большинство людей редко сталкиваются, и еще реже находят документацию по ним. Аналогично фильтру сокетов. Это позор, потому что они оба действительно довольно полезные интерфейсы, которые могли бы служить различным целям, если бы они были лучше документированы.

Соединитель proc позволяет получать уведомления о событиях процесса, таких как вызовы fork и exec, а также об изменениях uid, gid или sid процесса (идентификатор сеанса). Они предоставляются через интерфейс на основе сокетов путем чтения экземпляров struct proc_event, определенных в заголовке ядра….

Интересующий заголовок:

 #include <linux/cn_proc.h>
  

Я нашел пример кода здесь:

http://bewareofgeek.livejournal.com/2945.html

 /* This file is licensed under the GPL v2 (http://www.gnu.org/licenses/gpl2.txt) (some parts was originally borrowed from proc events example)

pmon.c

code highlighted with GNU source-highlight 3.1
*/

#define _XOPEN_SOURCE 700
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>
#include <signal.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/*
* connect to netlink
* returns netlink socket, or -1 on error
*/
static int nl_connect()
{
int rc;
int nl_sock;
struct sockaddr_nl sa_nl;

nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (nl_sock == -1) {
    perror("socket");
    return -1;
}

sa_nl.nl_family = AF_NETLINK;
sa_nl.nl_groups = CN_IDX_PROC;
sa_nl.nl_pid = getpid();

rc = bind(nl_sock, (struct sockaddr *)amp;sa_nl, sizeof(sa_nl));
if (rc == -1) {
    perror("bind");
    close(nl_sock);
    return -1;
}

return nl_sock;
}

/*
* subscribe on proc events (process notifications)
*/
static int set_proc_ev_listen(int nl_sock, bool enable)
{
int rc;
struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
    struct nlmsghdr nl_hdr;
    struct __attribute__ ((__packed__)) {
    struct cn_msg cn_msg;
    enum proc_cn_mcast_op cn_mcast;
    };
} nlcn_msg;

memset(amp;nlcn_msg, 0, sizeof(nlcn_msg));
nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg);
nlcn_msg.nl_hdr.nlmsg_pid = getpid();
nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE;

nlcn_msg.cn_msg.id.idx = CN_IDX_PROC;
nlcn_msg.cn_msg.id.val = CN_VAL_PROC;
nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);

nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE;

rc = send(nl_sock, amp;nlcn_msg, sizeof(nlcn_msg), 0);
if (rc == -1) {
    perror("netlink send");
    return -1;
}

return 0;
}

/*
* handle a single process event
*/
static volatile bool need_exit = false;
static int handle_proc_ev(int nl_sock)
{
int rc;
struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
    struct nlmsghdr nl_hdr;
    struct __attribute__ ((__packed__)) {
    struct cn_msg cn_msg;
    struct proc_event proc_ev;
    };
} nlcn_msg;

while (!need_exit) {
    rc = recv(nl_sock, amp;nlcn_msg, sizeof(nlcn_msg), 0);
    if (rc == 0) {
    /* shutdown? */
    return 0;
    } else if (rc == -1) {
    if (errno == EINTR) continue;
    perror("netlink recv");
    return -1;
    }
    switch (nlcn_msg.proc_ev.what) {
    case PROC_EVENT_NONE:
        printf("set mcast listen okn");
        break;
    case PROC_EVENT_FORK:
        printf("fork: parent tid=%d pid=%d -> child tid=%d pid=%dn",
            nlcn_msg.proc_ev.event_data.fork.parent_pid,
            nlcn_msg.proc_ev.event_data.fork.parent_tgid,
            nlcn_msg.proc_ev.event_data.fork.child_pid,
            nlcn_msg.proc_ev.event_data.fork.child_tgid);
        break;
    case PROC_EVENT_EXEC:
        printf("exec: tid=%d pid=%dn",
            nlcn_msg.proc_ev.event_data.exec.process_pid,
            nlcn_msg.proc_ev.event_data.exec.process_tgid);
        break;
    case PROC_EVENT_UID:
        printf("uid change: tid=%d pid=%d from %d to %dn",
            nlcn_msg.proc_ev.event_data.id.process_pid,
            nlcn_msg.proc_ev.event_data.id.process_tgid,
            nlcn_msg.proc_ev.event_data.id.r.ruid,
            nlcn_msg.proc_ev.event_data.id.e.euid);
        break;
    case PROC_EVENT_GID:
        printf("gid change: tid=%d pid=%d from %d to %dn",
            nlcn_msg.proc_ev.event_data.id.process_pid,
            nlcn_msg.proc_ev.event_data.id.process_tgid,
            nlcn_msg.proc_ev.event_data.id.r.rgid,
            nlcn_msg.proc_ev.event_data.id.e.egid);
        break;
    case PROC_EVENT_EXIT:
        printf("exit: tid=%d pid=%d exit_code=%dn",
            nlcn_msg.proc_ev.event_data.exit.process_pid,
            nlcn_msg.proc_ev.event_data.exit.process_tgid,
            nlcn_msg.proc_ev.event_data.exit.exit_code);
        break;
    default:
        printf("unhandled proc eventn");
        break;
    }
}

return 0;
}

static void on_sigint(int unused)
{
need_exit = true;
}

int main(int argc, const char *argv[])
{
int nl_sock;
int rc = EXIT_SUCCESS;

signal(SIGINT, amp;on_sigint);
siginterrupt(SIGINT, true);

nl_sock = nl_connect();
if (nl_sock == -1)
    exit(EXIT_FAILURE);

rc = set_proc_ev_listen(nl_sock, true);
if (rc == -1) {
    rc = EXIT_FAILURE;
    goto out;
}

rc = handle_proc_ev(nl_sock);
if (rc == -1) {
    rc = EXIT_FAILURE;
    goto out;
}

    set_proc_ev_listen(nl_sock, false);

out:
close(nl_sock);
exit(rc);
}
  

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

Комментарии:

1. Великолепный — помогает создать решение для superuser.com/questions/354150 /… 😉

2. Это прекрасно, я уже начал думать, что мне придется погрузиться в код ядра…

3. Обратите внимание, что при желании вы можете использовать pid созданных процессов для чтения дополнительной информации о процессе из каталога /proc/<pid>. Я использовал это для извлечения информации из командной строки, чтобы также отобразить имя процесса при создании.

Ответ №2:

Мне было интересно попытаться выяснить, как это сделать без опроса. inotify (), похоже, не работает в / proc, так что эта идея отпадает.

Однако любая динамически связанная программа будет получать доступ к определенным файлам при запуске, таким как динамический компоновщик. Это было бы бесполезно в целях безопасности, поскольку не сработает в статически связанной программе, но все равно может представлять интерес:

 #include <stdio.h>
#include <sys/inotify.h>
#include <assert.h>
int main(int argc, char **argv) {
    char buf[256];
    struct inotify_event *event;
    int fd, wd;
    fd=inotify_init();
    assert(fd > -1);
    assert((wd=inotify_add_watch(fd, "/lib/ld-linux.so.2", IN_OPEN)) > 0);
    printf("Watching for events, wd is %xn", wd);
    while (read(fd, buf, sizeof(buf))) {
      event = (void *) buf;
      printf("watch %d mask %x name(len %d)="%s"n",
         event->wd, event->mask, event->len, event->name);
    }
    inotify_rm_watch(fd, wd);
    return 0;
}
  

События, которые это выводит, не содержат никакой интересной информации — pid процесса запуска, похоже, не предоставляется inotify. Однако его можно использовать для пробуждения и запуска повторного сканирования /proc

Также имейте в виду, что недолговечные программы могут снова исчезнуть до того, как эта штука проснется и завершит сканирование / proc — предположительно, вы узнаете, что они существовали, но не сможете узнать, что это было. И, конечно, любой может просто продолжать открывать и закрывать fd для dyanmic linker, чтобы заглушить вас шумом.

Ответ №3:

Используйте forkstat , это наиболее совершенный клиент для событий proc:

 sudo forkstat -e exec,comm,core
  

Упакован в Ubuntu, Debian и AUR.


До этого был cn_proc:

  bzr branch lp:~kees/ junk/cn_proc
  

Файл Makefile нуждается в небольшом изменении ( LDLIBS вместо LDFLAGS ).

cn_proc и exec-notify.c (которые опубликовал Арно) имеют общего предка; cn_proc обрабатывает еще несколько событий и имеет более чистый вывод, но не устойчив, когда процессы быстро завершаются.


О, нашел еще один форк exec-notify, extrace. Этот делает отступы дочерних процессов ниже их родительских (используя эвристику pid_depth).

Комментарии:

1. Пакеты apt Debian и Ubuntu: forkstat и extrace . Кстати: по какой-то причине оба инструмента не работают в моей Ubuntu 20.04 LTS на базе WSL2, они не получают уведомления о создании какого-либо процесса.

Ответ №4:

Взгляните на эту маленькую программу Себастьяна Крамера, она делает именно то, что вы просите, ресурсосберегающим способом и довольно простым кодом.

Для этого требуется, чтобы в вашем ядре была включена функция CONFIG_PROC_EVENTS, чего нет, например, в последнем образе Amazon Linux (2012.09).

ОБНОВЛЕНИЕ: После запроса к Amazon ядра образов Amazon Linux теперь поддерживают PROC_EVENTS

Комментарии:

1. ссылка «эта маленькая программа» не работает. Обновите это?: gist.github.com/iNarcissuss/4809c725eb04b070f439d9c6df28cdd7

Ответ №5:

Ключевое слово для выбранной вами поисковой машины — «process event connector».

Я нашел два инструмента, которые их используют, exec-notify и cn_proc.

Мне больше нравится более поздний вариант, но оба выполняют свою работу очень хорошо.

Ответ №6:

Я не знаю, существует ли лучший способ, но вы могли бы периодически сканировать /proc файловую систему.

Например, /proc/<pid>/exe является символической ссылкой на исполняемый файл процесса.

В моих системах (Ubuntu / RedHat) /proc/loadavg содержит количество запущенных процессов (число после косой черты), а также pid самого последнего запущенного процесса. Если ваш демон опрашивает файл, любое изменение любого из двух чисел сообщит ему, когда ему потребуется повторное сканирование /proc в поисках новых процессов.

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

Комментарии:

1. Спасибо! /proc/loadavg действительно хорош. Но как информация добавляется в /proc/loadavg?

2. @nub: Я предлагаю /proc/loadavg в качестве механизма уведомления (который должен быть опрошен). Это не скажет вам ничего, кроме того, что что-то изменилось. Чтобы выяснить, что изменилось, вам нужно выполнить сканирование /proc/[0-9]* .

3. Да, но как попала информация о новом pid? Потому что сначала эта информация попадает (как?), а затем отправляется в /proc/loadavg.

4. /proc/loadavg — это своего рода поддельный файл, экспортируемый ядром. Когда вы пытаетесь прочитать это, вы получаете информацию непосредственно из ядра.

5. Вы могли пропустить запуск локальной сети и завершение десятков процессов между двумя проверками, и ничего не произошло, пока процессы запускались и завершались.

Ответ №7:

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

Сканирование может быть выполнено системным вызовом ядра или путем считывания сведений о ядре в пользовательском пространстве, объявленных в файловой системе /proc). Обратите внимание, что сканирование не является гарантией того, что вы найдете какую-либо конкретную программу, поскольку, если программе удастся запустить и завершить работу между циклами сканирования, она вообще не будет обнаружена.

Возможны более сложные методы обнаружения процессов, но они также требуют более сложных реализаций. Важно знать, что действительно необходимо, прежде чем вы начнете искать экзотические решения (установка драйверов ядра и т.д.), Поскольку все, что вы делаете, не зависит от системы, которую вы отслеживаете; вы фактически изменяете среду, наблюдая за ней, и некоторые методы наблюдения за средой могут изменить ее ненадлежащим образом.

Ответ №8:

CONFIG_FTRACE и CONFIG_KPROBES через brendangregg/perf-tools

 git clone https://github.com/brendangregg/perf-tools.git
cd perf-tools
git checkout 98d42a2a1493d2d1c651a5c396e015d4f082eb20
sudo ./execsnoop
  

В другой оболочке:

 while true; do sleep 1; date; done
  

Первая оболочка отображает данные формата:

 Tracing exec()s. Ctrl-C to end.                                                        
Instrumenting sys_execve                                                               
   PID   PPID ARGS 
 20109   4336 date                                                                                       
 20110   4336 sleep 1                                                                                    
 20111   4336 date                                                                                                                                                                                                 
 20112   4336 sleep 1                                                                                    
 20113   4336 date                                                                                       
 20114   4336 sleep 1                                                                                    
 20115   4336 date                                                                                       
 20116   4336 sleep 1