Linux下如何检测程序的启动情况?

发布于 2024-11-08 13:21:14 字数 201 浏览 0 评论 0原文

我写了一个简单的守护进程。 当我运行任何程序时,这个守护进程应该做出响应。 如何做到这一点? 在一个大的守护进程循环中:

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

当我运行一个新程序(创建新进程)时,在linux中调用什么函数?

I wrote a simple daemon.
This daemon should respond when I run any program.
How to do this?
In a big daemon loop:

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

What functions to call in linux, when i'm running a new program (create new process)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

月隐月明月朦胧 2024-11-15 13:21:14

对于Linux,内核中似乎有一个接口。在研究这个问题时,我遇到人们使用 CONFIG_CONNECTOR 和 CONFIG_PROC_EVENTS 内核配置来获取进程死亡事件。

更多谷歌,我发现了这个:

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

Proc 连接器和套接字过滤器
发布于 2011 年 2 月 9 日,作者:scott

proc 连接器是大多数人很少遇到的有趣的内核功能之一,甚至更很少找到相关文档。套接字过滤器也是如此。这是一种耻辱,因为它们都是非常有用的接口,如果有更好的文档记录,它们可能有多种用途。

proc 连接器允许您接收进程事件的通知,例如 fork 和 exec 调用,以及进程的 uid、gid 或 sid(会话 ID)的更改。这些是通过基于套接字的接口通过读取内核头中定义的 struct proc_event 实例来提供的......

感兴趣的头是:

#include <linux/cn_proc.h>

我在这里找到了示例代码:

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

我发现此代码需要以 root 身份运行才能获取通知。

For Linux, there appears to be an interface in the kernel. Whilst researching this problem I came across people using CONFIG_CONNECTOR and CONFIG_PROC_EVENTS kernel configuration to get events on process death.

Some more google and I found this:

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

The Proc Connector and Socket Filters
Posted on February 9, 2011 by scott

The proc connector is one of those interesting kernel features that most people rarely come across, and even more rarely find documentation on. Likewise the socket filter. This is a shame, because they’re both really quite useful interfaces that might serve a variety of purposes if they were better documented.

The proc connector allows you to receive notification of process events such fork and exec calls, as well as changes to a process’s uid, gid or sid (session id). These are provided through a socket-based interface by reading instances of struct proc_event defined in the kernel header....

The header of interest is:

#include <linux/cn_proc.h>

I found example code here:

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

I found that this code needs to run as root to get the notifications.

方圜几里 2024-11-15 13:21:14

我有兴趣尝试找出如何在不进行民意调查的情况下做到这一点。 inotify() 似乎不适用于 /proc,所以这个想法已经过时了。

然而,任何动态链接的程序都会在启动时访问某些文件,例如动态链接器。这对于安全目的来说是无用的,因为它不会在静态链接的程序上触发,但可能仍然令人感兴趣:

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

打印出的事件不包含任何有趣的信息 - 触发进程的 pid 似乎不包含任何信息。由 inotify 提供。然而,它可以用来唤醒并触发对 /proc 的重新扫描。

还要注意,在这个东西唤醒并完成扫描 /proc 之前,短暂的程序可能会再次消失 - 也许您会知道它们已经存在,但无法了解它们是什么。当然,任何人都可以不断打开和关闭动态链接器的 fd,从而将您淹没在噪音中。

I was interested in trying to figure out how to do this without polling. inotify() does not seem to work on /proc, so that idea is out.

However, any program which is dynamically linked is going to access certain files on startup, such as the dynamic linker. This would be useless for security purposes since it won't trigger on a statically linked program, but might still be of interest:

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

The events this prints out don't contain any interesting information - the pid of the triggering process doesn't seem to be provided by inotify. However it could be used to wake up and trigger a rescan of /proc

Also be aware that short-lived programs might vanish again before this thing wakes up and finishes scanning /proc - presumably you would learn that they had existed, but not be able to learn what they were. And of course anybody could just keep opening and closing an fd to the dyanmic linker to drown you in noise.

回忆那么伤 2024-11-15 13:21:14

使用forkstat,它是最完整的 proc 事件客户端:

sudo forkstat -e exec,comm,core

打包在 Ubuntu、Debian 和 AUR 中。


在此之前,有 cn_proc

 bzr branch lp:~kees/+junk/cn_proc

Makefile 需要进行一些小更改(LDLIBS而不是LDFLAGS)。

cn_proc 和 exec-notify.c (Arnaud 发布的)具有共同的祖先; cn_proc 处理更多事件并具有更清晰的输出,但当进程快速退出时不具有弹性。


哦,发现了 exec-notify 的另一个分支,extrace
这会将子进程缩进其父进程下方(使用 pid_depth 启发式)。

Use forkstat, it's the most complete client for proc events:

sudo forkstat -e exec,comm,core

Packaged in Ubuntu, Debian and the AUR.


Before that, there was cn_proc:

 bzr branch lp:~kees/+junk/cn_proc

The Makefile needs a small change (LDLIBS instead of LDFLAGS).

cn_proc and exec-notify.c (which Arnaud posted) share a common ancestor; cn_proc handles a few more events and has cleaner output, but isn't resilient when processes exit quickly.


Ooh, found another fork of exec-notify, extrace.
This one indents child processes below their parent (using a pid_depth heuristic).

水波映月 2024-11-15 13:21:14

看看 Sebastian Krahmer 的这个小程序,它完全符合您在资源中要求的内容有效的方法和非常简单的代码。

它确实要求您的内核启用 CONFIG_PROC_EVENTS,但最新的 Amazon Linux 映像 (2012.09) 上的情况并非如此。

更新:根据向 Amazon 请求,Amazon Linux 映像内核现在支持过程事件

Have a look at this little program by Sebastian Krahmer it does exactly what you are asking in a resource efficient way and pretty simple code.

It does require that your kernel has CONFIG_PROC_EVENTS enabled, which is not the case on the latest Amazon Linux Image (2012.09) for example.

UPDATE: Following a request to Amazon the Amazon Linux Image kernels now support PROC_EVENTS

时光匆匆的小流年 2024-11-15 13:21:14

您选择的搜索机的关键字是“处理事件连接器”。

我发现了两个利用它们的工具,exec-notifycn_proc

我更喜欢后者,但两者都做得很好。

The keyword for the search machine of your choice is "process event connector".

I found two tools that utilize them, exec-notify and cn_proc.

I like the later more, but both do their jobs very well.

沙与沫 2024-11-15 13:21:14

我不知道是否存在更好的方法,但您可以定期扫描 /proc 文件系统。

例如,/proc//exe 是进程可执行文件的符号链接。

在我的系统 (Ubuntu/RedHat) 上,/proc/loadavg 包含正在运行的进程数(正斜杠后面的数字)以及最近启动的进程的 pid。如果您的守护进程轮询该文件,对这两个数字中任何一个的任何更改都会告诉它何时需要重新扫描 /proc 寻找新进程。

这绝不是万无一失的,但却是我能想到的最合适的机制。

I don't know if there exists a better way, but you could periodically scan the /proc filesystem.

For example, /proc/<pid>/exe is a symlink to the process's executable.

On my systems (Ubuntu/RedHat), /proc/loadavg contains the number of running processes (the number after the forward slash) as well as the pid of the most recently started process. If your daemon polls the file, any change to either of the two numbers will tell it when it needs to re-scan /proc looking for new processes.

This is by no means bullet-proof, but is the most suitable mechanism I can think of.

荆棘i 2024-11-15 13:21:14

您可以扫描操作系统以查找符合您标准的程序,也可以等待程序向您的守护程序报告自身情况。您选择哪种技术在很大程度上取决于您对非守护程序的控制程度。

扫描可以通过内核系统调用或通过读取用户空间公布的内核详细信息(与 /proc 文件系统一样)来完成。请注意,扫描并不能保证您会找到任何特定程序,就好像该程序设法在扫描周期之间启动和终止一样,它根本不会被检测到。

更复杂的过程检测技术是可能的,但它们也需要更复杂的实现。在开始寻求奇异的解决方案(插入内核驱动程序等)之前,了解真正需要什么非常重要,因为您所做的一切并不独立于您正在监视的系统;你实际上是通过观察来改变环境的,而某些观察环境的方法可能会不恰当地改变它。

You can either scan the operating system for programs that match your criterion or you can wait for a program to report itself to your daemon. Which technique you choose will depend heavily on how much control you have over your non-daemon programs.

Scanning can be accomplished by kernel system call, or by reading the user space advertised kernel details (as with the /proc file system). Note that scanning is not a guarantee that you will find any particular program, as if the program manages to launch and terminate between scan cycles, it will not be detected at all.

More complicated techniques of process detection are possible, but they also require more complicated implementations. It is important to know what is truly needed before you start reaching for solutions that are exotic (inserting kernel drivers, etc.), as everything you do is not independent of the system you are monitoring; you actually change the environment by watching it, and some methods of watching the environment might change it inappropriately.

热情消退 2024-11-15 13:21:14

CONFIG_FTRACECONFIG_KPROBES 通过 brendangregg/perf-tools

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

在另一个 shell 上:

while true; do sleep 1; date; done

第一个 shell 显示以下格式的数据:

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

CONFIG_FTRACE and CONFIG_KPROBES through brendangregg/perf-tools

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

On another shell:

while true; do sleep 1; date; done

First shell shows data of format:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文