Como rastrear processos recém-criados no Linux?

31

Eu sei que com pseu posso ver a lista ou a árvore dos processos atuais em execução no sistema. Mas o que eu quero alcançar é "seguir" os novos processos criados ao usar o computador.

Como analogia, quando você tail -fsegue o novo conteúdo anexado a um arquivo ou a qualquer entrada, desejo manter uma lista de acompanhamento do processo que está sendo criado no momento.

Isso é mesmo possível?

Pablo Matias Gomez
fonte

Respostas:

28

Se kprobes estão habilitados no kernel você pode usar execsnoopa partir de perf-tools :

No primeiro terminal:

% while true; do uptime; sleep 1; done

Em outro terminal:

% git clone https://github.com/brendangregg/perf-tools.git
% cd perf-tools
% sudo ./execsnoop
Tracing exec()s. Ctrl-C to end.
Instrumenting sys_execve
   PID   PPID ARGS
 83939  83937 cat -v trace_pipe
 83938  83934 gawk -v o=1 -v opt_name=0 -v name= -v opt_duration=0 [...]
 83940  76640 uptime
 83941  76640 sleep 1
 83942  76640 uptime
 83943  76640 sleep 1
 83944  76640 uptime
 83945  76640 sleep 1
^C
Ending tracing...
Kirill Spitsyn
fonte
2
Para novas versões do kernel (> = 4.17, se bem entendi) no x84_64, as ferramentas perf de Gregg não funcionam mais - elas são executadas, mas não há relatórios, pois instrumentam uma chamada não utilizada. De acordo com os comentários de Gregg em outros lugares, a solução correta para kernels> = 4.7 é usar a implementação BPF na coleção de compiladores BPF disponível aqui: github.com/iovisor/bcc#tools e no Ubuntu e linuxes modernos como bpfcc-tools.
Guss
7

A maneira mais fácil é habilitar a auditoria de chamadas do sistema

Veja o link a seguir para detalhes ,

Alguém sabe uma maneira simples de monitorar a geração do processo raiz | Falha no servidor

Se você estiver monitorando todos os processos, basta remover a -F uid=0parte

Os logs são gravados em /var/log/audit/audit.log

margarida
fonte
Nenhum desses 3 links responde à minha pergunta. Os dois primeiros são sobre codificar algo para resolver isso e o último também não responde. O que estou pedindo é sobre alguns comandos e não escrever algum pedaço de código
Pablo Matias Gomez
@ PabloMatíasGomez atualizado
daisy
3

CONFIG_PROC_EVENTS=y

Sessão de amostra:

$ su
# ./proc_events.out &
set mcast listen ok
# sleep 2 & sleep 1 &
fork: parent tid=48 pid=48 -> child tid=56 pid=56
fork: parent tid=48 pid=48 -> child tid=57 pid=57
exec: tid=57 pid=57
exec: tid=56 pid=56
exit: tid=57 pid=57 exit_code=0
exit: tid=56 pid=56 exit_code=0

CONFIG_PROC_EVENTSexpõe os eventos à terra do usuário por meio de um soquete netlink .

proc_events.c

#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>

static volatile bool need_exit = false;

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 *)&sa_nl, sizeof(sa_nl));
    if (rc == -1) {
        perror("bind");
        close(nl_sock);
        return -1;
    }
    return nl_sock;
}

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(&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, &nlcn_msg, sizeof(nlcn_msg), 0);
    if (rc == -1) {
        perror("netlink send");
        return -1;
    }

    return 0;
}

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, &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 ok\n");
                break;
            case PROC_EVENT_FORK:
                printf("fork: parent tid=%d pid=%d -> child tid=%d pid=%d\n",
                        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=%d\n",
                        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 %d\n",
                        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 %d\n",
                        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=%d\n",
                        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 event\n");
                break;
        }
    }

    return 0;
}

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

int main()
{
    int nl_sock;
    int rc = EXIT_SUCCESS;

    signal(SIGINT, &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);
}

Upsatream do GitHub , código adaptado de: https://bewareofgeek.livejournal.com/2945.html

No entanto, acho que não é possível obter dados do processo, como UID e argumentos do processo, porque exec_proc_eventcontém muito poucos dados: https://github.com/torvalds/linux/blob/v4.16/include/uapi/linux/cn_proc .h # L80 Poderíamos tentar lê-lo imediatamente /proc, mas existe o risco de o processo terminar e outro ter seu PID, para que não seja confiável.

Testado no Ubuntu 17.10, que foi CONFIG_PROC_EVENTS=yativado por padrão.

Ciro Santilli adicionou uma nova foto
fonte
2

Aparentemente, você pode seguir um processo usando strace. Se você conhece o PID do processo, pode fazer:

strace -o strace-<pid>.out -f -p <pid>

Observe o -finterruptor. Ele o ajudará a seguir processos recém-criados que são descendentes do processo cujo PID foi usado no comando acima. Para obter informações sobre strace, consulte esta pergunta.

tejus
fonte
Aparentemente, você pretendia se anexar ao processo init, com pid = 1, certo? Infelizmente, isso não funciona, não vejo na saída nenhuma criação de novos processos, e o número de linhas é de algumas dezenas, enquanto o pid atual para novos processos passou por algumas centenas.
Hi-Angel