Como detectar o lançamento de programas no Linux?

Eu escrevi um daemon simples. Este daemon deve responder quando eu executar qualquer programa. Como fazer isso? Em um grande daemon loop:

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

Quais funções chamar no linux, quando estou executando um novo programa (criar novo processo)?

Não sei se existe uma maneira melhor, mas você pode verificar periodicamente o sistema de arquivos /proc .

Por exemplo, /proc//exe é um symlink para o executável do processo.

Nos meus sistemas (Ubuntu / RedHat), o /proc/loadavg contém o número de processos em execução (o número após a barra) assim como o pid do processo iniciado mais recentemente. Se o seu daemon pesquisar o arquivo, qualquer alteração em um dos dois números indicará quando ele precisar varrer novamente /proc procurando novos processos.

Isso não é de forma alguma à prova de balas, mas é o mecanismo mais adequado que posso imaginar.

Para o Linux, parece haver uma interface no kernel. Ao pesquisar esse problema, encontrei pessoas usando CONFIG_CONNECTOR e configuração de kernel CONFIG_PROC_EVENTS para obter events na morte do processo.

Mais alguns google e achei isso:

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

O Proc conector e soquete filtros Postado em 09 de fevereiro de 2011 por Scott

O conector proc é um desses resources interessantes do kernel que a maioria das pessoas raramente encontra e, ainda mais raramente encontra documentação. Da mesma forma o filtro de soquete. Isso é uma pena, porque ambas são interfaces bastante úteis que podem servir a uma variedade de propósitos se forem melhor documentadas.

O conector proc permite que você receba notifications de events de processo, tais como chamadas fork e exec, assim como alterações no uid, gid ou sid (id da session) de um processo. Estes são fornecidos através de uma interface baseada em socket lendo instâncias de struct proc_event definidas no header do kernel ….

O header de interesse é:

 #include  

Eu encontrei o código de exemplo aqui:

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  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  /* * 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 *)&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(&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; } /* * 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, &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(int unused) { need_exit = true; } int main(int argc, const char *argv[]) { 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); } 

Descobri que esse código precisa ser executado como root para receber as notifications.

Eu estava interessado em tentar descobrir como fazer isso sem pesquisa. inotify () parece não funcionar em / proc, então essa ideia está fora.

No entanto, qualquer programa que esteja vinculado dinamicamente acessará determinados arquivos na boot, como o vinculador dynamic. Isso seria inútil por motivos de segurança, uma vez que não será acionado em um programa vinculado estaticamente, mas ainda pode ser interessante:

 #include  #include  #include  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 %x\n", 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; } 

Os events impressos não contêm informações interessantes – o pid do processo de disparo não parece ser fornecido pelo inotify. No entanto, ele pode ser usado para ativar e acionar uma nova verificação de / proc

Também esteja ciente de que programas de vida curta podem desaparecer novamente antes que essa coisa acorde e termine a varredura / proc – presumivelmente você aprenderia que eles existiram, mas não será capaz de aprender o que eles eram. E, claro, qualquer um poderia continuar abrindo e fechando um fd para o link dyanmic para afogá-lo no ruído.

Dê uma olhada neste pequeno programa de Sebastian Krahmer, ele faz exatamente o que você está perguntando de uma maneira eficiente em resources e um código bastante simples.

Isso requer que seu kernel tenha o CONFIG_PROC_EVENTS ativado, o que não é o caso da imagem mais recente do Amazon Linux Image (2012.09), por exemplo.

ATUALIZAÇÃO: Após uma solicitação à Amazon, os kernels da Amazon Linux Image agora suportam PROC_EVENTS

Use forkstat , é o cliente mais completo para events proc:

 sudo forkstat -e exec,comm,core 

Embalado no Ubuntu, Debian e no AUR.


Antes disso, havia cn_proc :

  bzr branch lp:~kees/+junk/cn_proc 

O Makefile precisa de uma pequena alteração ( LDLIBS vez de LDFLAGS ).

cn_proc e exec-notify.c (que Arnaud postou) compartilham um ancestral comum; O cn_proc manipula mais alguns events e tem saída mais limpa, mas não é resiliente quando os processos saem rapidamente.


Ooh, encontrei outro fork do exec-notify, extrace . Este recua processos filhos abaixo de seus pais (usando uma heurística pid_depth).

A palavra-chave da máquina de pesquisa de sua escolha é “conector do evento de processo”.

Eu encontrei duas ferramentas que os utilizam, exec-notify e cn_proc .

Eu gosto mais do mais tarde, mas ambos fazem o seu trabalho muito bem.

Você pode verificar o sistema operacional em busca de programas que correspondam ao seu critério ou pode esperar que um programa se informe ao seu daemon. Qual técnica você escolherá dependerá de quanto controle você tem sobre seus programas que não são daemon.

A varredura pode ser realizada por chamada do sistema kernel ou lendo os detalhes do kernel anunciado pelo espaço do usuário (como no sistema de arquivos / proc). Observe que a varredura não é uma garantia de que você encontrará qualquer programa em particular, como se o programa conseguisse iniciar e terminar entre os ciclos de varredura, ele não seria detectado de forma alguma.

Técnicas mais complicadas de detecção de processos são possíveis, mas elas também exigem implementações mais complicadas. É importante saber o que é realmente necessário antes de você começar a procurar soluções que sejam exóticas (inserindo drivers de kernel, etc.), já que tudo que você faz não é independente do sistema que você está monitorando; você realmente muda o ambiente assistindo, e alguns methods de observar o ambiente podem alterá-lo de forma inadequada.

CONFIG_KPROBES e CONFIG_KPROBES através do brendangregg/perf-tools

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

Em outro shell:

 while true; do sleep 1; date; done 

Primeiro shell mostra dados de formato:

 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