diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.cpp b/userspace/libsinsp/sinsp_filtercheck_thread.cpp index 41f5754de9..3afa2a1b3d 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.cpp +++ b/userspace/libsinsp/sinsp_filtercheck_thread.cpp @@ -49,12 +49,15 @@ static const filtercheck_field_info sinsp_filter_check_thread_fields[] = {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exe", "First Argument", "The first command line argument argv[0] (truncated after 4096 bytes) which is usually the executable name but it could be also a custom string, it depends on what the user specifies. This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/cmdline."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pexe", "Parent First Argument", "The proc.exe (first command line argument argv[0]) of the parent process."}, {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.aexe", "Ancestor First Argument", "The proc.exe (first command line argument argv[0]) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aexe[1] retrieves the proc.exe of the parent process, proc.aexe[2] retrieves the proc.exe of the grandparent process, and so on. The current process's proc.exe line can be obtained using proc.aexe[0]. When used without any arguments, proc.aexe is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aexe endswith java` to match any process ancestor whose proc.exe ends with the term `java`."}, + {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.concat_aexe", "Ancestor First Argument Concatenated", "The proc.exe lineage is traced back to a specific process ancestor, up to the specified level of ancestors. The result is a string representing the concatenated lineage, with '->' as the delimiter, starting with the furthest ancestor and including the current process. For example, proc.concat_aexe[3] returns the string `/bin/java->/bin/bash->/bin/python->/bin/bash`, assuming a java process launched a bash process that ultimately launched python and finally bash."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.exepath", "Process Executable Path", "The full executable path of the process (it could be truncated after 1024 bytes if read from '/proc'). This field is collected directly from the kernel or, as a fallback, extracted resolving the path of /proc/PID/exe, so symlinks are resolved. If you are using eBPF drivers this path could be truncated due to verifier complexity limits. (legacy eBPF kernel version < 5.2) truncated after 24 path components. (legacy eBPF kernel version >= 5.2) truncated after 48 path components. (modern eBPF kernel) truncated after 96 path components."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pexepath", "Parent Process Executable Path", "The proc.exepath (full executable path) of the parent process."}, {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.aexepath", "Ancestor Executable Path", "The proc.exepath (full executable path) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aexepath[1] retrieves the proc.exepath of the parent process, proc.aexepath[2] retrieves the proc.exepath of the grandparent process, and so on. The current process's proc.exepath line can be obtained using proc.aexepath[0]. When used without any arguments, proc.aexepath is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aexepath endswith java` to match any process ancestor whose path ends with the term `java`."}, + {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.concat_aexepath", "Ancestor Executable Path Concatenated", "The proc.exepath lineage is traced back to a specific process ancestor, up to the specified level of ancestors. The result is a string representing the concatenated lineage, with '->' as the delimiter, starting with the furthest ancestor and including the current process. For example, proc.concat_aexepath[3] returns the string `/bin/java->/bin/bash->/bin/python->/bin/bash`, assuming a java process launched a bash process that ultimately launched python and finally bash."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.name", "Name", "The process name (truncated after 16 characters) generating the event (task->comm). Truncation is determined by kernel settings and not by Falco. This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/status. The name of the process and the name of the executable file on disk (if applicable) can be different if a process is given a custom name which is often the case for example for java applications."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pname", "Parent Name", "The proc.name truncated after 16 characters) of the process generating the event."}, {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.aname", "Ancestor Name", "The proc.name (truncated after 16 characters) for a specific process ancestor. You can access different levels of ancestors by using indices. For example, proc.aname[1] retrieves the proc.name of the parent process, proc.aname[2] retrieves the proc.name of the grandparent process, and so on. The current process's proc.name line can be obtained using proc.aname[0]. When used without any arguments, proc.aname is applicable only in filters and matches any of the process ancestors. For instance, you can use `proc.aname=bash` to match any process ancestor whose name is `bash`."}, + {PT_CHARBUF, EPF_ARG_ALLOWED, PF_NA, "proc.concat_aname", "Ancestor Name Concatenated", "The proc.name (truncated after 16 characters) lineage is traced back to a specific process ancestor, up to the specified level of ancestors. The result is a string representing the concatenated lineage, with '->' as the delimiter, starting with the furthest ancestor and including the current process. For example, proc.concat_aname[3] returns the string `java->bash->python->bash`, assuming a java process launched a bash process that ultimately launched python and finally bash."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.args", "Arguments", "The arguments passed on the command line when starting the process generating the event excluding argv[0] (truncated after 4096 bytes). This field is collected from the syscalls args or, as a fallback, extracted from /proc/PID/cmdline."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.cmdline", "Command Line", "The concatenation of `proc.name + proc.args` (truncated after 4096 bytes) when starting the process generating the event."}, {PT_CHARBUF, EPF_NONE, PF_NA, "proc.pcmdline", "Parent Command Line", "The proc.cmdline (full command line (proc.name + proc.args)) of the parent of the process generating the event."}, @@ -155,7 +158,10 @@ int32_t sinsp_filter_check_thread::extract_arg(std::string fldname, std::string m_field_id == TYPE_ANAME || m_field_id == TYPE_AEXE || m_field_id == TYPE_AEXEPATH || - m_field_id == TYPE_ACMDLINE) + m_field_id == TYPE_ACMDLINE || + m_field_id == TYPE_CONCAT_ANAME || + m_field_id == TYPE_CONCAT_AEXE || + m_field_id == TYPE_CONCAT_AEXEPATH) { if(val[fldname.size()] == '[') { @@ -279,6 +285,28 @@ int32_t sinsp_filter_check_thread::parse_field_name(const char* str, bool alloc_ return res; } + else if(STR_MATCH("proc.concat_aname")) + { + m_field_id = TYPE_CONCAT_ANAME; + m_field = &m_info.m_fields[m_field_id]; + + int32_t res = 0; + + try + { + res = extract_arg("proc.concat_aname", val, NULL); + } + catch(...) + { + if(val == "proc.concat_aname") + { + m_argid = -1; + res = (int32_t)val.size(); + } + } + + return res; + } else if(STR_MATCH("proc.aexepath")) { m_field_id = TYPE_AEXEPATH; @@ -301,6 +329,28 @@ int32_t sinsp_filter_check_thread::parse_field_name(const char* str, bool alloc_ return res; } + else if(STR_MATCH("proc.concat_aexepath")) + { + m_field_id = TYPE_CONCAT_AEXEPATH; + m_field = &m_info.m_fields[m_field_id]; + + int32_t res = 0; + + try + { + res = extract_arg("proc.concat_aexepath", val, NULL); + } + catch(...) + { + if(val == "proc.concat_aexepath") + { + m_argid = -1; + res = (int32_t)val.size(); + } + } + + return res; + } /* note: because of str similarity of proc.aexe to proc.aexepath, this needs to be placed after proc.aexepath */ else if(STR_MATCH("proc.aexe")) { @@ -324,6 +374,28 @@ int32_t sinsp_filter_check_thread::parse_field_name(const char* str, bool alloc_ return res; } + else if(STR_MATCH("proc.concat_aexe")) + { + m_field_id = TYPE_CONCAT_AEXEPATH; + m_field = &m_info.m_fields[m_field_id]; + + int32_t res = 0; + + try + { + res = extract_arg("proc.concat_aexe", val, NULL); + } + catch(...) + { + if(val == "proc.concat_aexe") + { + m_argid = -1; + res = (int32_t)val.size(); + } + } + + return res; + } else if(STR_MATCH("proc.acmdline")) { m_field_id = TYPE_ACMDLINE; @@ -554,254 +626,63 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b RETURN_EXTRACT_VAR(tinfo->m_vpgid); case TYPE_SNAME: { - int64_t sid = tinfo->m_sid; - - if(!tinfo->is_in_pid_namespace()) + auto session_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_sid;}); + if(session_leader) { - // Relying on the convention that a session id is the process id of the session leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* sinfo = m_inspector->get_thread_ref(sid, false, true).get(); - if(sinfo != NULL) - { - m_tstr = sinfo->get_comm(); - RETURN_EXTRACT_STRING(m_tstr); - } + m_tstr = session_leader->get_comm(); + RETURN_EXTRACT_STRING(m_tstr); } - - // This can occur when the session leader process has exited or if the process - // is running in a pid namespace and we only have the virtual session id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same session id and - // declare it to be the session leader. - sinsp_threadinfo* session_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [sid, &session_leader](sinsp_threadinfo* pt) - { - if(pt->m_sid != sid) - { - return false; - } - session_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // session_leader has been updated to the highest process that has the same session id. - // session_leader's comm is considered the session leader. - m_tstr = session_leader->get_comm(); - RETURN_EXTRACT_STRING(m_tstr); + return nullptr; } case TYPE_SID_EXE: { - int64_t sid = tinfo->m_sid; - - if(!tinfo->is_in_pid_namespace()) + auto session_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_sid;}); + if(session_leader) { - // Relying on the convention that a session id is the process id of the session leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* sinfo = m_inspector->get_thread_ref(sid, false, true).get(); - if(sinfo != NULL) - { - m_tstr = sinfo->get_exe(); - RETURN_EXTRACT_STRING(m_tstr); - } + m_tstr = session_leader->get_exe(); + RETURN_EXTRACT_STRING(m_tstr); } - - // This can occur when the session leader process has exited or if the process - // is running in a pid namespace and we only have the virtual session id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same session id and - // declare it to be the session leader. - sinsp_threadinfo* session_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [sid, &session_leader](sinsp_threadinfo* pt) - { - if(pt->m_sid != sid) - { - return false; - } - session_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // session_leader has been updated to the highest process that has the same session id. - // session_leader's exe is considered the session leader. - m_tstr = session_leader->get_exe(); - RETURN_EXTRACT_STRING(m_tstr); + return nullptr; } case TYPE_SID_EXEPATH: { - int64_t sid = tinfo->m_sid; - - if(!tinfo->is_in_pid_namespace()) + auto session_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_sid;}); + if(session_leader) { - // Relying on the convention that a session id is the process id of the session leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* sinfo = m_inspector->get_thread_ref(sid, false, true).get(); - if(sinfo != NULL) - { - m_tstr = sinfo->get_exepath(); - RETURN_EXTRACT_STRING(m_tstr); - } + m_tstr = session_leader->get_exepath(); + RETURN_EXTRACT_STRING(m_tstr); } - - // This can occur when the session leader process has exited or if the process - // is running in a pid namespace and we only have the virtual session id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same session id and - // declare it to be the session leader. - sinsp_threadinfo* session_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [sid, &session_leader](sinsp_threadinfo* pt) - { - if(pt->m_sid != sid) - { - return false; - } - session_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // session_leader has been updated to the highest process that has the same session id. - // session_leader's exepath is considered the session leader. - m_tstr = session_leader->get_exepath(); - RETURN_EXTRACT_STRING(m_tstr); + return nullptr; } case TYPE_VPGID_NAME: { - int64_t vpgid = tinfo->m_vpgid; - - if(!tinfo->is_in_pid_namespace()) + auto group_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_vpgid;}); + if(group_leader) { - // Relying on the convention that a process group id is the process id of the process group leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* vpgidinfo = m_inspector->get_thread_ref(vpgid, false, true).get(); - if(vpgidinfo != NULL) - { - m_tstr = vpgidinfo->get_comm(); - RETURN_EXTRACT_STRING(m_tstr); - } + m_tstr = group_leader->get_comm(); + RETURN_EXTRACT_STRING(m_tstr); } - // This can occur when the process group leader process has exited or if the process - // is running in a pid namespace and we only have the virtual process group id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same process group id and - // declare it to be the process group leader. - sinsp_threadinfo* group_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [vpgid, &group_leader](sinsp_threadinfo* pt) - { - if(pt->m_vpgid != vpgid) - { - return false; - } - group_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // group_leader has been updated to the highest process that has the same process group id. - // group_leader's comm is considered the process group leader. - m_tstr = group_leader->get_comm(); - RETURN_EXTRACT_STRING(m_tstr); + return nullptr; } case TYPE_VPGID_EXE: { - int64_t vpgid = tinfo->m_vpgid; - - if(!tinfo->is_in_pid_namespace()) + auto group_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_vpgid;}); + if(group_leader) { - // Relying on the convention that a process group id is the process id of the process group leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* vpgidinfo = m_inspector->get_thread_ref(vpgid, false, true).get(); - if(vpgidinfo != NULL) - { - m_tstr = vpgidinfo->get_exe(); - RETURN_EXTRACT_STRING(m_tstr); - } + m_tstr = group_leader->get_exe(); + RETURN_EXTRACT_STRING(m_tstr); } - // This can occur when the process group leader process has exited or if the process - // is running in a pid namespace and we only have the virtual process group id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same process group id and - // declare it to be the process group leader. - sinsp_threadinfo* group_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [vpgid, &group_leader](sinsp_threadinfo* pt) - { - if(pt->m_vpgid != vpgid) - { - return false; - } - group_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // group_leader has been updated to the highest process that has the same process group id. - // group_leader's exe is considered the process group leader. - m_tstr = group_leader->get_exe(); - RETURN_EXTRACT_STRING(m_tstr); - + return nullptr; } case TYPE_VPGID_EXEPATH: { - int64_t vpgid = tinfo->m_vpgid; - - if(!tinfo->is_in_pid_namespace()) + auto group_leader = tinfo->get_oldest_matching_ancestor([](sinsp_threadinfo* t) {return t->m_vpgid;}); + if(group_leader) { - // Relying on the convention that a process group id is the process id of the process group leader. - // `threadinfo` lookup only applies when the process is running on the host and not in a pid - // namespace. However, if the process is running in a pid namespace, we instead traverse the process - // lineage until we find a match. - sinsp_threadinfo* vpgidinfo = m_inspector->get_thread_ref(vpgid, false, true).get(); - if(vpgidinfo != NULL) - { - m_tstr = vpgidinfo->get_exepath(); - RETURN_EXTRACT_STRING(m_tstr); - } + m_tstr = group_leader->get_exepath(); + RETURN_EXTRACT_STRING(m_tstr); } - - // This can occur when the process group leader process has exited or if the process - // is running in a pid namespace and we only have the virtual process group id, as - // seen from its pid namespace. - // Find the highest ancestor process that has the same process group id and - // declare it to be the process group leader. - sinsp_threadinfo* group_leader = tinfo; - - sinsp_threadinfo::visitor_func_t visitor = [vpgid, &group_leader](sinsp_threadinfo* pt) - { - if(pt->m_vpgid != vpgid) - { - return false; - } - group_leader = pt; - return true; - }; - - tinfo->traverse_parent_state(visitor); - - // group_leader has been updated to the highest process that has the same process group id. - // group_leader's exepath is considered the process group leader. - m_tstr = group_leader->get_exepath(); - RETURN_EXTRACT_STRING(m_tstr); + return nullptr; } case TYPE_TTY: RETURN_EXTRACT_VAR(tinfo->m_tty); @@ -860,18 +741,10 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } // get current tinfo / init for subsequent parent lineage traversal - sinsp_threadinfo* mt = NULL; - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else + sinsp_threadinfo* mt = tinfo->get_main_thread(); + if(mt == NULL) { - mt = tinfo->get_main_thread(); - if(mt == NULL) - { - RETURN_EXTRACT_STRING(m_tstr); - } + RETURN_EXTRACT_STRING(m_tstr); } if(!m_argname.empty()) // extract a specific ENV_NAME value @@ -1049,20 +922,12 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_ACMDLINE: { - sinsp_threadinfo* mt = NULL; - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(mt == NULL) - { - return NULL; - } + if(mt == NULL) + { + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1079,20 +944,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_APID: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } // @@ -1116,20 +972,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_ANAME: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else + if (mt == NULL) { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1145,6 +992,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b m_tstr = mt->get_comm(); RETURN_EXTRACT_STRING(m_tstr); } + case TYPE_CONCAT_ANAME: + { + m_tstr = tinfo->concat_attribute_thread_lineage([](sinsp_threadinfo* t) {return t->get_comm();}, m_argid); + RETURN_EXTRACT_STRING(m_tstr); + } case TYPE_PEXE: { sinsp_threadinfo* ptinfo = @@ -1162,20 +1014,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_AEXE: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1191,6 +1034,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b m_tstr = mt->get_exe(); RETURN_EXTRACT_STRING(m_tstr); } + case TYPE_CONCAT_AEXE: + { + m_tstr = tinfo->concat_attribute_thread_lineage([](sinsp_threadinfo* t) {return t->get_exe();}, m_argid); + RETURN_EXTRACT_STRING(m_tstr); + } case TYPE_PEXEPATH: { sinsp_threadinfo* ptinfo = @@ -1208,20 +1056,11 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b } case TYPE_AEXEPATH: { - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) - { - mt = tinfo; - } - else + if (mt == NULL) { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } for(int32_t j = 0; j < m_argid; j++) @@ -1237,23 +1076,19 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b m_tstr = mt->get_exepath(); RETURN_EXTRACT_STRING(m_tstr); } + case TYPE_CONCAT_AEXEPATH: + { + m_tstr = tinfo->concat_attribute_thread_lineage([](sinsp_threadinfo* t) {return t->get_exepath();}, m_argid); + RETURN_EXTRACT_STRING(m_tstr); + } case TYPE_LOGINSHELLID: { - sinsp_threadinfo* mt = NULL; int64_t* res = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return NULL; - } + return NULL; } sinsp_threadinfo::visitor_func_t check_thread_for_shell = [&res] (sinsp_threadinfo *pt) @@ -1624,20 +1459,11 @@ bool sinsp_filter_check_thread::compare_full_apid(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1677,20 +1503,11 @@ bool sinsp_filter_check_thread::compare_full_aname(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1730,20 +1547,11 @@ bool sinsp_filter_check_thread::compare_full_aexe(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1783,20 +1591,11 @@ bool sinsp_filter_check_thread::compare_full_aexepath(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1836,20 +1635,11 @@ bool sinsp_filter_check_thread::compare_full_acmdline(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // @@ -1891,20 +1681,11 @@ bool sinsp_filter_check_thread::compare_full_aenv(sinsp_evt *evt) return false; } - sinsp_threadinfo* mt = NULL; + sinsp_threadinfo* mt = tinfo->get_main_thread(); - if(tinfo->is_main_thread()) + if (mt == NULL) { - mt = tinfo; - } - else - { - mt = tinfo->get_main_thread(); - - if(mt == NULL) - { - return false; - } + return false; } // diff --git a/userspace/libsinsp/sinsp_filtercheck_thread.h b/userspace/libsinsp/sinsp_filtercheck_thread.h index 7f519edb28..7312fe522c 100644 --- a/userspace/libsinsp/sinsp_filtercheck_thread.h +++ b/userspace/libsinsp/sinsp_filtercheck_thread.h @@ -29,12 +29,15 @@ class sinsp_filter_check_thread : public sinsp_filter_check TYPE_EXE = 0, TYPE_PEXE, TYPE_AEXE, + TYPE_CONCAT_AEXE, TYPE_EXEPATH, TYPE_PEXEPATH, TYPE_AEXEPATH, + TYPE_CONCAT_AEXEPATH, TYPE_NAME, TYPE_PNAME, TYPE_ANAME, + TYPE_CONCAT_ANAME, TYPE_ARGS, TYPE_CMDLINE, TYPE_PCMDLINE, diff --git a/userspace/libsinsp/test/events_proc.ut.cpp b/userspace/libsinsp/test/events_proc.ut.cpp index 1cc88128bc..6c061927db 100644 --- a/userspace/libsinsp/test/events_proc.ut.cpp +++ b/userspace/libsinsp/test/events_proc.ut.cpp @@ -352,6 +352,9 @@ TEST_F(sinsp_with_test_input, spawn_process) ASSERT_EQ(get_field_as_string(evt, "proc.name"), "test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aname[0]"), "test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aname"), "test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aname[0]"), "test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aname[2]"), "init->test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aname"), "test-exe"); // check that the pid is updated ASSERT_EQ(get_field_as_string(evt, "proc.pid"), "20"); @@ -363,11 +366,17 @@ TEST_F(sinsp_with_test_input, spawn_process) ASSERT_EQ(get_field_as_string(evt, "proc.exe"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexe[0]"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexe"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexe[0]"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexe[2]"), "/sbin/init->/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexe"), "/bin/test-exe"); // check that the exepath is updated ASSERT_EQ(get_field_as_string(evt, "proc.exepath"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexepath[0]"), "/bin/test-exe"); ASSERT_EQ(get_field_as_string(evt, "proc.aexepath"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexepath[0]"), "/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexepath[2]"), "/sbin/init->/bin/test-exe"); + ASSERT_EQ(get_field_as_string(evt, "proc.concat_aexepath"), "/bin/test-exe"); // check session leader (sid) related fields ASSERT_EQ(get_field_as_string(evt, "proc.sid"), "0"); diff --git a/userspace/libsinsp/threadinfo.cpp b/userspace/libsinsp/threadinfo.cpp index 7557fca4e6..952aaea884 100644 --- a/userspace/libsinsp/threadinfo.cpp +++ b/userspace/libsinsp/threadinfo.cpp @@ -102,10 +102,10 @@ sinsp_threadinfo::sinsp_threadinfo(sinsp* inspector, std::shared_ptr& get_attribute_func, int32_t max_level) +{ + auto mt = get_main_thread(); + if(mt == NULL) + { + return ""; + } + + std::string result = get_attribute_func(mt); + for (int32_t j = 0; j < max_level; j++) + { + mt = mt->get_parent_thread(); + if(mt == NULL) + { + return result; + } + result = get_attribute_func(mt) + "->" + result; + } + + return result; +} + +sinsp_threadinfo* sinsp_threadinfo::get_oldest_matching_ancestor(const std::function& get_thread_id, bool query_os_if_not_found) +{ + int64_t id = get_thread_id(this); + if(id == - 1) + { + // the id is not set + return nullptr; + } + + // If in the init namespace we can use the id for the thread table access + // try this first since it is faster! + sinsp_threadinfo* leader = nullptr; + if(!is_in_pid_namespace()) + { + leader = m_inspector->get_thread_ref(id, query_os_if_not_found).get(); + if(leader!=nullptr) + { + return leader; + } + } + + // If we are in a pid_namespace we cannot use directly m_sid to access the table + // since it could be related to a pid namespace. + sinsp_threadinfo::visitor_func_t visitor = [id, &leader, get_thread_id](sinsp_threadinfo* pt) + { + if(get_thread_id(pt) != id) + { + return false; + } + leader = pt; + return true; + }; + + traverse_parent_state(visitor); + return leader; +} + void sinsp_threadinfo::set_args(const char* args, size_t len) { m_args.clear(); diff --git a/userspace/libsinsp/threadinfo.h b/userspace/libsinsp/threadinfo.h index a6897b9bb3..5d18c49f39 100644 --- a/userspace/libsinsp/threadinfo.h +++ b/userspace/libsinsp/threadinfo.h @@ -245,6 +245,23 @@ class SINSP_PUBLIC sinsp_threadinfo : public libsinsp::state::table_entry return possible_main; } + std::string concat_attribute_thread_lineage(const std::function& get_attribute_func, int32_t max_level); + + /*! + \brief Return the "oldest ancestor" of this thread that matches a condition. + The "oldest ancestor" is the oldest thread in the lineage that matches the following condition: + `get_thread_id(this) = get_thread_id(ancestor)` + + \param get_thread_id custom function to select the ancestor we want to extract. + If we want to obtain the oldest session leader ancestor we can pass: + `[](sinsp_threadinfo* t) { return t->m_sid; }` + + \param query_os_if_not_found if this is a live a capture and this flag is + set to true, scan the /proc file system to find process information in + case the thread is not in the table. + */ + sinsp_threadinfo* get_oldest_matching_ancestor(const std::function& get_thread_id, bool query_os_if_not_found = false); + inline const sinsp_threadinfo* get_main_thread() const { return const_cast(this)->get_main_thread(); @@ -418,7 +435,7 @@ class SINSP_PUBLIC sinsp_threadinfo : public libsinsp::state::table_entry int64_t m_pid; ///< The id of the process containing this thread. In single thread threads, this is equal to tid. int64_t m_ptid; ///< The id of the process that started this thread. int64_t m_reaper_tid; ///< The id of the reaper for this thread - int64_t m_sid; ///< The session id of the process containing this thread. + int64_t m_sid; ///< The session id of the process containing this thread. Please note that this is referred to the pid_namespace in which the process is running, is not always referred to the init_pid_namespace, so it cannot be used as a key in our thread table. std::string m_comm; ///< Command name (e.g. "top") std::string m_exe; ///< argv[0] (e.g. "sshd: user@pts/4") std::string m_exepath; ///< full executable path