From a491956bde9cd5b788a9124d47d1db72bdeea932 Mon Sep 17 00:00:00 2001 From: Roberto Scolaro Date: Wed, 17 Apr 2024 14:14:26 +0000 Subject: [PATCH] new: add chisels Signed-off-by: Roberto Scolaro --- cmake/modules/luajit.cmake | 117 ++ userspace/chisel/chisel.cpp | 1784 +++++++++++++++++ userspace/chisel/chisel.h | 197 ++ userspace/chisel/chisel_api.cpp | 1578 +++++++++++++++ userspace/chisel/chisel_api.h | 77 + .../chisel_capture_interrupt_exception.h | 38 + userspace/chisel/chisel_fields_info.cpp | 197 ++ userspace/chisel/chisel_fields_info.h | 32 + userspace/chisel/chisel_table.cpp | 1627 +++++++++++++++ userspace/chisel/chisel_table.h | 397 ++++ userspace/chisel/chisel_utils.cpp | 177 ++ userspace/chisel/chisel_utils.h | 39 + userspace/chisel/chisel_viewinfo.cpp | 408 ++++ userspace/chisel/chisel_viewinfo.h | 234 +++ userspace/sysdig/CMakeLists.txt | 46 +- 15 files changed, 6937 insertions(+), 11 deletions(-) create mode 100644 cmake/modules/luajit.cmake create mode 100644 userspace/chisel/chisel.cpp create mode 100644 userspace/chisel/chisel.h create mode 100644 userspace/chisel/chisel_api.cpp create mode 100644 userspace/chisel/chisel_api.h create mode 100644 userspace/chisel/chisel_capture_interrupt_exception.h create mode 100644 userspace/chisel/chisel_fields_info.cpp create mode 100644 userspace/chisel/chisel_fields_info.h create mode 100644 userspace/chisel/chisel_table.cpp create mode 100644 userspace/chisel/chisel_table.h create mode 100644 userspace/chisel/chisel_utils.cpp create mode 100644 userspace/chisel/chisel_utils.h create mode 100644 userspace/chisel/chisel_viewinfo.cpp create mode 100644 userspace/chisel/chisel_viewinfo.h diff --git a/cmake/modules/luajit.cmake b/cmake/modules/luajit.cmake new file mode 100644 index 0000000000..77b6c45e33 --- /dev/null +++ b/cmake/modules/luajit.cmake @@ -0,0 +1,117 @@ +# 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. +# +# The following file was originally developed in the sysdig cli +# project and then contributed to the falcosecurity/libs project +# (https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +# with the #1737 PR). At the end, given its removal from the +# falcosecurity/libs project, has been reintroduced to the +# draios/sysdig project. +# + +option(USE_BUNDLED_LUAJIT "Enable building of the bundled LuaJIT" ${USE_BUNDLED_DEPS}) + +if(LUAJIT_INCLUDE) + # we already have luajit +elseif(NOT USE_BUNDLED_LUAJIT) + find_path(LUAJIT_INCLUDE luajit.h PATH_SUFFIXES luajit-2.0 luajit-2.1 luajit) + find_library(LUAJIT_LIB NAMES luajit luajit-5.1) + if(LUAJIT_INCLUDE AND LUAJIT_LIB) + message(STATUS "Found LuaJIT: include: ${LUAJIT_INCLUDE}, lib: ${LUAJIT_LIB}") + else() + # alternatively try stock Lua + find_package(Lua REQUIRED) + set(LUAJIT_LIB ${LUA_LIBRARY}) + set(LUAJIT_INCLUDE ${LUA_INCLUDE_DIR}) + endif() +else() + set(LUAJIT_SRC "${PROJECT_BINARY_DIR}/luajit-prefix/src/luajit/src") + set(LUAJIT_INCLUDE "${LUAJIT_SRC}/") + + if(NOT WIN32) + set(LUAJIT_LIB "${LUAJIT_SRC}/libluajit.a") + else() + set(LUAJIT_LIB "${LUAJIT_SRC}/lua51.lib") + endif() + + if(NOT TARGET luajit) + message(STATUS "Using bundled LuaJIT in '${LUAJIT_SRC}'") + if(NOT WIN32) + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64le") + ExternalProject_Add(luajit + PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" + GIT_REPOSITORY "https://github.com/moonjit/moonjit" + GIT_TAG "2.1.2" + CONFIGURE_COMMAND "" + BUILD_COMMAND make + BUILD_IN_SOURCE 1 + BUILD_BYPRODUCTS ${LUAJIT_LIB} + UPDATE_COMMAND "" + INSTALL_COMMAND "") + elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "s390x") + ExternalProject_Add(luajit + PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" + GIT_REPOSITORY "https://github.com/linux-on-ibm-z/LuaJIT.git" + GIT_TAG "v2.1" + CONFIGURE_COMMAND "" + BUILD_COMMAND make + BUILD_IN_SOURCE 1 + BUILD_BYPRODUCTS ${LUAJIT_LIB} + UPDATE_COMMAND "" + INSTALL_COMMAND "") + elseif(APPLE) + ExternalProject_Add(luajit + PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" + URL "https://github.com/LuaJIT/LuaJIT/archive/8635cbabf3094c4d8bd00578c7d812bea87bb2d3.tar.gz" + URL_HASH "SHA256=835035b244c3dc3d3d19bdd5ac623af90b84207e6330fb78f9fa51d6e200d760" + CONFIGURE_COMMAND "" + BUILD_COMMAND make MACOSX_DEPLOYMENT_TARGET=10.14 + BUILD_IN_SOURCE 1 + BUILD_BYPRODUCTS ${LUAJIT_LIB} + INSTALL_COMMAND "") + else() + ExternalProject_Add(luajit + PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" + GIT_REPOSITORY "https://github.com/LuaJIT/LuaJIT" + GIT_TAG "f3c856915b4ce7ccd24341e8ac73e8a9fd934171" + CONFIGURE_COMMAND "" + BUILD_COMMAND make + BUILD_IN_SOURCE 1 + BUILD_BYPRODUCTS ${LUAJIT_LIB} + UPDATE_COMMAND "" + INSTALL_COMMAND "") + endif() + install(FILES "${LUAJIT_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" + COMPONENT "libs-deps") + install(DIRECTORY "${LUAJIT_INCLUDE}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBS_PACKAGE_NAME}" + COMPONENT "libs-deps" + FILES_MATCHING PATTERN "*.h") + else() + ExternalProject_Add(luajit + PREFIX "${PROJECT_BINARY_DIR}/luajit-prefix" + URL "https://github.com/LuaJIT/LuaJIT/archive/v2.1.0-beta3.tar.gz" + URL_HASH "SHA256=409f7fe570d3c16558e594421c47bdd130238323c9d6fd6c83dedd2aaeb082a8" + CONFIGURE_COMMAND "" + BUILD_COMMAND msvcbuild.bat + BUILD_BYPRODUCTS ${LUAJIT_LIB} + BINARY_DIR "${LUAJIT_SRC}" + INSTALL_COMMAND "") + endif() + endif() +endif() + +if(NOT TARGET luajit) + add_custom_target(luajit) +endif() + +include_directories("${LUAJIT_INCLUDE}") diff --git a/userspace/chisel/chisel.cpp b/userspace/chisel/chisel.cpp new file mode 100644 index 0000000000..940a695aa6 --- /dev/null +++ b/userspace/chisel/chisel.cpp @@ -0,0 +1,1784 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#include + +#include +#include +#include +#include + +#define HAS_LUA_CHISELS + +#ifdef HAS_LUA_CHISELS + +extern "C" { +#define LUA_COMPAT_ALL +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} +#endif + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +using namespace std; + +extern vector* g_chisel_dirs; +extern sinsp_evttables g_infotables; + +// todo(jasondellaluce): this list is static and prevents chisels from using +// plugin-defined extraction fields. The right way would be to have a filtercheck +// list owned by each chisel itself and populate depending on the loaded plugins. +static sinsp_filter_check_list s_filterlist; + +/////////////////////////////////////////////////////////////////////////////// +// For Lua debugging +/////////////////////////////////////////////////////////////////////////////// +#ifdef HAS_LUA_CHISELS +void lua_stackdump(lua_State *L) +{ + int i; + int top = lua_gettop(L); + for (i = 1; i <= top; i++) + { + int t = lua_type(L, i); + switch (t) + { + + case LUA_TSTRING: // strings + printf("`%s'", lua_tostring(L, i)); + break; + + case LUA_TBOOLEAN: // booleans + printf(lua_toboolean(L, i) ? "true" : "false"); + break; + + case LUA_TNUMBER: // numbers + printf("%g", lua_tonumber(L, i)); + break; + + default: // other values + printf("%s", lua_typename(L, t)); + break; + } + + printf(" "); // put a separator + } + + printf("\n"); // end the listing +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Lua callbacks +/////////////////////////////////////////////////////////////////////////////// +#ifdef HAS_LUA_CHISELS +const static struct luaL_Reg ll_tool [] = +{ + {"set_filter", &lua_cbacks::set_global_filter}, + {"set_snaplen", &lua_cbacks::set_snaplen}, + {"set_output_format", &lua_cbacks::set_output_format}, + {"set_fatfile_dump_mode", &lua_cbacks::set_fatfile_dump_mode}, + {"is_live", &lua_cbacks::is_live}, + {"is_tty", &lua_cbacks::is_tty}, + {"get_terminal_info", &lua_cbacks::get_terminal_info}, + {"get_filter", &lua_cbacks::get_filter}, + {"get_machine_info", &lua_cbacks::get_machine_info}, + {"get_thread_table", &lua_cbacks::get_thread_table}, + {"get_thread_table_nofds", &lua_cbacks::get_thread_table_nofds}, + {"get_thread_table_barebone", &lua_cbacks::get_thread_table_barebone}, + {"get_thread_table_barebone_nofds", &lua_cbacks::get_thread_table_barebone_nofds}, + {"get_container_table", &lua_cbacks::get_container_table}, + {"is_print_container_data", &lua_cbacks::is_print_container_data}, + {"get_output_format", &lua_cbacks::get_output_format}, + {"get_evtsource_name", &lua_cbacks::get_evtsource_name}, + {"get_firstevent_ts", &lua_cbacks::get_firstevent_ts}, + {"get_lastevent_ts", &lua_cbacks::get_lastevent_ts}, + {"make_ts", &lua_cbacks::make_ts}, + {"add_ts", &lua_cbacks::add_ts}, + {"subtract_ts", &lua_cbacks::subtract_ts}, + {"run_app", &lua_cbacks::run_app}, + {"end_capture", &lua_cbacks::end_capture}, + {"log", &lua_cbacks::log}, + {"udp_setpeername", &lua_cbacks::udp_setpeername}, + {"udp_send", &lua_cbacks::udp_send}, + {"get_read_progress", &lua_cbacks::get_read_progress}, + {"push_metric", &lua_cbacks::push_metric}, + {NULL,NULL} +}; + +const static struct luaL_Reg ll_chisel [] = +{ + {"request_field", &lua_cbacks::request_field}, + {"set_filter", &lua_cbacks::set_filter}, + {"set_event_formatter", &lua_cbacks::set_event_formatter}, + {"set_interval_ns", &lua_cbacks::set_interval_ns}, + {"set_interval_s", &lua_cbacks::set_interval_s}, + {"set_precise_interval_ns", &lua_cbacks::set_precise_interval_ns}, + {"exec", &lua_cbacks::exec}, + {NULL,NULL} +}; + +const static struct luaL_Reg ll_evt [] = +{ + {"field", &lua_cbacks::field}, + {"get_num", &lua_cbacks::get_num}, + {"get_ts", &lua_cbacks::get_ts}, + {"get_type", &lua_cbacks::get_type}, + {"get_cpuid", &lua_cbacks::get_cpuid}, + {NULL,NULL} +}; +#endif // HAS_LUA_CHISELS + +/////////////////////////////////////////////////////////////////////////////// +// chiselinfo implementation +/////////////////////////////////////////////////////////////////////////////// +chiselinfo::chiselinfo(sinsp* inspector) +{ + m_filter = NULL; + m_formatter = NULL; + m_dumper = NULL; + m_inspector = inspector; + m_has_nextrun_args = false; + m_end_capture = false; + +#ifdef HAS_LUA_CHISELS + m_callback_interval = 0; + m_callback_precise_interval = 0; +#endif +} + +chiselinfo::~chiselinfo() +{ + if(m_filter) + { + delete m_filter; + } + + if(m_formatter) + { + delete m_formatter; + } + + if(m_dumper) + { + delete m_dumper; + } +} + +void chiselinfo::init(string filterstr, string formatterstr) +{ + set_filter(filterstr); + set_formatter(formatterstr); +} + +void chiselinfo::set_filter(string filterstr) +{ + + sinsp_filter_compiler compiler(m_inspector, filterstr); + if(m_filter) + { + delete m_filter; + m_filter = NULL; + } + + if(filterstr != "") + { + m_filter = compiler.compile().release(); + } +} + +void chiselinfo::set_formatter(string formatterstr) +{ + if(m_formatter) + { + delete m_formatter; + m_formatter = NULL; + } + + if(formatterstr == "" || formatterstr == "default") + { + m_formatter = new sinsp_evt_formatter(m_inspector, DEFAULT_OUTPUT_STR, s_filterlist); + } + else + { + m_formatter = new sinsp_evt_formatter(m_inspector, formatterstr, s_filterlist); + } +} + +#ifdef HAS_LUA_CHISELS +void chiselinfo::set_callback_interval(uint64_t interval) +{ + m_callback_interval = interval; +} + +void chiselinfo::set_callback_precise_interval(uint64_t interval) +{ + m_callback_precise_interval = interval; +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// chisel implementation +/////////////////////////////////////////////////////////////////////////////// +sinsp_chisel::sinsp_chisel(sinsp* inspector, string filename, bool is_file) +{ + m_inspector = inspector; + m_ls = NULL; + m_lua_has_handle_evt = false; + m_lua_is_first_evt = true; + m_lua_cinfo = NULL; + m_lua_last_interval_sample_time = 0; + m_lua_last_interval_ts = 0; + m_udp_socket = 0; + + load(filename, is_file); +} + +sinsp_chisel::~sinsp_chisel() +{ + free_lua_chisel(); +} + +void sinsp_chisel::free_lua_chisel() +{ +#ifdef HAS_LUA_CHISELS + if(m_ls) + { + lua_close(m_ls); + m_ls = NULL; + } + + for(uint32_t j = 0; j < m_allocated_fltchecks.size(); j++) + { + delete m_allocated_fltchecks[j]; + } + m_allocated_fltchecks.clear(); + + if(m_lua_cinfo != NULL) + { + delete m_lua_cinfo; + m_lua_cinfo = NULL; + } + + m_lua_script_info.reset(); + + if(m_udp_socket > 0) + { +#ifdef _WIN32 + closesocket(m_udp_socket); +#else + close(m_udp_socket); +#endif + m_udp_socket = 0; + } +#endif +} + +#ifdef HAS_LUA_CHISELS +void parse_lua_chisel_arg(lua_State *ls, OUT chisel_desc* cd) +{ + lua_pushnil(ls); + string name; + string type; + string desc; + bool optional = false; + + while(lua_next(ls, -2) != 0) + { + if(lua_isstring(ls, -1)) + { + if(string(lua_tostring(ls, -2)) == "name") + { + name = lua_tostring(ls, -1); + } + else if(string(lua_tostring(ls, -2)) == "argtype") + { + type = lua_tostring(ls, -1); + } + else if(string(lua_tostring(ls, -2)) == "description") + { + desc = lua_tostring(ls, -1); + } + } + else if(lua_isboolean(ls, -1)) + { + if(string(lua_tostring(ls, -2)) == "optional") + { + optional = (lua_toboolean(ls, -1) != 0); + } + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " is not a string"); + } + + lua_pop(ls, 1); + } + + cd->m_args.push_back(chiselarg_desc(name, type, desc, optional)); +} + +void parse_lua_chisel_args(lua_State *ls, OUT chisel_desc* cd) +{ + lua_pushnil(ls); + + while(lua_next(ls, -2) != 0) + { + if(lua_isstring(ls, -1)) + { + printf("%s = %s\n", lua_tostring(ls, -2), lua_tostring(ls, -1)); + cd->m_description = lua_tostring(ls, -1); + } + else if(lua_istable(ls, -1)) + { + parse_lua_chisel_arg(ls, cd); + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " is not a string"); + } + + lua_pop(ls, 1); + } +} + +void sinsp_chisel::add_lua_package_path(lua_State* ls, const std::string& path) +{ + lua_getglobal(ls, "package"); + lua_getfield(ls, -1, "path"); + + string cur_path = lua_tostring(ls, -1 ); + cur_path += ';'; + cur_path.append(path); + lua_pop(ls, 1); + + lua_pushstring(ls, cur_path.c_str()); + lua_setfield(ls, -2, "path"); + lua_pop(ls, 1); +} +#endif + +chisel_field_aggregation sinsp_chisel::string_to_aggregation(string ag) +{ + chisel_field_aggregation res = A_NONE; + + if(ag == "SUM") + { + res = A_SUM; + } + else if(ag == "AVG") + { + res = A_AVG; + } + else if(ag == "TIME_AVG") + { + res = A_TIME_AVG; + } + else if(ag == "MIN") + { + res = A_MIN; + } + else if(ag == "MAX") + { + res = A_MAX; + } + else + { + throw sinsp_exception("unknown view column aggregation " + ag); + } + + return res; +} + +void sinsp_chisel::parse_view_column(lua_State *ls, OUT chisel_desc* cd, OUT void* columns) +{ + vector* cols = (vector*)columns; + + lua_pushnil(ls); + + string tmpstr; + string name; + string description; + string field; + string filterfield; + uint32_t colsize = 0xffffffff; + uint32_t flags = TEF_NONE; + chisel_field_aggregation aggregation = A_NONE; + chisel_field_aggregation groupby_aggregation = A_NONE; + vector tags; + + while(lua_next(ls, -2) != 0) + { + string fldname = lua_tostring(ls, -2); + + if(fldname == "name") + { + name = lua_tostring(ls, -1); + } + else if(fldname == "description") + { + description = lua_tostring(ls, -1); + } + else if(fldname == "field") + { + field = lua_tostring(ls, -1); + } + else if(fldname == "filterfield") + { + filterfield = lua_tostring(ls, -1); + } + else if(fldname == "colsize") + { + if(lua_isnumber(ls, -1)) + { + colsize = (uint32_t)lua_tonumber(ls, -1); + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " must be a number"); + } + } + else if(fldname == "is_key") + { + if(lua_isboolean(ls, -1)) + { + bool ik = (lua_toboolean(ls, -1) != 0); + if(ik) + { + flags |= TEF_IS_KEY; + } + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " must be a boolean value"); + } + } + else if(fldname == "filter_in_child_only") + { + if(lua_isboolean(ls, -1)) + { + bool ik = (lua_toboolean(ls, -1) != 0); + if(ik) + { + flags |= TEF_FILTER_IN_CHILD_ONLY; + } + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " must be a boolean value"); + } + } + else if(fldname == "is_groupby_key") + { + if(lua_isboolean(ls, -1)) + { + bool ik = (lua_toboolean(ls, -1) != 0); + if(ik) + { + flags |= TEF_IS_GROUPBY_KEY; + } + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " must be a boolean value"); + } + } + else if(fldname == "is_sorting") + { + if(lua_isboolean(ls, -1)) + { + bool ik = (lua_toboolean(ls, -1) != 0); + if(ik) + { + flags |= TEF_IS_SORT_COLUMN; + } + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " must be a boolean value"); + } + } + else if(fldname == "aggregation") + { + if(lua_isstring(ls, -1)) + { + string ag = lua_tostring(ls, -1); + + aggregation = string_to_aggregation(ag); + } + } + else if(fldname == "groupby_aggregation") + { + if(lua_isstring(ls, -1)) + { + string ag = lua_tostring(ls, -1); + + groupby_aggregation = string_to_aggregation(ag); + } + } + else if(fldname == "tags") + { + if(lua_istable(ls, -1)) + { + lua_pushnil(ls); + + while(lua_next(ls, -2) != 0) + { + if(lua_isstring(ls, -1)) + { + tmpstr = lua_tostring(ls, -1); + tags.push_back(tmpstr); + } + else + { + throw sinsp_exception("tags column entries must be strings"); + } + + lua_pop(ls, 1); + } + } + else + { + throw sinsp_exception(string(lua_tostring(ls, -2)) + " is not a table"); + } + } + + lua_pop(ls, 1); + } + + if(filterfield != "" && ((flags & TEF_IS_KEY) == 0) && ((flags & TEF_IS_GROUPBY_KEY) == 0)) + { + throw sinsp_exception("wrong view column syntax: filterfield specified for a non key column"); + } + + cols->push_back(chisel_view_column_info(field, + name, + description, + colsize, + (uint32_t)flags, + aggregation, + groupby_aggregation, + tags, + filterfield)); +} + +void sinsp_chisel::parse_view_columns(lua_State *ls, OUT chisel_desc* cd, OUT void* columns) +{ + string name; + string type; + string desc; + + lua_pushnil(ls); + + while(lua_next(ls, -2) != 0) + { + if(lua_istable(ls, -1)) + { + parse_view_column(ls, cd, columns); + } + else + { + throw sinsp_exception("view_info column entries must be tables"); + } + + lua_pop(ls, 1); + } +} + +void sinsp_chisel::parse_view_action(lua_State *ls, OUT chisel_desc* cd, OUT void* actions) +{ + vector* keys = (vector*)actions; + + lua_pushnil(ls); + + char key = 0; + string command; + string description; + string tmpstr; + bool ask_confirmation = false; + bool waitfinish = true; + + while(lua_next(ls, -2) != 0) + { + string fldname = lua_tostring(ls, -2); + + if(fldname == "hotkey") + { + tmpstr = lua_tostring(ls, -1); + if(tmpstr.size() == 1) + { + key = tmpstr[0]; + } + else + { + throw sinsp_exception("action 'key' field must be a single character string"); + } + } + else if(fldname == "command") + { + command = lua_tostring(ls, -1); + } + else if(fldname == "description") + { + description = lua_tostring(ls, -1); + } + else if(fldname == "wait_finish") + { + int wf = lua_toboolean(ls, -1); + + if(wf == 0) + { + waitfinish = false; + } + } + else if(fldname == "ask_confirmation") + { + int wf = lua_toboolean(ls, -1); + + if(wf == 1) + { + ask_confirmation = true; + } + } + + lua_pop(ls, 1); + } + + if(key == 0) + { + throw sinsp_exception("action missing the 'key' value"); + } + + if(command == "") + { + throw sinsp_exception("action missing the 'command' value"); + } + + keys->push_back(chisel_view_action_info(key, + command, + description, + ask_confirmation, + waitfinish)); +} + +void sinsp_chisel::parse_view_actions(lua_State *ls, OUT chisel_desc* cd, OUT void* actions) +{ + string name; + string type; + string desc; + + lua_pushnil(ls); + + while(lua_next(ls, -2) != 0) + { + if(lua_istable(ls, -1)) + { + parse_view_action(ls, cd, actions); + } + else + { + throw sinsp_exception("view_info action entries must be tables"); + } + + lua_pop(ls, 1); + } +} + +bool sinsp_chisel::parse_view_info(lua_State *ls, OUT chisel_desc* cd) +{ + lua_getglobal(ls, "view_info"); + if(lua_isnoneornil(ls, -1)) + { + lua_close(ls); + return false; + } + + lua_pushnil(ls); + + string tmpstr; + string id; + string name; + string description; + vector applies_to; + string filter; + bool use_defaults = false; + chisel_view_info::viewtype vt = chisel_view_info::T_TABLE; + vector columns; + vector actions; + vector tags; + vector tips; + string drilldown_target; + string spectro_type; + bool drilldown_increase_depth = false; + bool is_root = false; + bool propagate_filter = true; + + while(lua_next(ls, -2) != 0) + { + string fldname = lua_tostring(ls, -2); + + if(fldname == "name") + { + name = lua_tostring(ls, -1); + } + else if(fldname == "id") + { + id = lua_tostring(ls, -1); + } + else if(fldname == "description") + { + description = lua_tostring(ls, -1); + } + else if(fldname == "tags") + { + if(lua_istable(ls, -1)) + { + lua_pushnil(ls); + + while(lua_next(ls, -2) != 0) + { + if(lua_isstring(ls, -1)) + { + tmpstr = lua_tostring(ls, -1); + tags.push_back(tmpstr); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + "tags entries must be strings"); + } + + lua_pop(ls, 1); + } + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " is not a table"); + } + } + else if(fldname == "tips") + { + if(lua_istable(ls, -1)) + { + lua_pushnil(ls); + + while(lua_next(ls, -2) != 0) + { + if(lua_isstring(ls, -1)) + { + tmpstr = lua_tostring(ls, -1); + tips.push_back(tmpstr); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + "tips column entries must be strings"); + } + + lua_pop(ls, 1); + } + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " is not a table"); + } + } + else if(fldname == "view_type") + { + tmpstr = lua_tostring(ls, -1); + + if(tmpstr == "table") + { + vt = chisel_view_info::T_TABLE; + } + else if(tmpstr == "list") + { + vt = chisel_view_info::T_LIST; + } + else if(tmpstr == "spectrogram") + { + vt = chisel_view_info::T_SPECTRO; + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " must be either 'table' or 'list'"); + } + } + else if(fldname == "drilldown_target") + { + drilldown_target = lua_tostring(ls, -1); + } + else if(fldname == "spectro_type") + { + spectro_type = lua_tostring(ls, -1); + } + else if(fldname == "applies_to") + { + if(lua_istable(ls, -1)) + { + lua_pushnil(ls); + + while(lua_next(ls, -2) != 0) + { + if(lua_isstring(ls, -1)) + { + tmpstr = lua_tostring(ls, -1); + applies_to.push_back(tmpstr); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + "tips column entries must be strings"); + } + + lua_pop(ls, 1); + } + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " is not a table"); + } + } + else if(fldname == "filter") + { + filter = lua_tostring(ls, -1); + } + else if(fldname == "use_defaults") + { + if(lua_isboolean(ls, -1)) + { + use_defaults = (lua_toboolean(ls, -1) != 0); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " must be a boolean"); + } + } + else if(fldname == "is_root") + { + if(lua_isboolean(ls, -1)) + { + is_root = (lua_toboolean(ls, -1) != 0); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " must be a boolean"); + } + } + else if(fldname == "columns") + { + if(lua_istable(ls, -1)) + { + parse_view_columns(ls, cd, &columns); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " is not a table"); + } + } + else if(fldname == "actions") + { + if(lua_istable(ls, -1)) + { + parse_view_actions(ls, cd, &actions); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " is not a table"); + } + } + else if(fldname == "drilldown_increase_depth") + { + if(lua_isboolean(ls, -1)) + { + drilldown_increase_depth = (lua_toboolean(ls, -1) != 0); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " must be a boolean"); + } + } + else if(fldname == "propagate_filter") + { + if(lua_isboolean(ls, -1)) + { + propagate_filter = (lua_toboolean(ls, -1) != 0); + } + else + { + throw sinsp_exception("error in view " + cd->m_name + ": " + string(lua_tostring(ls, -2)) + " must be a boolean"); + } + } + + lua_pop(ls, 1); + } + + cd->m_viewinfo = chisel_view_info(vt, + id, + name, + description, + tags, + tips, + columns, + applies_to, + filter, + drilldown_target, + use_defaults, + is_root, + actions, + drilldown_increase_depth, + spectro_type, + propagate_filter); + + return true; +} + + +#ifdef HAS_LUA_CHISELS +// Initializes a lua chisel +bool sinsp_chisel::init_lua_chisel(chisel_desc &cd, string const &fpath) +{ + lua_State* ls = luaL_newstate(); + if(ls == NULL) + { + return false; + } + + luaL_openlibs(ls); + + // + // Load our own lua libs + // + lua_newtable(ls); + luaL_setfuncs(ls, ll_tool, 0); + lua_setglobal(ls, CHISEL_TOOL_LIBRARY_NAME); + + lua_newtable(ls); + luaL_setfuncs(ls, ll_chisel, 0); + lua_setglobal(ls, "chisel"); + + lua_newtable(ls); + luaL_setfuncs(ls, ll_evt, 0); + lua_setglobal(ls, "evt"); + + // + // Add our chisel paths to package.path + // + for(auto& el : *g_chisel_dirs) + { + add_lua_package_path(ls, el.m_dir + "?.lua"); + } + + // + // Load the script + // + if(luaL_loadfile(ls, fpath.c_str()) || lua_pcall(ls, 0, 0, 0)) + { + goto failure; + } + + // + // Extract the description + // + lua_getglobal(ls, "description"); + if(!lua_isstring(ls, -1)) + { + return parse_view_info(ls, &cd); + } + cd.m_description = lua_tostring(ls, -1); + + // + // Extract the short description + // + lua_getglobal(ls, "short_description"); + if(!lua_isstring(ls, -1)) + { + goto failure; + } + cd.m_shortdesc = lua_tostring(ls, -1); + + // + // Extract the category + // + cd.m_category = ""; + lua_getglobal(ls, "category"); + if(lua_isstring(ls, -1)) + { + cd.m_category = lua_tostring(ls, -1); + } + + // + // Extract the hidden flag and skip the chisel if it's set + // + lua_getglobal(ls, "hidden"); + if(lua_isboolean(ls, -1)) + { + int sares = lua_toboolean(ls, -1); + if(sares) + { + goto failure; + } + } + + // + // Extract the args + // + lua_getglobal(ls, "args"); + if(lua_isnoneornil(ls, -1)) + { + goto failure; + } + + try + { + parse_lua_chisel_args(ls, &cd); + } + catch(...) + { + goto failure; + } + + return true; + +failure: + lua_close(ls); + return false; +} +#endif + +struct filename +{ + bool valid; + string name; + string ext; +}; + +// +// 1. Iterates through the chisel files on disk (.sc and .lua) +// 2. Opens them and extracts the fields (name, description, etc) +// 3. Adds them to the chisel_descs vector. +// +void sinsp_chisel::get_chisel_list(vector* chisel_descs) +{ +#ifdef HAS_LUA_CHISELS + for(auto& dir_info : *g_chisel_dirs) + { + if(dir_info.m_dir.empty()) + { + continue; + } + + std::error_code ec; + for (auto const& dir_entry : filesystem::directory_iterator(dir_info.m_dir, ec)) + { + if(!ec && dir_entry.path().extension() == ".lua") + { + auto res = find_if(chisel_descs->begin(), chisel_descs->end(), + [&dir_entry](auto& desc) { return dir_entry.path().filename() == desc.m_name; }); + if (res != chisel_descs->end()) + { + continue; + } + + chisel_desc cd; + cd.m_name = dir_entry.path().filename().generic_string(); + + if (init_lua_chisel(cd, dir_entry.path().generic_string())) + { + chisel_descs->emplace_back(std::move(cd)); + } + } + } + } +#endif +} + +// +// If the function succeeds, is is initialized to point to the file. +// Otherwise, the return value is "false". +// +bool sinsp_chisel::openfile(string filename, OUT ifstream* is) +{ + uint32_t j; + + for(j = 0; j < g_chisel_dirs->size(); j++) + { + is->open(string(g_chisel_dirs->at(j).m_dir) + filename); + if(is->is_open()) + { + return true; + } + } + + return false; +} + +void sinsp_chisel::load(string cmdstr, bool is_file) +{ + if (is_file) { + trim(cmdstr); + m_filename = cmdstr; + } else { + m_filename = ""; + } + + ifstream is; + std::string scriptstr; + + if (is_file) { + // + // Try to open the file with lua extension + // + if(!openfile(m_filename + ".lua", &is)) + { + // + // Try to open the file as is + // + if(!openfile(m_filename, &is)) + { + throw sinsp_exception("can't open file " + m_filename); + } + } + + // + // Load the file + // + std::istreambuf_iterator eos; + scriptstr.assign(std::istreambuf_iterator(is), eos); + } else { + scriptstr = cmdstr; + } +#ifdef HAS_LUA_CHISELS + + // + // Open the script + // + m_ls = luaL_newstate(); + + luaL_openlibs(m_ls); + + // + // Load our own lua libs + // + lua_newtable(m_ls); + luaL_setfuncs(m_ls, ll_tool, 0); + lua_setglobal(m_ls, CHISEL_TOOL_LIBRARY_NAME); + + lua_newtable(m_ls); + luaL_setfuncs(m_ls, ll_chisel, 0); + lua_setglobal(m_ls, "chisel"); + + lua_newtable(m_ls); + luaL_setfuncs(m_ls, ll_evt, 0); + lua_setglobal(m_ls, "evt"); + + // + // Add our chisel paths to package.path + // + for(auto& el : *g_chisel_dirs) + { + add_lua_package_path(m_ls, el.m_dir + "?.lua"); + } + + // + // Load the script + // + if(luaL_loadstring(m_ls, scriptstr.c_str()) || lua_pcall(m_ls, 0, 0, 0)) + { + throw sinsp_exception("Failed to load chisel " + + m_filename + ": " + lua_tostring(m_ls, -1)); + } + + // + // Allocate the chisel context for the script + // + m_lua_cinfo = new chiselinfo(m_inspector); + + // + // Set the context globals + // + lua_pushlightuserdata(m_ls, this); + lua_setglobal(m_ls, "sichisel"); + + // + // Extract the args + // + lua_getglobal(m_ls, "args"); + if(!lua_istable(m_ls, -1)) + { + throw sinsp_exception("Failed to load chisel " + + m_filename + ": args table missing"); + } + + parse_lua_chisel_args(m_ls, &m_lua_script_info); + + // + // Check if the script has an on_event + // + lua_getglobal(m_ls, "on_event"); + if(lua_isfunction(m_ls, -1)) + { + m_lua_has_handle_evt = true; + lua_pop(m_ls, 1); + } +#endif + + is.close(); +} + +uint32_t sinsp_chisel::get_n_args() +{ + ASSERT(m_ls); + +#ifdef HAS_LUA_CHISELS + return (uint32_t)m_lua_script_info.m_args.size(); +#else + return 0; +#endif +} + +uint32_t sinsp_chisel::get_n_optional_args() +{ + uint32_t j; + uint32_t res = 0; + + for(j = 0; j < m_lua_script_info.m_args.size(); j++) + { + if(m_lua_script_info.m_args[j].m_optional) + { + res++; + } + } + + return res; +} + +uint32_t sinsp_chisel::get_n_required_args() +{ + uint32_t j; + uint32_t res = 0; + + for(j = 0; j < m_lua_script_info.m_args.size(); j++) + { + if(!m_lua_script_info.m_args[j].m_optional) + { + res++; + } + } + + return res; +} + +void sinsp_chisel::set_args(string args) +{ +#ifdef HAS_LUA_CHISELS + uint32_t j; + uint32_t n_required_args = get_n_required_args(); + uint32_t n_optional_args = get_n_optional_args(); + + ASSERT(m_ls); + + // + // Split the argument string into tokens + // + uint32_t token_begin = 0; + bool inquotes = false; + uint32_t quote_correction = 0; + + trim(args); + + if(args.size() != 0) + { + for(j = 0; j < args.size(); j++) + { + if(args[j] == ' ' && !inquotes) + { + m_argvals.push_back(args.substr(token_begin, j - quote_correction - token_begin)); + token_begin = j + 1; + quote_correction = 0; + } + else if(args[j] == '\'' || args[j] == '`') + { + if(inquotes) + { + quote_correction = 1; + inquotes = false; + } + else { + token_begin++; + inquotes = true; + } + } + } + + if(inquotes) + { + throw sinsp_exception("corrupted parameters for chisel " + m_filename); + } + + m_argvals.push_back(args.substr(token_begin, j - quote_correction - token_begin)); + } + + // + // Validate the arguments + // + if(m_argvals.size() < n_required_args) + { + throw sinsp_exception("wrong number of parameters for chisel " + m_filename + + ", " + to_string((long long int)n_required_args) + " required, " + + to_string((long long int)m_argvals.size()) + " given"); + } + else if(m_argvals.size() > n_optional_args + n_required_args) + { + throw sinsp_exception("too many parameters for chisel " + m_filename + + ", " + to_string((long long int)(n_required_args)) + " required, " + + to_string((long long int)(n_optional_args)) + " optional, " + + to_string((long long int)m_argvals.size()) + " given"); + } + + // + // Create the arguments vector + // + vector> vargs; + + for(j = 0; j < m_argvals.size(); j++) + { + vargs.push_back(pair(m_lua_script_info.m_args[j].m_name, + m_argvals[j])); + } + + set_args(vargs); +#endif +} + +void sinsp_chisel::set_args(vector> args) +{ +#ifdef HAS_LUA_CHISELS + uint32_t j; + uint32_t n_required_args = get_n_required_args(); + uint32_t n_optional_args = get_n_optional_args(); + + ASSERT(m_ls); + + // + // Validate the arguments + // + if(args.size() < n_required_args) + { + throw sinsp_exception("wrong number of parameters for chisel " + m_filename + + ", " + to_string((long long int)n_required_args) + " required, " + + to_string((long long int)args.size()) + " given"); + } + else if(args.size() > n_optional_args + n_required_args) + { + throw sinsp_exception("too many parameters for chisel " + m_filename + + ", " + to_string((long long int)(n_required_args)) + " required, " + + to_string((long long int)(n_optional_args)) + " optional, " + + to_string((long long int)args.size()) + " given"); + } + + // + // Push the arguments + // + for(j = 0; j < args.size(); j++) + { + lua_getglobal(m_ls, "on_set_arg"); + if(!lua_isfunction(m_ls, -1)) + { + lua_pop(m_ls, 1); + throw sinsp_exception("chisel " + m_filename + " misses a set_arg() function."); + } + + lua_pushstring(m_ls, args[j].first.c_str()); + lua_pushstring(m_ls, args[j].second.c_str()); + + // + // call get_info() + // + if(lua_pcall(m_ls, 2, 1, 0) != 0) + { + throw sinsp_exception(m_filename + " chisel error: " + lua_tostring(m_ls, -1)); + } + + if(!lua_isboolean(m_ls, -1)) + { + throw sinsp_exception(m_filename + " chisel error: wrong set_arg() return value."); + } + + int sares = lua_toboolean(m_ls, -1); + + if(!sares) + { + throw sinsp_exception("set_arg() for chisel " + m_filename + " failed."); + } + + lua_pop(m_ls, 1); + } +#endif +} + +void sinsp_chisel::on_init() +{ + // + // Done with the arguments, call init() + // + lua_getglobal(m_ls, "on_init"); + + if(!lua_isfunction(m_ls, -1)) + { + // + // No on_init. + // That's ok. Just return. + // + return; + } + + if(lua_pcall(m_ls, 0, 1, 0) != 0) + { + // + // Exception running init + // + const char* lerr = lua_tostring(m_ls, -1); + string err = m_filename + ": error in init(): " + lerr; + throw sinsp_exception(err); + } + + if(m_new_chisel_to_exec == "") + { + if(!lua_isboolean(m_ls, -1)) + { + throw sinsp_exception(m_filename + " chisel error: wrong init() return value."); + } + + if(!lua_toboolean(m_ls, -1)) + { + throw sinsp_exception("init() for chisel " + m_filename + " failed."); + } + } + + lua_pop(m_ls, 1); + + // + // If the chisel called chisel.exec(), free this chisel and load the new one + // + if(m_new_chisel_to_exec != "") + { + free_lua_chisel(); + load(m_new_chisel_to_exec); + m_new_chisel_to_exec = ""; + + string args; + for(uint32_t j = 0; j < m_argvals.size(); j++) + { + if(m_argvals[j].find(" ") == string::npos) + { + args += m_argvals[j]; + } + else + { + args += string("'") + m_argvals[j] + "'"; + } + + if(j < m_argvals.size() - 1) + { + args += " "; + } + } + + m_argvals.clear(); + set_args(args); + + on_init(); + } +} + +void sinsp_chisel::first_event_inits(sinsp_evt* evt) +{ + uint64_t ts = evt->get_ts(); + + if(m_lua_cinfo->m_callback_interval != 0) + { + m_lua_last_interval_sample_time = ts - ts % m_lua_cinfo->m_callback_interval; + } + else if(m_lua_cinfo->m_callback_precise_interval != 0) + { + m_lua_last_interval_sample_time = ts; + } + + m_lua_is_first_evt = false; +} + +bool sinsp_chisel::run(sinsp_evt* evt) +{ +#ifdef HAS_LUA_CHISELS + string line; + + ASSERT(m_ls); + + // + // Make the event available to the API + // + lua_pushlightuserdata(m_ls, evt); + lua_setglobal(m_ls, "sievt"); + + // + // If there is a timeout callback, see if it's time to call it + // + do_timeout(evt); + + // + // If there is a filter, run it + // + if(m_lua_cinfo->m_filter != NULL) + { + if(!m_lua_cinfo->m_filter->run(evt)) + { + return false; + } + } + + // + // If the script has the on_event callback, call it + // + if(m_lua_has_handle_evt) + { + lua_getglobal(m_ls, "on_event"); + + if(lua_pcall(m_ls, 0, 1, 0) != 0) + { + throw sinsp_exception(m_filename + " chisel error: " + lua_tostring(m_ls, -1)); + } + + int oeres = lua_toboolean(m_ls, -1); + lua_pop(m_ls, 1); + + if(m_lua_cinfo->m_end_capture == true) + { + throw chisel_capture_interrupt_exception(); + } + + if(oeres == false) + { + return false; + } + } + + // + // If the script has a formatter, run it + // + if(m_lua_cinfo->m_formatter != NULL) + { + if(m_lua_cinfo->m_formatter->tostring(evt, &line)) + { + cout << line << endl; + } + } + + return true; +#endif +} + +void sinsp_chisel::do_timeout(sinsp_evt* evt) +{ + if(m_lua_is_first_evt) + { + // + // If this is the first event, put the event pointer on the stack. + // We assume that the event pointer will never change. + // + if(m_lua_is_first_evt) + { + first_event_inits(evt); + } + + return; + } + + if(m_lua_cinfo->m_callback_interval != 0) + { + uint64_t ts = evt->get_ts(); + uint64_t sample_time = ts - ts % m_lua_cinfo->m_callback_interval; + + if(sample_time != m_lua_last_interval_sample_time) + { + int64_t delta = 0; + + if(m_lua_last_interval_ts != 0) + { + delta = ts - m_lua_last_interval_ts; + if(delta == 0) + { + return; + } + } + + lua_getglobal(m_ls, "on_interval"); + + lua_pushnumber(m_ls, (double)(ts / 1000000000)); + lua_pushnumber(m_ls, (double)(ts % 1000000000)); + lua_pushnumber(m_ls, (double)delta); + + if(lua_pcall(m_ls, 3, 1, 0) != 0) + { + throw sinsp_exception(m_filename + " chisel error: calling on_interval() failed:" + lua_tostring(m_ls, -1)); + } + + int oeres = lua_toboolean(m_ls, -1); + lua_pop(m_ls, 1); + + if(oeres == false) + { + throw sinsp_exception("execution terminated by the " + m_filename + " chisel"); + } + + m_lua_last_interval_sample_time = sample_time; + m_lua_last_interval_ts = ts; + } + } + else if(m_lua_cinfo->m_callback_precise_interval != 0) + { + uint64_t ts = evt->get_ts(); + uint64_t interval = m_lua_cinfo->m_callback_precise_interval; + + if(ts - m_lua_last_interval_sample_time >= interval) + { + uint64_t t; + + for(t = m_lua_last_interval_sample_time; t <= ts - interval; t += interval) + { + lua_getglobal(m_ls, "on_interval"); + + lua_pushnumber(m_ls, (double)(t / 1000000000)); + lua_pushnumber(m_ls, (double)(t % 1000000000)); + lua_pushnumber(m_ls, (double)interval); + + if(lua_pcall(m_ls, 3, 1, 0) != 0) + { + throw sinsp_exception(m_filename + " chisel error: calling on_interval() failed:" + lua_tostring(m_ls, -1)); + } + + int oeres = lua_toboolean(m_ls, -1); + lua_pop(m_ls, 1); + + if(oeres == false) + { + throw sinsp_exception("execution terminated by the " + m_filename + " chisel"); + } + } + + m_lua_last_interval_sample_time = t; + } + } +} + +void sinsp_chisel::do_end_of_sample() +{ +#ifdef HAS_LUA_CHISELS + lua_getglobal(m_ls, "on_end_of_sample"); + + if(lua_pcall(m_ls, 0, 1, 0) != 0) + { + throw sinsp_exception(m_filename + " chisel error: calling on_end_of_sample() failed:" + lua_tostring(m_ls, -1)); + } + + int oeres = lua_toboolean(m_ls, -1); + lua_pop(m_ls, 1); + + if(oeres == false) + { + throw sinsp_exception("execution terminated by the " + m_filename + " chisel"); + } +#endif // HAS_LUA_CHISELS +} + +void sinsp_chisel::on_capture_start() +{ +#ifdef HAS_LUA_CHISELS + lua_getglobal(m_ls, "on_capture_start"); + + if(lua_isfunction(m_ls, -1)) + { + if(lua_pcall(m_ls, 0, 1, 0) != 0) + { + throw sinsp_exception(m_filename + " chisel error: " + lua_tostring(m_ls, -1)); + } + + if(!lua_isboolean(m_ls, -1)) + { + throw sinsp_exception(m_filename + " chisel error: wrong on_capture_start() return value. Boolean expected."); + } + + if(!lua_toboolean(m_ls, -1)) + { + throw sinsp_exception("init() for chisel " + m_filename + " failed."); + } + + lua_pop(m_ls, 1); + } +#endif // HAS_LUA_CHISELS +} + +void sinsp_chisel::on_capture_end() +{ +#ifdef HAS_LUA_CHISELS + lua_getglobal(m_ls, "on_capture_end"); + + if(lua_isfunction(m_ls, -1)) + { + uint64_t ts = m_inspector->m_firstevent_ts; + uint64_t te = m_inspector->get_lastevent_ts(); + int64_t delta = te - ts; + + lua_pushnumber(m_ls, (double)(te / 1000000000)); + lua_pushnumber(m_ls, (double)(te % 1000000000)); + lua_pushnumber(m_ls, (double)delta); + + if(lua_pcall(m_ls, 3, 0, 0) != 0) + { + throw sinsp_exception(m_filename + " chisel error: " + lua_tostring(m_ls, -1)); + } + + lua_pop(m_ls, 1); + } +#endif // HAS_LUA_CHISELS +} + +bool sinsp_chisel::get_nextrun_args(OUT string* args) +{ + ASSERT(m_lua_cinfo != NULL); + + *args = m_lua_cinfo->m_nextrun_args; + return m_lua_cinfo->m_has_nextrun_args; +} diff --git a/userspace/chisel/chisel.h b/userspace/chisel/chisel.h new file mode 100644 index 0000000000..a44376f163 --- /dev/null +++ b/userspace/chisel/chisel.h @@ -0,0 +1,197 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#pragma once + +#include +#include + +class sinsp_filter_check; + +/*! + \brief Add a new directory containing chisels. + + \parame front_add if true, the chisel directory is added at the front of + the search list and therefore gets priority. + + \note This function is not reentrant. +*/ +void chisel_add_dir(std::string dirname, bool front_add); + +typedef struct lua_State lua_State; + +/** @defgroup filter Filtering events + * Filtering infrastructure. + * @{ + */ + +/*! + \brief This is the class that compiles and runs filters. +*/ +struct chiseldir_info +{ + bool m_need_to_resolve; + std::string m_dir; +}; + +class chiselarg_desc +{ +public: + chiselarg_desc(std::string name, std::string type, std::string description, bool optional) + { + m_name = name; + m_type = type; + m_description = description; + m_optional = optional; + } + + std::string m_name; + std::string m_type; + std::string m_description; + bool m_optional; +}; + +class chisel_desc +{ +public: + void reset() + { + m_name = ""; + m_description = ""; + m_category = ""; + m_shortdesc = ""; + m_args.clear(); + } + + std::string m_name; + std::string m_description; + std::string m_category; + std::string m_shortdesc; + std::vector m_args; + chisel_view_info m_viewinfo; +}; + +class chisel_metric +{ +public: + void reset() + { + m_name = ""; + m_value = 0; + m_tags.clear(); + } + + std::string m_name; + double m_value = 0; + std::map m_tags; +}; + +class chiselinfo +{ +public: + chiselinfo(sinsp* inspector); + void init(std::string filterstr, std::string formatterstr); + void set_filter(std::string filterstr); + void set_formatter(std::string formatterstr); + void set_callback_interval(uint64_t interval); + void set_callback_precise_interval(uint64_t interval); + ~chiselinfo(); + sinsp_filter* m_filter; + sinsp_evt_formatter* m_formatter; + sinsp_dumper* m_dumper; + uint64_t m_callback_interval; + uint64_t m_callback_precise_interval; + bool m_has_nextrun_args; + std::string m_nextrun_args; + bool m_end_capture; + +private: + sinsp* m_inspector; +}; + +class SINSP_PUBLIC sinsp_chisel +{ +public: + sinsp_chisel(sinsp* inspector, std::string filename, bool is_file = true); + ~sinsp_chisel(); + + static void add_lua_package_path(lua_State* ls, const std::string& path); + static void get_chisel_list(std::vector* chisel_descs); + + void load(std::string cmdstr, bool is_file = true); + std::string get_name() + { + return m_filename; + } + uint32_t get_n_args(); + uint32_t get_n_optional_args(); + uint32_t get_n_required_args(); + void set_args(std::string args); + void set_args(std::vector> args); + bool run(sinsp_evt* evt); + void do_timeout(sinsp_evt* evt); + void do_end_of_sample(); + void on_init(); + void on_capture_start(); + void on_capture_end(); + bool get_nextrun_args(OUT std::string* args); + chisel_desc* get_lua_script_info() + { + return &m_lua_script_info; + } + +private: + bool openfile(std::string filename, OUT std::ifstream* is); + void free_lua_chisel(); + static chisel_field_aggregation string_to_aggregation(std::string ag); + static void parse_view_column(lua_State *ls, OUT chisel_desc* cd, OUT void* columns); + static void parse_view_columns(lua_State *ls, OUT chisel_desc* cd, OUT void* columns); + static void parse_view_action(lua_State *ls, OUT chisel_desc* cd, OUT void* actions); + static void parse_view_actions(lua_State *ls, OUT chisel_desc* cd, OUT void* actions); + static bool parse_view_info(lua_State *ls, OUT chisel_desc* cd); + static bool init_lua_chisel(chisel_desc &cd, std::string const &path); + void first_event_inits(sinsp_evt* evt); + + sinsp* m_inspector; + std::string m_description; + std::vector m_argvals; + std::string m_filename; + lua_State* m_ls; + chisel_desc m_lua_script_info; + bool m_lua_has_handle_evt; + bool m_lua_is_first_evt; + uint64_t m_lua_last_interval_sample_time; + uint64_t m_lua_last_interval_ts; + std::vector m_allocated_fltchecks; + char m_lua_fld_storage[PPM_MAX_ARG_SIZE]; + chiselinfo* m_lua_cinfo; + std::string m_new_chisel_to_exec; + int m_udp_socket; + struct sockaddr_in m_serveraddr; + + friend class lua_cbacks; +}; + +/*@}*/ diff --git a/userspace/chisel/chisel_api.cpp b/userspace/chisel/chisel_api.cpp new file mode 100644 index 0000000000..0a3381495c --- /dev/null +++ b/userspace/chisel/chisel_api.cpp @@ -0,0 +1,1578 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define HAS_LUA_CHISELS + +#ifdef HAS_LUA_CHISELS +extern "C" { +#define LUA_COMPAT_ALL +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} +#endif + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#endif + +using namespace std; + +extern vector* g_chisel_dirs; +extern sinsp_evttables g_infotables; +void lua_stackdump(lua_State *L); + +// todo(jasondellaluce): this list is static and prevents chisels from using +// plugin-defined extraction fields. The right way would be to have a filtercheck +// list owned by each chisel itself and populate depending on the loaded plugins. +static sinsp_filter_check_list s_filterlist; + +/////////////////////////////////////////////////////////////////////////////// +// Lua callbacks +/////////////////////////////////////////////////////////////////////////////// +#ifdef HAS_LUA_CHISELS + +uint32_t lua_cbacks::rawval_to_lua_stack(lua_State *ls, uint8_t* rawval, ppm_param_type ptype, uint32_t len) +{ + ASSERT(rawval != NULL); + + switch(ptype) + { + case PT_INT8: + lua_pushnumber(ls, *(int8_t*)rawval); + return 1; + case PT_INT16: + lua_pushnumber(ls, *(int16_t*)rawval); + return 1; + case PT_INT32: + lua_pushnumber(ls, *(int32_t*)rawval); + return 1; + case PT_INT64: + case PT_ERRNO: + case PT_PID: + case PT_FD: + lua_pushnumber(ls, (double)*(int64_t*)rawval); + return 1; + case PT_L4PROTO: // This can be resolved in the future + case PT_FLAGS8: + case PT_UINT8: + case PT_ENUMFLAGS8: + lua_pushnumber(ls, *(uint8_t*)rawval); + return 1; + case PT_PORT: // This can be resolved in the future + case PT_FLAGS16: + case PT_UINT16: + case PT_ENUMFLAGS16: + lua_pushnumber(ls, *(uint16_t*)rawval); + return 1; + case PT_FLAGS32: + case PT_UINT32: + case PT_MODE: + case PT_UID: + case PT_GID: + case PT_ENUMFLAGS32: + lua_pushnumber(ls, *(uint32_t*)rawval); + return 1; + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + lua_pushnumber(ls, (double)*(uint64_t*)rawval); + return 1; + case PT_DOUBLE: + lua_pushnumber(ls, *(double*)rawval); + return 1; + case PT_CHARBUF: + case PT_FSPATH: + case PT_FSRELPATH: + lua_pushlstring(ls, (char*)rawval, len); + return 1; + case PT_BYTEBUF: + if(rawval[len] == 0) + { + lua_pushlstring(ls, (char*)rawval, len); + return 1; + } + else + { + lua_getglobal(ls, "sichisel"); + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + uint32_t max_len = len < sizeof(ch->m_lua_fld_storage) ? + len : sizeof(ch->m_lua_fld_storage) - 1; + + memcpy(ch->m_lua_fld_storage, rawval, max_len); + ch->m_lua_fld_storage[max_len] = 0; + lua_pushlstring(ls, (char*)ch->m_lua_fld_storage, max_len); + return 1; + } + case PT_SOCKADDR: + ASSERT(false); + return 0; + case PT_SOCKFAMILY: + ASSERT(false); + return 0; + case PT_BOOL: + lua_pushboolean(ls, (*(uint32_t*)rawval != 0)); + return 1; + case PT_IPV4ADDR: + { + lua_getglobal(ls, "sichisel"); + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + snprintf(ch->m_lua_fld_storage, + sizeof(ch->m_lua_fld_storage), + "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, + rawval[0], + rawval[1], + rawval[2], + rawval[3]); + + lua_pushstring(ls, ch->m_lua_fld_storage); + return 1; + } + case PT_IPV6ADDR: + { + char address[100]; + ipv6addr *ip = (ipv6addr *) rawval; + + lua_getglobal(ls, "sichisel"); + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + if(NULL == inet_ntop(AF_INET6, ip->m_b, address, 100)) + { + strlcpy(address, "", sizeof(address)); + } + + strlcpy(ch->m_lua_fld_storage, + address, + sizeof(ch->m_lua_fld_storage)); + + lua_pushstring(ls, ch->m_lua_fld_storage); + return 1; + } + case PT_IPADDR: + { + if(len == sizeof(struct in_addr)) + { + return rawval_to_lua_stack(ls, rawval, PT_IPV4ADDR, len); + } + else if(len == sizeof(struct in6_addr)) + { + return rawval_to_lua_stack(ls, rawval, PT_IPV6ADDR, len); + } + else + { + throw sinsp_exception("rawval_to_lua_stack called with IP address of incorrect size " + to_string(len)); + } + } + break; + default: + ASSERT(false); + string err = "wrong event type " + to_string((long long) ptype); + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } +} + +int lua_cbacks::get_num(lua_State *ls) +{ + lua_getglobal(ls, "sievt"); + sinsp_evt* evt = (sinsp_evt*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + if(evt == NULL) + { + string err = "invalid call to evt.get_num()"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + lua_pushnumber(ls, (double)evt->get_num()); + return 1; +} + +int lua_cbacks::get_ts(lua_State *ls) +{ + lua_getglobal(ls, "sievt"); + sinsp_evt* evt = (sinsp_evt*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + if(evt == NULL) + { + string err = "invalid call to evt.get_ts()"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + uint64_t ts = evt->get_ts(); + + lua_pushinteger(ls, (uint32_t)(ts / 1000000000)); + lua_pushinteger(ls, (uint32_t)(ts % 1000000000)); + return 2; +} + +int lua_cbacks::get_type(lua_State *ls) +{ + lua_getglobal(ls, "sievt"); + sinsp_evt* evt = (sinsp_evt*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + if(evt == NULL) + { + string err = "invalid call to evt.get_type()"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + const char* evname; + uint16_t etype = evt->get_type(); + + if(etype == PPME_GENERIC_E || etype == PPME_GENERIC_X) + { + /* We have no name associated with a generic event, for this reason + * we get the ppm_sc from the event (first param) and we consider + * the syscall name as the event name. + */ + uint16_t ppm_sc = evt->get_param(0)->as(); + + evname = scap_get_ppm_sc_name((ppm_sc_code)ppm_sc); + } + else + { + evname = evt->get_name(); + } + + lua_pushstring(ls, evname); + + return 1; +} + +int lua_cbacks::get_cpuid(lua_State *ls) +{ + lua_getglobal(ls, "sievt"); + sinsp_evt* evt = (sinsp_evt*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + if(evt == NULL) + { + string err = "invalid call to evt.get_cpuid()"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + uint32_t cpuid = evt->get_cpuid(); + + lua_pushinteger(ls, cpuid); + return 1; +} + +int lua_cbacks::request_field(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + sinsp* inspector = ch->m_inspector; + + const char* fld = lua_tostring(ls, 1); + + if(fld == NULL) + { + string err = "chisel requesting nil field"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + auto chk = s_filterlist.new_filter_check_from_fldname(fld, + inspector, + false).release(); + + if(chk == NULL) + { + string err = "chisel requesting nonexistent field " + string(fld); + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + chk->parse_field_name(fld, true, false); + + lua_pushlightuserdata(ls, chk); + + ch->m_allocated_fltchecks.push_back(chk); + + return 1; +} + +int lua_cbacks::field(lua_State *ls) +{ + lua_getglobal(ls, "sievt"); + sinsp_evt* evt = (sinsp_evt*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + if(evt == NULL) + { + string err = "invalid call to evt.field()"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + sinsp_filter_check* chk = (sinsp_filter_check*)lua_topointer(ls, 1); + if(chk == NULL) + { + // + // This happens if the lua code is calling field() without invoking + // request_field() before. + // + lua_pushnil(ls); + return 1; + } + + chk->m_extracted_values.clear(); + if (chk->extract(evt, chk->m_extracted_values)) + { + // todo: Do something better here. For now, only support single-value extracted fields + return rawval_to_lua_stack(ls, chk->m_extracted_values[0].ptr, chk->get_field_info()->m_type, chk->m_extracted_values[0].len); + } + else + { + lua_pushnil(ls); + return 1; + } +} + +int lua_cbacks::set_global_filter(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + const char* filter = lua_tostring(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + try + { + ch->m_inspector->set_filter(filter); + } + catch(const sinsp_exception& e) + { + string err = "invalid filter in chisel " + ch->m_filename + ": " + e.what(); + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + return 0; +} + +int lua_cbacks::set_filter(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + const char* filter = lua_tostring(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + try + { + ch->m_lua_cinfo->set_filter(filter); + } + catch(const sinsp_exception& e) + { + string err = "invalid filter in chisel " + ch->m_filename + ": " + e.what(); + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + return 0; +} + +int lua_cbacks::set_snaplen(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + const uint32_t snaplen = (uint32_t)lua_tointeger(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_inspector->set_snaplen(snaplen); + + return 0; +} + +int lua_cbacks::set_output_format(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + if(ch->m_inspector->get_buffer_format() != sinsp_evt::PF_NORMAL) + { + // + // This means that the user has forced the format on the command line. + // We give that priority and we do nothing. + // + return 0; + } + + const char* fmt = lua_tostring(ls, 1); + + if(string(fmt) == "normal") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_NORMAL); + } + else if(string(fmt) == "json") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_JSON); + } + else if(string(fmt) == "simple") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_SIMPLE); + } + else if(string(fmt) == "hex") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_HEX); + } + else if(string(fmt) == "hexascii") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_HEXASCII); + } + else if(string(fmt) == "ascii") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_EOLS); + } + else if(string(fmt) == "base64") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_BASE64); + } + else if(string(fmt) == "jsonbase64") + { + ch->m_inspector->set_buffer_format(sinsp_evt::PF_JSONBASE64); + } + else + { + string err = "invalid set_output_format value in chisel " + ch->m_filename; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + return 0; +} + +int lua_cbacks::set_fatfile_dump_mode(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + int mode = lua_toboolean(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_inspector->set_fatfile_dump_mode(mode != 0); + + return 0; +} + +int lua_cbacks::make_ts(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + uint32_t op1 = (uint32_t)lua_tointeger(ls, 1); + lua_pop(ls, 1); + uint32_t op2 = (uint32_t)lua_tointeger(ls, 2); + lua_pop(ls, 1); + + uint64_t sum = (uint64_t)op1 * ONE_SECOND_IN_NS + op2; + + lua_pushstring(ls, to_string((long long) sum).c_str()); + return 1; +} + +int lua_cbacks::add_ts(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + uint64_t op1 = sinsp_numparser::parseu64(lua_tostring(ls, 1)); + lua_pop(ls, 1); + uint64_t op2 = sinsp_numparser::parseu64(lua_tostring(ls, 2)); + lua_pop(ls, 1); + + uint64_t sum = (op1 + op2); + + lua_pushstring(ls, to_string((long long) sum).c_str()); + return 1; +} + +int lua_cbacks::subtract_ts(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + uint64_t op1 = sinsp_numparser::parseu64(lua_tostring(ls, 1)); + lua_pop(ls, 1); + uint64_t op2 = sinsp_numparser::parseu64(lua_tostring(ls, 2)); + lua_pop(ls, 1); + + uint64_t sum = (op1 - op2); + + lua_pushstring(ls, to_string((long long) sum).c_str()); + return 1; +} + +int lua_cbacks::run_app(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + const char* args = lua_tostring(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_lua_cinfo->m_has_nextrun_args = true; + ch->m_lua_cinfo->m_nextrun_args = args; + + return 0; +} + +int lua_cbacks::end_capture(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_lua_cinfo->m_end_capture = true; + + return 0; +} + +int lua_cbacks::is_live(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + lua_pushboolean(ls, ch->m_inspector->is_live()); + return 1; +} + +int lua_cbacks::is_tty(lua_State *ls) +{ +#ifdef _WIN32 + int use_color = false; +#else + int use_color = isatty(1); +#endif + + lua_pushboolean(ls, use_color); + return 1; +} + +int lua_cbacks::get_terminal_info(lua_State *ls) +{ + int32_t width = -1; + int32_t height = -1; +#ifndef _WIN32 + struct winsize w; + + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) + { + width = w.ws_col; + height = w.ws_row; + } +#endif + + lua_newtable(ls); + lua_pushstring(ls, "width"); + lua_pushnumber(ls, width); + lua_settable(ls, -3); + lua_pushstring(ls, "height"); + lua_pushnumber(ls, height); + lua_settable(ls, -3); + + return 1; +} + +int lua_cbacks::get_filter(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_inspector); + + string flts = ch->m_inspector->get_filter(); + + lua_pushstring(ls, flts.c_str()); + + return 1; +} + +int lua_cbacks::get_machine_info(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + const scap_machine_info* minfo = ch->m_inspector->get_machine_info(); + + if(minfo == NULL) + { + string err = "get_machine_info can only be called from the on_capture_start callback"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + lua_newtable(ls); + lua_pushstring(ls, "num_cpus"); + lua_pushnumber(ls, minfo->num_cpus); + lua_settable(ls, -3); + lua_pushstring(ls, "memory_size_bytes"); + lua_pushnumber(ls, (double)minfo->memory_size_bytes); + lua_settable(ls, -3); + lua_pushstring(ls, "max_pid"); + lua_pushnumber(ls, (double)minfo->max_pid); + lua_settable(ls, -3); + lua_pushstring(ls, "hostname"); + lua_pushstring(ls, minfo->hostname); + lua_settable(ls, -3); + + return 1; +} + +int lua_cbacks::get_thread_table_int(lua_State *ls, bool include_fds, bool barebone) +{ + uint32_t j; + std::unique_ptr compiler; + std::unique_ptr filter; + sinsp_evt tevt; + scap_evt tscapevt; + + // + // Get the chisel state + // + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + ASSERT(ch->m_inspector); + + // + // If the caller specified a filter, compile it + // + if(lua_isstring(ls, 1)) + { + string filterstr = lua_tostring(ls, 1); + lua_pop(ls, 1); + + if(filterstr != "") + { + try + { + compiler = std::make_unique(ch->m_inspector, filterstr); + filter = compiler->compile(); + } + catch(const sinsp_exception& e) + { + string err = "invalid filter argument for get_thread_table in chisel " + ch->m_filename + ": " + e.what(); + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + tscapevt.ts = ch->m_inspector->get_lastevent_ts(); + tscapevt.type = PPME_SYSCALL_READ_X; + tscapevt.len = 0; + tscapevt.nparams = 0; + + tevt.set_inspector(ch->m_inspector); + tevt.set_info(&(g_infotables.m_event_info[PPME_SYSCALL_READ_X])); + tevt.set_scap_evt(&tscapevt); + tevt.set_cpuid(0); + tevt.set_num(0); + } + } + + threadinfo_map_t* threadtable = ch->m_inspector->m_thread_manager->get_threads(); + + ASSERT(threadtable != NULL); + + lua_newtable(ls); + + threadtable->loop([&] (sinsp_threadinfo& tinfo) { + // + // Check if there's at least an fd that matches the filter. + // If not, skip this thread + // + sinsp_fdtable* fdtable = tinfo.get_fd_table(); + if(fdtable == nullptr) + { + // If no fdtable is available we can't do anything + return true; + } + + if(filter != NULL) + { + bool match = false; + + fdtable->loop([&](int64_t fd, sinsp_fdinfo& fdi) { + tevt.set_tinfo(&tinfo); + tevt.set_fd_info(&fdi); + tscapevt.tid = tinfo.m_tid; + int64_t tlefd = tevt.get_tinfo()->m_lastevent_fd; + tevt.get_tinfo()->m_lastevent_fd = fd; + + if(filter->run(&tevt)) + { + match = true; + return false; // break out of the loop + } + + tevt.get_tinfo()->m_lastevent_fd = tlefd; + return true; + }); + + if(!match) + { + return true; + } + } + + // + // Set the thread properties + // + lua_newtable(ls); + lua_pushliteral(ls, "tid"); + lua_pushnumber(ls, (uint32_t)tinfo.m_tid); + lua_settable(ls, -3); + lua_pushliteral(ls, "pid"); + lua_pushnumber(ls, (uint32_t)tinfo.m_pid); + lua_settable(ls, -3); + if(!barebone) + { + lua_pushliteral(ls, "ptid"); + lua_pushnumber(ls, (uint32_t)tinfo.m_ptid); + lua_settable(ls, -3); + lua_pushliteral(ls, "comm"); + lua_pushstring(ls, tinfo.m_comm.c_str()); + lua_settable(ls, -3); + lua_pushliteral(ls, "exe"); + lua_pushstring(ls, tinfo.m_exe.c_str()); + lua_settable(ls, -3); + lua_pushliteral(ls, "flags"); + lua_pushnumber(ls, (uint32_t)tinfo.m_flags); + lua_settable(ls, -3); + lua_pushliteral(ls, "fdlimit"); + lua_pushnumber(ls, (uint32_t)tinfo.m_fdlimit); + lua_settable(ls, -3); + lua_pushliteral(ls, "uid"); + lua_pushnumber(ls, (uint32_t)tinfo.m_user.uid); + lua_settable(ls, -3); + lua_pushliteral(ls, "gid"); + lua_pushnumber(ls, (uint32_t)tinfo.m_group.gid); + lua_settable(ls, -3); + lua_pushliteral(ls, "nchilds"); + lua_pushnumber(ls, (uint32_t)tinfo.get_num_not_leader_threads()); + lua_settable(ls, -3); + lua_pushliteral(ls, "vmsize_kb"); + lua_pushnumber(ls, (uint32_t)tinfo.m_vmsize_kb); + lua_settable(ls, -3); + lua_pushliteral(ls, "vmrss_kb"); + lua_pushnumber(ls, (uint32_t)tinfo.m_vmrss_kb); + lua_settable(ls, -3); + lua_pushliteral(ls, "vmswap_kb"); + lua_pushnumber(ls, (uint32_t)tinfo.m_vmswap_kb); + lua_settable(ls, -3); + lua_pushliteral(ls, "pfmajor"); + lua_pushnumber(ls, (uint32_t)tinfo.m_pfmajor); + lua_settable(ls, -3); + lua_pushliteral(ls, "pfminor"); + lua_pushnumber(ls, (uint32_t)tinfo.m_pfminor); + lua_settable(ls, -3); + lua_pushliteral(ls, "clone_ts"); + lua_pushstring(ls, to_string((long long int)tinfo.m_clone_ts).c_str()); + lua_settable(ls, -3); + + // + // Extract the user name + // + lua_pushliteral(ls, "username"); + lua_pushstring(ls, tinfo.m_user.name); + lua_settable(ls, -3); + + // + // Create the arguments sub-table + // + lua_pushstring(ls, "args"); + + vector* args = &tinfo.m_args; + lua_newtable(ls); + for(j = 0; j < args->size(); j++) + { + lua_pushinteger(ls, j + 1); + lua_pushstring(ls, args->at(j).c_str()); + lua_settable(ls, -3); + } + lua_settable(ls,-3); + + // + // Create the environment variables sub-table + // + lua_pushstring(ls, "env"); + + const auto& env = tinfo.get_env(); + lua_newtable(ls); + for(j = 0; j < env.size(); j++) + { + lua_pushinteger(ls, j + 1); + lua_pushstring(ls, env.at(j).c_str()); + lua_settable(ls, -3); + } + lua_settable(ls,-3); + } + + // + // Create and populate the FD table + // + lua_pushstring(ls, "fdtable"); + lua_newtable(ls); + + if(include_fds) + { + fdtable->loop([&](int64_t fd, sinsp_fdinfo& fdi) { + tevt.set_tinfo(&tinfo); + tevt.set_fd_info(&fdi); + tscapevt.tid = tinfo.m_tid; + int64_t tlefd = tevt.get_tinfo()->m_lastevent_fd; + tevt.get_tinfo()->m_lastevent_fd = fd; + + if(filter != NULL) + { + if(filter->run(&tevt) == false) + { + // continue the loop to next iteration + return true; + } + } + + tevt.get_tinfo()->m_lastevent_fd = tlefd; + + lua_newtable(ls); + if(!barebone) + { + lua_pushliteral(ls, "name"); + lua_pushstring(ls, fdi.tostring_clean().c_str()); + lua_settable(ls, -3); + lua_pushliteral(ls, "type"); + lua_pushstring(ls, fdi.get_typestring()); + lua_settable(ls, -3); + } + + scap_fd_type evt_type = fdi.m_type; + if(evt_type == SCAP_FD_IPV4_SOCK || evt_type == SCAP_FD_IPV4_SERVSOCK || + evt_type == SCAP_FD_IPV6_SOCK || evt_type == SCAP_FD_IPV6_SERVSOCK) + { + bool include_client; + char sipbuf[128], cipbuf[128]; + uint8_t *sip, *cip; + uint16_t sport, cport; + bool is_server; + int af; + + if(evt_type == SCAP_FD_IPV4_SOCK) + { + include_client = true; + af = AF_INET; + cip = (uint8_t*)&(fdi.m_sockinfo.m_ipv4info.m_fields.m_sip); + sip = (uint8_t*)&(fdi.m_sockinfo.m_ipv4info.m_fields.m_dip); + cport = fdi.m_sockinfo.m_ipv4info.m_fields.m_sport; + sport = fdi.m_sockinfo.m_ipv4info.m_fields.m_dport; + is_server = fdi.is_role_server(); + } + else if (evt_type == SCAP_FD_IPV4_SERVSOCK) + { + include_client = false; + af = AF_INET; + cip = NULL; + sip = (uint8_t*)&(fdi.m_sockinfo.m_ipv4serverinfo.m_ip); + sport = fdi.m_sockinfo.m_ipv4serverinfo.m_port; + is_server = true; + } + else if (evt_type == SCAP_FD_IPV6_SOCK) + { + include_client = true; + af = AF_INET6; + cip = (uint8_t*)&(fdi.m_sockinfo.m_ipv6info.m_fields.m_sip); + sip = (uint8_t*)&(fdi.m_sockinfo.m_ipv6info.m_fields.m_dip); + cport = fdi.m_sockinfo.m_ipv6info.m_fields.m_sport; + sport = fdi.m_sockinfo.m_ipv6info.m_fields.m_dport; + is_server = fdi.is_role_server(); + } + else + { + include_client = false; + af = AF_INET6; + cip = NULL; + sip = (uint8_t*)&(fdi.m_sockinfo.m_ipv6serverinfo.m_ip); + sport = fdi.m_sockinfo.m_ipv6serverinfo.m_port; + is_server = true; + } + + // Now convert the raw sip/cip to strings + if(NULL == inet_ntop(af, sip, sipbuf, sizeof(sipbuf))) + { + strlcpy(sipbuf, "", sizeof(sipbuf)); + } + + if(cip) + { + if(NULL == inet_ntop(af, cip, cipbuf, sizeof(cipbuf))) + { + strlcpy(cipbuf, "", sizeof(cipbuf)); + } + } + + if(include_client) + { + // cip + lua_pushliteral(ls, "cip"); + lua_pushstring(ls, cipbuf); + lua_settable(ls, -3); + } + + // sip + lua_pushliteral(ls, "sip"); + lua_pushstring(ls, sipbuf); + lua_settable(ls, -3); + + if(include_client) + { + // cport + lua_pushliteral(ls, "cport"); + lua_pushnumber(ls, cport); + lua_settable(ls, -3); + } + + // sport + lua_pushliteral(ls, "sport"); + lua_pushnumber(ls, sport); + lua_settable(ls, -3); + + // is_server + lua_pushliteral(ls, "is_server"); + lua_pushboolean(ls, is_server); + lua_settable(ls, -3); + + // l4proto + const char* l4ps; + scap_l4_proto l4p = fdi.get_l4proto(); + + switch(l4p) + { + case SCAP_L4_TCP: + l4ps = "tcp"; + break; + case SCAP_L4_UDP: + l4ps = "udp"; + break; + case SCAP_L4_ICMP: + l4ps = "icmp"; + break; + case SCAP_L4_RAW: + l4ps = "raw"; + break; + default: + l4ps = ""; + break; + } + + // l4proto + lua_pushliteral(ls, "l4proto"); + lua_pushstring(ls, l4ps); + lua_settable(ls, -3); + } + + // is_server + string l4proto; + + lua_rawseti(ls,-2, (uint32_t)fd); + return true; + }); + } + + + lua_settable(ls,-3); + + // + // Set the key for this entry + // + lua_rawseti(ls,-2, (uint32_t)tinfo.m_tid); + return true; + }); + + return 1; +} + +int lua_cbacks::get_thread_table(lua_State *ls) +{ + return get_thread_table_int(ls, true, false); +} + +int lua_cbacks::get_thread_table_nofds(lua_State *ls) +{ + return get_thread_table_int(ls, false, false); +} + +int lua_cbacks::get_thread_table_barebone(lua_State *ls) +{ + return get_thread_table_int(ls, true, true); +} + +int lua_cbacks::get_thread_table_barebone_nofds(lua_State *ls) +{ + return get_thread_table_int(ls, false, true); +} + +int lua_cbacks::get_container_table(lua_State *ls) +{ +#ifndef _WIN32 + uint32_t j; + sinsp_evt tevt; + + // + // Get the chisel state + // + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + ASSERT(ch->m_inspector); + + // + // Retrieve the container list + // + const sinsp_container_manager::map_ptr_t ctable = ch->m_inspector->m_container_manager.get_containers(); + + lua_newtable(ls); + + // + // Go through the list + // + j = 0; + for(auto it = ctable->begin(); it != ctable->end(); ++it) + { + lua_newtable(ls); + lua_pushliteral(ls, "id"); + lua_pushstring(ls, it->second->m_id.c_str()); + lua_settable(ls, -3); + lua_pushliteral(ls, "name"); + lua_pushstring(ls, it->second->m_name.c_str()); + lua_settable(ls, -3); + lua_pushliteral(ls, "image"); + lua_pushstring(ls, it->second->m_image.c_str()); + lua_settable(ls, -3); + + lua_pushliteral(ls, "type"); + if(it->second->m_type == CT_DOCKER) + { + lua_pushstring(ls, "docker"); + } + else if(it->second->m_type == CT_LXC) + { + lua_pushstring(ls, "lxc"); + } + else if(it->second->m_type == CT_LIBVIRT_LXC) + { + lua_pushstring(ls, "libvirt_lxc"); + } + else if(it->second->m_type == CT_MESOS) + { + lua_pushstring(ls, "mesos"); + } + else if(it->second->m_type == CT_RKT) + { + lua_pushstring(ls, "rkt"); + } + else if(it->second->m_type == CT_CRI) + { + lua_pushstring(ls, "cri"); + } + else if(it->second->m_type == CT_CONTAINERD) + { + lua_pushstring(ls, "containerd"); + } + else if(it->second->m_type == CT_CRIO) + { + lua_pushstring(ls, "cri-o"); + } + else if(it->second->m_type == CT_BPM) + { + lua_pushstring(ls, "bpm"); + } + else + { + ASSERT(false); + lua_pushstring(ls, "unknown"); + } + lua_settable(ls, -3); + + // + // Set the key for this entry + // + lua_rawseti(ls,-2, (uint32_t)++j); + } + +#endif + return 1; +} + +int lua_cbacks::is_print_container_data(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + lua_pushboolean(ls, ch->m_inspector->is_print_container_data()); + return 1; +} + + +int lua_cbacks::get_output_format(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + sinsp_evt::param_fmt fmt = ch->m_inspector->get_buffer_format(); + + if(fmt & sinsp_evt::PF_JSON) + { + lua_pushstring(ls, "json"); + } + else + { + lua_pushstring(ls, "normal"); + } + + return 1; +} + +int lua_cbacks::get_evtsource_name(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + if(ch->m_inspector->is_live()) + { + lua_pushstring(ls, ""); + } + else + { + lua_pushstring(ls, ch->m_inspector->get_input_filename().c_str()); + } + + return 1; +} + +int lua_cbacks::get_firstevent_ts(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + lua_pushstring(ls, to_string(ch->m_inspector->m_firstevent_ts).c_str()); + + return 1; +} + +int lua_cbacks::get_lastevent_ts(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + lua_pushstring(ls, to_string(ch->m_inspector->get_lastevent_ts()).c_str()); + + return 1; +} + +int lua_cbacks::set_event_formatter(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + const char* formatter = lua_tostring(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_lua_cinfo->set_formatter(formatter); + + return 0; +} + +int lua_cbacks::set_interval_ns(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + uint64_t interval = (uint64_t)lua_tonumber(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_lua_cinfo->set_callback_interval(interval); + + return 0; +} + +int lua_cbacks::set_interval_s(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + uint64_t interval = (uint64_t)lua_tonumber(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_lua_cinfo->set_callback_interval(interval * 1000000000); + + return 0; +} + +int lua_cbacks::set_precise_interval_ns(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + uint64_t interval = (uint64_t)lua_tonumber(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + ch->m_lua_cinfo->set_callback_precise_interval(interval); + + return 0; +} + +int lua_cbacks::exec(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + const char* chname = lua_tostring(ls, 1); + if(chname == NULL) + { + string err = "invalid exec field name in chisel " + ch->m_filename; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + ch->m_new_chisel_to_exec = chname; + + ch->m_argvals.clear(); + uint32_t stackpos = 2; + + while(true) + { + const char* argval = lua_tostring(ls, stackpos++); + if(argval == NULL) + { + break; + } + + ch->m_argvals.push_back(argval); + } + + return 0; +} + +int lua_cbacks::log(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + string message(lua_tostring(ls, 1)); + string sevstr(lua_tostring(ls, 2)); + + sinsp_logger::severity sevcode = sinsp_logger::SEV_INFO; + + if(sevstr == "debug") + { + sevcode = sinsp_logger::SEV_DEBUG; + } + else if(sevstr == "info") + { + sevcode = sinsp_logger::SEV_INFO; + } + else if(sevstr == "warning") + { + sevcode = sinsp_logger::SEV_WARNING; + } + else if(sevstr == "error") + { + sevcode = sinsp_logger::SEV_ERROR; + } + else if(sevstr == "critical") + { + sevcode = sinsp_logger::SEV_CRITICAL; + } + + libsinsp_logger()->log(message, sevcode); + + return 0; +} + +int lua_cbacks::udp_setpeername(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + + string addr(lua_tostring(ls, 1)); + string ports(lua_tostring(ls, 2)); + uint16_t port = htons(sinsp_numparser::parseu16(ports)); + + ch->m_udp_socket = socket(AF_INET, SOCK_DGRAM, 0); + if(ch->m_udp_socket < 0) + { + string err = "udp_setpeername error: unable to create the socket"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + memset(&ch->m_serveraddr, 0, sizeof(ch->m_serveraddr)); + ch->m_serveraddr.sin_family = AF_INET; + ch->m_serveraddr.sin_port = port; + if(inet_pton(AF_INET, addr.c_str(), &ch->m_serveraddr.sin_addr) <= 0) + { + string err = "inet_pton error occurred"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + return 0; +} + +int lua_cbacks::udp_send(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + + string message(lua_tostring(ls, 1)); + + if(sendto(ch->m_udp_socket, message.c_str(), message.size(), 0, + (sockaddr*)&ch->m_serveraddr, sizeof(ch->m_serveraddr)) < 0) + { + string err = "udp_send error: cannot send the buffer: "; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + return 0; +} + +int lua_cbacks::get_read_progress(lua_State *ls) +{ + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_inspector); + + double pr = ch->m_inspector->get_read_progress(); + + lua_pushnumber(ls, pr); + + return 1; +} + +int lua_cbacks::push_metric(lua_State *ls) +{ + chisel_metric metric; + + lua_getglobal(ls, "sichisel"); + + sinsp_chisel* ch = (sinsp_chisel*)lua_touserdata(ls, -1); + lua_pop(ls, 1); + + ASSERT(ch); + ASSERT(ch->m_lua_cinfo); + + sinsp* inspector = ch->m_inspector; + + // + // tags + // + if(lua_istable(ls, 3)) + { + lua_pushnil(ls); + + while(lua_next(ls, 3) != 0) + { + string tag = lua_tostring(ls, -1); + metric.m_tags[tag] = ""; + lua_pop(ls, 1); + } + + lua_pop(ls, 1); + } + else + { + string err = "error in chisel " + ch->m_filename + ": third argument must be a table"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + // + // Name + // + if(lua_isstring(ls, 1)) + { + metric.m_name = lua_tostring(ls, 1); + } + else + { + string err = "errord in chisel " + ch->m_filename + ": first argument must be a string"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + // + // Value + // + if(lua_isnumber(ls, 2)) + { + metric.m_value = lua_tonumber(ls, 2); + } + else + { + string err = "errord in chisel " + ch->m_filename + ": second argument must be a number"; + fprintf(stderr, "%s\n", err.c_str()); + throw sinsp_exception("chisel error"); + } + + //if (inspector->m_external_event_processor) + //{ + // inspector->m_external_event_processor->add_chisel_metric(&metric); + //} + + return 0; +} + + +#endif // HAS_LUA_CHISELS diff --git a/userspace/chisel/chisel_api.h b/userspace/chisel/chisel_api.h new file mode 100644 index 0000000000..b882ce4cac --- /dev/null +++ b/userspace/chisel/chisel_api.h @@ -0,0 +1,77 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#pragma once + +class lua_cbacks +{ +public: + static uint32_t rawval_to_lua_stack(lua_State *ls, uint8_t* rawval, ppm_param_type ptype, uint32_t len); + + static int get_num(lua_State *ls); + static int get_ts(lua_State *ls); + static int get_type(lua_State *ls); + static int get_cpuid(lua_State *ls); + static int request_field(lua_State *ls); + static int field(lua_State *ls); + static int set_global_filter(lua_State *ls); + static int set_filter(lua_State *ls); + static int set_snaplen(lua_State *ls); + static int set_output_format(lua_State *ls); + static int set_fatfile_dump_mode(lua_State *ls); + static int make_ts(lua_State *ls); + static int add_ts(lua_State *ls); + static int subtract_ts(lua_State *ls); + static int run_app(lua_State *ls); + static int end_capture(lua_State *ls); + static int is_live(lua_State *ls); + static int is_tty(lua_State *ls); + static int get_terminal_info(lua_State *ls); + static int get_filter(lua_State *ls); + static int get_machine_info(lua_State *ls); + static int get_thread_table(lua_State *ls); + static int get_thread_table_nofds(lua_State *ls); + static int get_thread_table_barebone(lua_State *ls); + static int get_thread_table_barebone_nofds(lua_State *ls); + static int get_container_table(lua_State *ls); + static int is_print_container_data(lua_State *ls); + static int get_output_format(lua_State *ls); + static int get_evtsource_name(lua_State *ls); + static int get_firstevent_ts(lua_State *ls); + static int get_lastevent_ts(lua_State *ls); + static int set_event_formatter(lua_State *ls); + static int set_interval_ns(lua_State *ls); + static int set_interval_s(lua_State *ls); + static int set_precise_interval_ns(lua_State *ls); + static int exec(lua_State *ls); + static int log(lua_State *ls); + static int udp_setpeername(lua_State *ls); + static int udp_send(lua_State *ls); + static int get_read_progress(lua_State *ls); + static int push_metric(lua_State *ls); +private: + static int get_thread_table_int(lua_State *ls, bool include_fds, bool barebone); +}; + diff --git a/userspace/chisel/chisel_capture_interrupt_exception.h b/userspace/chisel/chisel_capture_interrupt_exception.h new file mode 100644 index 0000000000..fe7604eab7 --- /dev/null +++ b/userspace/chisel/chisel_capture_interrupt_exception.h @@ -0,0 +1,38 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ +#pragma once + +#include + +/*! + \brief sinsp library exception. +*/ +class chisel_capture_interrupt_exception : public sinsp_exception +{ +public: + chisel_capture_interrupt_exception(): + sinsp_exception("capture interrupted") + { } +}; diff --git a/userspace/chisel/chisel_fields_info.cpp b/userspace/chisel/chisel_fields_info.cpp new file mode 100644 index 0000000000..c46e9c2d21 --- /dev/null +++ b/userspace/chisel/chisel_fields_info.cpp @@ -0,0 +1,197 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +// +// Various helper functions to render stuff on the screen +// +#include +#include +#include +#include + +#include +#include +#include + +// Must match the value in the zsh tab completion +#define DESCRIPTION_TEXT_START 16 + +#define CONSOLE_LINE_LEN 79 +#define PRINTF_WRAP_CPROC(x) #x +#define PRINTF_WRAP(x) PRINTF_WRAP_CPROC(x) + +using namespace std; + +struct summary_chisel_comparer +{ + bool operator()(const chisel_desc& first, const chisel_desc& second) const + { + return (first.m_category == second.m_category) + ? first.m_name < second.m_name + : first.m_category < second.m_category; + } +}; + +void list_chisels(vector* chlist, bool verbose); + +void print_chisel_info(chisel_desc* cd) +{ + // First we create a single list composed of + // just this chisel and then run the short_description + // over it in order to get those fields for free. + std::vector chlist; + chlist.push_back(cd[0]); + + list_chisels(&chlist, false); + + // Now we have to do the real work + printf("\n"); + + uint32_t l; + string astr; + + string desc = cd->m_description; + size_t desclen = desc.size(); + + for(l = 0; l < desclen; l++) + { + if(l % CONSOLE_LINE_LEN == 0 && l != 0) + { + printf("\n"); + } + + printf("%c", desc[l]); + } + + printf("\n"); + + astr += "Args:\n"; + + if(cd->m_args.size() != 0) + { + + for(l = 0; l < cd->m_args.size(); l++) + { + astr += "[" + cd->m_args[l].m_type + "] " + cd->m_args[l].m_name + " - "; + astr += cd->m_args[l].m_description + "\n"; + } + } + else + { + astr += "(None)"; + } + + size_t astrlen = astr.size(); + int linepos = 0; + + for(l = 0; l < astrlen; l++, linepos++) + { + if(astr[l] == '\n') + linepos = -1; + else if(linepos % (CONSOLE_LINE_LEN - DESCRIPTION_TEXT_START) == 0 && linepos != 0) + { + printf("\n%" PRINTF_WRAP(DESCRIPTION_TEXT_START) "s", ""); + } + + printf("%c", astr[l]); + } + + // just for good measure + printf("\n"); +} + +void list_chisels(vector* chlist, bool verbose) +{ + uint32_t j, l; + + // + // Sort the list by name + // + sort(chlist->begin(), chlist->end(), summary_chisel_comparer()); + string last_category; + + // + // Print the list to the screen + // + for(j = 0; j < chlist->size(); j++) + { + chisel_desc* cd = &(chlist->at(j)); + + if(cd->m_viewinfo.m_valid) + { + continue; + } + + string category = cd->m_category; + + if(category != last_category) + { + string fullcatstr = "Category: " + category; + + printf("\n%s\n", fullcatstr.c_str()); + for(l = 0; l < fullcatstr.size(); l++) + { + putchar('-'); + } + + printf("\n"); + last_category = category; + } + + printf("%s", cd->m_name.c_str()); + uint32_t namelen = (uint32_t)cd->m_name.size(); + + if(namelen >= DESCRIPTION_TEXT_START) + { + printf("\n"); + namelen = 0; + } + + for(l = 0; l < (DESCRIPTION_TEXT_START - namelen); l++) + { + printf(" "); + } + + string desc = cd->m_shortdesc; + size_t desclen = desc.size(); + + for(l = 0; l < desclen; l++) + { + if(l % (CONSOLE_LINE_LEN - DESCRIPTION_TEXT_START) == 0 && l != 0) + { + printf("\n%" PRINTF_WRAP(DESCRIPTION_TEXT_START) "s", ""); + } + + printf("%c", desc[l]); + } + + printf("\n"); + } + + if(verbose) + { + printf("\nUse the -i flag to get detailed information about a specific chisel\n"); + } +} diff --git a/userspace/chisel/chisel_fields_info.h b/userspace/chisel/chisel_fields_info.h new file mode 100644 index 0000000000..ae3e60606d --- /dev/null +++ b/userspace/chisel/chisel_fields_info.h @@ -0,0 +1,32 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#pragma once + +#include + +void print_chisel_info(chisel_desc* cd); +void list_chisels(std::vector* chlist, bool verbose); diff --git a/userspace/chisel/chisel_table.cpp b/userspace/chisel/chisel_table.cpp new file mode 100644 index 0000000000..d6f7cd0cb0 --- /dev/null +++ b/userspace/chisel/chisel_table.cpp @@ -0,0 +1,1627 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#include + +#include +#include + +using namespace std; + +extern sinsp_evttables g_infotables; + +// todo(jasondellaluce): this list is static and prevents chisels from using +// plugin-defined extraction fields. The right way would be to have a filtercheck +// list owned by each chisel itself and populate depending on the loaded plugins. +static sinsp_filter_check_list s_filterlist; + +// +// +// Table sorter functor +struct table_row_cmp +{ + bool operator()(const chisel_sample_row& src, const chisel_sample_row& dst) + { + cmpop op; + + if(m_ascending) + { + op = CO_LT; + } + else + { + op = CO_GT; + } + + if(src.m_values[m_colid].m_cnt > 1 || + dst.m_values[m_colid].m_cnt > 1) + { + return flt_compare_avg(op, m_type, + src.m_values[m_colid].m_val, + dst.m_values[m_colid].m_val, + src.m_values[m_colid].m_len, + dst.m_values[m_colid].m_len, + src.m_values[m_colid].m_cnt, + dst.m_values[m_colid].m_cnt); + } + else + { + return flt_compare(op, m_type, + src.m_values[m_colid].m_val, + dst.m_values[m_colid].m_val, + src.m_values[m_colid].m_len, + dst.m_values[m_colid].m_len); + } + } + + uint32_t m_colid; + ppm_param_type m_type; + bool m_ascending; +}; + +chisel_table::chisel_table(sinsp* inspector, tabletype type, uint64_t refresh_interval_ns, + chisel_table::output_type output_type, uint32_t json_first_row, uint32_t json_last_row) +{ + m_inspector = inspector; + m_type = type; + m_is_key_present = false; + m_is_groupby_key_present = false; + m_fld_pointers = NULL; + m_premerge_fld_pointers = NULL; + m_postmerge_fld_pointers = NULL; + m_n_fields = 0; + m_n_premerge_fields = 0; + m_n_postmerge_fields = 0; + m_refresh_interval_ns = refresh_interval_ns; + m_output_type = output_type; + m_next_flush_time_ns = 0; + m_prev_flush_time_ns = 0; + m_printer = new sinsp_filter_check_reference(); + m_buffer = &m_buffer1; + m_is_sorting_ascending = false; + m_sorting_col = -1; + m_just_sorted = true; + m_do_merging = true; + m_types = &m_premerge_types; + m_table = &m_premerge_table; + m_extractors = &m_premerge_extractors; + m_filter = NULL; + m_use_defaults = false; + m_zero_u64 = 0; + m_zero_double = 0; + m_paused = false; + m_sample_data = NULL; + m_json_first_row = json_first_row; + m_json_last_row = json_last_row; +} + +chisel_table::~chisel_table() +{ + uint32_t j; + + for(j = 0; j < m_chks_to_free.size(); j++) + { + delete m_chks_to_free[j]; + } + + if(m_premerge_fld_pointers != NULL) + { + delete[] m_premerge_fld_pointers; + } + + if(m_postmerge_fld_pointers != NULL) + { + delete[] m_postmerge_fld_pointers; + } + + if(m_filter != NULL) + { + delete m_filter; + } + + delete m_printer; +} + +void chisel_table::configure(vector* entries, const string& filter, + bool use_defaults, uint32_t view_depth) +{ + m_use_defaults = use_defaults; + m_view_depth = view_depth; + + // + // If this is a list table, increase the refresh time to improve realtimyiness + // + if(m_type == chisel_table::TT_LIST) + { + set_refresh_interval(200000000); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // If a filter has been specified, compile it + ////////////////////////////////////////////////////////////////////////////////////// + if(filter != "") + { + sinsp_filter_compiler compiler(m_inspector, filter); + m_filter = compiler.compile().release(); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // Extract the tokens + ////////////////////////////////////////////////////////////////////////////////////// + m_premerge_extractors.clear(); + + for(auto vit : *entries) + { + auto chk = s_filterlist.new_filter_check_from_fldname(vit.get_field(m_view_depth), + m_inspector, + false).release(); + + if(chk == NULL) + { + throw sinsp_exception("invalid field name " + vit.get_field(m_view_depth)); + } + + check_wrapper* chk_wrap = new check_wrapper(chk, (chisel_field_aggregation)vit.m_aggregation); + m_chks_to_free.push_back(chk_wrap); + + chk->parse_field_name(vit.get_field(m_view_depth).c_str(), true, false); + + if((vit.m_flags & TEF_IS_KEY) != 0) + { + if(m_is_key_present) + { + throw sinsp_exception("invalid table configuration: multiple keys specified"); + } + + m_premerge_extractors.insert(m_premerge_extractors.begin(), chk_wrap); + m_is_key_present = true; + } + else + { + m_premerge_extractors.push_back(chk_wrap); + } + } + + if(m_type == chisel_table::TT_TABLE) + { + // + // Make sure this is a valid table + // + if(!m_is_key_present) + { + throw sinsp_exception("table is missing the key"); + } + } + else + { + auto chk = s_filterlist.new_filter_check_from_fldname("util.cnt", + m_inspector, + false).release(); + + if(chk == NULL) + { + throw sinsp_exception("internal table error"); + } + + check_wrapper* chk_wrap = new check_wrapper(chk, A_NONE); + m_chks_to_free.push_back(chk_wrap); + + chk->parse_field_name("util.cnt", true, false); + + if(m_is_key_present) + { + throw sinsp_exception("list table can't have a key"); + } + + m_premerge_extractors.insert(m_premerge_extractors.begin(), chk_wrap); + m_is_key_present = true; + } + + m_premerge_fld_pointers = new chisel_table_field[m_premerge_extractors.size()]; + m_fld_pointers = m_premerge_fld_pointers; + m_n_premerge_fields = (uint32_t)m_premerge_extractors.size(); + m_n_fields = m_n_premerge_fields; + + if(m_n_fields < 2) + { + throw sinsp_exception("table has no values"); + } + + for(auto it = m_premerge_extractors.begin(); it != m_premerge_extractors.end(); ++it) + { + m_premerge_types.push_back((*it)->m_check->get_field_info()->m_type); + m_premerge_legend.push_back(*(*it)->m_check->get_field_info()); + } + + m_premerge_vals_array_sz = (m_n_fields - 1) * sizeof(chisel_table_field); + m_vals_array_sz = m_premerge_vals_array_sz; + + ////////////////////////////////////////////////////////////////////////////////////// + // If a merge has been specified, configure it + ////////////////////////////////////////////////////////////////////////////////////// + uint32_t n_gby_keys = 0; + + for(auto vit : *entries) + { + if((vit.m_flags & TEF_IS_GROUPBY_KEY) != 0) + { + n_gby_keys++; + } + } + + if(n_gby_keys == 0) + { + // + // No merge string. We can stop here + // + m_do_merging = false; + return; + } + else if(n_gby_keys > 1) + { + throw sinsp_exception("invalid table definition: multiple groupby keys"); + } + + // + // Merging not supported for lists + // + if(m_type != chisel_table::TT_TABLE) + { + throw sinsp_exception("group by not supported for list tables"); + } + + m_do_merging = true; + + for(uint32_t j = 0; j < entries->size(); j++) + { + auto vit = entries->at(j); + + // + // Skip original key when grouping + // + if((vit.m_flags & TEF_IS_KEY) != 0) + { + continue; + } + + + check_wrapper* chk_wrap = m_premerge_extractors[j]; + + chk_wrap->m_merge_aggregation = (chisel_field_aggregation)vit.m_groupby_aggregation; + + if((vit.m_flags & TEF_IS_GROUPBY_KEY) != 0) + { + if(m_is_groupby_key_present) + { + throw sinsp_exception("invalid table configuration: more than one groupby key specified"); + } + + m_is_groupby_key_present = true; + m_postmerge_extractors.insert(m_postmerge_extractors.begin(), chk_wrap); + m_groupby_columns.insert(m_groupby_columns.begin(), j); + } + else + { + m_postmerge_extractors.push_back(chk_wrap); + m_groupby_columns.push_back(j); + } + } + + m_postmerge_fld_pointers = new chisel_table_field[m_postmerge_extractors.size()]; + m_n_postmerge_fields = (uint32_t)m_postmerge_extractors.size(); + + if(!m_is_groupby_key_present) + { + throw sinsp_exception("table is missing the groupby key"); + } + + if(m_groupby_columns.size() < 2) + { + throw sinsp_exception("groupby table has no values"); + } + + for(auto it = m_postmerge_extractors.begin(); it != m_postmerge_extractors.end(); ++it) + { + m_postmerge_types.push_back((*it)->m_check->get_field_info()->m_type); + m_postmerge_legend.push_back(*(*it)->m_check->get_field_info()); + } + + m_postmerge_vals_array_sz = (m_n_postmerge_fields - 1) * sizeof(chisel_table_field); +} + +void chisel_table::add_row(bool merging) +{ + uint32_t j; + + chisel_table_field key(m_fld_pointers[0].m_val, + m_fld_pointers[0].m_len, + m_fld_pointers[0].m_cnt); + + if(m_type == chisel_table::TT_TABLE) + { + // + // This is a table. Do a proper key lookup and update the entry + // + auto it = m_table->find(key); + + if(it == m_table->end()) + { + // + // New entry + // + key.m_val = key.m_val; + key.m_cnt = 1; + m_vals = (chisel_table_field*)m_buffer->reserve(m_vals_array_sz); + + for(j = 1; j < m_n_fields; j++) + { + uint32_t vlen = get_field_len(j); + m_vals[j - 1].m_val = m_fld_pointers[j].m_val; + m_vals[j - 1].m_len = vlen; + m_vals[j - 1].m_cnt = m_fld_pointers[j].m_cnt; + } + + (*m_table)[key] = m_vals; + } + else + { + // + // Existing entry + // + m_vals = it->second; + + for(j = 1; j < m_n_fields; j++) + { + if(merging) + { + add_fields(j, &m_fld_pointers[j], m_postmerge_extractors[j]->m_merge_aggregation); + } + else + { + add_fields(j, &m_fld_pointers[j], m_premerge_extractors[j]->m_aggregation); + } + } + } + } + else + { + // + // We are in list mode. Just append the row to the end of the sample + // + if(m_paused) + { + return; + } + + chisel_sample_row row; + + // + // This is a list. Create the new entry and push it back. + // + key.m_val = key.m_val; + key.m_cnt = 1; + row.m_key = key; + + m_vals = (chisel_table_field*)m_buffer->reserve(m_vals_array_sz); + + for(j = 1; j < m_n_fields; j++) + { + uint32_t vlen = get_field_len(j); + m_vals[j - 1].m_val = m_fld_pointers[j].m_val; + m_vals[j - 1].m_len = vlen; + m_vals[j - 1].m_cnt = 1; + row.m_values.push_back(m_vals[j - 1]); + } + + m_full_sample_data.push_back(row); + } +} + +void chisel_table::process_event(sinsp_evt* evt) +{ + uint32_t j; + + // + // Apply the filter + // + if(m_filter) + { + if(!m_filter->run(evt)) + { + return; + } + } + + // + // Extract the values and create the row to add + // + for(j = 0; j < m_n_premerge_fields; j++) + { + chisel_table_field* pfld = &(m_premerge_fld_pointers[j]); + + // + // XXX For the moment, we only support defaults for numeric fields. + // At a certain point we will want to introduce the concept of zero + // for other fields too. + // + m_premerge_extractors[j]->m_check->m_extracted_values.clear(); + if(!m_premerge_extractors[j]->m_check->extract(evt, m_premerge_extractors[j]->m_check->m_extracted_values)) + { + if(m_use_defaults) + { + pfld->m_val = get_default_val(&m_premerge_legend[j]); + if(pfld->m_val == NULL) + { + return; + } + + pfld->m_len = get_field_len(j); + pfld->m_val = m_buffer->copy(pfld->m_val, pfld->m_len); + pfld->m_cnt = 0; + } + else + { + return; + } + } + else + { + // todo: Do something better here. For now, only support single-value extracted fields + // Set the val in the m_premerge_fld_pointers; note: at this stage, + // m_fld_pointers points to m_premerge_fld_pointers. + // This is only used to eventually compute the field len for BYTE_BUF + pfld->m_val = m_premerge_extractors[j]->m_check->m_extracted_values[0].ptr; + // Compute len + // NOTE: this internally uses m_fld_pointers thus the m_val must be already set, as above. + pfld->m_len = get_field_len(j); + // Finally, create the buffer copy and store it to val. + pfld->m_val = m_buffer->copy(m_premerge_extractors[j]->m_check->m_extracted_values[0].ptr, pfld->m_len); + pfld->m_cnt = 1; + } + } + + // + // Add the row + // + add_row(false); + + return; +} + +void chisel_table::process_proctable(sinsp_evt* evt) +{ + sinsp_evt tevt; + scap_evt tscapevt; + + threadinfo_map_t* threadtable = m_inspector->m_thread_manager->get_threads(); + ASSERT(threadtable != NULL); + + uint64_t ts = evt->get_ts(); + uint64_t ts_s = ts - (ts % ONE_SECOND_IN_NS); + tscapevt.ts = ts_s - 1; + + // + // Note: as the event type for this fake event, we pick one of the unused + // numbers, so we guarantee that filter checks will not wrongly pick it up + // + tscapevt.type = PPME_SCAPEVENT_X; + tscapevt.len = 0; + tscapevt.nparams = 0; + + tevt.set_inspector(m_inspector); + tevt.set_info(&(g_infotables.m_event_info[PPME_SCAPEVENT_X])); + tevt.set_cpuid(0); + tevt.set_num(0); + tevt.set_scap_evt(&tscapevt); + tevt.set_fdinfo_ref(nullptr); + tevt.set_fd_info(NULL); + + threadtable->loop([&] (sinsp_threadinfo& tinfo) { + tevt.set_tinfo(&tinfo); + tscapevt.tid = tevt.get_tinfo()->m_tid; + + if(m_filter) + { + if(!m_filter->run(&tevt)) + { + return true; + } + } + + process_event(&tevt); + return true; + }); +} + +void chisel_table::flush(sinsp_evt* evt) +{ + if(!m_paused) + { + if(m_next_flush_time_ns != 0) + { + // + // Time to emit the sample! + // Add the proctable as a sample at the end of the second + // + process_proctable(evt); + + // + // If there is a merging step, switch the types to point to the merging ones. + // + if(m_do_merging) + { + m_types = &m_postmerge_types; + m_table = &m_merge_table; + m_n_fields = m_n_postmerge_fields; + m_vals_array_sz = m_postmerge_vals_array_sz; + m_fld_pointers = m_postmerge_fld_pointers; + m_extractors = &m_postmerge_extractors; + } + + // + // Emit the sample + // + create_sample(); + + if(m_type == chisel_table::TT_TABLE) + { + // + // Switch the data storage so that the current one is still usable by the + // consumers of the table. + // + switch_buffers(); + + // + // Clear the current data storage + // + m_buffer->clear(); + } + + // + // Reinitialize the tables + // + m_premerge_table.clear(); + m_merge_table.clear(); + } + } + + uint64_t ts = evt->get_ts(); + + m_prev_flush_time_ns = m_next_flush_time_ns; + m_next_flush_time_ns = ts - (ts % m_refresh_interval_ns) + m_refresh_interval_ns; + + return; +} + +void chisel_table::print_raw(vector* sample_data, uint64_t time_delta) +{ + vector* legend = get_legend(); + + for(auto it = sample_data->begin(); it != sample_data->end(); ++it) + { + for(uint32_t j = 0; j < m_n_fields - 1; j++) + { + check_wrapper* extractor = m_extractors->at(j + 1); + uint64_t td = 0; + + if(extractor->m_aggregation == A_TIME_AVG || + extractor->m_merge_aggregation == A_TIME_AVG) + { + td = time_delta; + } + + m_printer->set_val(m_types->at(j + 1), + EPF_NONE, + it->m_values[j].m_val, + it->m_values[j].m_len, + it->m_values[j].m_cnt, + legend->at(j + 1).m_print_format); + char* prstr = m_printer->tostring_nice(NULL, 10, td); + printf("%s ", prstr); + //printf("%s ", m_printer->tostring(NULL)); + } + + printf("\n"); + } + + printf("----------------------\n"); +} + +void chisel_table::print_json(vector* sample_data, uint64_t time_delta) +{ + Json::FastWriter writer; + vector* legend = get_legend(); + string res; + uint32_t j = 0; + uint32_t k = 0; + m_json_output_lines_count = 0; + + if(sample_data->size() == 0) + { + return; + } + + if(m_json_first_row >= sample_data->size()) + { + return; + } + + if(m_json_last_row == 0 || m_json_last_row >= sample_data->size() - 1) + { + m_json_last_row = sample_data->size() - 1; + } + + printf("\"data\": [\n"); + + for(k = m_json_first_row; k <= m_json_last_row; k++) + { + Json::Value root; + Json::Value jd; + auto row = sample_data->at(k); + + for(uint32_t j = 0; j < m_n_fields - 1; j++) + { + check_wrapper* extractor = m_extractors->at(j + 1); + uint64_t td = 0; + + if(extractor->m_aggregation == A_TIME_AVG || + extractor->m_merge_aggregation == A_TIME_AVG) + { + td = time_delta; + } + + m_printer->set_val(m_types->at(j + 1), + EPF_NONE, + row.m_values[j].m_val, + row.m_values[j].m_len, + row.m_values[j].m_cnt, + legend->at(j + 1).m_print_format); + + jd.append(m_printer->tojson(NULL, 10, td)); + } + + + auto key = get_row_key_name_and_val(k, false); + + root["k"] = key.second; + root["d"] = jd; + + res = writer.write(root); + printf("%s", res.substr(0, res.size() - 1).c_str()); + + m_json_output_lines_count++; + + if(k >= m_json_last_row) + { + break; + } + + if(j < sample_data->size() - 1) + { + printf(","); + } + printf("\n"); + + j++; + } + + printf("],\n"); +} + +void chisel_table::filter_sample() +{ + vector* legend = get_legend(); + + m_filtered_sample_data.clear(); + + for(auto it : m_full_sample_data) + { + for(uint32_t j = 0; j < it.m_values.size(); j++) + { + ppm_param_type type; + + if(m_do_merging) + { + type = m_postmerge_types[j + 1]; + } + else + { + type = m_premerge_types[j + 1]; + } + + if(type == PT_CHARBUF || type == PT_BYTEBUF || type == PT_SYSCALLID || + type == PT_PORT || type == PT_L4PROTO || type == PT_SOCKFAMILY || type == PT_IPV4ADDR || + type == PT_IPV6ADDR || + type == PT_UID || type == PT_GID) + { + m_printer->set_val(type, + EPF_NONE, + it.m_values[j].m_val, + it.m_values[j].m_len, + it.m_values[j].m_cnt, + legend->at(j + 1).m_print_format); + + string strval = m_printer->tostring_nice(NULL, 0, 0); + + if(strval.find(m_freetext_filter) != string::npos) + { + m_filtered_sample_data.push_back(it); + break; + } + } + } + } +} + +// +// Returns the key of the first match, or NULL if no match +// +chisel_table_field* chisel_table::search_in_sample(string text) +{ + vector* legend = get_legend(); + + for(auto it = m_full_sample_data.begin(); it != m_full_sample_data.end(); ++it) + { + for(uint32_t j = 0; j < it->m_values.size(); j++) + { + ppm_param_type type; + + if(m_do_merging) + { + ASSERT(m_types->size() == it->m_values.size() + 2); + type = m_types->at(j + 2); + } + else + { + ASSERT(m_types->size() == it->m_values.size() + 1); + type = m_types->at(j + 1); + } + + if(type == PT_CHARBUF || type == PT_BYTEBUF || type == PT_SYSCALLID || + type == PT_PORT || type == PT_L4PROTO || type == PT_SOCKFAMILY || type == PT_IPV4ADDR || + type == PT_IPV6ADDR || + type == PT_UID || type == PT_GID) + { + m_printer->set_val(type, + EPF_NONE, + it->m_values[j].m_val, + it->m_values[j].m_len, + it->m_values[j].m_cnt, + legend->at(j + 1).m_print_format); + + string strval = m_printer->tostring_nice(NULL, 0, 0); + + if(strval.find(text) != string::npos) + { + return &(it->m_key); + } + } + } + } + + return NULL; +} + +void chisel_table::sort_sample() +{ + if(m_type == chisel_table::TT_LIST) + { + if(m_sorting_col == -1 || !m_just_sorted) + { + return; + } + + m_just_sorted = false; + } + + if(m_sample_data->size() != 0) + { + if(m_sorting_col >= (int32_t)m_sample_data->at(0).m_values.size()) + { + throw sinsp_exception("invalid table sorting column"); + } + + table_row_cmp cc; + cc.m_colid = m_sorting_col; + cc.m_ascending = m_is_sorting_ascending; + uint32_t tyid = m_do_merging? m_sorting_col + 2 : m_sorting_col + 1; + cc.m_type = m_premerge_types[tyid]; + + sort(m_sample_data->begin(), + m_sample_data->end(), + cc); + } +} + +vector* chisel_table::get_sample(uint64_t time_delta) +{ + // + // No sample generation happens when the table is paused + // + if(!m_paused) + { + // + // If we have a freetext filter, we start by filtering the sample + // + if(m_freetext_filter != "") + { + filter_sample(); + m_sample_data = &m_filtered_sample_data; + } + else + { + m_sample_data = &m_full_sample_data; + } + + // + // Sort the sample + // + sort_sample(); + } + + // + // If required, emit the sample to stdout + // +#ifndef _WIN32 + if(m_output_type != chisel_table::OT_CURSES) + { +#endif + if(m_output_type == chisel_table::OT_RAW) + { + print_raw(m_sample_data, time_delta); + } + else if(m_output_type == chisel_table::OT_JSON) + { + print_json(m_sample_data, time_delta); + } + else + { + ASSERT(false); + } +#ifndef _WIN32 + } +#endif + + // + // Restore the lists used for event processing + // + m_types = &m_premerge_types; + m_table = &m_premerge_table; + m_n_fields = m_n_premerge_fields; + m_vals_array_sz = m_premerge_vals_array_sz; + m_fld_pointers = m_premerge_fld_pointers; + m_extractors = &m_premerge_extractors; + + return m_sample_data; +} + +void chisel_table::set_sorting_col(uint32_t col) +{ + uint32_t n_fields; + vector* types; + + if(m_do_merging) + { + n_fields = m_n_postmerge_fields; + types = &m_postmerge_types; + } + else + { + n_fields = m_n_premerge_fields; + types = &m_premerge_types; + } + + if(col == 0) + { + if(m_type == chisel_table::TT_TABLE) + { + throw sinsp_exception("cannot sort by key"); + } + else + { + m_sorting_col = -1; + return; + } + } + + if(col >= n_fields) + { + throw sinsp_exception("invalid table sorting column"); + } + + if(col == (uint32_t)(m_sorting_col + 1)) + { + m_is_sorting_ascending = !m_is_sorting_ascending; + } + else + { + switch(types->at(col)) + { + case PT_INT8: + case PT_INT16: + case PT_INT32: + case PT_INT64: + case PT_UINT8: + case PT_UINT16: + case PT_UINT32: + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + case PT_DOUBLE: + case PT_BOOL: + m_is_sorting_ascending = false; + break; + default: + m_is_sorting_ascending = true; + break; + } + } + + m_just_sorted = true; + m_sorting_col = col - 1; +} + +uint32_t chisel_table::get_sorting_col() const +{ + return (uint32_t)m_sorting_col + 1; +} + +void chisel_table::create_sample() +{ + if(m_type == chisel_table::TT_TABLE) + { + uint32_t j; + m_full_sample_data.clear(); + chisel_sample_row row; + + // + // If merging is on, perform the merge and switch to the merged table + // + if(m_do_merging) + { + m_table = &m_merge_table; + m_merge_table.clear(); + + for(auto it = m_premerge_table.begin(); it != m_premerge_table.end(); ++it) + { + for(j = 0; j < m_n_postmerge_fields; j++) + { + chisel_table_field* pfld = &(m_postmerge_fld_pointers[j]); + + uint32_t col = m_groupby_columns[j]; + if(col == 0) + { + pfld->m_val = it->first.m_val; + pfld->m_len = it->first.m_len; + pfld->m_cnt = it->first.m_cnt; + } + else + { + pfld->m_val = it->second[col - 1].m_val; + pfld->m_len = it->second[col - 1].m_len; + pfld->m_cnt = it->second[col - 1].m_cnt; + } + } + + add_row(true); + } + } + else + { + m_table = &m_premerge_table; + } + + // + // Emit the table + // + for(auto it = m_table->begin(); it != m_table->end(); ++it) + { + row.m_key = it->first; + + row.m_values.clear(); + + chisel_table_field* fields = it->second; + for(j = 0; j < m_n_fields - 1; j++) + { + row.m_values.push_back(fields[j]); + } + + m_full_sample_data.push_back(row); + } + } + else + { + // + // If this is a list, there's nothing to be done, since m_full_sample_data + // is already prepared and doesn't need to be cleaned. + // + return; + } +} + +void chisel_table::add_fields_sum(ppm_param_type type, chisel_table_field *dst, chisel_table_field *src) +{ + uint8_t* operand1 = dst->m_val; + uint8_t* operand2 = src->m_val; + + switch(type) + { + case PT_INT8: + *(int8_t*)operand1 += *(int8_t*)operand2; + return; + case PT_INT16: + *(int16_t*)operand1 += *(int16_t*)operand2; + return; + case PT_INT32: + *(int32_t*)operand1 += *(int32_t*)operand2; + return; + case PT_INT64: + *(int64_t*)operand1 += *(int64_t*)operand2; + return; + case PT_UINT8: + *(uint8_t*)operand1 += *(uint8_t*)operand2; + return; + case PT_UINT16: + *(uint16_t*)operand1 += *(uint16_t*)operand2; + return; + case PT_UINT32: + case PT_BOOL: + *(uint32_t*)operand1 += *(uint32_t*)operand2; + return; + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + *(uint64_t*)operand1 += *(uint64_t*)operand2; + return; + case PT_DOUBLE: + *(double*)operand1 += *(double*)operand2; + return; + default: + return; + } +} + +void chisel_table::add_fields_sum_of_avg(ppm_param_type type, chisel_table_field *dst, chisel_table_field *src) +{ + uint8_t* operand1 = dst->m_val; + uint8_t* operand2 = src->m_val; + uint32_t cnt1 = dst->m_cnt; + uint32_t cnt2 = src->m_cnt; + + switch(type) + { + case PT_INT8: + if(cnt1 > 1) + { + *(int8_t*)operand1 = *(int8_t*)operand1 / cnt1; + } + + *(int8_t*)operand1 += (*(int8_t*)operand2) / cnt2; + break; + case PT_INT16: + if(cnt1 > 1) + { + *(int16_t*)operand1 = *(int16_t*)operand1 / cnt1; + } + + *(int16_t*)operand1 += (*(int16_t*)operand2) / cnt2; + break; + case PT_INT32: + if(cnt1 > 1) + { + *(int32_t*)operand1 = *(int32_t*)operand1 / cnt1; + } + + *(int32_t*)operand1 += (*(int32_t*)operand2) / cnt2; + break; + case PT_INT64: + if(cnt1 > 1) + { + *(int64_t*)operand1 = *(int64_t*)operand1 / cnt1; + } + + *(int64_t*)operand1 += (*(int64_t*)operand2) / cnt2; + break; + case PT_UINT8: + if(cnt1 > 1) + { + *(uint8_t*)operand1 = *(uint8_t*)operand1 / cnt1; + } + + *(uint8_t*)operand1 += (*(uint8_t*)operand2) / cnt2; + break; + case PT_UINT16: + if(cnt1 > 1) + { + *(uint16_t*)operand1 = *(uint16_t*)operand1 / cnt1; + } + + *(uint16_t*)operand1 += (*(uint16_t*)operand2) / cnt2; + break; + case PT_UINT32: + case PT_BOOL: + if(cnt1 > 1) + { + *(uint32_t*)operand1 = *(uint32_t*)operand1 / cnt1; + } + + *(uint32_t*)operand1 += (*(uint32_t*)operand2) / cnt2; + break; + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + if(cnt1 > 1) + { + *(uint64_t*)operand1 = *(uint64_t*)operand1 / cnt1; + } + + *(uint64_t*)operand1 += (*(uint64_t*)operand2) / cnt2; + break; + case PT_DOUBLE: + if(cnt1 > 1) + { + *(double*)operand1 = *(double*)operand1 / cnt1; + } + + *(double*)operand1 += (*(double*)operand2) / cnt2; + break; + default: + break; + } + + src->m_cnt = 1; + dst->m_cnt = 1; +} + +void chisel_table::add_fields_max(ppm_param_type type, chisel_table_field *dst, chisel_table_field *src) +{ + uint8_t* operand1 = dst->m_val; + uint8_t* operand2 = src->m_val; + + switch(type) + { + case PT_INT8: + if(*(int8_t*)operand1 < *(int8_t*)operand2) + { + *(int8_t*)operand1 = *(int8_t*)operand2; + } + return; + case PT_INT16: + if(*(int16_t*)operand1 < *(int16_t*)operand2) + { + *(int16_t*)operand1 = *(int16_t*)operand2; + } + return; + case PT_INT32: + if(*(int32_t*)operand1 < *(int32_t*)operand2) + { + *(int32_t*)operand1 = *(int32_t*)operand2; + } + return; + case PT_INT64: + if(*(int64_t*)operand1 < *(int64_t*)operand2) + { + *(int64_t*)operand1 = *(int64_t*)operand2; + } + return; + case PT_UINT8: + if(*(uint8_t*)operand1 < *(uint8_t*)operand2) + { + *(uint8_t*)operand1 = *(uint8_t*)operand2; + } + return; + case PT_UINT16: + if(*(uint16_t*)operand1 < *(uint16_t*)operand2) + { + *(uint16_t*)operand1 = *(uint16_t*)operand2; + } + return; + case PT_UINT32: + case PT_BOOL: + if(*(uint32_t*)operand1 < *(uint32_t*)operand2) + { + *(uint32_t*)operand1 = *(uint32_t*)operand2; + } + return; + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + if(*(uint64_t*)operand1 < *(uint64_t*)operand2) + { + *(uint64_t*)operand1 = *(uint64_t*)operand2; + } + return; + case PT_DOUBLE: + if(*(double*)operand1 < *(double*)operand2) + { + *(double*)operand1 = *(double*)operand2; + } + return; + case PT_CHARBUF: + case PT_BYTEBUF: + if(dst->m_len >= src->m_len) + { + memcpy(dst->m_val, src->m_val, src->m_len); + } + else + { + dst->m_val = m_buffer->copy(src->m_val, src->m_len); + } + + dst->m_len = src->m_len; + default: + return; + } +} + +void chisel_table::add_fields_min(ppm_param_type type, chisel_table_field *dst, chisel_table_field *src) +{ + uint8_t* operand1 = dst->m_val; + uint8_t* operand2 = src->m_val; + + switch(type) + { + case PT_INT8: + if(*(int8_t*)operand1 > *(int8_t*)operand2) + { + *(int8_t*)operand1 = *(int8_t*)operand2; + } + return; + case PT_INT16: + if(*(int16_t*)operand1 > *(int16_t*)operand2) + { + *(int16_t*)operand1 = *(int16_t*)operand2; + } + return; + case PT_INT32: + if(*(int32_t*)operand1 > *(int32_t*)operand2) + { + *(int32_t*)operand1 = *(int32_t*)operand2; + } + return; + case PT_INT64: + if(*(int64_t*)operand1 > *(int64_t*)operand2) + { + *(int64_t*)operand1 = *(int64_t*)operand2; + } + return; + case PT_UINT8: + if(*(uint8_t*)operand1 > *(uint8_t*)operand2) + { + *(uint8_t*)operand1 = *(uint8_t*)operand2; + } + return; + case PT_UINT16: + if(*(uint16_t*)operand1 > *(uint16_t*)operand2) + { + *(uint16_t*)operand1 = *(uint16_t*)operand2; + } + return; + case PT_UINT32: + case PT_BOOL: + if(*(uint32_t*)operand1 > *(uint32_t*)operand2) + { + *(uint32_t*)operand1 = *(uint32_t*)operand2; + } + return; + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + if(*(uint64_t*)operand1 > *(uint64_t*)operand2) + { + *(uint64_t*)operand1 = *(uint64_t*)operand2; + } + return; + case PT_DOUBLE: + if(*(double*)operand1 > *(double*)operand2) + { + *(double*)operand1 = *(double*)operand2; + } + return; + case PT_CHARBUF: + case PT_BYTEBUF: + ASSERT(false); // Not supposed to use this + if(dst->m_len >= src->m_len) + { + memcpy(dst->m_val, src->m_val, src->m_len); + } + else + { + dst->m_val = m_buffer->copy(src->m_val, src->m_len); + } + + dst->m_len = src->m_len; + default: + return; + } +} + +void chisel_table::add_fields(uint32_t dst_id, chisel_table_field* src, uint32_t aggr) +{ + ppm_param_type type = (*m_types)[dst_id]; + chisel_table_field* dst = &(m_vals[dst_id - 1]); + + switch(aggr) + { + case A_NONE: + return; + case A_SUM: + case A_TIME_AVG: + if(src->m_cnt < 2) + { + add_fields_sum(type, dst, src); + } + else + { + add_fields_sum_of_avg(type, dst, src); + } + + return; + case A_AVG: + dst->m_cnt += src->m_cnt; + add_fields_sum(type, dst, src); + return; + case A_MAX: + add_fields_max(type, dst, src); + return; + case A_MIN: + if(src->m_cnt != 0) + { + if(dst->m_cnt == 0) + { + add_fields_sum(type, dst, src); + dst->m_cnt++; + } + else + { + add_fields_min(type, dst, src); + } + } + return; + default: + ASSERT(false); + return; + } +} + +uint32_t chisel_table::get_field_len(uint32_t id) const +{ + ppm_param_type type; + chisel_table_field *fld; + + type = (*m_types)[id]; + fld = &(m_fld_pointers[id]); + + switch(type) + { + case PT_INT8: + return 1; + case PT_INT16: + return 2; + case PT_INT32: + return 4; + case PT_INT64: + case PT_FD: + case PT_PID: + case PT_ERRNO: + return 8; + case PT_FLAGS8: + case PT_ENUMFLAGS8: + case PT_UINT8: + case PT_SIGTYPE: + return 1; + case PT_FLAGS16: + case PT_UINT16: + case PT_ENUMFLAGS16: + case PT_PORT: + case PT_SYSCALLID: + return 2; + case PT_UINT32: + case PT_FLAGS32: + case PT_ENUMFLAGS32: + case PT_MODE: + case PT_BOOL: + case PT_IPV4ADDR: + case PT_SIGSET: + return 4; + case PT_UINT64: + case PT_RELTIME: + case PT_ABSTIME: + return 8; + case PT_CHARBUF: + return (uint32_t)(strlen((char*)fld->m_val) + 1); + case PT_BYTEBUF: + return fld->m_len; + case PT_DOUBLE: + return sizeof(double); + case PT_IPV6ADDR: + return sizeof(ipv6addr); + case PT_IPADDR: + case PT_IPNET: + if(fld->m_len == sizeof(struct in_addr)) + { + return 4; + } + else + { + return sizeof(ipv6addr); + } + case PT_SOCKADDR: + case PT_SOCKTUPLE: + case PT_FDLIST: + case PT_FSPATH: + case PT_FSRELPATH: + default: + ASSERT(false); + return false; + } +} + +uint8_t* chisel_table::get_default_val(filtercheck_field_info* fld) +{ + switch(fld->m_type) + { + case PT_INT8: + case PT_INT16: + case PT_INT32: + case PT_INT64: + case PT_UINT8: + case PT_UINT16: + case PT_UINT32: + case PT_UINT64: + case PT_BOOL: + case PT_RELTIME: + case PT_ABSTIME: + if(fld->m_print_format == PF_DEC) + { + return (uint8_t*)&m_zero_u64; + } + else + { + return NULL; + } + case PT_DOUBLE: + return (uint8_t*)&m_zero_double; + case PT_CHARBUF: + return (uint8_t*)&m_zero_u64; + case PT_PORT: + case PT_IPV4ADDR: + case PT_IPV6ADDR: + return NULL; + default: + ASSERT(false); + return NULL; + } +} + +void chisel_table::switch_buffers() +{ + if(m_buffer == &m_buffer1) + { + m_buffer = &m_buffer2; + } + else + { + m_buffer = &m_buffer1; + } +} + +pair chisel_table::get_row_key_name_and_val(uint32_t rownum, bool force) +{ + pair res; + vector* extractors; + vector* types; + + if(m_do_merging) + { + extractors = &m_postmerge_extractors; + types = &m_postmerge_types; + } + else + { + extractors = &m_premerge_extractors; + types = &m_premerge_types; + } + + if(m_sample_data == NULL || rownum >= m_sample_data->size()) + { + ASSERT(m_sample_data == NULL || m_sample_data->size() == 0); + if(force) + { + res.first = (filtercheck_field_info*)((*extractors)[0])->m_check->get_field_info(); + ASSERT(res.first != NULL); + } + else + { + res.first = NULL; + } + res.second = ""; + } + else + { + vector* legend = get_legend(); + res.first = (filtercheck_field_info*)((*extractors)[0])->m_check->get_field_info(); + ASSERT(res.first != NULL); + + m_printer->set_val(types->at(0), + EPF_NONE, + m_sample_data->at(rownum).m_key.m_val, + m_sample_data->at(rownum).m_key.m_len, + m_sample_data->at(rownum).m_key.m_cnt, + legend->at(0).m_print_format); + + res.second = m_printer->tostring(NULL); + } + + return res; +} + +chisel_table_field* chisel_table::get_row_key(uint32_t rownum) +{ + if(rownum >= m_sample_data->size()) + { + return NULL; + } + + return &m_sample_data->at(rownum).m_key; +} + +int32_t chisel_table::get_row_from_key(chisel_table_field* key) const +{ + uint32_t j; + + for(j = 0; j < m_sample_data->size(); j++) + { + chisel_table_field* rowkey = &(m_sample_data->at(j).m_key); + + if(rowkey->m_len == key->m_len) + { + if(memcmp(rowkey->m_val, key->m_val, key->m_len) == 0) + { + return j; + } + } + } + + return -1; +} + +void chisel_table::set_paused(bool paused) +{ + m_paused = paused; +} + +void chisel_table::clear() +{ + if(m_type == chisel_table::TT_LIST) + { + m_full_sample_data.clear(); + m_buffer->clear(); + } + else + { + ASSERT(false); + } +} diff --git a/userspace/chisel/chisel_table.h b/userspace/chisel/chisel_table.h new file mode 100644 index 0000000000..41538872b1 --- /dev/null +++ b/userspace/chisel/chisel_table.h @@ -0,0 +1,397 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#include +#include +#include + +#define CHISEL_TABLE_DEFAULT_REFRESH_INTERVAL_NS 1000000000 +#define CHISEL_TABLE_BUFFER_ENTRY_SIZE 16384 + +enum chisel_table_action +{ + STA_NONE, + STA_PARENT_HANDLE, + STA_QUIT, + STA_SWITCH_VIEW, + STA_SWITCH_SPY, + STA_DRILLDOWN, + STA_DRILLDOWN_TEMPLATE, + STA_DRILLUP, + STA_SPY, + STA_DIG, + STA_SPECTRO, + STA_SPECTRO_FILE, + STA_DESTROY_CHILD, +}; + +class chisel_table_field +{ +public: + chisel_table_field() + { + m_val = NULL; + } + + chisel_table_field(uint8_t* val, uint32_t len, uint32_t cnt) + { + m_len = len; + m_val = val; + m_cnt = cnt; + } + + bool operator==(const chisel_table_field &other) const + { + if(m_len!= other.m_len) + { + return false; + } + + if(memcmp(m_val, other.m_val, m_len) == 0) + { + return true; + } + else + { + return false; + } + } + + uint32_t m_len; + uint32_t m_cnt; // For averages, this stores the entry count + uint8_t* m_val; + + friend class curses_table; +}; + +#define STF_STORAGE_BUFSIZE 512 + +class chisel_table_field_storage : public chisel_table_field +{ +public: + chisel_table_field_storage() + { + m_storage_len = STF_STORAGE_BUFSIZE; + m_val = new uint8_t[m_storage_len]; + m_isvalid = false; + } + + ~chisel_table_field_storage() + { + if(m_val != NULL) + { + delete[] m_val; + } + } + + void copy(chisel_table_field* other) + { + if(other->m_len > m_storage_len) + { + resize(other->m_len); + } + + m_len = other->m_len; + + memcpy(m_val, other->m_val, m_len); + } + + bool m_isvalid; + +private: + void resize(uint32_t newlen) + { + delete[] m_val; + m_val = NULL; + m_storage_len = newlen; + m_val = new uint8_t[m_storage_len]; + } + + uint32_t m_storage_len; +}; + +struct chisel_table_field_hasher +{ + size_t operator()(const chisel_table_field& k) const + { + size_t h = 0; + uint8_t* s = k.m_val; + uint32_t len = k.m_len; + + while(--len) + { + h = h * 101 + (unsigned) *s++; + } + + return h; + } +}; + +class chisel_table_buffer +{ +public: + chisel_table_buffer() + { + push_buffer(); + } + + ~chisel_table_buffer() + { + for(auto it = m_bufs.begin(); it != m_bufs.end(); ++it) + { + delete[] *it; + } + } + + void push_buffer() + { + m_curbuf = new uint8_t[CHISEL_TABLE_BUFFER_ENTRY_SIZE]; + m_bufs.push_back(m_curbuf); + m_pos = 0; + } + + uint8_t* copy(uint8_t* src, uint32_t len) + { + if(m_pos + len >= CHISEL_TABLE_BUFFER_ENTRY_SIZE) + { + push_buffer(); + } + + uint8_t* dest = m_curbuf + m_pos; + memcpy(dest, src, len); + m_pos += len; + return dest; + } + + uint8_t* reserve(uint32_t len) + { + if(len >= CHISEL_TABLE_BUFFER_ENTRY_SIZE) + { + ASSERT(false); + throw sinsp_exception("field value too long"); + } + + if(m_pos + len >= CHISEL_TABLE_BUFFER_ENTRY_SIZE) + { + push_buffer(); + } + + uint8_t* dest = m_curbuf + m_pos; + m_pos += len; + return dest; + } + + void clear() + { + for(auto it = m_bufs.begin(); it != m_bufs.end(); ++it) + { + delete[] *it; + } + + m_bufs.clear(); + push_buffer(); + m_pos = 0; + } + + std::vector m_bufs; + uint8_t* m_curbuf; + uint32_t m_pos; +}; + +class chisel_sample_row +{ +public: + chisel_table_field m_key; + std::vector m_values; +}; + +class chisel_table +{ +public: + enum tabletype + { + TT_NONE = 0, + TT_TABLE, + TT_LIST, + }; + + enum output_type + { + OT_CURSES, + OT_RAW, + OT_JSON, + }; + + struct check_wrapper + { + check_wrapper( + sinsp_filter_check* check, + chisel_field_aggregation aggregation=A_NONE, + chisel_field_aggregation merge_aggregation=A_NONE): + m_check(check), + m_aggregation(aggregation), + m_merge_aggregation(merge_aggregation) + { + } + + ~check_wrapper() + { + delete m_check; + } + + sinsp_filter_check* m_check; + chisel_field_aggregation m_aggregation; + chisel_field_aggregation m_merge_aggregation; + }; + + chisel_table(sinsp* inspector, tabletype type, + uint64_t refresh_interval_ns, chisel_table::output_type output_type, + uint32_t json_first_row, uint32_t json_last_row); + ~chisel_table(); + void configure(std::vector* entries, const std::string& filter, bool use_defaults, uint32_t view_depth); + void process_event(sinsp_evt* evt); + void flush(sinsp_evt* evt); + void filter_sample(); + // + // Returns the key of the first match, or NULL if no match + // + chisel_table_field* search_in_sample(std::string text); + void sort_sample(); + std::vector* get_sample(uint64_t time_delta); + std::vector* get_legend() + { + if(m_do_merging) + { + return &m_postmerge_legend; + } + else + { + return &m_premerge_legend; + } + } + void set_sorting_col(uint32_t col); + uint32_t get_sorting_col() const; + std::pair get_row_key_name_and_val(uint32_t rownum, bool force); + chisel_table_field* get_row_key(uint32_t rownum); + int32_t get_row_from_key(chisel_table_field* key) const; + void set_paused(bool paused); + void set_freetext_filter(std::string filter) + { + m_freetext_filter = filter; + } + tabletype get_type() const + { + return m_type; + } + void set_refresh_interval(uint64_t newinterval_ns) + { + m_refresh_interval_ns = newinterval_ns; + } + void clear(); + bool is_merging() const + { + return m_do_merging; + } + bool is_sorting_ascending() const + { + return m_is_sorting_ascending; + } + void set_is_sorting_ascending(bool is_sorting_ascending) + { + m_is_sorting_ascending = is_sorting_ascending; + } + + uint64_t m_next_flush_time_ns; + uint64_t m_prev_flush_time_ns; + uint64_t m_refresh_interval_ns; + std::vector* m_types; + uint64_t m_json_output_lines_count; + +private: + inline void add_row(bool merging); + inline void add_fields_sum(ppm_param_type type, chisel_table_field* dst, chisel_table_field* src); + inline void add_fields_sum_of_avg(ppm_param_type type, chisel_table_field* dst, chisel_table_field* src); + inline void add_fields_max(ppm_param_type type, chisel_table_field* dst, chisel_table_field* src); + inline void add_fields_min(ppm_param_type type, chisel_table_field* dst, chisel_table_field* src); + inline void add_fields(uint32_t dst_id, chisel_table_field* src, uint32_t aggr); + void process_proctable(sinsp_evt* evt); + inline uint32_t get_field_len(uint32_t id) const; + inline uint8_t* get_default_val(filtercheck_field_info* fld); + void create_sample(); + void switch_buffers(); + void print_raw(std::vector* sample_data, uint64_t time_delta); + void print_json(std::vector* sample_data, uint64_t time_delta); + + sinsp* m_inspector; + std::unordered_map* m_table; + std::unordered_map m_premerge_table; + std::unordered_map m_merge_table; + std::vector m_premerge_legend; + std::vector m_premerge_extractors; + std::vector m_postmerge_extractors; + std::vector* m_extractors; + std::vector m_chks_to_free; + std::vector m_premerge_types; + std::vector m_postmerge_types; + bool m_is_key_present; + bool m_is_groupby_key_present; + std::vector m_groupby_columns; + std::vector m_postmerge_legend; + chisel_table_field* m_fld_pointers; + chisel_table_field* m_premerge_fld_pointers; + chisel_table_field* m_postmerge_fld_pointers; + uint32_t m_n_fields; + uint32_t m_n_premerge_fields; + uint32_t m_n_postmerge_fields; + chisel_table_buffer* m_buffer; + chisel_table_buffer m_buffer1; + chisel_table_buffer m_buffer2; + uint32_t m_vals_array_sz; + uint32_t m_premerge_vals_array_sz; + uint32_t m_postmerge_vals_array_sz; + sinsp_filter_check_reference* m_printer; + std::vector m_full_sample_data; + std::vector m_filtered_sample_data; + std::vector* m_sample_data; + chisel_table_field* m_vals; + int32_t m_sorting_col; + bool m_just_sorted; + bool m_is_sorting_ascending; + bool m_do_merging; + sinsp_filter* m_filter; + bool m_use_defaults; + uint64_t m_zero_u64; + uint64_t m_zero_double; + bool m_paused; + std::string m_freetext_filter; + tabletype m_type; + output_type m_output_type; + uint32_t m_view_depth; + uint32_t m_json_first_row; + uint32_t m_json_last_row; + + friend class curses_table; + friend class sinsp_cursesui; +}; diff --git a/userspace/chisel/chisel_utils.cpp b/userspace/chisel/chisel_utils.cpp new file mode 100644 index 0000000000..c32eaea40f --- /dev/null +++ b/userspace/chisel/chisel_utils.cpp @@ -0,0 +1,177 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#ifndef _WIN32 +#include +#include +#include +#include +#ifdef __GLIBC__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#else +#pragma comment(lib, "Ws2_32.lib") +#include +#include "Shlwapi.h" +#pragma comment(lib, "shlwapi.lib") +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +using namespace std; + +const chiseldir_info g_chisel_dirs_array[] = + { + {false, ""}, // file as is +#ifdef _WIN32 + {false, "c:/" CHISEL_TOOL_LIBRARY_NAME "/chisels/"}, +#endif + {false, "./chisels/"}, + {true, "~/.chisels/"}, +}; + +vector* g_chisel_dirs = NULL; +chisel_initializer g_chisel_initializer; + +#ifndef _WIN32 +static std::string realpath_ex(const std::string& path) +{ + char *home; + char* resolved; + + if(!path.empty() && path[0]=='~' && (home = getenv("HOME"))) + { + std::string expanded_home = home; + expanded_home += path.c_str()+1; + resolved = realpath(expanded_home.c_str(), nullptr); + } + else + { + resolved = realpath(path.c_str(), nullptr); + } + + if (!resolved) + { + return ""; + } + std::string ret = resolved; + free(resolved); + return ret; +} +#endif + +// +// loading time initializations +// +chisel_initializer::chisel_initializer() +{ + // + // Init the chisel directory list + // + g_chisel_dirs = NULL; + g_chisel_dirs = new vector(); + + for(uint32_t j = 0; j < sizeof(g_chisel_dirs_array) / sizeof(g_chisel_dirs_array[0]); j++) + { + if(g_chisel_dirs_array[j].m_need_to_resolve) + { +#ifndef _WIN32 + std::string resolved_path = realpath_ex(g_chisel_dirs_array[j].m_dir); + if(!resolved_path.empty()) + { + if(resolved_path[resolved_path.size() - 1] != '/') + { + resolved_path += '/'; + } + + chiseldir_info cdi; + cdi.m_need_to_resolve = false; + cdi.m_dir = std::move(resolved_path); + g_chisel_dirs->push_back(cdi); + } +#else + g_chisel_dirs->push_back(g_chisel_dirs_array[j]); +#endif + } + else + { + g_chisel_dirs->push_back(g_chisel_dirs_array[j]); + } + } +} + +chisel_initializer::~chisel_initializer() +{ + if(g_chisel_dirs) + { + delete g_chisel_dirs; + } +} + +void chisel_add_dir(string dirname, bool front_add) +{ + trim(dirname); + + if(dirname[dirname.size() -1] != '/') + { + dirname += "/"; + } + + chiseldir_info ncdi; + + ncdi.m_dir = std::move(dirname); + ncdi.m_need_to_resolve = false; + + if(front_add) + { + g_chisel_dirs->insert(g_chisel_dirs->begin(), ncdi); + } + else + { + g_chisel_dirs->push_back(ncdi); + } +} diff --git a/userspace/chisel/chisel_utils.h b/userspace/chisel/chisel_utils.h new file mode 100644 index 0000000000..3c44ef2338 --- /dev/null +++ b/userspace/chisel/chisel_utils.h @@ -0,0 +1,39 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#pragma once + +/////////////////////////////////////////////////////////////////////////////// +// Initializer class. +// An instance of this class is created when the library is loaded. +// ONE-SHOT INIT-TIME OPERATIONS SHOULD BE DONE IN THE CONSTRUCTOR OF THIS +// CLASS TO KEEP THEM UNDER A SINGLE PLACE. +/////////////////////////////////////////////////////////////////////////////// +class chisel_initializer +{ +public: + chisel_initializer(); + ~chisel_initializer(); +}; diff --git a/userspace/chisel/chisel_viewinfo.cpp b/userspace/chisel/chisel_viewinfo.cpp new file mode 100644 index 0000000000..64c9279886 --- /dev/null +++ b/userspace/chisel/chisel_viewinfo.cpp @@ -0,0 +1,408 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ +#include +#include +#include + +using namespace std; + +/////////////////////////////////////////////////////////////////////////////// +// chisel_view_column_info implementation +/////////////////////////////////////////////////////////////////////////////// +string chisel_view_column_info::get_field(uint32_t depth) +{ + // Trim the string + replace_in_place(m_field, " ", ""); + replace_in_place(m_field, "\t", ""); + + if(m_field.find("%depth-1") != string::npos) + { + string res = m_field; + replace_in_place(res, "%depth-1", to_string(depth - 1)); + return res; + } + else if(m_field.find("%depth+1") != string::npos) + { + string res = m_field; + replace_in_place(res, "%depth+1", to_string(depth - 1)); + return res; + } + else if(m_field.find("%depth") != string::npos) + { + string res = m_field; + replace_in_place(res, "%depth", to_string(depth)); + return res; + } + else + { + return m_field; + } +} + +string chisel_view_column_info::get_filter_field(uint32_t depth) +{ + // + // If m_filterfield, return it as an override to m_field + // + if(m_filterfield != "") + { + return m_filterfield; + } + else + { + return get_field(depth); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// chisel_view_info implementation +/////////////////////////////////////////////////////////////////////////////// +chisel_view_info::chisel_view_info() +{ + m_valid = false; +} + +chisel_view_info::chisel_view_info(viewtype type, + string id, + string name, + string description, + vector tags, + vector tips, + vector columns, + vector applies_to, + string filter, + string drilldown_target, + bool use_defaults, + bool is_root, + vector actions, + bool drilldown_increase_depth, + string spectro_type, + bool propagate_filter) +{ + m_id = id; + m_name = name; + m_description = description; + m_does_groupby = false; + m_type = type; + m_tags = tags; + m_tips = tips; + m_columns = columns; + m_drilldown_target = drilldown_target; + m_is_root = is_root; + m_applies_to = applies_to; + m_drilldown_increase_depth = drilldown_increase_depth; + m_spectro_type = spectro_type; + m_propagate_filter = propagate_filter; + + m_use_defaults = use_defaults; + + // + // Make sure the keys go at the beginning + // + move_key_to_front(TEF_IS_GROUPBY_KEY); + move_key_to_front(TEF_IS_KEY); + + // + // Determine the sorting and grouping columns + // + set_sorting_col(); + + m_filter = filter; + m_valid = true; + m_actions = actions; + // init the array for hotkeys for sorting columns + set_col_sorting_hotkeys(); +} + +void chisel_view_info::set_col_sorting_hotkeys() +{ + const char shift_number_keys [] = {'!', '@', '#', '$', '%', '^', '&', '*', '('}; + uint32_t size = sizeof(shift_number_keys) / sizeof(shift_number_keys[0]); + for(uint32_t i=0; i 1) + { + throw sinsp_exception("view format error: more than one sorting column"); + } + + if(m_sortingcol < 0) + { + ASSERT(false); + throw sinsp_exception("view sorting column configuration error"); + } +} + +void chisel_view_info::apply_tag(string tag) +{ + for(auto it = m_columns.begin(); it != m_columns.end();) + { + bool found = false; + + if(it->m_tags.size() != 0) + { + for(string t : it->m_tags) + { + if(t == tag) + { + found = true; + break; + } + } + + if(!found) + { + it = m_columns.erase(it); + continue; + } + } + + ++it; + } + + // + // Make sure to recalculate the sorting and grouping columns, which could change + // if we remove columns. + // + set_sorting_col(); +} + +void chisel_view_info::get_col_names_and_sizes(OUT vector* colnames, OUT vector* colsizes) +{ + if(m_type == viewtype::T_LIST) + { + colsizes->push_back(-1); + colnames->push_back(""); + } + + for(auto fit : m_columns) + { + if(m_does_groupby) + { + if((fit.m_flags & TEF_IS_KEY) != 0) + { + continue; + } + + if((fit.m_flags & TEF_IS_GROUPBY_KEY) != 0) + { + colsizes->insert(colsizes->begin(), fit.m_colsize); + colnames->insert(colnames->begin(), fit.m_name); + continue; + } + } + + colsizes->push_back(fit.m_colsize); + colnames->push_back(fit.m_name); + } +} + +void chisel_view_info::move_key_to_front(uint32_t keyflag) +{ + for(uint32_t j = 0; j < m_columns.size(); j++) + { + if((m_columns[j].m_flags & keyflag) != 0) + { + chisel_view_column_info ci = m_columns[j]; + + m_columns.erase(m_columns.begin() +j); + m_columns.insert(m_columns.begin(), ci); + return; + } + } +} + +chisel_view_column_info* chisel_view_info::get_key() +{ + for(uint32_t j = 0; j < m_columns.size(); j++) + { + if((m_columns[j].m_flags & TEF_IS_GROUPBY_KEY) != 0) + { + return &m_columns[j]; + } + } + + for(uint32_t j = 0; j < m_columns.size(); j++) + { + if((m_columns[j].m_flags & TEF_IS_KEY) != 0) + { + return &m_columns[j]; + } + } + + // The *must* be a key + return NULL; +} + +string chisel_view_info::get_filter(uint32_t depth) const +{ + if(m_filter.find("%depth+1") != string::npos) + { + string res = m_filter; + replace_in_place(res, "%depth+1", to_string(depth + 1)); + replace_in_place(res, "%depth + 1", to_string(depth + 1)); + return res; + } + else if(m_filter.find("%depth-1") != string::npos) + { + string res = m_filter; + replace_in_place(res, "%depth-1", to_string(depth - 1)); + replace_in_place(res, "%depth - 1", to_string(depth - 1)); + return res; + } + else if(m_filter.find("%depth") != string::npos) + { + string res = m_filter; + replace_in_place(res, "%depth", to_string(depth)); + return res; + } + else + { + return m_filter; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// chisel_view_manager implementation +/////////////////////////////////////////////////////////////////////////////// +void chisel_view_manager::add(chisel_view_info* vinfo) +{ + m_views.push_back(*vinfo); +} + +struct view_cmp +{ + bool operator()(const chisel_view_info& src, const chisel_view_info& dst) + { + return src.m_name < dst.m_name; + } +}; + +void chisel_view_manager::sort_views() +{ + view_cmp cc; + + // + // Sort the list alphabetically + // + sort(m_views.begin(), + m_views.end(), + cc); + + // + // Print the view list for debugging purposes + // + //for(uint32_t j = 0; j < m_views.size(); j++) + //{ + // libsinsp_logger()->format("> %d) %s", j, m_views[j].m_name.c_str()); + //} +} + +vector* chisel_view_manager::get_views() +{ + sort_views(); + return &m_views; +} + +uint32_t chisel_view_manager::get_selected_view() +{ + sort_views(); + + if(m_selected_view_id != "") + { + for(uint32_t j = 0; j < m_views.size(); j++) + { + if(m_views[j].m_id == m_selected_view_id) + { + return j; + } + } + + if(m_selected_view_id == "echo") + { + return VIEW_ID_SPY; + } + else if(m_selected_view_id == "dig") + { + return VIEW_ID_DIG; + } + } + else + { + for(uint32_t j = 0; j < m_views.size(); j++) + { + if(m_views[j].m_is_root) + { + return j; + } + } + } + + throw sinsp_exception("view " + m_selected_view_id + " not found"); + return 0; +} + +void chisel_view_manager::set_selected_view(string viewid) +{ + m_selected_view_id = viewid; +} diff --git a/userspace/chisel/chisel_viewinfo.h b/userspace/chisel/chisel_viewinfo.h new file mode 100644 index 0000000000..9ac54017a4 --- /dev/null +++ b/userspace/chisel/chisel_viewinfo.h @@ -0,0 +1,234 @@ +// 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. + +The following file was originally developed in the sysdig cli +project and then contributed to the falcosecurity/libs project +(https://github.com/draios/sysdig/commit/abf90f39f8e5ea6eb4276cc3f980dbd878816ecd +with the #1737 PR). At the end, given its removal from the +falcosecurity/libs project, has been reintroduced to the +draios/sysdig project. + +*/ + +#pragma once + +#define VIEW_ID_SPY -1 +#define VIEW_ID_DIG -2 +#define VIEW_ID_INFO -3 + +// +// Aggregation type for table fields +// +enum chisel_field_aggregation +{ + A_NONE, + A_SUM, + A_AVG, + A_TIME_AVG, + A_MIN, + A_MAX, +}; + +// +// chisel_view_column_info flags +// +#define TEF_NONE 0 +#define TEF_IS_KEY 1 +#define TEF_IS_SORT_COLUMN (1 << 1) +#define TEF_IS_GROUPBY_KEY (1 << 2) +#define TEF_FILTER_IN_CHILD_ONLY (1 << 3) + +/////////////////////////////////////////////////////////////////////////////// +// Column information +/////////////////////////////////////////////////////////////////////////////// +class chisel_view_column_info +{ +public: + chisel_view_column_info() + { + } + + chisel_view_column_info(std::string field, + std::string name, + std::string description, + uint32_t colsize, + uint32_t flags, + chisel_field_aggregation aggregation, + chisel_field_aggregation groupby_aggregation, + std::vector tags, + std::string filterfield) + { + m_field = field; + m_name = name; + m_description = description; + m_colsize = colsize; + m_aggregation = aggregation; + m_groupby_aggregation = groupby_aggregation; + m_flags = flags; + m_tags = tags; + m_filterfield = filterfield; + } + + std::string get_field(uint32_t depth); + std::string get_filter_field(uint32_t depth); + + std::string m_field; + std::string m_name; + std::string m_description; + uint32_t m_colsize; + chisel_field_aggregation m_aggregation; + chisel_field_aggregation m_groupby_aggregation; + uint32_t m_flags; + std::vector m_tags; + std::string m_filterfield; +}; + +/////////////////////////////////////////////////////////////////////////////// +// action information +/////////////////////////////////////////////////////////////////////////////// +class chisel_view_action_info +{ +public: + chisel_view_action_info(char hotkey, + std::string command, + std::string description, + bool ask_confirmation, + bool waitfinish) + { + m_hotkey = hotkey; + m_command = command; + m_description = description; + m_ask_confirmation = ask_confirmation; + m_waitfinish = waitfinish; + } + + char m_hotkey; + std::string m_command; + std::string m_description; + bool m_ask_confirmation; + bool m_waitfinish; +}; + +/////////////////////////////////////////////////////////////////////////////// +// View information +/////////////////////////////////////////////////////////////////////////////// +class chisel_view_info +{ +public: + enum viewtype + { + T_NONE = 0, + T_TABLE, + T_LIST, + T_TEXT, + T_SPECTRO, + }; + + chisel_view_info(); + chisel_view_info(viewtype type, + std::string id, + std::string name, + std::string description, + std::vector tags, + std::vector tips, + std::vector columns, + std::vector applies_to, + std::string filter, + std::string drilldown_target, + bool use_defaults, + bool is_root, + std::vector actions, + bool drilldown_increase_depth, + std::string spectro_type, + bool propagate_filter); + + void get_col_names_and_sizes(OUT std::vector* colnames, OUT std::vector* colsizes); + chisel_view_column_info* get_key(); + std::string get_filter(uint32_t depth) const; + viewtype get_type() + { + return m_type; + } + + bool does_groupby() + { + return m_does_groupby; + } + + void apply_tag(std::string tag); + + void run_action(chisel_view_action_info* action); + std::string m_id; + std::string m_name; + std::string m_description; + std::vector m_tags; + std::vector m_tips; + int32_t m_sortingcol; + std::vector m_applies_to; + std::vector m_columns; + bool m_use_defaults; + bool m_does_groupby; + viewtype m_type; + bool m_valid; + std::string m_drilldown_target; + bool m_is_root; + std::vector m_actions; + std::vector m_col_sort_hotkeys; + uint32_t max_col_sort_hotkeys; + bool m_drilldown_increase_depth; + bool m_propagate_filter; + std::string m_spectro_type; + std::string m_filter; + +private: + void set_sorting_col(); + void move_key_to_front(uint32_t keyflag); + void set_col_sorting_hotkeys(); + + uint32_t m_n_sorting_cols; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// View manager +/////////////////////////////////////////////////////////////////////////////// +class chisel_view_manager +{ +public: + void add(chisel_view_info* vinfo); + std::vector* get_views(); + uint32_t get_selected_view(); + void set_selected_view(std::string viewid); + size_t size() const + { + return m_views.size(); + } + chisel_view_info* at(uint32_t viewnum) + { + return &m_views[viewnum]; + } + const chisel_view_info* at(uint32_t viewnum) const + { + return &m_views[viewnum]; + } + +private: + void sort_views(); + + std::vector m_views; + + std::string m_selected_view_id; +}; diff --git a/userspace/sysdig/CMakeLists.txt b/userspace/sysdig/CMakeLists.txt index 90e2f52cf7..60f8072adc 100644 --- a/userspace/sysdig/CMakeLists.txt +++ b/userspace/sysdig/CMakeLists.txt @@ -21,6 +21,7 @@ if(NOT WIN32) endif() # NOT WIN32 include(zlib) +include(luajit) include_directories("${PROJECT_BINARY_DIR}/userspace/sinspui") include_directories(../sinspui) @@ -60,6 +61,17 @@ list(APPEND SOURCE_FILES_CSYSDIG ../sinspui/cursestable.cpp ../sinspui/cursesui.cpp) +list(APPEND CHISEL_SOURCES + ../chisel/chisel_api.cpp + ../chisel/chisel_fields_info.cpp + ../chisel/chisel_utils.cpp + ../chisel/chisel.cpp + ../chisel/chisel_viewinfo.cpp + ../chisel/chisel_table.cpp) + +list(APPEND SOURCE_FILES ${CHISEL_SOURCES}) +list(APPEND SOURCE_FILES_CSYSDIG ${CHISEL_SOURCES}) + add_executable(sysdig ${SOURCE_FILES}) add_executable(csysdig ${SOURCE_FILES_CSYSDIG}) @@ -68,25 +80,36 @@ if(USE_BUNDLED_DEPS) add_dependencies(csysdig njson) add_dependencies(sysdig yaml-cpp) add_dependencies(csysdig yaml-cpp) + add_dependencies(sysdig luajit) + add_dependencies(csysdig luajit) endif() target_include_directories( sysdig PUBLIC "${YAMLCPP_INCLUDE_DIR}" - "${NJSON_INCLUDE_DIR}") + "${NJSON_INCLUDE_DIR}" + "${LUAJIT_INCLUDE}" + ../chisel + .. + ) target_include_directories( csysdig PUBLIC "${YAMLCPP_INCLUDE_DIR}" - "${NJSON_INCLUDE_DIR}") + "${NJSON_INCLUDE_DIR}" + "${LUAJIT_INCLUDE}" + ../chisel + .. + ) if(NOT WIN32) include_directories(${PROJECT_BINARY_DIR}/driver/src) target_link_libraries(sysdig sinsp + "${LUAJIT_LIB}" "${YAMLCPP_LIB}") if(USE_BUNDLED_NCURSES) @@ -95,6 +118,7 @@ if(NOT WIN32) target_link_libraries(csysdig sinsp + "${LUAJIT_LIB}" "${CURSES_LIBRARIES}" "${YAMLCPP_LIB}") @@ -113,26 +137,26 @@ if(NOT WIN32) DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") else() - include(luajit) - add_definitions(-DNOCURSESUI) target_link_libraries(sysdig sinsp + "${LUAJIT_LIB}" "${YAMLCPP_LIB}") target_link_libraries(csysdig sinsp + "${LUAJIT_LIB}" "${YAMLCPP_LIB}") target_link_libraries(sysdig odbc32.lib odbccp32.lib Netapi32.lib Iphlpapi.lib) target_link_libraries(csysdig odbc32.lib odbccp32.lib Netapi32.lib Iphlpapi.lib) - add_custom_command(TARGET sysdig POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different - "${LUAJIT_SRC}/lua51.dll" - "${PROJECT_BINARY_DIR}/$(Configuration)/lua51.dll") + #add_custom_command(TARGET sysdig POST_BUILD + # COMMAND "${CMAKE_COMMAND}" -E copy_if_different + # "${LUAJIT_SRC}/lua51.dll" + # "${PROJECT_BINARY_DIR}/$(Configuration)/lua51.dll") add_custom_command(TARGET sysdig POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory @@ -161,9 +185,9 @@ else() DESTINATION programs COMPONENT "${SYSDIG_COMPONENT_NAME}") - install(FILES "${LUAJIT_SRC}/lua51.dll" - DESTINATION programs - COMPONENT "${SYSDIG_COMPONENT_NAME}") + #install(FILES "${LUAJIT_SRC}/lua51.dll" + # DESTINATION programs + # COMPONENT "${SYSDIG_COMPONENT_NAME}") endif()