// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2023 The Falco Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

#include <math.h>

#include <libsinsp/sinsp_filtercheck_event.h>
#include <libsinsp/sinsp.h>
#include <libsinsp/sinsp_int.h>
#include <libsinsp/plugin.h>
#include <libsinsp/plugin_manager.h>
#include <libsinsp/value_parser.h>

using namespace std;

extern sinsp_evttables g_infotables;

#define UESTORAGE_INITIAL_BUFSIZE 256

#define RETURN_EXTRACT_VAR(x)  \
	do {                       \
		*len = sizeof((x));    \
		return (uint8_t*)&(x); \
	} while(0)

#define RETURN_EXTRACT_STRING(x)      \
	do {                              \
		*len = (x).size();            \
		return (uint8_t*)(x).c_str(); \
	} while(0)

#define RETURN_EXTRACT_CSTR(x)           \
	do {                                 \
		if((x)) {                        \
			*len = strlen((char*)((x))); \
		}                                \
		return (uint8_t*)((x));          \
	} while(0)

static inline bool str_match_start(std::string_view val, size_t len, const char* m) {
	return val.compare(0, len, m) == 0;
}

#define STR_MATCH(s) str_match_start(val, sizeof(s) - 1, s)

const filtercheck_field_info sinsp_filter_check_event_fields[] = {
        {PT_RELTIME,
         EPF_NONE | EPF_DEPRECATED,
         PF_DEC,
         "evt.latency",
         "Latency",
         "delta between an exit event and the correspondent enter event, in nanoseconds."},
        {PT_RELTIME,
         EPF_NONE | EPF_DEPRECATED,
         PF_DEC,
         "evt.latency.s",
         "Latency (s)",
         "integer part of the event latency delta."},
        {PT_RELTIME,
         EPF_NONE | EPF_DEPRECATED,
         PF_10_PADDED_DEC,
         "evt.latency.ns",
         "Latency (ns)",
         "fractional part of the event latency delta."},
        {PT_UINT64,
         EPF_TABLE_ONLY | EPF_DEPRECATED,
         PF_DEC,
         "evt.latency.quantized",
         "Quantized Latency",
         "10-base log of the delta between an exit event and the correspondent enter event."},
        {PT_CHARBUF,
         EPF_NONE | EPF_DEPRECATED,
         PF_NA,
         "evt.latency.human",
         "Human-Readable Latency",
         "delta between an exit event and the correspondent enter event, as a human readable "
         "string (e.g. 10.3ms)."},
        {PT_RELTIME,
         EPF_NONE,
         PF_DEC,
         "evt.deltatime",
         "Delta",
         "delta between this event and the previous event, in nanoseconds."},
        {PT_RELTIME,
         EPF_NONE,
         PF_DEC,
         "evt.deltatime.s",
         "Delta (s)",
         "integer part of the delta between this event and the previous event."},
        {PT_RELTIME,
         EPF_NONE,
         PF_10_PADDED_DEC,
         "evt.deltatime.ns",
         "Delta (ns)",
         "fractional part of the delta between this event and the previous event."},
        {PT_CHARBUF,
         EPF_PRINT_ONLY,
         PF_NA,
         "evt.outputtime",
         "Output Time",
         "this depends on -t param, default is %evt.time ('h')."},
        {PT_CHARBUF,
         EPF_NONE | EPF_DEPRECATED,
         PF_DIR,
         "evt.dir",
         "Direction",
         "event direction can be either '>' for enter events or '<' for exit events."},
        {PT_CHARBUF, EPF_NONE, PF_NA, "evt.type", "Type", "The name of the event (e.g. 'open')."},
        {PT_UINT32,
         EPF_ARG_REQUIRED,
         PF_NA,
         "evt.type.is",
         "Type Is",
         "allows one to specify an event type, and returns 1 for events that are of that type. For "
         "example, evt.type.is.open returns 1 for open events, 0 for any other event."},
        {PT_CHARBUF,
         EPF_NONE,
         PF_NA,
         "syscall.type",
         "Syscall Type",
         "For system call events, the name of the system call (e.g. 'open'). Unset for other "
         "events (e.g. switch or internal events). Use this field instead of evt.type if you need "
         "to make sure that the filtered/printed value is actually a system call."},
        {PT_CHARBUF,
         EPF_NONE,
         PF_NA,
         "evt.category",
         "Category",
         "The event category. Example values are 'file' (for file operations like open and close), "
         "'net' (for network operations like socket and bind), memory (for things like brk or "
         "mmap), and so on."},
        {PT_INT16,
         EPF_NONE,
         PF_ID,
         "evt.cpu",
         "CPU Number",
         "number of the CPU where this event happened."},
        {PT_CHARBUF,
         EPF_NONE,
         PF_NA,
         "evt.args",
         "Arguments",
         "all the event arguments, aggregated into a single string."},
        {PT_CHARBUF,
         EPF_ARG_REQUIRED | EPF_NO_PTR_STABILITY,
         PF_NA,
         "evt.arg",
         "Argument",
         "one of the event arguments specified by name or by number. Some events (e.g. return "
         "codes or FDs) will be converted into a text representation when possible. E.g. "
         "'evt.arg.fd' or 'evt.arg[0]'."},
        {PT_DYN,
         EPF_ARG_REQUIRED | EPF_NO_RHS | EPF_NO_TRANSFORMER,
         PF_NA,
         "evt.rawarg",
         "Raw Argument",
         "one of the event arguments specified by name. E.g. 'evt.rawarg.fd'."},
        {PT_CHARBUF,
         EPF_NONE,
         PF_NA,
         "evt.info",
         "Information",
         "Currently, this field returns the same value as 'evt.args'."},
        {PT_BYTEBUF,
         EPF_NO_PTR_STABILITY,
         PF_NA,
         "evt.buffer",
         "Buffer",
         "the binary data buffer for events that have one, like read(), recvfrom(), etc. Use this "
         "field in filters with 'contains' to search into I/O data buffers."},
        {PT_UINT64,
         EPF_NONE,
         PF_DEC,
         "evt.buflen",
         "Buffer Length",
         "the length of the binary data buffer for events that have one, like read(), recvfrom(), "
         "etc."},
        {PT_CHARBUF,
         EPF_NO_PTR_STABILITY,
         PF_DEC,
         "evt.res",
         "Return Value",
         "event return value, as a string. If the event failed, the result is an error code string "
         "(e.g. 'ENOENT'), otherwise the result is the string 'SUCCESS'."},
        {PT_INT64,
         EPF_NONE,
         PF_DEC,
         "evt.rawres",
         "Raw Return Value",
         "event return value, as a number (e.g. -2). Useful for range comparisons."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.failed",
         "Failed",
         "'true' for events that returned an error status."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_io",
         "Is I/O",
         "'true' for events that read or write to FDs, like read(), send, recvfrom(), etc."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_io_read",
         "Is Read",
         "'true' for events that read from FDs, like read(), recv(), recvfrom(), etc."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_io_write",
         "Is Write",
         "'true' for events that write to FDs, like write(), send(), etc."},
        {PT_CHARBUF,
         EPF_NONE,
         PF_NA,
         "evt.io_dir",
         "I/O Direction",
         "'r' for events that read from FDs, like read(); 'w' for events that write to FDs, like "
         "write()."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_wait",
         "Is Wait",
         "'true' for events that make the thread wait, e.g. sleep(), select(), poll()."},
        {PT_RELTIME,
         EPF_NONE | EPF_DEPRECATED,
         PF_DEC,
         "evt.wait_latency",
         "Wait Latency",
         "for events that make the thread wait (e.g. sleep(), select(), poll()), this is the time "
         "spent waiting for the event to return, in nanoseconds."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_syslog",
         "Is Syslog",
         "'true' for events that are writes to /dev/log."},
        {PT_UINT32, EPF_NONE, PF_DEC, "evt.count", "Count", "This filter field always returns 1."},
        {PT_UINT32,
         EPF_NONE,
         PF_DEC,
         "evt.count.error",
         "Error Count",
         "This filter field returns 1 for events that returned with an error."},
        {PT_UINT32,
         EPF_NONE,
         PF_DEC,
         "evt.count.error.file",
         "File Error Count",
         "This filter field returns 1 for events that returned with an error and are related to "
         "file I/O."},
        {PT_UINT32,
         EPF_NONE,
         PF_DEC,
         "evt.count.error.net",
         "Network Error Count",
         "This filter field returns 1 for events that returned with an error and are related to "
         "network I/O."},
        {PT_UINT32,
         EPF_NONE,
         PF_DEC,
         "evt.count.error.memory",
         "Memory Error Count",
         "This filter field returns 1 for events that returned with an error and are related to "
         "memory allocation."},
        {PT_UINT32,
         EPF_NONE,
         PF_DEC,
         "evt.count.error.other",
         "Other Error Count",
         "This filter field returns 1 for events that returned with an error and are related to "
         "none of the previous categories."},
        {PT_UINT32,
         EPF_NONE,
         PF_DEC,
         "evt.count.exit",
         "Exit Count",
         "This filter field returns 1 for exit events."},
        {PT_UINT32,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.count.procinfo",
         "Procinfo Count",
         "This filter field returns 1 for procinfo events generated by process main threads."},
        {PT_UINT32,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.count.threadinfo",
         "Thread Info Count",
         "This filter field returns 1 for procinfo events."},
        {PT_UINT64,
         (filtercheck_field_flags)(EPF_FILTER_ONLY | EPF_ARG_REQUIRED | EPF_NO_RHS |
                                   EPF_NO_TRANSFORMER),
         PF_DEC,
         "evt.around",
         "Around Interval",
         "Accepts the event if it's around the specified time interval. The syntax is "
         "evt.around[T]=D, where T is the value returned by %evt.rawtime for the event and D is a "
         "delta in milliseconds. For example, evt.around[1404996934793590564]=1000 will return the "
         "events with timestamp with one second before the timestamp and one second after it, for "
         "a total of two seconds of capture."},
        {PT_CHARBUF,
         EPF_ARG_REQUIRED,
         PF_NA,
         "evt.abspath",
         "Absolute Path",
         "Absolute path calculated from dirfd and name during syscalls like renameat and "
         "symlinkat. Use 'evt.abspath.src' or 'evt.abspath.dst' for syscalls that support multiple "
         "paths."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.in",
         "Input Buffer Length",
         "the length of the binary data buffer, but only for input I/O events."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.out",
         "Output Buffer Length",
         "the length of the binary data buffer, but only for output I/O events."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.file",
         "File Buffer Length",
         "the length of the binary data buffer, but only for file I/O events."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.file.in",
         "File Input Buffer Length",
         "the length of the binary data buffer, but only for input file I/O events."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.file.out",
         "File Output Buffer Length",
         "the length of the binary data buffer, but only for output file I/O events."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.net",
         "Network Buffer Length",
         "the length of the binary data buffer, but only for network I/O events."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.net.in",
         "Network Input Buffer Length",
         "the length of the binary data buffer, but only for input network I/O events."},
        {PT_UINT64,
         EPF_TABLE_ONLY,
         PF_DEC,
         "evt.buflen.net.out",
         "Network Output Buffer Length",
         "the length of the binary data buffer, but only for output network I/O events."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_open_read",
         "Is Opened For Reading",
         "'true' for open/openat/openat2/open_by_handle_at events where the path was opened for "
         "reading"},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_open_write",
         "Is Opened For Writing",
         "'true' for open/openat/openat2/open_by_handle_at events where the path was opened for "
         "writing"},
        {PT_CHARBUF,
         EPF_TABLE_ONLY,
         PF_NA,
         "evt.infra.docker.name",
         "Docker Name",
         "for docker infrastructure events, the name of the event."},
        {PT_CHARBUF,
         EPF_TABLE_ONLY,
         PF_NA,
         "evt.infra.docker.container.id",
         "Docker ID",
         "for docker infrastructure events, the id of the impacted container."},
        {PT_CHARBUF,
         EPF_TABLE_ONLY,
         PF_NA,
         "evt.infra.docker.container.name",
         "Container Name",
         "for docker infrastructure events, the name of the impacted container."},
        {PT_CHARBUF,
         EPF_TABLE_ONLY,
         PF_NA,
         "evt.infra.docker.container.image",
         "Container Image",
         "for docker infrastructure events, the image name of the impacted container."},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_open_exec",
         "Is Created With Execute Permissions",
         "'true' for open/openat/openat2/open_by_handle_at or creat events where a file is created "
         "with execute permissions"},
        {PT_BOOL,
         EPF_NONE,
         PF_NA,
         "evt.is_open_create",
         "Is Created",
         "'true' for for open/openat/openat2/open_by_handle_at events where a file is created."},
};

sinsp_filter_check_event::sinsp_filter_check_event() {
	static const filter_check_info s_field_infos = {
	        "evt",
	        "Syscall events only",
	        "Event fields applicable to syscall events. Note that for most events you can access "
	        "the individual arguments/parameters of each syscall via evt.arg, e.g. "
	        "evt.arg.filename.",
	        sizeof(sinsp_filter_check_event_fields) / sizeof(sinsp_filter_check_event_fields[0]),
	        sinsp_filter_check_event_fields,
	        filter_check_info::FL_NONE,
	};
	m_is_compare = false;
	m_info = &s_field_infos;
	memset(&m_val, 0, sizeof(m_val));
	m_converter = std::make_unique<sinsp_filter_check_reference>();
}

std::unique_ptr<sinsp_filter_check> sinsp_filter_check_event::allocate_new() {
	return std::make_unique<sinsp_filter_check_event>();
}

int32_t sinsp_filter_check_event::extract_arg(string_view fldname,
                                              string_view val,
                                              const ppm_param_info** parinfo) {
	uint32_t parsed_len = 0;

	//
	// 'arg' and 'resarg' are handled in a custom way
	//
	if(val.size() > fldname.size() && val.at(fldname.size()) == '[') {
		if(parinfo != NULL) {
			throw sinsp_exception("evt.arg fields must be expressed explicitly");
		}

		parsed_len = (uint32_t)val.find(']');
		string numstr(val.substr(fldname.size() + 1, parsed_len - fldname.size() - 1));

		if(m_field_id == TYPE_AROUND) {
			m_val.u64 = sinsp_numparser::parseu64(numstr);
		} else {
			m_argid = sinsp_numparser::parsed32(numstr);
		}

		parsed_len++;
	} else if(val.size() > fldname.size() && val.at(fldname.size()) == '.') {
		if(m_field_id == TYPE_AROUND) {
			throw sinsp_exception("wrong syntax for evt.around");
		}

		const ppm_param_info* pi =
		        sinsp_utils::find_longest_matching_evt_param(val.substr(fldname.size() + 1));

		if(pi == NULL) {
			throw sinsp_exception("unknown event argument " +
			                      string(val.substr(fldname.size() + 1)));
		}

		m_argname = pi->name;
		parsed_len = (uint32_t)(fldname.size() + strlen(pi->name) + 1);
		m_argid = -1;

		if(parinfo != NULL) {
			*parinfo = pi;
		}
	} else {
		throw sinsp_exception("filter syntax error: " + string(val));
	}

	return parsed_len;
}

int32_t sinsp_filter_check_event::extract_type(string_view fldname,
                                               string_view val,
                                               const ppm_param_info** parinfo) {
	uint32_t parsed_len = 0;

	if(val.size() > fldname.size() && val.at(fldname.size()) == '.') {
		string itype(val.substr(fldname.size() + 1));

		if(sinsp_numparser::tryparseu32(itype, &m_evtid)) {
			m_evtid1 = PPM_EVENT_MAX;
			parsed_len = (uint32_t)(fldname.size() + itype.size() + 1);
			return parsed_len;
		}

		for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) {
			const ppm_event_info* ei = &g_infotables.m_event_info[j];

			if(itype == ei->name) {
				m_evtid = j;
				m_evtid1 = j + 1;
				parsed_len = (uint32_t)(fldname.size() + strlen(ei->name) + 1);
				break;
			}
		}
	} else {
		throw sinsp_exception("filter syntax error: " + string(val));
	}

	return parsed_len;
}

int32_t sinsp_filter_check_event::parse_field_name(std::string_view val,
                                                   bool alloc_state,
                                                   bool needed_for_filtering) {
	int32_t res = 0;

	//
	// A couple of fields are handled in a custom way
	//
	if(STR_MATCH("evt.arg") && !STR_MATCH("evt.args")) {
		m_field_id = TYPE_ARGSTR;
		m_field = &m_info->m_fields[m_field_id];

		res = extract_arg("evt.arg", val, NULL);
	} else if(STR_MATCH("evt.rawarg")) {
		m_field_id = TYPE_ARGRAW;
		m_customfield = m_info->m_fields[m_field_id];
		m_field = &m_customfield;

		res = extract_arg("evt.rawarg", val, &m_arginfo);

		// These fields will be overridden with correct ones from the current event
		// while extracting from event.
		// But we also need to set them at init time to something different than `PT_DYN`
		// to allow `eval_filter` expressions to be properly compiled.
		// Note: even if they'll be overridden,
		// it is safe to guess that eg: `size` param will always be a numeric type.
		// Of course if we had a `size` param that is string, it would be troublesome.
		m_customfield.m_type = m_arginfo->type;
		m_customfield.m_print_format = m_arginfo->fmt;
	} else if(STR_MATCH("evt.around")) {
		m_field_id = TYPE_AROUND;
		m_field = &m_info->m_fields[m_field_id];

		res = extract_arg("evt.around", val, NULL);
	} else if(STR_MATCH("evt.latency") || STR_MATCH("evt.latency.s") ||
	          STR_MATCH("evt.latency.ns") || STR_MATCH("evt.latency.quantized") ||
	          STR_MATCH("evt.latency.human")) {
		res = sinsp_filter_check::parse_field_name(val, alloc_state, needed_for_filtering);
	} else if(STR_MATCH("evt.abspath")) {
		m_field_id = TYPE_ABSPATH;
		m_field = &m_info->m_fields[m_field_id];

		if(STR_MATCH("evt.abspath.src")) {
			m_argid = 1;
			res = sizeof("evt.abspath.src") - 1;
		} else if(STR_MATCH("evt.abspath.dst")) {
			m_argid = 2;
			res = sizeof("evt.abspath.dst") - 1;
		} else {
			m_argid = 0;
			res = sizeof("evt.abspath") - 1;
		}
	} else if(STR_MATCH("evt.type.is")) {
		m_field_id = TYPE_TYPE_IS;
		m_field = &m_info->m_fields[m_field_id];

		res = extract_type("evt.type.is", val, NULL);
	} else {
		res = sinsp_filter_check::parse_field_name(val, alloc_state, needed_for_filtering);
	}

	return res;
}

size_t sinsp_filter_check_event::parse_filter_value(const char* str,
                                                    uint32_t len,
                                                    uint8_t* storage,
                                                    uint32_t storage_len) {
	size_t parsed_len;
	if(m_field_id == sinsp_filter_check_event::TYPE_ARGRAW) {
		ASSERT(m_arginfo != NULL);
		parsed_len = sinsp_filter_value_parser::string_to_rawval(str,
		                                                         len,
		                                                         storage,
		                                                         storage_len,
		                                                         m_arginfo->type);
	} else {
		parsed_len = sinsp_filter_check::parse_filter_value(str, len, storage, storage_len);
	}

	validate_filter_value(str, parsed_len);

	return parsed_len;
}

void sinsp_filter_check_event::validate_filter_value(const char* str, uint32_t len) {
	if(m_field_id == TYPE_TYPE) {
		sinsp_evttables* einfo = m_inspector->get_event_info_tables();
		const ppm_event_info* etable = einfo->m_event_info;
		string stype(str, len);

		for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) {
			if(stype == etable[j].name) {
				return;
			}
		}

		for(uint16_t j = 0; j < PPM_SC_MAX; j++) {
			if(stype == scap_get_ppm_sc_name((ppm_sc_code)j)) {
				return;
			}
		}

		// note: plugins can potentially define meta-events with a certain
		// name, which will be extracted as valid values for evt.type
		// we loop over all plugins and check if at least one defines a
		// meta-event with the given name
		for(auto& p : m_inspector->get_plugin_manager()->plugins()) {
			if(p->caps() & CAP_ASYNC) {
				const auto& names = p->async_event_names();
				if(names.find(stype) != names.end()) {
					return;
				}
			}
		}

		throw sinsp_exception("unknown event type " + stype);
	} else if(m_field_id == TYPE_AROUND) {
		if(m_cmpop != CO_EQ) {
			throw sinsp_exception("evt.around supports only '=' comparison operator");
		}

		m_tsdelta = sinsp_numparser::parseu64(str) * 1000000;

		return;
	}
}

uint8_t* sinsp_filter_check_event::extract_argraw(sinsp_evt* evt,
                                                  uint32_t* len,
                                                  const char* argname) {
	const auto pi = evt->get_param_by_name(argname);
	if(pi == nullptr) {
		return nullptr;
	}

	m_arginfo = pi->get_info();
	m_customfield.m_type = m_arginfo->type;
	m_customfield.m_print_format = m_arginfo->fmt;
	const auto [data, data_len] = pi->data_and_len_with_legacy_null_encoding();
	*len = data_len;
	// TODO(ekoops): remove const_cast once we update the signature to return a pointer to a
	//   constant buffer.
	return const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(data));
}

uint8_t* sinsp_filter_check_event::extract_abspath(sinsp_evt* evt, uint32_t* len) {
	std::string spath;

	if(evt->get_tinfo() == nullptr) {
		return nullptr;
	}

	uint16_t etype = evt->get_type();

	const char *dirfdarg = nullptr, *patharg = nullptr;
	if(etype == PPME_SYSCALL_RENAMEAT_X || etype == PPME_SYSCALL_RENAMEAT2_X) {
		if(m_argid == 0 || m_argid == 1) {
			dirfdarg = "olddirfd";
			patharg = "oldpath";
		} else if(m_argid == 2) {
			dirfdarg = "newdirfd";
			patharg = "newpath";
		}
	} else if(etype == PPME_SYSCALL_SYMLINKAT_X) {
		dirfdarg = "linkdirfd";
		patharg = "linkpath";
	} else if(etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT2_X) {
		dirfdarg = "dirfd";
		patharg = "name";
	} else if(etype == PPME_SYSCALL_OPEN_BY_HANDLE_AT_X) {
		int fd = 0;
		std::string fullname;

		//
		// We can extract the file path only in case of a successful file opening (fd>0).
		//
		fd = evt->get_syscall_return_value();

		if(fd > 0) {
			//
			// Get the file path directly from the ring buffer.
			// concatenate_paths takes care of resolving the path
			//
			m_strstorage = sinsp_utils::concatenate_paths("", evt->get_param(3)->as<std::string>());

			RETURN_EXTRACT_STRING(m_strstorage);
		}
	} else if(etype == PPME_SYSCALL_LINKAT_2_X) {
		if(m_argid == 0 || m_argid == 1) {
			dirfdarg = "olddir";
			patharg = "oldpath";
		} else if(m_argid == 2) {
			dirfdarg = "newdir";
			patharg = "newpath";
		}
	} else if(etype == PPME_SYSCALL_UNLINKAT_2_X) {
		dirfdarg = "dirfd";
		patharg = "name";
	} else if(etype == PPME_SYSCALL_MKDIRAT_X) {
		dirfdarg = "dirfd";
		patharg = "path";
	} else if(etype == PPME_SYSCALL_FCHMODAT_X) {
		dirfdarg = "dirfd";
		patharg = "filename";
	} else if(etype == PPME_SYSCALL_FCHOWNAT_X) {
		dirfdarg = "dirfd";
		patharg = "pathname";
	}

	if(!dirfdarg || !patharg) {
		return nullptr;
	}

	int dirfdargidx = -1, pathargidx = -1, idx = 0;
	while(((dirfdargidx < 0) || (pathargidx < 0)) && (idx < (int)evt->get_num_params())) {
		const char* name = evt->get_param_name(idx);
		if((dirfdargidx < 0) && (strcmp(name, dirfdarg) == 0)) {
			dirfdargidx = idx;
		}
		if((pathargidx < 0) && (strcmp(name, patharg) == 0)) {
			pathargidx = idx;
		}
		idx++;
	}

	if((dirfdargidx < 0) || (pathargidx < 0)) {
		return nullptr;
	}

	// Make sure that the parameters are not empty.
	const auto dirfd_param = evt->get_param(dirfdargidx);
	const auto path_param = evt->get_param(pathargidx);
	if(dirfd_param->empty() || path_param->empty()) {
		return nullptr;
	}

	const auto dirfd = dirfd_param->as<int64_t>();
	const auto path = path_param->as<std::string_view>();

	string sdir;

	bool is_absolute = (path[0] == '/');
	if(is_absolute) {
		//
		// The path is absolute.
		// Some processes (e.g. irqbalance) actually do this: they pass an invalid fd and
		// and absolute path, and openat succeeds.
		//
		sdir = ".";
	} else if(dirfd == PPM_AT_FDCWD) {
		sdir = evt->get_tinfo()->get_cwd();
	} else {
		evt->set_fd_info(evt->get_tinfo()->get_fd(dirfd));

		if(evt->get_fd_info() == nullptr) {
			ASSERT(false);
			sdir = "<UNKNOWN>/";
		} else {
			if(evt->get_fd_info()->m_name[evt->get_fd_info()->m_name.length()] == '/') {
				sdir = evt->get_fd_info()->m_name;
			} else {
				sdir = evt->get_fd_info()->m_name + '/';
			}
		}
	}

	m_strstorage = sinsp_utils::concatenate_paths(sdir, path);

	RETURN_EXTRACT_STRING(m_strstorage);
}

inline uint8_t* sinsp_filter_check_event::extract_buflen(sinsp_evt* evt, uint32_t* len) {
	if(evt->get_direction() == SCAP_ED_OUT) {
		//
		// Extract the return value
		//
		m_val.s64 = evt->get_syscall_return_value();

		if(m_val.s64 >= 0) {
			RETURN_EXTRACT_VAR(m_val.s64);
		}
	}

	return NULL;
}

Json::Value sinsp_filter_check_event::extract_as_js(sinsp_evt* evt, uint32_t* len) {
	switch(m_field_id) {
	case TYPE_RUNTIME_TIME_OUTPUT_FORMAT:
		return (Json::Value::Int64)evt->get_ts();

	case TYPE_LATENCY:
	case TYPE_LATENCY_S:
	case TYPE_LATENCY_NS:
	case TYPE_DELTA:
	case TYPE_DELTA_S:
	case TYPE_DELTA_NS:
		return (Json::Value::Int64) * (uint64_t*)extract_single(evt, len);
	case TYPE_COUNT:
		m_val.u32 = 1;
		return m_val.u32;

	default:
		return Json::nullValue;
	}

	return Json::nullValue;
}

uint8_t* sinsp_filter_check_event::extract_error_count(sinsp_evt* evt, uint32_t* len) {
	// todo!: we need to understand if we want to return `0` or `<NA>`.
	// At the moment we return `<NA>`.
	if(!evt->has_return_value()) {
		return NULL;
	}

	m_val.u32 = 0;
	if(evt->get_syscall_return_value() < 0) {
		m_val.u32 = 1;
	}
	RETURN_EXTRACT_VAR(m_val.u32);
}

uint8_t* sinsp_filter_check_event::extract_single(sinsp_evt* evt,
                                                  uint32_t* len,
                                                  bool sanitize_strings) {
	*len = 0;
	switch(m_field_id) {
	case TYPE_LATENCY:
	case TYPE_LATENCY_S:
	case TYPE_LATENCY_NS:
	case TYPE_LATENCY_QUANTIZED:
	case TYPE_WAIT_LATENCY: {
		// Hardcoded value to 0 for now to avoid breaking changes.
		// TODO(irozzo): get rid of this once the deprecated fields are removed.
		m_val.u64 = 0;
		RETURN_EXTRACT_VAR(m_val.u64);
	}
	case TYPE_LATENCY_HUMAN: {
		// Hardcoded value to "0ns" for now to avoid breaking changes.
		// TODO(irozzo): get rid of this once the deprecated fields are removed.
		m_strstorage = "0ns";
		RETURN_EXTRACT_STRING(m_strstorage);
	}
	case TYPE_DELTA:
	case TYPE_DELTA_S:
	case TYPE_DELTA_NS: {
		if(m_val.u64 == 0) {
			m_val.u64 = evt->get_ts();
			m_tsdelta = 0;
		} else {
			uint64_t tts = evt->get_ts();

			if(m_field_id == TYPE_DELTA) {
				m_tsdelta = tts - m_val.u64;
			} else if(m_field_id == TYPE_DELTA_S) {
				m_tsdelta = (tts - m_val.u64) / ONE_SECOND_IN_NS;
			} else if(m_field_id == TYPE_DELTA_NS) {
				m_tsdelta = (tts - m_val.u64) % ONE_SECOND_IN_NS;
			}

			m_val.u64 = tts;
		}

		RETURN_EXTRACT_VAR(m_tsdelta);
	}
	case TYPE_RUNTIME_TIME_OUTPUT_FORMAT: {
		char timebuffer[100];
		m_strstorage = "";
		switch(m_inspector->get_time_output_mode()) {
		case 'h':
			sinsp_utils::ts_to_string(evt->get_ts(), &m_strstorage, false, true);
			RETURN_EXTRACT_STRING(m_strstorage);

		case 'a':
			m_strstorage += to_string(evt->get_ts() / ONE_SECOND_IN_NS);
			m_strstorage += ".";
			m_strstorage += to_string(evt->get_ts() % ONE_SECOND_IN_NS);
			RETURN_EXTRACT_STRING(m_strstorage);

		case 'r':
			m_strstorage +=
			        to_string((evt->get_ts() - m_inspector->m_firstevent_ts) / ONE_SECOND_IN_NS);
			m_strstorage += ".";
			snprintf(timebuffer,
			         sizeof(timebuffer),
			         "%09llu",
			         (evt->get_ts() - m_inspector->m_firstevent_ts) % ONE_SECOND_IN_NS);
			m_strstorage += string(timebuffer);
			RETURN_EXTRACT_STRING(m_strstorage);

		case 'd': {
			m_strstorage = "0.000000000";
			RETURN_EXTRACT_STRING(m_strstorage);
		}

		case 'D':
			if(m_val.u64 == 0) {
				m_val.u64 = evt->get_ts();
				m_tsdelta = 0;
			}
			uint64_t tts = evt->get_ts();

			m_strstorage += to_string((tts - m_val.u64) / ONE_SECOND_IN_NS);
			m_tsdelta = (tts - m_val.u64) / ONE_SECOND_IN_NS;
			m_strstorage += ".";
			snprintf(timebuffer,
			         sizeof(timebuffer),
			         "%09llu",
			         (tts - m_val.u64) % ONE_SECOND_IN_NS);
			m_strstorage += string(timebuffer);
			m_tsdelta = (tts - m_val.u64) % ONE_SECOND_IN_NS;

			m_val.u64 = tts;
			RETURN_EXTRACT_STRING(m_strstorage);
		}
	}
	case TYPE_DIR:
		if(PPME_IS_ENTER(evt->get_type())) {
			RETURN_EXTRACT_CSTR(">");
		} else {
			RETURN_EXTRACT_CSTR("<");
		}
	case TYPE_TYPE: {
		// TODO(ekoops): from each case, remove the following const_casts once the method signature
		//   is updated to return a pointer to a const buffer.
		char* evt_name;
		switch(evt->get_scap_evt()->type) {
		case PPME_GENERIC_X: {
			uint16_t ppm_sc = evt->get_param(0)->as<uint16_t>();
			// Try to enforce a forward compatibility for syscalls added after a scap file was
			// generated, by looking up using nativeID. Of course, this will only reliably work for
			// same architecture scap capture->replay.
			// Old scap-files doesn't contain the generic nativeID params, so check its presence in
			// capture mode.
			if(const auto native_id_param = evt->get_param(1);
			   m_inspector && m_inspector->is_capture() && ppm_sc == PPM_SC_UNKNOWN &&
			   !native_id_param->empty()) {
				const auto native_id = native_id_param->as<uint16_t>();
				ppm_sc = scap_native_id_to_ppm_sc(native_id);
			}
			evt_name = const_cast<char*>(scap_get_ppm_sc_name(static_cast<ppm_sc_code>(ppm_sc)));
			break;
		}
		case PPME_ASYNCEVENT_E: {
			// Note: for async events, the event name is encoded inside the event itself. In this
			// case libsinsp's evt.type field acts as an alias of evt.asynctype.
			const auto name_param = evt->get_param(1);
			const auto [data, _] = name_param->data_and_len_with_legacy_null_encoding();
			evt_name = const_cast<char*>(data);
			break;
		}
		default: {
			evt_name = const_cast<char*>(evt->get_name());
		}
		}

		RETURN_EXTRACT_CSTR(evt_name);
	} break;
	case TYPE_TYPE_IS: {
		uint16_t etype = evt->get_scap_evt()->type;

		if(etype == m_evtid || etype == m_evtid1) {
			m_val.u32 = 1;
		} else {
			m_val.u32 = 0;
		}

		RETURN_EXTRACT_VAR(m_val.u32);
	} break;
	case TYPE_SYSCALL_TYPE: {
		const auto etype = static_cast<ppm_event_code>(evt->get_scap_evt()->type);
		if(!libsinsp::events::is_syscall_event(etype)) {
			return nullptr;
		}

		// TODO(ekoops): from each case, remove the following const_casts once the method signature
		//   is updated to return a pointer to a const buffer.
		char* evt_name;
		if(etype == PPME_GENERIC_X) {
			uint16_t ppm_sc = evt->get_param(0)->as<uint16_t>();

			// Try to enforce a forward compatibility for syscalls added after a scap file was
			// generated, by looking up using nativeID. Of course, this will only reliably work for
			// same architecture scap capture->replay.
			// Old scap-files doesn't contain the generic nativeID params, so check its presence in
			// capture mode.
			if(const auto native_id_param = evt->get_param(1);
			   m_inspector && m_inspector->is_capture() && ppm_sc == PPM_SC_UNKNOWN &&
			   !native_id_param->empty()) {
				const auto native_id = native_id_param->as<uint16_t>();
				ppm_sc = scap_native_id_to_ppm_sc(native_id);
			}
			evt_name = const_cast<char*>(scap_get_ppm_sc_name(static_cast<ppm_sc_code>(ppm_sc)));
		} else {
			evt_name = const_cast<char*>(evt->get_name());
		}

		RETURN_EXTRACT_CSTR(evt_name);
	} break;
	case TYPE_CATEGORY:
		sinsp_evt::category cat;
		evt->get_category(&cat);

		switch(cat.m_category) {
		case EC_UNKNOWN:
			m_strstorage = "unknown";
			break;
		case EC_OTHER:
			m_strstorage = "other";
			break;
		case EC_FILE:
			m_strstorage = "file";
			break;
		case EC_NET:
			m_strstorage = "net";
			break;
		case EC_IPC:
			m_strstorage = "IPC";
			break;
		case EC_MEMORY:
			m_strstorage = "memory";
			break;
		case EC_PROCESS:
			m_strstorage = "process";
			break;
		case EC_SLEEP:
			m_strstorage = "sleep";
			break;
		case EC_SYSTEM:
			m_strstorage = "system";
			break;
		case EC_SIGNAL:
			m_strstorage = "signal";
			break;
		case EC_USER:
			m_strstorage = "user";
			break;
		case EC_TIME:
			m_strstorage = "time";
			break;
		case EC_PROCESSING:
			m_strstorage = "processing";
			break;
		case EC_IO_READ:
		case EC_IO_WRITE:
		case EC_IO_OTHER: {
			switch(cat.m_subcategory) {
			case sinsp_evt::SC_FILE:
				m_strstorage = "file";
				break;
			case sinsp_evt::SC_NET:
				m_strstorage = "net";
				break;
			case sinsp_evt::SC_IPC:
				m_strstorage = "ipc";
				break;
			case sinsp_evt::SC_NONE:
			case sinsp_evt::SC_UNKNOWN:
			case sinsp_evt::SC_OTHER:
				m_strstorage = "unknown";
				break;
			default:
				ASSERT(false);
				m_strstorage = "unknown";
				break;
			}
		} break;
		case EC_WAIT:
			m_strstorage = "wait";
			break;
		case EC_SCHEDULER:
			m_strstorage = "scheduler";
			break;
		case EC_INTERNAL:
			m_strstorage = "internal";
			break;
		case EC_SYSCALL:
			m_strstorage = "syscall";
			break;
		case EC_TRACEPOINT:
			m_strstorage = "tracepoint";
			break;
		case EC_PLUGIN:
			m_strstorage = "plugin";
			break;
		case EC_METAEVENT:
			m_strstorage = "meta";
			break;
		default:
			m_strstorage = "unknown";
			break;
		}

		RETURN_EXTRACT_STRING(m_strstorage);
	case TYPE_CPU:
		m_val.u16 = evt->get_cpuid();
		RETURN_EXTRACT_VAR(m_val.u16);
	case TYPE_ARGRAW:
		return extract_argraw(evt, len, m_argname.c_str());
		break;
	case TYPE_ARGSTR: {
		const char* resolved_argstr;
		const char* argstr;

		ASSERT(m_inspector != NULL);

		sinsp_evt::param_fmt format =
		        m_is_compare ? sinsp_evt::param_fmt::PF_NORMAL : m_inspector->get_buffer_format();

		if(m_argid != -1) {
			if(m_argid >= (int32_t)evt->get_num_params()) {
				return NULL;
			}

			argstr = evt->get_param_as_str(m_argid, &resolved_argstr, format);
		} else {
			argstr = evt->get_param_value_str(m_argname, &resolved_argstr, format);
		}

		if(resolved_argstr != NULL && resolved_argstr[0] != 0) {
			RETURN_EXTRACT_CSTR(resolved_argstr);
		} else {
			RETURN_EXTRACT_CSTR(argstr);
		}
	} break;
	case TYPE_INFO: {
		// No info line support at the moment.
	}
	// NOTE: this falls through to TYPE_ARGS, and that's what we want! Please don't add anything
	// here!
	case TYPE_ARGS: {
		if(evt->get_type() == PPME_GENERIC_X) {
			// Don't print the arguments for generic events: they have only internal use.
			RETURN_EXTRACT_CSTR("");
		}

		const char* resolved_argstr = NULL;
		const char* argstr = NULL;
		uint32_t nargs = evt->get_num_params();
		m_strstorage.clear();

		for(uint32_t j = 0; j < nargs; j++) {
			ASSERT(m_inspector != NULL);

			argstr = evt->get_param_as_str(j, &resolved_argstr, m_inspector->get_buffer_format());

			if(resolved_argstr[0] == 0) {
				m_strstorage += evt->get_param_name(j);
				m_strstorage += '=';
				m_strstorage += argstr;
				m_strstorage += " ";
			} else {
				m_strstorage += evt->get_param_name(j);
				m_strstorage += '=';
				m_strstorage += argstr;
				m_strstorage += string("(") + resolved_argstr + ") ";
			}
		}

		if(!m_strstorage.empty()) {
			m_strstorage.pop_back();
		}
		RETURN_EXTRACT_STRING(m_strstorage);
	} break;
	case TYPE_BUFFER: {
		if(m_is_compare) {
			return extract_argraw(evt, len, "data");
		}

		const char* resolved_argstr;
		const char* argstr;
		argstr = evt->get_param_value_str("data",
		                                  &resolved_argstr,
		                                  m_inspector->get_buffer_format());
		*len = evt->get_rawbuf_str_len();

		return (uint8_t*)argstr;
	}
	case TYPE_BUFLEN:
		if(evt->get_fd_info() && evt->get_category() & EC_IO_BASE) {
			return extract_buflen(evt, len);
		}
		break;
	case TYPE_RESRAW:
		if(!evt->has_return_value()) {
			return NULL;
		}

		m_val.s64 = evt->get_syscall_return_value();
		RETURN_EXTRACT_VAR(m_val.s64);
	case TYPE_RESSTR: {
		if(!evt->has_return_value()) {
			return NULL;
		}

		int64_t res = evt->get_syscall_return_value();
		if(res >= 0) {
			RETURN_EXTRACT_CSTR("SUCCESS");
		}

		// todo!: we should check if a failed syscall can return something that is not an errno.
		// When res is < 0 usually it is an errno. In that case we could directly call
		// `sinsp_utils::errno_to_str`
		//
		// m_strstorage = sinsp_utils::errno_to_str((int32_t)res);
		m_strstorage = evt->get_param_value_str(0, true);
		RETURN_EXTRACT_STRING(m_strstorage);
	} break;
	case TYPE_ISIO: {
		ppm_event_flags eflags = evt->get_info_flags();
		if(eflags & (EF_READS_FROM_FD | EF_WRITES_TO_FD)) {
			m_val.u32 = 1;
		} else {
			m_val.u32 = 0;
		}
	}

		RETURN_EXTRACT_VAR(m_val.u32);
	case TYPE_ISIO_READ: {
		ppm_event_flags eflags = evt->get_info_flags();
		if(eflags & EF_READS_FROM_FD) {
			m_val.u32 = 1;
		} else {
			m_val.u32 = 0;
		}

		RETURN_EXTRACT_VAR(m_val.u32);
	}
	case TYPE_ISIO_WRITE: {
		ppm_event_flags eflags = evt->get_info_flags();
		if(eflags & EF_WRITES_TO_FD) {
			m_val.u32 = 1;
		} else {
			m_val.u32 = 0;
		}

		RETURN_EXTRACT_VAR(m_val.u32);
	}
	case TYPE_IODIR: {
		ppm_event_flags eflags = evt->get_info_flags();
		if(eflags & EF_WRITES_TO_FD) {
			m_strstorage = "write";
		} else if(eflags & EF_READS_FROM_FD) {
			m_strstorage = "read";
		} else {
			return NULL;
		}

		RETURN_EXTRACT_STRING(m_strstorage);
	}
	case TYPE_ISWAIT: {
		ppm_event_flags eflags = evt->get_info_flags();
		if(eflags & (EF_WAITS)) {
			m_val.u32 = 1;
		} else {
			m_val.u32 = 0;
		}
	}

		RETURN_EXTRACT_VAR(m_val.u32);
	case TYPE_ISSYSLOG: {
		m_val.u32 = 0;

		ppm_event_flags eflags = evt->get_info_flags();
		if(eflags & EF_WRITES_TO_FD) {
			sinsp_fdinfo* fdinfo = evt->get_fd_info();

			if(fdinfo != NULL && fdinfo->is_syslog()) {
				m_val.u32 = 1;
			}
		}

		RETURN_EXTRACT_VAR(m_val.u32);
	}
	case TYPE_COUNT:
		m_val.u32 = 1;
		RETURN_EXTRACT_VAR(m_val.u32);
	case TYPE_FAILED:
	case TYPE_COUNT_ERROR:
		return extract_error_count(evt, len);
	case TYPE_COUNT_ERROR_FILE: {
		sinsp_fdinfo* fdinfo = evt->get_fd_info();

		if(fdinfo != NULL) {
			if(fdinfo->m_type == SCAP_FD_FILE || fdinfo->m_type == SCAP_FD_FILE_V2 ||
			   fdinfo->m_type == SCAP_FD_DIRECTORY) {
				return extract_error_count(evt, len);
			}
		} else {
			if(const auto etype = evt->get_type();
			   etype == PPME_SYSCALL_OPEN_X || etype == PPME_SYSCALL_CREAT_X ||
			   etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT2_X ||
			   etype == PPME_SYSCALL_OPEN_BY_HANDLE_AT_X) {
				return extract_error_count(evt, len);
			}
		}

		return NULL;
	}
	case TYPE_COUNT_ERROR_NET: {
		sinsp_fdinfo* fdinfo = evt->get_fd_info();

		if(fdinfo != NULL) {
			if(fdinfo->m_type == SCAP_FD_IPV4_SOCK || fdinfo->m_type == SCAP_FD_IPV6_SOCK ||
			   fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK || fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK ||
			   fdinfo->m_type == SCAP_FD_UNIX_SOCK) {
				return extract_error_count(evt, len);
			}
		} else {
			uint16_t etype = evt->get_type();

			if(etype == PPME_SOCKET_ACCEPT_5_X || etype == PPME_SOCKET_ACCEPT4_6_X ||
			   etype == PPME_SOCKET_CONNECT_X) {
				return extract_error_count(evt, len);
			}
		}

		return NULL;
	}
	case TYPE_COUNT_ERROR_MEMORY: {
		if(evt->get_category() == EC_MEMORY) {
			return extract_error_count(evt, len);
		} else {
			return NULL;
		}
	}
	case TYPE_COUNT_ERROR_OTHER: {
		sinsp_fdinfo* fdinfo = evt->get_fd_info();

		if(fdinfo != NULL) {
			if(!(fdinfo->m_type == SCAP_FD_FILE || fdinfo->m_type == SCAP_FD_FILE_V2 ||
			     fdinfo->m_type == SCAP_FD_DIRECTORY || fdinfo->m_type == SCAP_FD_IPV4_SOCK ||
			     fdinfo->m_type == SCAP_FD_IPV6_SOCK || fdinfo->m_type == SCAP_FD_IPV4_SERVSOCK ||
			     fdinfo->m_type == SCAP_FD_IPV6_SERVSOCK || fdinfo->m_type == SCAP_FD_UNIX_SOCK)) {
				return extract_error_count(evt, len);
			}
		} else {
			if(const auto etype = evt->get_type();
			   !(etype == PPME_SYSCALL_OPEN_X || etype == PPME_SYSCALL_CREAT_X ||
			     etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT2_X ||
			     etype == PPME_SYSCALL_OPEN_BY_HANDLE_AT_X || etype == PPME_SOCKET_ACCEPT_5_X ||
			     etype == PPME_SOCKET_ACCEPT4_6_X || etype == PPME_SOCKET_CONNECT_X ||
			     evt->get_category() == EC_MEMORY)) {
				return extract_error_count(evt, len);
			}
		}

		return NULL;
	}
	case TYPE_COUNT_EXIT:
		if(PPME_IS_EXIT(evt->get_type())) {
			m_val.u32 = 1;
			RETURN_EXTRACT_VAR(m_val.u32);
		} else {
			return NULL;
		}
	case TYPE_COUNT_PROCINFO:
	case TYPE_COUNT_THREADINFO: {
		m_val.u32 = 0;
		if(evt->get_type() != PPME_PROCINFO_E) {
			RETURN_EXTRACT_VAR(m_val.u32);
		}

		if(m_field_id == TYPE_COUNT_THREADINFO) {
			m_val.u32 = 1;
		} else if(m_field_id == TYPE_COUNT_PROCINFO) {
			sinsp_threadinfo* tinfo = evt->get_thread_info();
			if(tinfo != NULL && tinfo->is_main_thread()) {
				m_val.u32 = 1;
			}
		}
		RETURN_EXTRACT_VAR(m_val.u32);
	}
	case TYPE_ABSPATH:
		return extract_abspath(evt, len);
	case TYPE_BUFLEN_IN:
		if(evt->get_fd_info() && evt->get_category() == EC_IO_READ) {
			return extract_buflen(evt, len);
		}

		break;
	case TYPE_BUFLEN_OUT:
		if(evt->get_fd_info() && evt->get_category() == EC_IO_WRITE) {
			return extract_buflen(evt, len);
		}

		break;
	case TYPE_BUFLEN_FILE:
		if(evt->get_fd_info() && evt->get_category() & EC_IO_BASE) {
			if(evt->get_fd_info()->m_type == SCAP_FD_FILE ||
			   evt->get_fd_info()->m_type == SCAP_FD_FILE_V2) {
				return extract_buflen(evt, len);
			}
		}

		break;
	case TYPE_BUFLEN_FILE_IN:
		if(evt->get_fd_info() && evt->get_category() == EC_IO_READ) {
			if(evt->get_fd_info()->m_type == SCAP_FD_FILE ||
			   evt->get_fd_info()->m_type == SCAP_FD_FILE_V2) {
				return extract_buflen(evt, len);
			}
		}

		break;
	case TYPE_BUFLEN_FILE_OUT:
		if(evt->get_fd_info() && evt->get_category() == EC_IO_WRITE) {
			if(evt->get_fd_info()->m_type == SCAP_FD_FILE ||
			   evt->get_fd_info()->m_type == SCAP_FD_FILE_V2) {
				return extract_buflen(evt, len);
			}
		}

		break;
	case TYPE_BUFLEN_NET:
		if(evt->get_fd_info() && evt->get_category() & EC_IO_BASE) {
			scap_fd_type etype = evt->get_fd_info()->m_type;

			if(etype >= SCAP_FD_IPV4_SOCK && etype <= SCAP_FD_IPV6_SERVSOCK) {
				return extract_buflen(evt, len);
			}
		}

		break;
	case TYPE_BUFLEN_NET_IN:
		if(evt->get_fd_info() && evt->get_category() == EC_IO_READ) {
			scap_fd_type etype = evt->get_fd_info()->m_type;

			if(etype >= SCAP_FD_IPV4_SOCK && etype <= SCAP_FD_IPV6_SERVSOCK) {
				return extract_buflen(evt, len);
			}
		}

		break;
	case TYPE_BUFLEN_NET_OUT:
		if(evt->get_fd_info() && evt->get_category() == EC_IO_WRITE) {
			scap_fd_type etype = evt->get_fd_info()->m_type;

			if(etype >= SCAP_FD_IPV4_SOCK && etype <= SCAP_FD_IPV6_SERVSOCK) {
				return extract_buflen(evt, len);
			}
		}

		break;
	case TYPE_ISOPEN_READ:
	case TYPE_ISOPEN_WRITE:
	case TYPE_ISOPEN_EXEC:
	case TYPE_ISOPEN_CREATE: {
		uint16_t etype = evt->get_type();

		m_val.u32 = 0;
		// If any of the exec bits is on, we consider this an open+exec
		uint32_t is_exec_mask = (PPM_S_IXUSR | PPM_S_IXGRP | PPM_S_IXOTH);

		if(etype == PPME_SYSCALL_OPEN_X || etype == PPME_SYSCALL_OPENAT_2_X ||
		   etype == PPME_SYSCALL_OPENAT2_X || etype == PPME_SYSCALL_OPEN_BY_HANDLE_AT_X) {
			bool is_new_version =
			        etype == PPME_SYSCALL_OPENAT_2_X || etype == PPME_SYSCALL_OPENAT2_X;
			// For both OPEN_X and PPME_SYSCALL_OPEN_BY_HANDLE_AT_X, flags is the 3rd argument.
			uint32_t flags = 0;
			if(const auto flags_param = evt->get_param(is_new_version ? 3 : 2);
			   !flags_param->empty()) {
				flags = flags_param->as<uint32_t>();
			}

			// PPM open flags use 0x11 for
			// PPM_O_RDWR, so there's no need to
			// check that value explicitly.
			if(m_field_id == TYPE_ISOPEN_READ && flags & PPM_O_RDONLY) {
				m_val.u32 = 1;
			}

			if(m_field_id == TYPE_ISOPEN_WRITE && flags & PPM_O_WRONLY) {
				m_val.u32 = 1;
			}

			if(m_field_id == TYPE_ISOPEN_CREATE) {
				// If PPM_O_F_CREATED is set the file is created
				if(flags & PPM_O_F_CREATED) {
					m_val.u32 = 1;
				}

				// If PPM_O_TMPFILE is set and syscall is successful the file is created
				if((flags & PPM_O_TMPFILE) && evt->has_return_value()) {
					int64_t retval = evt->get_syscall_return_value();

					if(retval >= 0) {
						m_val.u32 = 1;
					}
				}
			}

			/* `open_by_handle_at` exit event has no `mode` parameter. */
			if(m_field_id == TYPE_ISOPEN_EXEC && (flags & (PPM_O_TMPFILE | PPM_O_CREAT) &&
			                                      etype != PPME_SYSCALL_OPEN_BY_HANDLE_AT_X)) {
				if(const auto mode_param = evt->get_param(is_new_version ? 4 : 3);
				   !mode_param->empty()) {
					uint32_t mode_bits = mode_param->as<uint32_t>();
					m_val.u32 = (mode_bits & is_exec_mask) ? 1 : 0;
				}
			}
		} else if((m_field_id == TYPE_ISOPEN_EXEC) && (etype == PPME_SYSCALL_CREAT_X)) {
			uint32_t mode_bits = evt->get_param(2)->as<uint32_t>();
			m_val.u32 = (mode_bits & is_exec_mask) ? 1 : 0;
		}

		RETURN_EXTRACT_VAR(m_val.u32);
	}

	break;
	case TYPE_INFRA_DOCKER_NAME:
	case TYPE_INFRA_DOCKER_CONTAINER_ID:
	case TYPE_INFRA_DOCKER_CONTAINER_NAME:
	case TYPE_INFRA_DOCKER_CONTAINER_IMAGE: {
		uint16_t etype = evt->get_scap_evt()->type;

		if(etype == PPME_INFRASTRUCTURE_EVENT_E) {
			std::string descstr{evt->get_param(2)->as<std::string>()};
			vector<string> elements = sinsp_split(descstr, ';');
			for(string ute : elements) {
				string e = trim(ute);

				if(m_field_id == TYPE_INFRA_DOCKER_NAME) {
					if(e.substr(0, sizeof("Event") - 1) == "Event") {
						vector<string> subelements = sinsp_split(e, ':');
						ASSERT(subelements.size() == 2);
						m_strstorage = trim(subelements[1]);
						RETURN_EXTRACT_STRING(m_strstorage);
					}
				} else if(m_field_id == TYPE_INFRA_DOCKER_CONTAINER_ID) {
					if(e.substr(0, sizeof("ID") - 1) == "ID") {
						vector<string> subelements = sinsp_split(e, ':');
						ASSERT(subelements.size() == 2);
						m_strstorage = trim(subelements[1]);
						if(m_strstorage.length() > 12) {
							m_strstorage = m_strstorage.substr(0, 12);
						}
						RETURN_EXTRACT_STRING(m_strstorage);
					}
				} else if(m_field_id == TYPE_INFRA_DOCKER_CONTAINER_NAME) {
					if(e.substr(0, sizeof("name") - 1) == "name") {
						vector<string> subelements = sinsp_split(e, ':');
						ASSERT(subelements.size() == 2);
						m_strstorage = trim(subelements[1]);
						RETURN_EXTRACT_STRING(m_strstorage);
					}
				} else if(m_field_id == TYPE_INFRA_DOCKER_CONTAINER_IMAGE) {
					if(e.substr(0, sizeof("Image") - 1) == "Image") {
						vector<string> subelements = sinsp_split(e, ':');
						ASSERT(subelements.size() == 2);
						m_strstorage = subelements[1];

						if(m_strstorage.find("@") != string::npos) {
							m_strstorage = m_strstorage.substr(0, m_strstorage.find("@"));
						} else if(m_strstorage.find("sha256") != string::npos) {
							m_strstorage = e.substr(e.find(":") + 1);
						}
						m_strstorage = trim(m_strstorage);
						RETURN_EXTRACT_STRING(m_strstorage);
					}
				}
			}
		}
	} break;
	default:
		ASSERT(false);
		return NULL;
	}

	return NULL;
}

bool sinsp_filter_check_event::compare_nocache(sinsp_evt* evt) {
	bool res;

	m_is_compare = true;

	if(m_field_id == TYPE_ARGRAW) {
		uint32_t len;
		bool sanitize_strings = false;
		// note: this uses the single-value extract because this filtercheck
		// class does not support multi-valued extraction
		uint8_t* extracted_val = extract_single(evt, &len, sanitize_strings);

		if(extracted_val == NULL || len == 0) {
			return false;
		}

		ASSERT(m_arginfo != NULL);

		res = compare_rhs(m_cmpop, m_arginfo->type, extracted_val);
	} else if(m_field_id == TYPE_AROUND) {
		uint64_t ts = evt->get_ts();
		uint64_t t1 = ts - m_tsdelta;
		uint64_t t2 = ts + m_tsdelta;

		bool res1 = ::flt_compare(CO_GE, PT_UINT64, &m_val.u64, &t1);

		bool res2 = ::flt_compare(CO_LE, PT_UINT64, &m_val.u64, &t2);

		return res1 && res2;
	} else {
		res = sinsp_filter_check::compare_nocache(evt);
	}

	m_is_compare = false;

	return res;
}
