¿Cómo rastrear procesos recién creados en Linux?

31

Sé que con pspuedo ver la lista o el árbol de los procesos actuales que se ejecutan en el sistema. Pero lo que quiero lograr es "seguir" los nuevos procesos que se crean al usar la computadora.

Como analogía, cuando usa tail -fpara seguir los nuevos contenidos agregados a un archivo o cualquier entrada, quiero mantener una lista de seguimiento del proceso que se está creando actualmente.

¿Es esto posible?

Pablo Matias Gomez
fuente

Respuestas:

28

Si kprobes está habilitado en el kernel, puede usarlo execsnoopdesde perf-tools :

En primera terminal:

% while true; do uptime; sleep 1; done

En otra 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
fuente
2
Para las nuevas versiones del kernel (> = 4.17 si entiendo correctamente) en x84_64, las herramientas de perf de Gregg ya no funcionan: se ejecutan pero no hay informes, ya que instrumenta una llamada no utilizada. Según los comentarios de Gregg en otra parte, la solución correcta para los núcleos> = 4.7 es usar la implementación de BPF en la Colección de compiladores de BPF disponible aquí: github.com/iovisor/bcc#tools y en Ubuntu y linuxes modernos como bpfcc-tools.
Guss
7

La forma más fácil es habilitar la auditoría de llamadas del sistema

Vea el siguiente enlace para más detalles,

¿Alguien sabe una manera simple de controlar el engendro del proceso raíz | Falla del servidor

Si está monitoreando todos los procesos, simplemente elimine la -F uid=0pieza

Los registros se escriben en /var/log/audit/audit.log

margarita
fuente
Ninguno de esos 3 enlaces responde a mi pregunta. Los dos primeros tratan de codificar algo para resolver esto y el último no responde tampoco. Lo que estoy preguntando es sobre algún comando y no escribir algún código
Pablo Matias Gomez
@ PabloMatíasGomez actualizado
daisy
3

CONFIG_PROC_EVENTS=y

Sesión de muestra:

$ 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_EVENTSexpone los eventos a userland a través de un socket de enlace de red .

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);
}

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

Sin embargo, no creo que pueda obtener datos de proceso como UID y argumentos de proceso porque exec_proc_eventcontiene muy pocos datos: https://github.com/torvalds/linux/blob/v4.16/include/uapi/linux/cn_proc .h # L80 Podríamos intentar leerlo de inmediato /proc, pero existe el riesgo de que el proceso finalice y otro tome su PID, por lo que no sería confiable.

Probado en Ubuntu 17.10, que se ha CONFIG_PROC_EVENTS=yhabilitado de forma predeterminada.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
fuente
2

Aparentemente puedes seguir un proceso usando strace. Si conoce el PID del proceso, puede hacer lo siguiente:

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

Observe el -fcambio. Le ayudará a seguir los procesos recién creados que son descendientes del proceso cuyo PID se utilizó en el comando anterior. Para obtener información sobre strace, consulte esta pregunta.

tejus
fuente
Aparentemente querías unirte al proceso de inicio, con pid = 1, ¿verdad? Desafortunadamente no funciona, no veo en la salida ninguna creación de nuevos procesos, y el número de líneas es de unas pocas docenas, mientras que el pedido actual de nuevos procesos pasó por unos pocos cientos.
Hola Angel