Ich war daran interessiert, herauszufinden, wie man dies ohne Abfrage macht. inotify() scheint auf /proc nicht zu funktionieren, also ist diese Idee hinfällig.
Jedes dynamisch verknüpfte Programm greift jedoch beim Start auf bestimmte Dateien zu, z. B. den dynamischen Linker. Dies wäre aus Sicherheitsgründen nutzlos, da es bei einem statisch gelinkten Programm nicht ausgelöst wird, könnte aber trotzdem von Interesse sein:
#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;
}
Die von diesem Ausdruck ausgegebenen Ereignisse enthalten keine interessanten Informationen - die PID des auslösenden Prozesses scheint von inotify nicht bereitgestellt zu werden. Es könnte jedoch verwendet werden, um aufzuwachen und einen erneuten Scan von /proc
auszulösenBeachten Sie auch, dass kurzlebige Programme wieder verschwinden können, bevor dieses Ding aufwacht und das Scannen von /proc beendet - vermutlich würden Sie erfahren, dass sie existiert haben, aber nicht in der Lage sein, zu erfahren, was sie waren. Und natürlich könnte jeder einfach einen fd zum dynamischen Linker öffnen und schließen, um Sie im Rauschen zu ertränken.
Für Linux scheint es eine Schnittstelle im Kernel zu geben. Bei der Untersuchung dieses Problems bin ich auf Leute gestoßen, die die Kernelkonfiguration CONFIG_CONNECTOR und CONFIG_PROC_EVENTS verwenden, um Ereignisse beim Prozesstod zu erhalten.
Etwas mehr Google und ich habe das gefunden:
http://netsplit.com/2011/02/09/the-proc-connector-and-socket-filters/
The Proc Connector and Socket FiltersGepostet am 9. Februar 2011 von Scott
Der Proc-Konnektor ist eines dieser interessanten Kernel-Features, auf das die meisten Leute selten stoßen und noch seltener eine Dokumentation darüber finden. Ebenso der Steckdosenfilter. Das ist schade, denn beides sind wirklich sehr nützliche Schnittstellen, die einer Vielzahl von Zwecken dienen könnten, wenn sie besser dokumentiert wären.
Der Proc-Konnektor ermöglicht es Ihnen, Benachrichtigungen über Prozessereignisse wie Fork- und Exec-Aufrufe sowie über Änderungen an der UID, GID oder SID (Sitzungs-ID) eines Prozesses zu erhalten. Diese werden über eine Socket-basierte Schnittstelle bereitgestellt, indem Instanzen von struct proc_event gelesen werden, die im Kernel-Header definiert sind....
Der interessierende Header lautet:
#include <linux/cn_proc.h>
Beispielcode habe ich hier gefunden:
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 <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);
}
Ich habe festgestellt, dass dieser Code als root ausgeführt werden muss, um die Benachrichtigungen zu erhalten.