/**
@file
@brief 'exec', 'exit' and 'fork' tracepoints
@details Copyright (c) 2017-2021 Acronis International GmbH
@author Mikhail Krivtsov (mikhail.krivtsov@acronis.com)
@since $Id: $
*/
#include "tracepoints.h"
#include "compat.h"
#include "debug.h"
#include "exit_event.h"
#include "exec_event.h"
#include "ftrace_hooks/ftrace_events.h"
#include "fork_event.h"
#include "memory.h"
#include "message.h"
#include <linux/binfmts.h>
#include <linux/dcache.h> // d_path
#include <linux/file.h> // fput()
#include <linux/fs.h> // struct file
#include <linux/limits.h> // PATH_MAX
#include <linux/mm.h> // get_task_exe_file()
#include <linux/mm_types.h> // struct mm_struct
#include <linux/path.h> // struct path
#ifndef KERNEL_MOCK
#include <linux/sched.h> // struct task_struct
#else
#include <mock/mock_sched.h>
#endif
#include <linux/tracepoint.h>
#include <linux/version.h> // LINUX_VERSION_CODE, KERNEL_VERSION()
#include <trace/events/sched.h> // TRACE_EVENT(sched_*)
#ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT
static bool g_registered_exec = false;
#endif
static bool g_registered_exit = false;
static bool g_registered_fork = false;
static TRACE_CB_PROTO(sched_process_exit,
TP_PROTO(struct task_struct *p))
{
DPRINTF("exit() p=%p { pid=%d tgid=%d }", p, p->pid, p->tgid);
exit_event_nowait(p);
}
/*
* Here the caller only guarantees locking for struct file and struct inode.
* Locking must therefore be done in the probe to use the dentry.
*/
static TRACE_CB_PROTO(sched_process_fork,
TP_PROTO(struct task_struct *current_macro,
struct task_struct *p))
{
DPRINTF("fork() current=%p { pid=%d tgid=%d comm='%s' } "
"p=%p { pid=%d tgid=%d comm='%s' }",
current_macro, current_macro->pid, current_macro->tgid,
current_macro->comm,
p, p->pid, p->tgid, p->comm);
fork_event_nowait(current_macro, p);
}
#ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT
static TRACE_CB_PROTO(sched_process_exec,
TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm))
{
DPRINTF("exec() p=%p { pid=%d tgid=%d comm='%s' }", p, p->pid, p->tgid, p->comm);
exec_event_nowait(p, bprm);
}
#endif
int tracepoints_attach(void)
{
int ret;
if (!ftrace_post_event_have(FTRACE_POST_EVENT_EXIT)) {
ret = REGISTER_TRACE(sched_process_exit, TRACE_CB_NAME(sched_process_exit));
if (ret) {
EPRINTF("'register_trace_sched_process_exit()' failure %i", ret);
goto fail;
}
g_registered_exit = true;
}
if (!ftrace_post_event_have(FTRACE_POST_EVENT_FORK)) {
ret = REGISTER_TRACE(sched_process_fork, TRACE_CB_NAME(sched_process_fork));
if (ret) {
EPRINTF("'register_trace_sched_process_fork()' failure %i", ret);
goto fail;
}
g_registered_fork = true;
}
#ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT
if (!ftrace_post_event_have(FTRACE_POST_EVENT_EXEC)) {
ret = REGISTER_TRACE(sched_process_exec, TRACE_CB_NAME(sched_process_exec));
if (ret) {
EPRINTF("'register_trace_sched_process_exec()' failure %i", ret);
goto fail;
}
g_registered_exec = true;
}
#endif
IPRINTF("tracepoints attached");
return 0;
fail:
tracepoints_detach();
return ret;
}
void tracepoints_detach(void)
{
if (g_registered_fork) {
UNREGISTER_TRACE(sched_process_fork, TRACE_CB_NAME(sched_process_fork));
g_registered_fork = false;
}
if (g_registered_exit) {
UNREGISTER_TRACE(sched_process_exit, TRACE_CB_NAME(sched_process_exit));
g_registered_exit = false;
}
#ifdef HAVE_SCHED_PROCESS_EXEC_TRACEPOINT
if (g_registered_exec) {
UNREGISTER_TRACE(sched_process_exec, TRACE_CB_NAME(sched_process_exec));
g_registered_exec = false;
}
#endif
tracepoint_synchronize_unregister();
IPRINTF("tracepoints detached");
}
|