¿Cómo detectar el lanzamiento de progtwigs en Linux?

Escribí un daemon simple. Este daemon debería responder cuando ejecuto cualquier progtwig. ¿Como hacer esto? En un gran bucle de daemon:

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

¿Qué funciones llamar en Linux, cuando estoy ejecutando un nuevo progtwig (crear un nuevo proceso)?

No sé si existe una forma mejor, pero podría escanear periódicamente el sistema de archivos /proc .

Por ejemplo, /proc//exe es un enlace simbólico al ejecutable del proceso.

En mis sistemas (Ubuntu / RedHat), /proc/loadavg contiene la cantidad de procesos en ejecución (el número después de la barra inclinada) y el pid del último proceso iniciado. Si su daemon sondea el archivo, cualquier cambio en cualquiera de los dos números le dirá cuándo necesita volver a escanear /proc buscando nuevos procesos.

Esto de ninguna manera es a prueba de balas, pero es el mecanismo más adecuado que se me ocurre.

Para Linux, parece haber una interfaz en el kernel. Mientras investigaba este problema, encontré personas que usaban CONFIG_CONNECTOR y CONFIG_PROC_EVENTS la configuración del kernel para obtener eventos en el proceso de muerte.

Un poco más de google y encontré esto:

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

El conector de proc y los filtros de socket Publicado el 9 de febrero de 2011 por scott

El conector de proc es una de esas características interesantes del núcleo que la mayoría de las personas rara vez encuentra, y aún más raramente encuentra documentación. Del mismo modo el filtro de socket. Es una pena, porque ambas son interfaces realmente útiles que podrían servir para una variedad de propósitos si estuvieran mejor documentadas.

El conector de proc le permite recibir notificaciones de eventos de proceso, tales como llamadas de fork y exec, así como cambios en el uid, gid o sid (id de sesión) del proceso. Estos se proporcionan a través de una interfaz basada en socket leyendo instancias de struct proc_event definidas en el encabezado kernel ….

El encabezado de interés es:

 #include  

Encontré código de ejemplo aquí:

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

Descubrí que este código debe ejecutarse como root para obtener las notificaciones.

Estaba interesado en tratar de descubrir cómo hacer esto sin una encuesta. inotify () no parece funcionar en / proc, por lo que esa idea está fuera.

Sin embargo, cualquier progtwig que esté vinculado dinámicamente va a acceder a ciertos archivos al inicio, como el vinculador dynamic. Esto sería inútil por motivos de seguridad, ya que no se activará en un progtwig enlazado estáticamente, pero podría ser interesante:

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

Los eventos que este imprime no contienen ninguna información interesante: el pid del proceso de activación no parece ser proporcionado por inotify. Sin embargo, podría usarse para activar y desencadenar un nuevo escaneo de / proc

También tenga en cuenta que los progtwigs de corta duración podrían desaparecer de nuevo antes de que esto se active y termine de escanear / procesar, es de suponer que usted aprenderá que han existido, pero no podrán saber lo que eran. Y, por supuesto, cualquiera podría seguir abriendo y cerrando un fd al enlazador dyanmic para ahogarlo en ruido.

Echa un vistazo a este pequeño progtwig de Sebastian Krahmer. Hace exactamente lo que estás preguntando de una manera eficiente en cuanto a recursos y un código bastante simple.

Requiere que su kernel tenga habilitado CONFIG_PROC_EVENTS, que no es el caso en la última versión de Amazon Linux (2012.09), por ejemplo.

ACTUALIZACIÓN: después de una solicitud a Amazon, los kernels de Amazon Linux Image ahora son compatibles con PROC_EVENTS

Use forkstat , es el cliente más completo para los eventos de proc:

 sudo forkstat -e exec,comm,core 

Empaquetado en Ubuntu, Debian y AUR.


Antes de eso, había cn_proc :

  bzr branch lp:~kees/+junk/cn_proc 

El Makefile necesita un pequeño cambio ( LDLIBS lugar de LDFLAGS ).

cn_proc y exec-notify.c (que publicó Arnaud) comparten un ancestro común; cn_proc maneja algunos eventos más y tiene un resultado más limpio, pero no es resistente cuando los procesos se cierran rápidamente.


Ooh, encontré otro tenedor de exec- notify , extrace . Éste sangra el proceso secundario debajo de su padre (usando una heurística pid_depth).

La palabra clave para la máquina de búsqueda de su elección es “conector de evento de proceso”.

Encontré dos herramientas que los utilizan, exec- notify y cn_proc .

Me gusta más tarde, pero ambos hacen su trabajo muy bien.

Puede escanear el sistema operativo para buscar progtwigs que coincidan con su criterio o puede esperar a que un progtwig se informe a su daemon. La técnica que elijas dependerá en gran medida de la cantidad de control que tengas sobre los progtwigs que no sean demonios.

El escaneo se puede llevar a cabo mediante una llamada al sistema del kernel o leyendo los detalles del kernel anunciados por el espacio del usuario (como con el sistema de archivos / proc). Tenga en cuenta que el escaneo no es garantía de que encontrará ningún progtwig en particular, ya que si el progtwig se las arregla para iniciar y finalizar entre ciclos de escaneo, no se detectará en absoluto.

Las técnicas más complicadas de detección de procesos son posibles, pero también requieren implementaciones más complicadas. Es importante saber qué es lo que realmente se necesita antes de comenzar a buscar soluciones que sean exóticas (insertando controladores kernel, etc.), ya que todo lo que hace no es independiente del sistema que está monitoreando; en realidad cambias el entorno observándolo, y algunos métodos de observación del entorno pueden cambiarlo de manera inapropiada.

CONFIG_FTRACE y CONFIG_KPROBES través de brendangregg/perf-tools

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

En otro shell:

 while true; do sleep 1; date; done 

Primer shell muestra datos 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