2011-05-20 12 views
22

Escribí un daemon simple. Este daemon debe responder cuando ejecuto cualquier programa. ¿Cómo hacer esto? En una gran demonio de bucle:Detecta el lanzamiento de programas en la plataforma Linux

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

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

+4

Creo que vamos a necesitar un poco más de información –

+5

@ Adam Batkin - No estoy de acuerdo, la pregunta es lo suficientemente clara, el póster quiere que se informe sobre la creación del proceso –

+0

¿Este daemon está ejecutando firefox o Firefox está siendo iniciado externamente? el usuario y quieres ser notificado? Tu pregunta podría usar un poco de aclaración. –

Respuesta

1

No sé si existe una manera mejor, pero puede escanear periódicamente el sistema de archivos /proc. Por ejemplo, /proc/<pid>/exe es un enlace simbólico al ejecutable del proceso.

En mis sistemas (Ubuntu/RedHat), /proc/loadavg contiene el número de procesos en ejecución (el número después de la barra inclinada) así como también el pid del proceso iniciado más recientemente. Si su daemon sondea el archivo, cualquier cambio en cualquiera de los dos números indicará cuándo debe volver a escanear /proc buscando nuevos procesos.

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

+0

¡Gracias!/proc/loadavg realmente bueno. ¿Pero cómo se agrega la información en/proc/loadavg? – nub

+0

@nub: propongo '/ proc/loadavg' como mecanismo de notificación (que debe ser sondeado). No te dirá nada más allá de que algo ha cambiado. Para descubrir qué ha cambiado, necesitará escanear '/ proc/[0-9] *'. – NPE

+0

Sí, pero ¿cómo se obtuvo la información del nuevo pid? Porque primero se obtiene esta información (¿cómo?) Y luego se ingresa a/proc/loadavg. – nub

0

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

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

Son posibles técnicas más complicadas de detección de procesos, 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.

18

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

Sin embargo, cualquier programa que esté vinculado dinámicamente va a acceder a ciertos archivos al inicio, como el vinculador dinámico. Esto sería inútil para fines de seguridad, puesto que no se disparará en los programas enlazados estáticamente, pero todavía podría ser de interés:

#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 %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 Esto muestra no contienen ninguna información interesante - el pid del el proceso de activación no parece ser proporcionado por inotify. Sin embargo, podría utilizarse para reactivar y desencadenar un nuevo escaneo de/proc

También tenga en cuenta que los programas de corta duración podrían desaparecer de nuevo antes de que esto se active y termine de escanear/proc - presumiblemente usted sabría que han existido, pero no ser capaz de aprender lo que eran Y, por supuesto, cualquiera podría seguir abriendo y cerrando un fd al enlazador dyanmic para ahogarlo en ruido.

+1

Linda solución. +1 – 0xC0000022L

56

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 Proc y filtros socket Publicado el 9 de febrero de 2011 por scott

El conector proc es uno de los interesantes características del kernel que la mayoría de las personas rara vez encuentran, y aún más raramente encuentran 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 la toma mediante la lectura de las instancias de proc_event struct definido en la cabecera kernel ....

La cabecera de interés es:

#include <linux/cn_proc.h> 

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 
*/ 

#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 *)&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); 
} 

he encontrado que este código tiene que ejecutar como root para obtener el entr icaciones.

+0

Splendid one: ayuda a crear la solución para http://superuser.com/questions/354150/linux-service-to-set-proc-pid-oom-score-adj-of-new-processes ;-) –

+0

Esto es hermoso, estaba empezando a pensar que iba a tener que sumergirme en el código del kernel ... –

10

Eche un vistazo a this little program por Sebastian Krahmer que hace exactamente lo que está pidiendo 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 request to Amazon los granos de imagen Amazon Linux ahora soporta PROC_EVENTS

9

Uso forkstat, es el cliente más completa para eventos 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 en 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-notificar, extrace. Este uno sangra procesos hijo debajo de su padre (utilizando una heurística pid_depth).

3

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.

Cuestiones relacionadas